From 7262fe1ddc8020b8b33bb63ee955e2650aa656b0 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 12 Aug 2022 00:09:16 -0400 Subject: [PATCH 01/56] Avoid url string allocation in WebProxy.IsMatchInBypassList (#73807) * Avoid url string allocation in WebProxy.IsMatchInBypassList * Update src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.cs Co-authored-by: Miha Zupan Co-authored-by: Miha Zupan --- .../src/System.Net.WebProxy.csproj | 1 + .../src/System/Net/WebProxy.cs | 26 ++++++++++++++----- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Net.WebProxy/src/System.Net.WebProxy.csproj b/src/libraries/System.Net.WebProxy/src/System.Net.WebProxy.csproj index 980f1552ded92f..fbe0e197d113c3 100644 --- a/src/libraries/System.Net.WebProxy/src/System.Net.WebProxy.csproj +++ b/src/libraries/System.Net.WebProxy/src/System.Net.WebProxy.csproj @@ -10,6 +10,7 @@ + diff --git a/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.cs b/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.cs index f2e8b5fdbb5748..9460000a28e752 100644 --- a/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.cs +++ b/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.cs @@ -1,7 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers; using System.Collections; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.Serialization; @@ -154,16 +156,26 @@ private bool IsMatchInBypassList(Uri input) { UpdateRegexList(canThrow: false); - if (_regexBypassList != null) + if (_regexBypassList is Regex[] bypassList) { - Span stackBuffer = stackalloc char[128]; - string matchUriString = input.IsDefaultPort ? - string.Create(null, stackBuffer, $"{input.Scheme}://{input.Host}") : - string.Create(null, stackBuffer, $"{input.Scheme}://{input.Host}:{(uint)input.Port}"); + bool isDefaultPort = input.IsDefaultPort; + int lengthRequired = input.Scheme.Length + 3 + input.Host.Length; + if (!isDefaultPort) + { + lengthRequired += 1 + 5; // 1 for ':' and 5 for max formatted length of a port (16 bit value) + } + + int charsWritten; + Span url = lengthRequired <= 256 ? stackalloc char[256] : new char[lengthRequired]; + bool formatted = isDefaultPort ? + url.TryWrite($"{input.Scheme}://{input.Host}", out charsWritten) : + url.TryWrite($"{input.Scheme}://{input.Host}:{(uint)input.Port}", out charsWritten); + Debug.Assert(formatted); + url = url.Slice(0, charsWritten); - foreach (Regex r in _regexBypassList) + foreach (Regex r in bypassList) { - if (r.IsMatch(matchUriString)) + if (r.IsMatch(url)) { return true; } From 831d0861edfed54f7abf8cf0b484144b450f67a0 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 12 Aug 2022 09:24:18 +0200 Subject: [PATCH 02/56] [main] Update dependencies from 8 repositories (#72934) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update dependencies from https://github.com/dotnet/runtime-assets build 20220726.1 Microsoft.DotNet.CilStrip.Sources , System.ComponentModel.TypeConverter.TestData , System.Drawing.Common.TestData , System.Formats.Tar.TestData , System.IO.Compression.TestData , System.IO.Packaging.TestData , System.Net.TestData , System.Private.Runtime.UnicodeData , System.Runtime.Numerics.TestData , System.Runtime.TimeZoneData , System.Security.Cryptography.X509Certificates.TestData , System.Text.RegularExpressions.TestData , System.Windows.Extensions.TestData From Version 7.0.0-beta.22361.2 -> To Version 7.0.0-beta.22376.1 * Update dependencies from https://github.com/dotnet/emsdk build 20220726.1 Microsoft.NET.Workload.Emscripten.Manifest-7.0.100 From Version 7.0.0-rc.1.22368.1 -> To Version 7.0.0-rc.1.22376.1 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20220728.2 Microsoft.CodeAnalysis.NetAnalyzers From Version 7.0.0-preview1.22373.2 -> To Version 7.0.0-preview1.22378.2 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20220729.1 Microsoft.CodeAnalysis.NetAnalyzers From Version 7.0.0-preview1.22373.2 -> To Version 7.0.0-preview1.22379.1 * Update dependencies from https://github.com/dotnet/runtime build 20220731.5 Microsoft.NET.Sdk.IL , Microsoft.NETCore.App.Runtime.win-x64 , Microsoft.NETCore.DotNetHost , Microsoft.NETCore.DotNetHostPolicy , Microsoft.NETCore.ILAsm , runtime.native.System.IO.Ports , System.Text.Json From Version 7.0.0-rc.1.22374.4 -> To Version 7.0.0-rc.1.22381.5 * Update dependencies from https://github.com/dotnet/icu build 20220802.1 Microsoft.NETCore.Runtime.ICU.Transport From Version 7.0.0-rc.1.22375.1 -> To Version 7.0.0-rc.1.22402.1 * Update dependencies from https://github.com/dotnet/xharness build 20220801.2 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Common , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 1.0.0-prerelease.22375.5 -> To Version 1.0.0-prerelease.22401.2 * Update dependencies from https://github.com/dotnet/emsdk build 20220801.1 Microsoft.NET.Workload.Emscripten.Manifest-7.0.100 From Version 7.0.0-rc.1.22368.1 -> To Version 7.0.0-rc.1.22401.1 * Update dependencies from https://github.com/dotnet/runtime-assets build 20220802.1 Microsoft.DotNet.CilStrip.Sources , System.ComponentModel.TypeConverter.TestData , System.Drawing.Common.TestData , System.Formats.Tar.TestData , System.IO.Compression.TestData , System.IO.Packaging.TestData , System.Net.TestData , System.Private.Runtime.UnicodeData , System.Runtime.Numerics.TestData , System.Runtime.TimeZoneData , System.Security.Cryptography.X509Certificates.TestData , System.Text.RegularExpressions.TestData , System.Windows.Extensions.TestData From Version 7.0.0-beta.22361.2 -> To Version 7.0.0-beta.22402.1 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20220803.2 Microsoft.CodeAnalysis.NetAnalyzers From Version 7.0.0-preview1.22373.2 -> To Version 7.0.0-preview1.22403.2 * Update dependencies from https://github.com/dotnet/msquic build 20220803.1 System.Net.MsQuic.Transport From Version 7.0.0-alpha.1.22371.1 -> To Version 7.0.0-alpha.1.22403.1 * Update dependencies from https://github.com/dotnet/llvm-project build 20220804.1 runtime.linux-arm64.Microsoft.NETCore.Runtime.ObjWriter , runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.ObjWriter , runtime.linux-musl-x64.Microsoft.NETCore.Runtime.ObjWriter , runtime.linux-x64.Microsoft.NETCore.Runtime.ObjWriter , runtime.osx.11.0-arm64.Microsoft.NETCore.Runtime.ObjWriter , runtime.osx.10.12-x64.Microsoft.NETCore.Runtime.ObjWriter , runtime.win-arm64.Microsoft.NETCore.Runtime.ObjWriter , runtime.win-x64.Microsoft.NETCore.Runtime.ObjWriter From Version 1.0.0-alpha.1.22364.1 -> To Version 1.0.0-alpha.1.22404.1 * Update dependencies from https://github.com/dotnet/xharness build 20220805.1 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Common , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 1.0.0-prerelease.22375.5 -> To Version 1.0.0-prerelease.22405.1 * Update dependencies from https://github.com/dotnet/emsdk build 20220805.3 Microsoft.NET.Workload.Emscripten.Manifest-7.0.100 From Version 7.0.0-rc.1.22368.1 -> To Version 7.0.0-rc.1.22405.3 * Update dependencies from https://github.com/dotnet/msquic build 20220805.1 System.Net.MsQuic.Transport From Version 7.0.0-alpha.1.22371.1 -> To Version 7.0.0-alpha.1.22405.1 * Update dependencies from https://github.com/dotnet/llvm-project build 20220806.4 runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools , runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools , runtime.osx.10.12-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.osx.10.12-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools , runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools From Version 11.1.0-alpha.1.22376.4 -> To Version 11.1.0-alpha.1.22406.4 * Update dependencies from https://github.com/dotnet/runtime build 20220807.4 Microsoft.NET.Sdk.IL , Microsoft.NETCore.App.Runtime.win-x64 , Microsoft.NETCore.ILAsm , runtime.native.System.IO.Ports , System.Text.Json From Version 7.0.0-rc.1.22374.4 -> To Version 7.0.0-rc.1.22407.4 * Update dependencies from https://github.com/dotnet/icu build 20220808.1 Microsoft.NETCore.Runtime.ICU.Transport From Version 7.0.0-rc.1.22375.1 -> To Version 7.0.0-rc.1.22408.1 * Update dependencies from https://github.com/dotnet/xharness build 20220808.1 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Common , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 1.0.0-prerelease.22375.5 -> To Version 1.0.0-prerelease.22408.1 * Update dependencies from https://github.com/dotnet/emsdk build 20220808.2 Microsoft.NET.Workload.Emscripten.Manifest-7.0.100 From Version 7.0.0-rc.1.22368.1 -> To Version 7.0.0-rc.1.22408.2 * Update dependencies from https://github.com/dotnet/llvm-project build 20220808.2 runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools , runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools , runtime.osx.10.12-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.osx.10.12-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools , runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools From Version 11.1.0-alpha.1.22376.4 -> To Version 11.1.0-alpha.1.22408.2 * Update dependencies from https://github.com/dotnet/runtime-assets build 20220809.1 Microsoft.DotNet.CilStrip.Sources , System.ComponentModel.TypeConverter.TestData , System.Drawing.Common.TestData , System.Formats.Tar.TestData , System.IO.Compression.TestData , System.IO.Packaging.TestData , System.Net.TestData , System.Private.Runtime.UnicodeData , System.Runtime.Numerics.TestData , System.Runtime.TimeZoneData , System.Security.Cryptography.X509Certificates.TestData , System.Text.RegularExpressions.TestData , System.Windows.Extensions.TestData From Version 7.0.0-beta.22361.2 -> To Version 7.0.0-beta.22409.1 * Use stable VS2022 build images llvm-project uses that one too now so we shouldn't hit the MSVC ABI issue * Update dependencies from https://github.com/dotnet/llvm-project build 20220811.1 runtime.linux-arm64.Microsoft.NETCore.Runtime.ObjWriter , runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.ObjWriter , runtime.linux-musl-x64.Microsoft.NETCore.Runtime.ObjWriter , runtime.linux-x64.Microsoft.NETCore.Runtime.ObjWriter , runtime.osx.11.0-arm64.Microsoft.NETCore.Runtime.ObjWriter , runtime.osx.10.12-x64.Microsoft.NETCore.Runtime.ObjWriter , runtime.win-arm64.Microsoft.NETCore.Runtime.ObjWriter , runtime.win-x64.Microsoft.NETCore.Runtime.ObjWriter From Version 1.0.0-alpha.1.22364.1 -> To Version 1.0.0-alpha.1.22411.1 * Update dependencies from https://github.com/dotnet/emsdk build 20220811.1 Microsoft.NET.Workload.Emscripten.Manifest-7.0.100 From Version 7.0.0-rc.1.22368.1 -> To Version 7.0.0-rc.1.22411.1 Co-authored-by: dotnet-maestro[bot] Co-authored-by: Larry Ewing Co-authored-by: Ankit Jain Co-authored-by: Alexander Köplinger --- .config/dotnet-tools.json | 2 +- eng/Version.Details.xml | 168 ++++++++++++++------------- eng/Versions.props | 80 ++++++------- eng/pipelines/common/xplat-setup.yml | 4 +- global.json | 2 +- 5 files changed, 133 insertions(+), 123 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 4ef4e71b3bff41..856cc4c5edb1ff 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "1.0.0-prerelease.22375.5", + "version": "1.0.0-prerelease.22408.1", "commands": [ "xharness" ] diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 29b21fd47da85d..1c412944b83883 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,52 +1,52 @@ - + https://github.com/dotnet/icu - d65ae2e51ad292901e89d06b31d31d7038c30c0f + c04d1340510269c5cd07a285abb097f587924d5b https://github.com/dotnet/msquic dc012a715ceb9b5d5258f2fda77520586af5a36a - + https://github.com/dotnet/emsdk - 7b2cd1ee7169143248a7da9fd749caf22affa624 + 216093204c415b6e37dfadfcbcf183881b443636 https://github.com/dotnet/wcf 7f504aabb1988e9a093c1e74d8040bd52feb2f01 - + https://github.com/dotnet/llvm-project - 754d13817d834b716d339183e21aac7d2489c496 + e73d65f0f80655b463162bd41a8365377ba6565d - + https://github.com/dotnet/llvm-project - 754d13817d834b716d339183e21aac7d2489c496 + e73d65f0f80655b463162bd41a8365377ba6565d - + https://github.com/dotnet/llvm-project - 754d13817d834b716d339183e21aac7d2489c496 + e73d65f0f80655b463162bd41a8365377ba6565d - + https://github.com/dotnet/llvm-project - 754d13817d834b716d339183e21aac7d2489c496 + e73d65f0f80655b463162bd41a8365377ba6565d - + https://github.com/dotnet/llvm-project - 754d13817d834b716d339183e21aac7d2489c496 + e73d65f0f80655b463162bd41a8365377ba6565d - + https://github.com/dotnet/llvm-project - 754d13817d834b716d339183e21aac7d2489c496 + e73d65f0f80655b463162bd41a8365377ba6565d - + https://github.com/dotnet/llvm-project - 754d13817d834b716d339183e21aac7d2489c496 + e73d65f0f80655b463162bd41a8365377ba6565d - + https://github.com/dotnet/llvm-project - 754d13817d834b716d339183e21aac7d2489c496 + e73d65f0f80655b463162bd41a8365377ba6565d https://github.com/dotnet/command-line-api @@ -126,121 +126,129 @@ https://github.com/dotnet/arcade 13b342360ba81ca3fdf911fda985dc8420d51627 - + https://github.com/dotnet/runtime-assets - 86e7816b9869ab19ee3f44aba7ac5f0ef7d04c8c + 77acd39a813579e1e9b12cd98466787e7f90e059 - + https://github.com/dotnet/runtime-assets - 86e7816b9869ab19ee3f44aba7ac5f0ef7d04c8c + 77acd39a813579e1e9b12cd98466787e7f90e059 - + https://github.com/dotnet/runtime-assets - 86e7816b9869ab19ee3f44aba7ac5f0ef7d04c8c + 77acd39a813579e1e9b12cd98466787e7f90e059 - + https://github.com/dotnet/runtime-assets - 86e7816b9869ab19ee3f44aba7ac5f0ef7d04c8c + 77acd39a813579e1e9b12cd98466787e7f90e059 - + https://github.com/dotnet/runtime-assets - 86e7816b9869ab19ee3f44aba7ac5f0ef7d04c8c + 77acd39a813579e1e9b12cd98466787e7f90e059 - + https://github.com/dotnet/runtime-assets - 86e7816b9869ab19ee3f44aba7ac5f0ef7d04c8c + 77acd39a813579e1e9b12cd98466787e7f90e059 - + https://github.com/dotnet/runtime-assets - 86e7816b9869ab19ee3f44aba7ac5f0ef7d04c8c + 77acd39a813579e1e9b12cd98466787e7f90e059 - + https://github.com/dotnet/runtime-assets - 86e7816b9869ab19ee3f44aba7ac5f0ef7d04c8c + 77acd39a813579e1e9b12cd98466787e7f90e059 - + https://github.com/dotnet/runtime-assets - 86e7816b9869ab19ee3f44aba7ac5f0ef7d04c8c + 77acd39a813579e1e9b12cd98466787e7f90e059 - + https://github.com/dotnet/runtime-assets - 86e7816b9869ab19ee3f44aba7ac5f0ef7d04c8c + 77acd39a813579e1e9b12cd98466787e7f90e059 - + https://github.com/dotnet/runtime-assets - 86e7816b9869ab19ee3f44aba7ac5f0ef7d04c8c + 77acd39a813579e1e9b12cd98466787e7f90e059 - + https://github.com/dotnet/runtime-assets - 86e7816b9869ab19ee3f44aba7ac5f0ef7d04c8c + 77acd39a813579e1e9b12cd98466787e7f90e059 - + https://github.com/dotnet/llvm-project - d87702da408b8aad0680f4f1c8b858c87d98f1df + 527481d107abeb3d6aa2881643a3efeda949f244 - + https://github.com/dotnet/llvm-project - d87702da408b8aad0680f4f1c8b858c87d98f1df + 527481d107abeb3d6aa2881643a3efeda949f244 - + https://github.com/dotnet/llvm-project - d87702da408b8aad0680f4f1c8b858c87d98f1df + 527481d107abeb3d6aa2881643a3efeda949f244 - + https://github.com/dotnet/llvm-project - d87702da408b8aad0680f4f1c8b858c87d98f1df + 527481d107abeb3d6aa2881643a3efeda949f244 - + https://github.com/dotnet/llvm-project - d87702da408b8aad0680f4f1c8b858c87d98f1df + 527481d107abeb3d6aa2881643a3efeda949f244 - + https://github.com/dotnet/llvm-project - d87702da408b8aad0680f4f1c8b858c87d98f1df + 527481d107abeb3d6aa2881643a3efeda949f244 - + https://github.com/dotnet/llvm-project - d87702da408b8aad0680f4f1c8b858c87d98f1df + 527481d107abeb3d6aa2881643a3efeda949f244 - + https://github.com/dotnet/llvm-project - d87702da408b8aad0680f4f1c8b858c87d98f1df + 527481d107abeb3d6aa2881643a3efeda949f244 - + https://github.com/dotnet/runtime - 214ca6db481923aa49bac2d2b75b9aca4041b304 + 0c5ca95fec9b6e2cc5e757ad36d0b094f81a59a6 - + https://github.com/dotnet/runtime - 214ca6db481923aa49bac2d2b75b9aca4041b304 + 508fef51e841aa16ffed1aae32bf4793a2cea363 - + https://github.com/dotnet/runtime - 214ca6db481923aa49bac2d2b75b9aca4041b304 + 508fef51e841aa16ffed1aae32bf4793a2cea363 - + https://github.com/dotnet/runtime - 214ca6db481923aa49bac2d2b75b9aca4041b304 + 0c5ca95fec9b6e2cc5e757ad36d0b094f81a59a6 - + https://github.com/dotnet/runtime - 214ca6db481923aa49bac2d2b75b9aca4041b304 + 0c5ca95fec9b6e2cc5e757ad36d0b094f81a59a6 + + + https://github.com/dotnet/runtime + 0c5ca95fec9b6e2cc5e757ad36d0b094f81a59a6 + + + https://github.com/dotnet/runtime + 0c5ca95fec9b6e2cc5e757ad36d0b094f81a59a6 https://github.com/dotnet/linker f09bacf09ef10b61cf9f19825f8782171a816dab - + https://github.com/dotnet/xharness - d43453b9981bfd8705025250caff58d18e2abc7e + c98b5a327992608c54e50d48e24bb4dde0fa853e - + https://github.com/dotnet/xharness - d43453b9981bfd8705025250caff58d18e2abc7e + c98b5a327992608c54e50d48e24bb4dde0fa853e - + https://github.com/dotnet/xharness - d43453b9981bfd8705025250caff58d18e2abc7e + c98b5a327992608c54e50d48e24bb4dde0fa853e https://github.com/dotnet/arcade @@ -266,13 +274,13 @@ https://github.com/dotnet/hotreload-utils c6dfd18158f2290855b98e71734f5f169cf1dfe7 - + https://github.com/dotnet/runtime-assets - 86e7816b9869ab19ee3f44aba7ac5f0ef7d04c8c + 77acd39a813579e1e9b12cd98466787e7f90e059 - + https://github.com/dotnet/roslyn-analyzers - 4dce248f4ca16d6fa04be777c07d2a7a5f117370 + 793113a41d7f21b03470521bf48438f2abd9b12f https://github.com/dotnet/sdk diff --git a/eng/Versions.props b/eng/Versions.props index c9c2e20e739da0..06c3924253c555 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -44,7 +44,7 @@ 4.3.0-2.final 4.3.0-2.final 4.3.0-2.final - 7.0.0-preview1.22373.2 + 7.0.0-preview1.22403.2 4.3.0-2.final 6.0.0-preview.1.102 - 7.0.0-rc.1.22374.4 + 7.0.0-rc.1.22407.4 + 7.0.0-rc.1.22381.5 + 7.0.0-rc.1.22381.5 6.0.0 - 7.0.0-rc.1.22374.4 - 1.0.0-alpha.1.22364.1 - 1.0.0-alpha.1.22364.1 - 1.0.0-alpha.1.22364.1 - 1.0.0-alpha.1.22364.1 - 1.0.0-alpha.1.22364.1 - 1.0.0-alpha.1.22364.1 - 1.0.0-alpha.1.22364.1 - 1.0.0-alpha.1.22364.1 + 7.0.0-rc.1.22407.4 + 1.0.0-alpha.1.22411.1 + 1.0.0-alpha.1.22411.1 + 1.0.0-alpha.1.22411.1 + 1.0.0-alpha.1.22411.1 + 1.0.0-alpha.1.22411.1 + 1.0.0-alpha.1.22411.1 + 1.0.0-alpha.1.22411.1 + 1.0.0-alpha.1.22411.1 6.0.0 1.1.1 @@ -111,25 +113,25 @@ 5.0.0 5.0.0 4.9.0 - 7.0.0-rc.1.22374.4 + 7.0.0-rc.1.22407.4 6.0.0 4.5.4 4.5.0 - 7.0.0-rc.1.22374.4 + 7.0.0-rc.1.22407.4 - 7.0.0-beta.22361.2 - 7.0.0-beta.22361.2 - 7.0.0-beta.22361.2 - 7.0.0-beta.22361.2 - 7.0.0-beta.22361.2 - 7.0.0-beta.22361.2 - 7.0.0-beta.22361.2 - 7.0.0-beta.22361.2 - 7.0.0-beta.22361.2 - 7.0.0-beta.22361.2 - 7.0.0-beta.22361.2 - 7.0.0-beta.22361.2 - 7.0.0-beta.22361.2 + 7.0.0-beta.22409.1 + 7.0.0-beta.22409.1 + 7.0.0-beta.22409.1 + 7.0.0-beta.22409.1 + 7.0.0-beta.22409.1 + 7.0.0-beta.22409.1 + 7.0.0-beta.22409.1 + 7.0.0-beta.22409.1 + 7.0.0-beta.22409.1 + 7.0.0-beta.22409.1 + 7.0.0-beta.22409.1 + 7.0.0-beta.22409.1 + 7.0.0-beta.22409.1 1.0.0-prerelease.22375.7 1.0.0-prerelease.22375.7 @@ -150,9 +152,9 @@ 1.1.0 17.4.0-preview-20220707-01 - 1.0.0-prerelease.22375.5 - 1.0.0-prerelease.22375.5 - 1.0.0-prerelease.22375.5 + 1.0.0-prerelease.22408.1 + 1.0.0-prerelease.22408.1 + 1.0.0-prerelease.22408.1 1.1.0-alpha.0.22362.1 2.4.2-pre.22 0.12.0-pre.20 @@ -172,21 +174,21 @@ 7.0.100-1.22377.1 $(MicrosoftNETILLinkTasksVersion) - 7.0.0-rc.1.22375.1 + 7.0.0-rc.1.22408.1 2.1 7.0.0-alpha.1.22406.1 - 11.1.0-alpha.1.22376.4 - 11.1.0-alpha.1.22376.4 - 11.1.0-alpha.1.22376.4 - 11.1.0-alpha.1.22376.4 - 11.1.0-alpha.1.22376.4 - 11.1.0-alpha.1.22376.4 - 11.1.0-alpha.1.22376.4 - 11.1.0-alpha.1.22376.4 + 11.1.0-alpha.1.22408.2 + 11.1.0-alpha.1.22408.2 + 11.1.0-alpha.1.22408.2 + 11.1.0-alpha.1.22408.2 + 11.1.0-alpha.1.22408.2 + 11.1.0-alpha.1.22408.2 + 11.1.0-alpha.1.22408.2 + 11.1.0-alpha.1.22408.2 - 7.0.0-rc.1.22368.1 + 7.0.0-rc.1.22411.1 $(MicrosoftNETWorkloadEmscriptenManifest70100Version) 1.1.87-gba258badda diff --git a/eng/pipelines/common/xplat-setup.yml b/eng/pipelines/common/xplat-setup.yml index 9d45d7cddf15d8..83a0efd39874f0 100644 --- a/eng/pipelines/common/xplat-setup.yml +++ b/eng/pipelines/common/xplat-setup.yml @@ -150,12 +150,12 @@ jobs: # Official Build Windows Pool ${{ if and(eq(parameters.osGroup, 'windows'), ne(variables['System.TeamProject'], 'public')) }}: name: NetCore1ESPool-Internal - demands: ImageOverride -equals windows.vs2022preview.amd64 + demands: ImageOverride -equals windows.vs2022.amd64 # Public Windows Build Pool ${{ if and(or(eq(parameters.osGroup, 'windows'), eq(parameters.jobParameters.hostedOs, 'windows')), eq(variables['System.TeamProject'], 'public')) }}: name: NetCore1ESPool-Public - demands: ImageOverride -equals windows.vs2022preview.amd64.open + demands: ImageOverride -equals windows.vs2022.amd64.open ${{ if eq(parameters.helixQueuesTemplate, '') }}: diff --git a/global.json b/global.json index 81d425f8329cc2..31b8c266d8aabc 100644 --- a/global.json +++ b/global.json @@ -13,6 +13,6 @@ "Microsoft.DotNet.SharedFramework.Sdk": "7.0.0-beta.22408.3", "Microsoft.Build.NoTargets": "3.5.0", "Microsoft.Build.Traversal": "3.1.6", - "Microsoft.NET.Sdk.IL": "7.0.0-rc.1.22374.4" + "Microsoft.NET.Sdk.IL": "7.0.0-rc.1.22407.4" } } From 6a2c63f95561bf6c943781e8a5aaff562b06e2a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 12 Aug 2022 16:53:01 +0900 Subject: [PATCH 03/56] Fix repro project for Release builds (#73836) --- .../tools/aot/ILCompiler/reproNative/reproNative.vcxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler/reproNative/reproNative.vcxproj b/src/coreclr/tools/aot/ILCompiler/reproNative/reproNative.vcxproj index 86cfe3c795f883..2266c102c8ceaf 100644 --- a/src/coreclr/tools/aot/ILCompiler/reproNative/reproNative.vcxproj +++ b/src/coreclr/tools/aot/ILCompiler/reproNative/reproNative.vcxproj @@ -121,7 +121,7 @@ true true true - $(ArtifactsRoot)bin\repro\x64\Release\repro.obj;$(Win32SDKLibs);%(AdditionalDependencies);$(ArtifactsRoot)bin\coreclr\windows.x64.Release\aotsdk\Runtime.WorkstationGC.lib$(ArtifactsRoot)bin\coreclr\windows.x64.Release\aotsdk\System.Globalization.Native.Aot.lib + $(ArtifactsRoot)bin\repro\x64\Release\repro.obj;$(Win32SDKLibs);%(AdditionalDependencies);$(ArtifactsRoot)bin\coreclr\windows.x64.Release\aotsdk\Runtime.WorkstationGC.lib;$(ArtifactsRoot)bin\coreclr\windows.x64.Release\aotsdk\System.Globalization.Native.Aot.lib @@ -134,4 +134,4 @@ - \ No newline at end of file + From c55d76d43c6c451548590c479ed06d1566838f8b Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Fri, 12 Aug 2022 12:58:48 +0300 Subject: [PATCH 04/56] Port distributed transaction support for Windows (#72051) Closes #715 --- .../src/Interop/Windows/Interop.Libraries.cs | 1 + .../src/Resources/Strings.resx | 405 ++++- .../src/System.Transactions.Local.csproj | 75 +- .../Transactions/CommittableTransaction.cs | 9 +- .../Transactions/DependentTransaction.cs | 2 +- .../Transactions/DistributedTransaction.cs | 166 -- .../DtcInterfaces/IPrepareInfo.cs | 16 + .../DtcInterfaces/IResourceManager.cs | 30 + .../DtcInterfaces/IResourceManagerFactory2.cs | 24 + .../DtcInterfaces/IResourceManagerSink.cs | 13 + .../DtcProxyShim/DtcInterfaces/ITmNodeName.cs | 15 + .../DtcInterfaces/ITransaction.cs | 19 + .../DtcInterfaces/ITransactionCloner.cs | 14 + .../DtcInterfaces/ITransactionDispenser.cs | 22 + .../ITransactionEnlistmentAsync.cs | 18 + .../DtcInterfaces/ITransactionExport.cs | 20 + .../ITransactionExportFactory.cs | 19 + .../DtcInterfaces/ITransactionImport.cs | 18 + .../ITransactionImportWhereabouts.cs | 18 + .../DtcInterfaces/ITransactionOptions.cs | 16 + .../ITransactionOutcomeEvents.cs | 20 + .../ITransactionPhase0EnlistmentAsync.cs | 21 + .../ITransactionPhase0Factory.cs | 15 + .../ITransactionPhase0NotifyAsync.cs | 15 + .../DtcInterfaces/ITransactionReceiver.cs | 26 + .../ITransactionReceiverFactory.cs | 14 + .../ITransactionResourceAsync.cs | 25 + .../DtcInterfaces/ITransactionTransmitter.cs | 27 + .../ITransactionTransmitterFactory.cs | 14 + .../ITransactionVoterBallotAsync2.cs | 13 + .../ITransactionVoterFactory2.cs | 16 + .../ITransactionVoterNotifyAsync2.cs | 22 + .../DtcProxyShim/DtcProxyShimFactory.cs | 353 ++++ .../DtcProxyShim/EnlistmentNotifyShim.cs | 75 + .../DtcProxyShim/EnlistmentShim.cs | 82 + .../System/Transactions/DtcProxyShim/Guids.cs | 20 + .../Transactions/DtcProxyShim/NativeEnums.cs | 143 ++ .../DtcProxyShim/NotificationShimBase.cs | 24 + .../Transactions/DtcProxyShim/OletxHelper.cs | 55 + .../DtcProxyShim/OletxXactTransInfo.cs | 19 + .../DtcProxyShim/Phase0NotifyShim.cs | 27 + .../Transactions/DtcProxyShim/Phase0Shim.cs | 41 + .../DtcProxyShim/ResourceManagerNotifyShim.cs | 23 + .../DtcProxyShim/ResourceManagerShim.cs | 61 + .../DtcProxyShim/TransactionNotifyShim.cs | 44 + .../DtcProxyShim/TransactionOutcome.cs | 11 + .../DtcProxyShim/TransactionShim.cs | 98 ++ .../DtcProxyShim/VoterNotifyShim.cs | 51 + .../Transactions/DtcProxyShim/VoterShim.cs | 28 + .../Transactions/DtcProxyShim/Xactopt.cs | 20 + .../Transactions/DurableEnlistmentState.cs | 4 +- .../src/System/Transactions/Enlistment.cs | 18 +- .../System/Transactions/IDtcTransaction.cs | 19 + .../Transactions/InternalTransaction.cs | 15 +- .../Transactions/NonWindowsUnsupported.cs | 154 ++ .../Oletx/DtcTransactionManager.cs | 132 ++ .../Oletx/OletxCommittableTransaction.cs | 57 + .../Oletx/OletxDependentTransaction.cs | 64 + .../Transactions/Oletx/OletxEnlistment.cs | 1212 +++++++++++++ .../Oletx/OletxResourceManager.cs | 896 ++++++++++ .../Transactions/Oletx/OletxTransaction.cs | 1373 +++++++++++++++ .../Oletx/OletxTransactionManager.cs | 804 +++++++++ .../Oletx/OletxVolatileEnlistment.cs | 1508 +++++++++++++++++ .../src/System/Transactions/Transaction.cs | 14 +- .../System/Transactions/TransactionInterop.cs | 355 +++- .../TransactionInteropNonWindows.cs | 257 +++ .../System/Transactions/TransactionManager.cs | 66 +- .../System/Transactions/TransactionScope.cs | 2 +- .../System/Transactions/TransactionState.cs | 89 +- .../Transactions/TransactionsEtwProvider.cs | 554 ++++-- .../Transactions/VolatileEnlistmentState.cs | 10 +- .../tests/LTMEnlistmentTests.cs | 142 +- .../tests/NonMsdtcPromoterTests.cs | 125 -- .../tests/OleTxNonWindowsUnsupportedTests.cs | 64 + .../tests/OleTxTests.cs | 477 ++++++ .../System.Transactions.Local.Tests.csproj | 5 + .../tests/TestEnlistments.cs | 107 +- 77 files changed, 10091 insertions(+), 755 deletions(-) delete mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DistributedTransaction.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IPrepareInfo.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IResourceManager.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IResourceManagerFactory2.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IResourceManagerSink.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITmNodeName.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransaction.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionCloner.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionDispenser.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionEnlistmentAsync.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionExport.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionExportFactory.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionImport.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionImportWhereabouts.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionOptions.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionOutcomeEvents.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionPhase0EnlistmentAsync.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionPhase0Factory.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionPhase0NotifyAsync.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionReceiver.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionReceiverFactory.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionResourceAsync.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionTransmitter.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionTransmitterFactory.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionVoterBallotAsync2.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionVoterFactory2.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionVoterNotifyAsync2.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcProxyShimFactory.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/EnlistmentNotifyShim.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/EnlistmentShim.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/Guids.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/NativeEnums.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/NotificationShimBase.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/OletxHelper.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/OletxXactTransInfo.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/Phase0NotifyShim.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/Phase0Shim.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/ResourceManagerNotifyShim.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/ResourceManagerShim.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/TransactionNotifyShim.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/TransactionOutcome.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/TransactionShim.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/VoterNotifyShim.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/VoterShim.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/Xactopt.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/IDtcTransaction.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/NonWindowsUnsupported.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/DtcTransactionManager.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxCommittableTransaction.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxDependentTransaction.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxEnlistment.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxResourceManager.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxTransaction.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxTransactionManager.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxVolatileEnlistment.cs create mode 100644 src/libraries/System.Transactions.Local/src/System/Transactions/TransactionInteropNonWindows.cs create mode 100644 src/libraries/System.Transactions.Local/tests/OleTxNonWindowsUnsupportedTests.cs create mode 100644 src/libraries/System.Transactions.Local/tests/OleTxTests.cs diff --git a/src/libraries/Common/src/Interop/Windows/Interop.Libraries.cs b/src/libraries/Common/src/Interop/Windows/Interop.Libraries.cs index 81b31db1cff99a..bf53b2717ea2ec 100644 --- a/src/libraries/Common/src/Interop/Windows/Interop.Libraries.cs +++ b/src/libraries/Common/src/Interop/Windows/Interop.Libraries.cs @@ -46,5 +46,6 @@ internal static partial class Libraries internal const string MsQuic = "msquic.dll"; internal const string HostPolicy = "hostpolicy.dll"; internal const string Ucrtbase = "ucrtbase.dll"; + internal const string Xolehlp = "xolehlp.dll"; } } diff --git a/src/libraries/System.Transactions.Local/src/Resources/Strings.resx b/src/libraries/System.Transactions.Local/src/Resources/Strings.resx index a11f02e372d5c5..4880e4f544a4b8 100644 --- a/src/libraries/System.Transactions.Local/src/Resources/Strings.resx +++ b/src/libraries/System.Transactions.Local/src/Resources/Strings.resx @@ -1,4 +1,64 @@ - + + + @@ -57,41 +117,310 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - TransactionScope with TransactionScopeAsyncFlowOption.Enabled option is not supported when the TransactionScope is used within Enterprise Service context with Automatic or Full EnterpriseServicesInteropOption enabled in parent scope. - The IAsyncResult parameter must be the same parameter returned by BeginCommit. - Resource Manager Identifiers cannot be Guid.Empty. - Transactions with IsolationLevel Snapshot cannot be promoted. - Current cannot be set directly when Com+ Interop is enabled. - The delegate for an external current can only be set once. - The current TransactionScope is already complete. You should dispose the TransactionScope. - The operation is not valid for the current state of the enlistment. - Com+ Interop features cannot be supported. - Internal Error - The argument is invalid. - The specified IPromotableSinglePhaseNotification is not the same as the one provided to EnlistPromotableSinglePhase. - Transaction Manager in the Recovery Information does not match the configured transaction manager. - A TransactionScope must be disposed on the same thread that it was created. - There was an error promoting the transaction to a distributed transaction. - The Promote method returned an invalid value for the distributed transaction. - The transaction returned from Promote already exists as a distributed transaction. - It is too late to add enlistments to this transaction. - Transaction Timeout - The transaction has aborted. - DependentTransaction.Complete or CommittableTransaction.Commit has already been called for this transaction. - The transaction is in doubt. - Communication with the underlying transaction manager has failed. - The current TransactionScope is already complete. - Transaction.Current has changed inside of the TransactionScope. - TransactionScope nested incorrectly. - The transaction specified for TransactionScope has a different IsolationLevel than the value requested for the scope. - TransactionScope timer object is invalid. - The operation is not valid for the state of the transaction. - There was an unexpected failure of QueueUserWorkItem. - There was an unexpected failure of a timer. - The RecoveryInformation provided is not recognized by this version of System.Transactions. - Volatile enlistments do not generate recovery information. - {0} Distributed Transaction ID is {1} - The specified PromoterType is invalid. - There is a promotable enlistment for the transaction which has a PromoterType value that is not recognized by System.Transactions. {0} - This platform does not support distributed transactions. + + TransactionScope with TransactionScopeAsyncFlowOption.Enabled option is not supported when the TransactionScope is used within Enterprise Service context with Automatic or Full EnterpriseServicesInteropOption enabled in parent scope. + + + The IAsyncResult parameter must be the same parameter returned by BeginCommit. + + + Resource Manager Identifiers cannot be Guid.Empty. + + + Transactions with IsolationLevel Snapshot cannot be promoted. + + + Current cannot be set directly when Com+ Interop is enabled. + + + The delegate for an external current can only be set once. + + + The current TransactionScope is already complete. You should dispose the TransactionScope. + + + The operation is not valid for the current state of the enlistment. + + + Com+ Interop features cannot be supported. + + + Internal Error + + + The argument is invalid. + + + The specified IPromotableSinglePhaseNotification is not the same as the one provided to EnlistPromotableSinglePhase. + + + Transaction Manager in the Recovery Information does not match the configured transaction manager. + + + A TransactionScope must be disposed on the same thread that it was created. + + + There was an error promoting the transaction to a distributed transaction. + + + The Promote method returned an invalid value for the distributed transaction. + + + The transaction returned from Promote already exists as a distributed transaction. + + + It is too late to add enlistments to this transaction. + + + Transaction Timeout + + + The transaction has aborted. + + + DependentTransaction.Complete or CommittableTransaction.Commit has already been called for this transaction. + + + The transaction is in doubt. + + + Communication with the underlying transaction manager has failed. + + + The current TransactionScope is already complete. + + + Transaction.Current has changed inside of the TransactionScope. + + + TransactionScope nested incorrectly. + + + The transaction specified for TransactionScope has a different IsolationLevel than the value requested for the scope. + + + TransactionScope timer object is invalid. + + + The operation is not valid for the state of the transaction. + + + There was an unexpected failure of QueueUserWorkItem. + + + There was an unexpected failure of a timer. + + + The RecoveryInformation provided is not recognized by this version of System.Transactions. + + + Volatile enlistments do not generate recovery information. + + + {0} Distributed Transaction ID is {1} + + + The specified PromoterType is invalid. + + + There is a promotable enlistment for the transaction which has a PromoterType value that is not recognized by System.Transactions. {0} + + + This platform does not support distributed transactions. + + + [Distributed] + + + Configured DefaultTimeout is greater than configured DefaultMaximumTimeout - adjusting down to DefaultMaximumTimeout + + + Method Entered + + + Method Exited + + + The operation is invalid because the document is empty. + + + Cannot add an element to a closed document. + + + The text node already has a value. + + + Enlistment Created + + + Transaction Promoted + + + Enlistment Notification Call + + + Enlistment Callback Positive + + + Enlistment Callback Negative + + + Cannot close an element on a closed document. + + + Internal Error + + + InvalidOperationException Thrown + + + Exception Consumed + + + TransactionException Thrown + + + Transaction Deserialized + + + Transaction Serialized + + + Transaction Manager Instance Created + + + TransactionManager.Reenlist Called + + + Transaction Created + + + CommittableTransaction.Commit Called + + + Transaction Committed + + + Transaction Aborted + + + Transaction InDoubt + + + TransactionScope Created + + + TransactionScope Disposed + + + MSDTC Proxy cannot support specification of different node names in the same process. + + + Cannot support specification of node name for the distributed transaction manager through System.Transactions due to MSDTC Proxy version. + + + Failed to create trace source. + + + Failed to initialize trace source. + + + TransactionManager.RecoveryComplete Called + + + Clone Created + + + Dependent Clone Completed + + + Dependent Clone Created + + + TransactionScope Timeout + + + Transaction.Rollback Called + + + Trace Event Type: {0}\nTrace Code: {1}\nTrace Description {2}\nObject: {3} + + + Internal Error - Unexpected transaction status value in enlistment constructor. + + + Unable to deserialize the transaction. + + + Internal Error - Unable to deserialize the transaction. + + + The transaction has already been implicitly or explicitly committed or aborted. + + + Process Name: {0}\nProcess Id: {1}\nCode: {2}\nDescription: {3} + + + Source: {0} + + + Exception: {0} + + + Event ID: {0} + + + Other information : {0} + + + TransactionScope Current Transaction Changed + + + TransactionScope Nested Incorrectly + + + TransactionScope Incomplete + + + Distributed transaction manager is unable to create the NotificationShimFactory object. + + + Unable to obtain the transaction identifier. + + + Calling TransactionManager.Reenlist is not allowed after TransactionManager.RecoveryComplete is called for a given resource manager identifier. + + + RecoveryComplete must not be called twice by the same resource manager identifier instance. + + + MSDTC Transaction Manager is unavailable. + + + Network access for Distributed Transaction Manager (MSDTC) has been disabled. Please enable DTC for network access in the security configuration for MSDTC using the Component Services Administrative tool. + + + [Lightweight] + + + AppDomain unloading. + + + Unhandled Exception + + + The resourceManagerIdentifier does not match the contents of the specified recovery information. + + + The distributed transaction manager does not allow any more durable enlistments on the transaction. + + + Failed to trace event: {0}. + + + [Base] + + + Distributed transactions are currently unsupported on 32-bit version of the .NET runtime. + \ No newline at end of file diff --git a/src/libraries/System.Transactions.Local/src/System.Transactions.Local.csproj b/src/libraries/System.Transactions.Local/src/System.Transactions.Local.csproj index 9b7d1861a67279..86a68905ab6ed6 100644 --- a/src/libraries/System.Transactions.Local/src/System.Transactions.Local.csproj +++ b/src/libraries/System.Transactions.Local/src/System.Transactions.Local.csproj @@ -2,17 +2,20 @@ true true - $(NetCoreAppCurrent) + $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent) + CA1805;IDE0059;CS1591 + $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) + false - + @@ -25,7 +28,6 @@ - @@ -40,7 +42,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -48,5 +116,6 @@ + diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/CommittableTransaction.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/CommittableTransaction.cs index 20fc0d0e4eab8e..dfeace8bd4a531 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/CommittableTransaction.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/CommittableTransaction.cs @@ -5,6 +5,8 @@ using System.Runtime.Versioning; using System.Threading; +#pragma warning disable CS1591 + namespace System.Transactions { [UnsupportedOSPlatform("browser")] @@ -37,7 +39,7 @@ internal CommittableTransaction(IsolationLevel isoLevel, TimeSpan timeout) : bas TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.TransactionCreated(this, "CommittableTransaction"); + etwLog.TransactionCreated(TraceSourceType.TraceSourceLtm, TransactionTraceId, "CommittableTransaction"); } } @@ -47,7 +49,7 @@ public IAsyncResult BeginCommit(AsyncCallback? asyncCallback, object? asyncState if (etwLog.IsEnabled()) { etwLog.MethodEnter(TraceSourceType.TraceSourceLtm, this); - etwLog.TransactionCommit(this, "CommittableTransaction"); + etwLog.TransactionCommit(TraceSourceType.TraceSourceLtm, TransactionTraceId, "CommittableTransaction"); } ObjectDisposedException.ThrowIf(Disposed, this); @@ -81,7 +83,7 @@ public void Commit() if (etwLog.IsEnabled()) { etwLog.MethodEnter(TraceSourceType.TraceSourceLtm, this); - etwLog.TransactionCommit(this, "CommittableTransaction"); + etwLog.TransactionCommit(TraceSourceType.TraceSourceLtm, TransactionTraceId, "CommittableTransaction"); } ObjectDisposedException.ThrowIf(Disposed, this); @@ -113,7 +115,6 @@ public void Commit() { etwLog.MethodExit(TraceSourceType.TraceSourceLtm, this); } - } internal override void InternalDispose() diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DependentTransaction.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DependentTransaction.cs index 7f11478f773a32..9a5c890a076c6c 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/DependentTransaction.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DependentTransaction.cs @@ -61,7 +61,7 @@ public void Complete() if (etwLog.IsEnabled()) { - etwLog.TransactionDependentCloneComplete(this, "DependentTransaction"); + etwLog.TransactionDependentCloneComplete(TraceSourceType.TraceSourceLtm, TransactionTraceId, "DependentTransaction"); etwLog.MethodExit(TraceSourceType.TraceSourceLtm, this); } } diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DistributedTransaction.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DistributedTransaction.cs deleted file mode 100644 index 59d7fea1829f78..00000000000000 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/DistributedTransaction.cs +++ /dev/null @@ -1,166 +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.Runtime.Serialization; - -namespace System.Transactions.Distributed -{ - internal sealed class DistributedTransactionManager - { - internal object? NodeName { get; set; } - - internal static IPromotedEnlistment ReenlistTransaction(Guid resourceManagerIdentifier, byte[] resourceManagerRecoveryInformation, RecoveringInternalEnlistment internalEnlistment) - { - throw DistributedTransaction.NotSupported(); - } - - internal static DistributedCommittableTransaction CreateTransaction(TransactionOptions options) - { - throw DistributedTransaction.NotSupported(); - } - - internal static void ResourceManagerRecoveryComplete(Guid resourceManagerIdentifier) - { - throw DistributedTransaction.NotSupported(); - } - - internal static byte[] GetWhereabouts() - { - throw DistributedTransaction.NotSupported(); - } - - internal static Transaction GetTransactionFromDtcTransaction(IDtcTransaction transactionNative) - { - throw DistributedTransaction.NotSupported(); - } - - internal static DistributedTransaction GetTransactionFromExportCookie(byte[] cookie, Guid txId) - { - throw DistributedTransaction.NotSupported(); - } - - internal static DistributedTransaction GetDistributedTransactionFromTransmitterPropagationToken(byte[] propagationToken) - { - throw DistributedTransaction.NotSupported(); - } - } - - /// - /// A Transaction object represents a single transaction. It is created by TransactionManager - /// objects through CreateTransaction or through deserialization. Alternatively, the static Create - /// methods provided, which creates a "default" TransactionManager and requests that it create - /// a new transaction with default values. A transaction can only be committed by - /// the client application that created the transaction. If a client application wishes to allow - /// access to the transaction by multiple threads, but wants to prevent those other threads from - /// committing the transaction, the application can make a "clone" of the transaction. Transaction - /// clones have the same capabilities as the original transaction, except for the ability to commit - /// the transaction. - /// - internal class DistributedTransaction : ISerializable, IObjectReference - { - internal DistributedTransaction() - { - } - - protected DistributedTransaction(SerializationInfo serializationInfo, StreamingContext context) - { - //if (serializationInfo == null) - //{ - // throw new ArgumentNullException(nameof(serializationInfo)); - //} - - //throw NotSupported(); - throw new PlatformNotSupportedException(); - } - - internal Exception? InnerException { get; set; } - internal Guid Identifier { get; set; } - internal RealDistributedTransaction? RealTransaction { get; set; } - internal TransactionTraceIdentifier TransactionTraceId { get; set; } - internal IsolationLevel IsolationLevel { get; set; } - internal Transaction? SavedLtmPromotedTransaction { get; set; } - - internal static IPromotedEnlistment EnlistVolatile(InternalEnlistment internalEnlistment, EnlistmentOptions enlistmentOptions) - { - throw NotSupported(); - } - - internal static IPromotedEnlistment EnlistDurable(Guid resourceManagerIdentifier, DurableInternalEnlistment internalEnlistment, bool v, EnlistmentOptions enlistmentOptions) - { - throw NotSupported(); - } - - internal static void Rollback() - { - throw NotSupported(); - } - - internal static DistributedDependentTransaction DependentClone(bool v) - { - throw NotSupported(); - } - - internal static IPromotedEnlistment EnlistVolatile(VolatileDemultiplexer volatileDemux, EnlistmentOptions enlistmentOptions) - { - throw NotSupported(); - } - - internal static byte[] GetExportCookie(byte[] whereaboutsCopy) - { - throw NotSupported(); - } - - public object GetRealObject(StreamingContext context) - { - throw NotSupported(); - } - - internal static byte[] GetTransmitterPropagationToken() - { - throw NotSupported(); - } - - internal static IDtcTransaction GetDtcTransaction() - { - throw NotSupported(); - } - - void ISerializable.GetObjectData(SerializationInfo serializationInfo, StreamingContext context) - { - //if (serializationInfo == null) - //{ - // throw new ArgumentNullException(nameof(serializationInfo)); - //} - - //throw NotSupported(); - - throw new PlatformNotSupportedException(); - } - - internal static Exception NotSupported() - { - return new PlatformNotSupportedException(SR.DistributedNotSupported); - } - - internal sealed class RealDistributedTransaction - { - internal InternalTransaction? InternalTransaction { get; set; } - } - } - - internal sealed class DistributedDependentTransaction : DistributedTransaction - { - internal static void Complete() - { - throw NotSupported(); - } - } - - internal sealed class DistributedCommittableTransaction : DistributedTransaction - { - internal static void BeginCommit(InternalTransaction tx) - { - throw NotSupported(); - } - } -} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IPrepareInfo.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IPrepareInfo.cs new file mode 100644 index 00000000000000..0b6c1158f59b31 --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IPrepareInfo.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +namespace System.Transactions.DtcProxyShim.DtcInterfaces; + +// https://docs.microsoft.com/previous-versions/windows/desktop/ms686533(v=vs.85) +[ComImport, Guid("80c7bfd0-87ee-11ce-8081-0080c758527e"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +internal interface IPrepareInfo +{ + void GetPrepareInfoSize(out uint pcbPrepInfo); + + void GetPrepareInfo([MarshalAs(UnmanagedType.LPArray), Out] byte[] pPrepInfo); +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IResourceManager.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IResourceManager.cs new file mode 100644 index 00000000000000..4cec70b044228c --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IResourceManager.cs @@ -0,0 +1,30 @@ +// 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; + +namespace System.Transactions.DtcProxyShim.DtcInterfaces; + +// https://docs.microsoft.com/previous-versions/windows/desktop/ms681790(v=vs.85) +[ComImport, Guid(Guids.IID_IResourceManager), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +internal interface IResourceManager +{ + internal void Enlist( + [MarshalAs(UnmanagedType.Interface)] ITransaction pTransaction, + [MarshalAs(UnmanagedType.Interface)] ITransactionResourceAsync pRes, + out Guid pUOW, + out OletxTransactionIsolationLevel pisoLevel, + [MarshalAs(UnmanagedType.Interface)] out ITransactionEnlistmentAsync ppEnlist); + + internal void Reenlist( + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] pPrepInfo, + uint cbPrepInfom, + uint lTimeout, + [MarshalAs(UnmanagedType.I4)] out OletxXactStat pXactStat); + + void ReenlistmentComplete(); + + void GetDistributedTransactionManager( + in Guid riid, + [MarshalAs(UnmanagedType.Interface)] out object ppvObject); +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IResourceManagerFactory2.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IResourceManagerFactory2.cs new file mode 100644 index 00000000000000..b788a17032a5de --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IResourceManagerFactory2.cs @@ -0,0 +1,24 @@ +// 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; + +namespace System.Transactions.DtcProxyShim.DtcInterfaces; + +// https://docs.microsoft.com/previous-versions/windows/desktop/ms686489(v=vs.85) +[ComImport, Guid("6B369C21-FBD2-11d1-8F47-00C04F8EE57D"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +internal interface IResourceManagerFactory2 +{ + internal void Create( + Guid pguidRM, + [MarshalAs(UnmanagedType.LPStr)] string pszRMName, + [MarshalAs(UnmanagedType.Interface)] IResourceManagerSink pIResMgrSink, + [MarshalAs(UnmanagedType.Interface)] out IResourceManager rm); + + internal void CreateEx( + Guid pguidRM, + [MarshalAs(UnmanagedType.LPStr)] string pszRMName, + [MarshalAs(UnmanagedType.Interface)] IResourceManagerSink pIResMgrSink, + Guid riidRequested, + [MarshalAs(UnmanagedType.Interface)] out object rm); +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IResourceManagerSink.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IResourceManagerSink.cs new file mode 100644 index 00000000000000..0e872188cfb6b5 --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IResourceManagerSink.cs @@ -0,0 +1,13 @@ +// 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; + +namespace System.Transactions.DtcProxyShim.DtcInterfaces; + +// https://docs.microsoft.com/previous-versions/windows/desktop/ms686073(v=vs.85) +[ComImport, Guid("0D563181-DEFB-11CE-AED1-00AA0051E2C4"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +internal interface IResourceManagerSink +{ + void TMDown(); +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITmNodeName.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITmNodeName.cs new file mode 100644 index 00000000000000..4816352f86164f --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITmNodeName.cs @@ -0,0 +1,15 @@ +// 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; + +namespace System.Transactions.DtcProxyShim.DtcInterfaces; + +// https://docs.microsoft.com/previous-versions/windows/desktop/ms687122(v=vs.85) +[ComImport, Guid("30274F88-6EE4-474e-9B95-7807BC9EF8CF"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +internal interface ITmNodeName +{ + internal void GetNodeNameSize(out uint pcbNodeNameSize); + + internal void GetNodeName(uint cbNodeNameBufferSize, [MarshalAs(UnmanagedType.LPWStr)] out string pcbNodeSize); +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransaction.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransaction.cs new file mode 100644 index 00000000000000..e516d2c0d038fc --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransaction.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. + +using System; +using System.Runtime.InteropServices; +using System.Transactions.Oletx; + +namespace System.Transactions.DtcProxyShim.DtcInterfaces; + +// https://docs.microsoft.com/previous-versions/windows/desktop/ms686531(v=vs.85) +[ComImport, Guid(Guids.IID_ITransaction), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +internal interface ITransaction +{ + void Commit([MarshalAs(UnmanagedType.Bool)] bool fRetainingt, [MarshalAs(UnmanagedType.U4)] OletxXacttc grfTC, uint grfRM); + + void Abort(IntPtr reason, [MarshalAs(UnmanagedType.Bool)] bool retaining, [MarshalAs(UnmanagedType.Bool)] bool async); + + void GetTransactionInfo(out OletxXactTransInfo xactInfo); +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionCloner.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionCloner.cs new file mode 100644 index 00000000000000..7ce0b361bdf5ff --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionCloner.cs @@ -0,0 +1,14 @@ +// 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; + +namespace System.Transactions.DtcProxyShim.DtcInterfaces; + +// https://docs.microsoft.com/previous-versions/windows/desktop/ms684377(v=vs.85) +[ComImport, Guid("02656950-2152-11d0-944C-00A0C905416E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +internal interface ITransactionCloner +{ + void CloneWithCommitDisabled([MarshalAs(UnmanagedType.Interface)] out ITransaction ppITransaction); +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionDispenser.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionDispenser.cs new file mode 100644 index 00000000000000..c45ea2c5f11631 --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionDispenser.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 System.Transactions.DtcProxyShim; +using System.Transactions.Oletx; + +namespace System.Transactions.DtcProxyShim.DtcInterfaces; + +// https://docs.microsoft.com/previous-versions/windows/desktop/ms679525(v=vs.85) +[ComImport, Guid(Guids.IID_ITransactionDispenser), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +internal interface ITransactionDispenser +{ + void GetOptionsObject([MarshalAs(UnmanagedType.Interface)] out ITransactionOptions ppOptions); + + void BeginTransaction( + IntPtr punkOuter, + [MarshalAs(UnmanagedType.I4)] OletxTransactionIsolationLevel isoLevel, + [MarshalAs(UnmanagedType.I4)] OletxTransactionIsoFlags isoFlags, + [MarshalAs(UnmanagedType.Interface)] ITransactionOptions pOptions, + [MarshalAs(UnmanagedType.Interface)] out ITransaction ppTransaction); +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionEnlistmentAsync.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionEnlistmentAsync.cs new file mode 100644 index 00000000000000..da738568efd5ec --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionEnlistmentAsync.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 System; +using System.Runtime.InteropServices; + +namespace System.Transactions.DtcProxyShim.DtcInterfaces; + +// https://docs.microsoft.com/previous-versions/windows/desktop/ms686429(v=vs.85) +[ComImport, Guid("0fb15081-af41-11ce-bd2b-204c4f4f5020"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +internal interface ITransactionEnlistmentAsync +{ + void PrepareRequestDone(int hr, IntPtr pmk, IntPtr pboidReason); + + void CommitRequestDone(int hr); + + void AbortRequestDone(int hr); +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionExport.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionExport.cs new file mode 100644 index 00000000000000..02f64369697ae8 --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionExport.cs @@ -0,0 +1,20 @@ +// 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; + +namespace System.Transactions.DtcProxyShim.DtcInterfaces; + +// https://docs.microsoft.com/previous-versions/windows/desktop/ms678954(v=vs.85) +[ComImport, Guid("0141fda5-8fc0-11ce-bd18-204c4f4f5020"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +internal interface ITransactionExport +{ + void Export([MarshalAs(UnmanagedType.Interface)] ITransaction punkTransaction, out uint pcbTransactionCookie); + + void GetTransactionCookie( + [MarshalAs(UnmanagedType.Interface)] ITransaction pITransaction, + uint cbTransactionCookie, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1), Out] byte[] rgbTransactionCookie, + out uint pcbUsed); +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionExportFactory.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionExportFactory.cs new file mode 100644 index 00000000000000..b82c8f5ebac37f --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionExportFactory.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. + +using System; +using System.Runtime.InteropServices; + +namespace System.Transactions.DtcProxyShim.DtcInterfaces; + +// https://docs.microsoft.com/previous-versions/windows/desktop/ms686771(v=vs.85) +[ComImport, Guid("E1CF9B53-8745-11ce-A9BA-00AA006C3706"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +internal interface ITransactionExportFactory +{ + void GetRemoteClassId(out Guid pclsid); + + void Create( + uint cbWhereabouts, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] byte[] rgbWhereabouts, + [MarshalAs(UnmanagedType.Interface)] out ITransactionExport ppExport); +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionImport.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionImport.cs new file mode 100644 index 00000000000000..310f7d98a54eb7 --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionImport.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 System; +using System.Runtime.InteropServices; + +namespace System.Transactions.DtcProxyShim.DtcInterfaces; + +// https://docs.microsoft.com/previous-versions/windows/desktop/ms681296(v=vs.85) +[ComImport, Guid("E1CF9B5A-8745-11ce-A9BA-00AA006C3706"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +internal interface ITransactionImport +{ + void Import( + uint cbTransactionCookie, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] byte[] rgbTransactionCookie, + Guid piid, + [MarshalAs(UnmanagedType.Interface)] out object ppvTransaction); +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionImportWhereabouts.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionImportWhereabouts.cs new file mode 100644 index 00000000000000..28f932e72dbed2 --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionImportWhereabouts.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 System.Runtime.InteropServices; + +namespace System.Transactions.DtcProxyShim.DtcInterfaces; + +// https://docs.microsoft.com/previous-versions/windows/desktop/ms682783(v=vs.85) +[ComImport, Guid("0141fda4-8fc0-11ce-bd18-204c4f4f5020"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +internal interface ITransactionImportWhereabouts +{ + internal void GetWhereaboutsSize(out uint pcbSize); + + internal void GetWhereabouts( + uint cbWhereabouts, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), Out] byte[] rgbWhereabouts, + out uint pcbUsed); +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionOptions.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionOptions.cs new file mode 100644 index 00000000000000..645b4cbcf98b33 --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionOptions.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +namespace System.Transactions.DtcProxyShim.DtcInterfaces; + +// https://docs.microsoft.com/previous-versions/windows/desktop/ms686489(v=vs.85) +[ComImport, Guid("3A6AD9E0-23B9-11cf-AD60-00AA00A74CCD"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +internal interface ITransactionOptions +{ + void SetOptions(Xactopt pOptions); + + void GetOptions(); +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionOutcomeEvents.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionOutcomeEvents.cs new file mode 100644 index 00000000000000..d0cabc4d708311 --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionOutcomeEvents.cs @@ -0,0 +1,20 @@ +// 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 System.Transactions.Oletx; + +namespace System.Transactions.DtcProxyShim.DtcInterfaces; + +// https://docs.microsoft.com/previous-versions/windows/desktop/ms686465(v=vs.85) +[ComImport, Guid("3A6AD9E2-23B9-11cf-AD60-00AA00A74CCD"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +internal interface ITransactionOutcomeEvents +{ + void Committed([MarshalAs(UnmanagedType.Bool)] bool fRetaining, IntPtr pNewUOW /* always null? */, int hresult); + + void Aborted(IntPtr pboidReason, [MarshalAs(UnmanagedType.Bool)] bool fRetaining, IntPtr pNewUOW, int hresult); + + void HeuristicDecision([MarshalAs(UnmanagedType.U4)] OletxTransactionHeuristic dwDecision, IntPtr pboidReason, int hresult); + + void Indoubt(); +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionPhase0EnlistmentAsync.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionPhase0EnlistmentAsync.cs new file mode 100644 index 00000000000000..ca6872fca528ad --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionPhase0EnlistmentAsync.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. + +using System.Runtime.InteropServices; + +namespace System.Transactions.DtcProxyShim.DtcInterfaces; + +// https://docs.microsoft.com/previous-versions/windows/desktop/ms685087(v=vs.85). _notifications = new(); + + private readonly ConcurrentQueue _cachedOptions = new(); + private readonly ConcurrentQueue _cachedTransmitters = new(); + private readonly ConcurrentQueue _cachedReceivers = new(); + + private readonly EventWaitHandle _eventHandle; + + private ITransactionDispenser _transactionDispenser = null!; // Late-initialized in ConnectToProxy + + internal DtcProxyShimFactory(EventWaitHandle notificationEventHandle) + => _eventHandle = notificationEventHandle; + + // https://docs.microsoft.com/previous-versions/windows/desktop/ms678898(v=vs.85) + [DllImport(Interop.Libraries.Xolehlp, CharSet = CharSet.Unicode, ExactSpelling = true, PreserveSig = false)] + private static extern void DtcGetTransactionManagerExW( + [MarshalAs(UnmanagedType.LPWStr)] string? pszHost, + [MarshalAs(UnmanagedType.LPWStr)] string? pszTmName, + in Guid riid, + int grfOptions, + object? pvConfigPararms, + [MarshalAs(UnmanagedType.Interface)] out ITransactionDispenser ppvObject); + + [UnconditionalSuppressMessage("Trimming", "IL2050", Justification = "Leave me alone")] + public void ConnectToProxy( + string? nodeName, + Guid resourceManagerIdentifier, + object managedIdentifier, + out bool nodeNameMatches, + out byte[] whereabouts, + out ResourceManagerShim resourceManagerShim) + { + if (RuntimeInformation.ProcessArchitecture == Architecture.X86) + { + throw new PlatformNotSupportedException(SR.DistributedNotSupportOn32Bits); + } + + lock (_proxyInitLock) + { + DtcGetTransactionManagerExW(nodeName, null, Guids.IID_ITransactionDispenser_Guid, 0, null, out ITransactionDispenser? localDispenser); + + // Check to make sure the node name matches. + if (nodeName is not null) + { + var pTmNodeName = (ITmNodeName)localDispenser; + pTmNodeName.GetNodeNameSize(out uint tmNodeNameLength); + pTmNodeName.GetNodeName(tmNodeNameLength, out string tmNodeName); + + nodeNameMatches = tmNodeName == nodeName; + } + else + { + nodeNameMatches = true; + } + + var pImportWhereabouts = (ITransactionImportWhereabouts)localDispenser; + + // Adding retry logic as a work around for MSDTC's GetWhereAbouts/GetWhereAboutsSize API + // which is single threaded and will return XACT_E_ALREADYINPROGRESS if another thread invokes the API. + uint whereaboutsSize = 0; + OletxHelper.Retry(() => pImportWhereabouts.GetWhereaboutsSize(out whereaboutsSize)); + + var tmpWhereabouts = new byte[(int)whereaboutsSize]; + + // Adding retry logic as a work around for MSDTC's GetWhereAbouts/GetWhereAboutsSize API + // which is single threaded and will return XACT_E_ALREADYINPROGRESS if another thread invokes the API. + OletxHelper.Retry(() => + { + pImportWhereabouts.GetWhereabouts(whereaboutsSize, tmpWhereabouts, out uint pcbUsed); + Debug.Assert(pcbUsed == tmpWhereabouts.Length); + }); + whereabouts = tmpWhereabouts; + + // Now we need to create the internal resource manager. + var rmFactory = (IResourceManagerFactory2)localDispenser; + + var rmNotifyShim = new ResourceManagerNotifyShim(this, managedIdentifier); + var rmShim = new ResourceManagerShim(this); + + OletxHelper.Retry(() => + { + rmFactory.CreateEx( + resourceManagerIdentifier, + "System.Transactions.InternalRM", + rmNotifyShim, + Guids.IID_IResourceManager_Guid, + out object? rm); + + rmShim.ResourceManager = (IResourceManager)rm; + }); + + resourceManagerShim = rmShim; + _transactionDispenser = localDispenser; + } + } + + internal void NewNotification(NotificationShimBase notification) + { + lock (_notificationLock) + { + _notifications.Enqueue(notification); + } + + _eventHandle.Set(); + } + + public void ReleaseNotificationLock() + => Monitor.Exit(_notificationLock); + + public void BeginTransaction( + uint timeout, + OletxTransactionIsolationLevel isolationLevel, + object? managedIdentifier, + out Guid transactionIdentifier, + out TransactionShim transactionShim) + { + ITransactionOptions options = GetCachedOptions(); + + try + { + var xactopt = new Xactopt(timeout, string.Empty); + options.SetOptions(xactopt); + + _transactionDispenser.BeginTransaction(IntPtr.Zero, isolationLevel, OletxTransactionIsoFlags.ISOFLAG_NONE, options, out ITransaction? pTx); + + SetupTransaction(pTx, managedIdentifier, out transactionIdentifier, out OletxTransactionIsolationLevel localIsoLevel, out transactionShim); + } + finally + { + ReturnCachedOptions(options); + } + } + + public void CreateResourceManager( + Guid resourceManagerIdentifier, + OletxResourceManager managedIdentifier, + out ResourceManagerShim resourceManagerShim) + { + var rmFactory = (IResourceManagerFactory2)_transactionDispenser; + + var rmNotifyShim = new ResourceManagerNotifyShim(this, managedIdentifier); + var rmShim = new ResourceManagerShim(this); + + OletxHelper.Retry(() => + { + rmFactory.CreateEx( + resourceManagerIdentifier, + "System.Transactions.ResourceManager", + rmNotifyShim, + Guids.IID_IResourceManager_Guid, + out object? rm); + + rmShim.ResourceManager = (IResourceManager)rm; + }); + + resourceManagerShim = rmShim; + } + + public void Import( + byte[] cookie, + OutcomeEnlistment managedIdentifier, + out Guid transactionIdentifier, + out OletxTransactionIsolationLevel isolationLevel, + out TransactionShim transactionShim) + { + var txImport = (ITransactionImport)_transactionDispenser; + txImport.Import(Convert.ToUInt32(cookie.Length), cookie, Guids.IID_ITransaction_Guid, out object? tx); + + SetupTransaction((ITransaction)tx, managedIdentifier, out transactionIdentifier, out isolationLevel, out transactionShim); + } + + public void ReceiveTransaction( + byte[] propagationToken, + OutcomeEnlistment managedIdentifier, + out Guid transactionIdentifier, + out OletxTransactionIsolationLevel isolationLevel, + out TransactionShim transactionShim) + { + ITransactionReceiver receiver = GetCachedReceiver(); + + try + { + receiver.UnmarshalPropagationToken( + Convert.ToUInt32(propagationToken.Length), + propagationToken, + out ITransaction? tx); + + SetupTransaction(tx, managedIdentifier, out transactionIdentifier, out isolationLevel, out transactionShim); + } + finally + { + ReturnCachedReceiver(receiver); + } + } + + public void CreateTransactionShim( + IDtcTransaction transactionNative, + OutcomeEnlistment managedIdentifier, + out Guid transactionIdentifier, + out OletxTransactionIsolationLevel isolationLevel, + out TransactionShim transactionShim) + { + var cloner = (ITransactionCloner)transactionNative; + cloner.CloneWithCommitDisabled(out ITransaction transaction); + + SetupTransaction(transaction, managedIdentifier, out transactionIdentifier, out isolationLevel, out transactionShim); + } + + internal ITransactionExportFactory ExportFactory + => (ITransactionExportFactory)_transactionDispenser; + + internal ITransactionVoterFactory2 VoterFactory + => (ITransactionVoterFactory2)_transactionDispenser; + + public void GetNotification( + out object? managedIdentifier, + out ShimNotificationType shimNotificationType, + out bool isSinglePhase, + out bool abortingHint, + out bool releaseLock, + out byte[]? prepareInfo) + { + managedIdentifier = null; + shimNotificationType = ShimNotificationType.None; + isSinglePhase = false; + abortingHint = false; + releaseLock = false; + prepareInfo = null; + + Monitor.Enter(_notificationLock); + + bool entryRemoved = _notifications.TryDequeue(out NotificationShimBase? notification); + if (entryRemoved) + { + managedIdentifier = notification!.EnlistmentIdentifier; + shimNotificationType = notification.NotificationType; + isSinglePhase = notification.IsSinglePhase; + abortingHint = notification.AbortingHint; + prepareInfo = notification.PrepareInfo; + } + + // We release the lock if we didn't find an entry or if the notification type + // is NOT ResourceManagerTMDownNotify. If it is a ResourceManagerTMDownNotify, the managed + // code will call ReleaseNotificationLock after processing the TMDown. We need to prevent + // other notifications from being processed while we are processing TMDown. But we don't want + // to force 3 roundtrips to this NotificationShimFactory for all notifications ( 1 to grab the lock, + // one to get the notification, and one to release the lock). + if (!entryRemoved || shimNotificationType != ShimNotificationType.ResourceManagerTmDownNotify) + { + Monitor.Exit(_notificationLock); + } + else + { + releaseLock = true; + } + } + + private void SetupTransaction( + ITransaction transaction, + object? managedIdentifier, + out Guid pTransactionIdentifier, + out OletxTransactionIsolationLevel pIsolationLevel, + out TransactionShim ppTransactionShim) + { + var transactionNotifyShim = new TransactionNotifyShim(this, managedIdentifier); + + // Get the transaction id. + transaction.GetTransactionInfo(out OletxXactTransInfo xactInfo); + + // Register for outcome events. + var pContainer = (IConnectionPointContainer)transaction; + var guid = Guids.IID_ITransactionOutcomeEvents_Guid; + pContainer.FindConnectionPoint(ref guid, out IConnectionPoint? pConnPoint); + pConnPoint!.Advise(transactionNotifyShim, out int connPointCookie); + + var transactionShim = new TransactionShim(this, transactionNotifyShim, transaction); + pTransactionIdentifier = xactInfo.Uow; + pIsolationLevel = xactInfo.IsoLevel; + ppTransactionShim = transactionShim; + } + + private ITransactionOptions GetCachedOptions() + { + if (_cachedOptions.TryDequeue(out ITransactionOptions? options)) + { + return options; + } + + _transactionDispenser.GetOptionsObject(out ITransactionOptions? transactionOptions); + return transactionOptions; + } + + internal void ReturnCachedOptions(ITransactionOptions options) + => _cachedOptions.Enqueue(options); + + internal ITransactionTransmitter GetCachedTransmitter(ITransaction transaction) + { + if (!_cachedTransmitters.TryDequeue(out ITransactionTransmitter? transmitter)) + { + var transmitterFactory = (ITransactionTransmitterFactory)_transactionDispenser; + transmitterFactory.Create(out transmitter); + } + + transmitter.Set(transaction); + + return transmitter; + } + + internal void ReturnCachedTransmitter(ITransactionTransmitter transmitter) + => _cachedTransmitters.Enqueue(transmitter); + + internal ITransactionReceiver GetCachedReceiver() + { + if (_cachedReceivers.TryDequeue(out ITransactionReceiver? receiver)) + { + return receiver; + } + + var receiverFactory = (ITransactionReceiverFactory)_transactionDispenser; + receiverFactory.Create(out ITransactionReceiver transactionReceiver); + + return transactionReceiver; + } + + internal void ReturnCachedReceiver(ITransactionReceiver receiver) + => _cachedReceivers.Enqueue(receiver); +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/EnlistmentNotifyShim.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/EnlistmentNotifyShim.cs new file mode 100644 index 00000000000000..b8f46ce85bd7ff --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/EnlistmentNotifyShim.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; +using System.Threading; +using System.Transactions.DtcProxyShim.DtcInterfaces; +using System.Transactions.Oletx; + +namespace System.Transactions.DtcProxyShim; + +internal sealed class EnlistmentNotifyShim : NotificationShimBase, ITransactionResourceAsync +{ + internal ITransactionEnlistmentAsync? EnlistmentAsync; + + // MSDTCPRX behaves unpredictably in that if the TM is down when we vote + // no it will send an AbortRequest. However if the TM does not go down + // the enlistment is not go down the AbortRequest is not sent. This + // makes reliable cleanup a problem. To work around this the enlisment + // shim will eat the AbortRequest if it knows that it has voted No. + + // On Win2k this same problem applies to responding Committed to a + // single phase commit request. + private bool _ignoreSpuriousProxyNotifications; + + internal EnlistmentNotifyShim(DtcProxyShimFactory shimFactory, OletxEnlistment enlistmentIdentifier) + : base(shimFactory, enlistmentIdentifier) + { + _ignoreSpuriousProxyNotifications = false; + } + + internal void SetIgnoreSpuriousProxyNotifications() + => _ignoreSpuriousProxyNotifications = true; + + public void PrepareRequest(bool fRetaining, OletxXactRm grfRM, bool fWantMoniker, bool fSinglePhase) + { + ITransactionEnlistmentAsync? pEnlistmentAsync = Interlocked.Exchange(ref EnlistmentAsync, null); + + if (pEnlistmentAsync is null) + { + throw new InvalidOperationException("Unexpected null in pEnlistmentAsync"); + } + + var pPrepareInfo = (IPrepareInfo)pEnlistmentAsync; + pPrepareInfo.GetPrepareInfoSize(out uint prepareInfoLength); + var prepareInfoBuffer = new byte[prepareInfoLength]; + pPrepareInfo.GetPrepareInfo(prepareInfoBuffer); + + PrepareInfo = prepareInfoBuffer; + IsSinglePhase = fSinglePhase; + NotificationType = ShimNotificationType.PrepareRequestNotify; + ShimFactory.NewNotification(this); + } + + public void CommitRequest(OletxXactRm grfRM, IntPtr pNewUOW) + { + NotificationType = ShimNotificationType.CommitRequestNotify; + ShimFactory.NewNotification(this); + } + + public void AbortRequest(IntPtr pboidReason, bool fRetaining, IntPtr pNewUOW) + { + if (!_ignoreSpuriousProxyNotifications) + { + // Only create the notification if we have not already voted. + NotificationType = ShimNotificationType.AbortRequestNotify; + ShimFactory.NewNotification(this); + } + } + + public void TMDown() + { + NotificationType = ShimNotificationType.ResourceManagerTmDownNotify; + ShimFactory.NewNotification(this); + } +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/EnlistmentShim.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/EnlistmentShim.cs new file mode 100644 index 00000000000000..844bc36f9d76c7 --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/EnlistmentShim.cs @@ -0,0 +1,82 @@ +// 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.Transactions.DtcProxyShim.DtcInterfaces; + +namespace System.Transactions.DtcProxyShim; + +internal sealed class EnlistmentShim +{ + private readonly EnlistmentNotifyShim _enlistmentNotifyShim; + + internal ITransactionEnlistmentAsync? EnlistmentAsync { get; set; } + + internal EnlistmentShim(EnlistmentNotifyShim notifyShim) + => _enlistmentNotifyShim = notifyShim; + + public void PrepareRequestDone(OletxPrepareVoteType voteType) + { + var voteHr = OletxHelper.S_OK; + var releaseEnlistment = false; + + switch (voteType) + { + case OletxPrepareVoteType.ReadOnly: + { + // On W2k Proxy may send a spurious aborted notification if the TM goes down. + _enlistmentNotifyShim.SetIgnoreSpuriousProxyNotifications(); + voteHr = OletxHelper.XACT_S_READONLY; + break; + } + + case OletxPrepareVoteType.SinglePhase: + { + // On W2k Proxy may send a spurious aborted notification if the TM goes down. + _enlistmentNotifyShim.SetIgnoreSpuriousProxyNotifications(); + voteHr = OletxHelper.XACT_S_SINGLEPHASE; + break; + } + + case OletxPrepareVoteType.Prepared: + { + voteHr = OletxHelper.S_OK; + break; + } + + case OletxPrepareVoteType.Failed: + { + // Proxy may send a spurious aborted notification if the TM goes down. + _enlistmentNotifyShim.SetIgnoreSpuriousProxyNotifications(); + voteHr = OletxHelper.E_FAIL; + break; + } + + case OletxPrepareVoteType.InDoubt: + { + releaseEnlistment = true; + break; + } + + default: // unexpected, vote no. + { + voteHr = OletxHelper.E_FAIL; + break; + } + } + + if (!releaseEnlistment) + { + EnlistmentAsync!.PrepareRequestDone( + voteHr, + IntPtr.Zero, + IntPtr.Zero); + } + } + + public void CommitRequestDone() + => EnlistmentAsync!.CommitRequestDone(OletxHelper.S_OK); + + public void AbortRequestDone() + => EnlistmentAsync!.AbortRequestDone(OletxHelper.S_OK); +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/Guids.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/Guids.cs new file mode 100644 index 00000000000000..9e395a2154037b --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/Guids.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +namespace System.Transactions.DtcProxyShim; + +internal static class Guids +{ + internal const string IID_ITransactionDispenser = "3A6AD9E1-23B9-11cf-AD60-00AA00A74CCD"; + internal const string IID_IResourceManager = "13741D21-87EB-11CE-8081-0080C758527E"; + internal const string IID_ITransactionOutcomeEvents = "3A6AD9E2-23B9-11cf-AD60-00AA00A74CCD"; + internal const string IID_ITransaction = "0fb15084-af41-11ce-bd2b-204c4f4f5020"; + + internal static readonly Guid IID_ITransactionDispenser_Guid = Guid.Parse(IID_ITransactionDispenser); + internal static readonly Guid IID_IResourceManager_Guid = Guid.Parse(IID_IResourceManager); + internal static readonly Guid IID_ITransactionOutcomeEvents_Guid = Guid.Parse(IID_ITransactionOutcomeEvents); + internal static readonly Guid IID_ITransaction_Guid = Guid.Parse(IID_ITransaction); +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/NativeEnums.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/NativeEnums.cs new file mode 100644 index 00000000000000..f852af728def2e --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/NativeEnums.cs @@ -0,0 +1,143 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Transactions.DtcProxyShim; + +internal enum ShimNotificationType +{ + None = 0, + Phase0RequestNotify = 1, + VoteRequestNotify = 2, + PrepareRequestNotify = 3, + CommitRequestNotify = 4, + AbortRequestNotify = 5, + CommittedNotify = 6, + AbortedNotify = 7, + InDoubtNotify = 8, + EnlistmentTmDownNotify = 9, + ResourceManagerTmDownNotify = 10 +} + +internal enum OletxPrepareVoteType +{ + ReadOnly = 0, + SinglePhase = 1, + Prepared = 2, + Failed = 3, + InDoubt = 4 +} + +internal enum OletxTransactionOutcome +{ + NotKnownYet = 0, + Committed = 1, + Aborted = 2 +} + +internal enum OletxTransactionIsolationLevel +{ + ISOLATIONLEVEL_UNSPECIFIED = -1, + ISOLATIONLEVEL_CHAOS = 0x10, + ISOLATIONLEVEL_READUNCOMMITTED = 0x100, + ISOLATIONLEVEL_BROWSE = 0x100, + ISOLATIONLEVEL_CURSORSTABILITY = 0x1000, + ISOLATIONLEVEL_READCOMMITTED = 0x1000, + ISOLATIONLEVEL_REPEATABLEREAD = 0x10000, + ISOLATIONLEVEL_SERIALIZABLE = 0x100000, + ISOLATIONLEVEL_ISOLATED = 0x100000 +} + +[Flags] +internal enum OletxTransactionIsoFlags +{ + ISOFLAG_NONE = 0, + ISOFLAG_RETAIN_COMMIT_DC = 1, + ISOFLAG_RETAIN_COMMIT = 2, + ISOFLAG_RETAIN_COMMIT_NO = 3, + ISOFLAG_RETAIN_ABORT_DC = 4, + ISOFLAG_RETAIN_ABORT = 8, + ISOFLAG_RETAIN_ABORT_NO = 12, + ISOFLAG_RETAIN_DONTCARE = ISOFLAG_RETAIN_COMMIT_DC | ISOFLAG_RETAIN_ABORT_DC, + ISOFLAG_RETAIN_BOTH = ISOFLAG_RETAIN_COMMIT | ISOFLAG_RETAIN_ABORT, + ISOFLAG_RETAIN_NONE = ISOFLAG_RETAIN_COMMIT_NO | ISOFLAG_RETAIN_ABORT_NO, + ISOFLAG_OPTIMISTIC = 16, + ISOFLAG_READONLY = 32 +} + +internal enum OletxXacttc : uint +{ + XACTTC_NONE = 0, + XACTTC_SYNC_PHASEONE = 1, + XACTTC_SYNC_PHASETWO = 2, + XACTTC_SYNC = 2, + XACTTC_ASYNC_PHASEONE = 4, + XACTTC_ASYNC = 4 +} + +internal enum OletxXactRm : uint +{ + XACTRM_OPTIMISTICLASTWINS = 1, + XACTRM_NOREADONLYPREPARES = 2 +} + +internal enum OletxTransactionStatus +{ + OLETX_TRANSACTION_STATUS_NONE = 0, + OLETX_TRANSACTION_STATUS_OPENNORMAL = 0x1, + OLETX_TRANSACTION_STATUS_OPENREFUSED = 0x2, + OLETX_TRANSACTION_STATUS_PREPARING = 0x4, + OLETX_TRANSACTION_STATUS_PREPARED = 0x8, + OLETX_TRANSACTION_STATUS_PREPARERETAINING = 0x10, + OLETX_TRANSACTION_STATUS_PREPARERETAINED = 0x20, + OLETX_TRANSACTION_STATUS_COMMITTING = 0x40, + OLETX_TRANSACTION_STATUS_COMMITRETAINING = 0x80, + OLETX_TRANSACTION_STATUS_ABORTING = 0x100, + OLETX_TRANSACTION_STATUS_ABORTED = 0x200, + OLETX_TRANSACTION_STATUS_COMMITTED = 0x400, + OLETX_TRANSACTION_STATUS_HEURISTIC_ABORT = 0x800, + OLETX_TRANSACTION_STATUS_HEURISTIC_COMMIT = 0x1000, + OLETX_TRANSACTION_STATUS_HEURISTIC_DAMAGE = 0x2000, + OLETX_TRANSACTION_STATUS_HEURISTIC_DANGER = 0x4000, + OLETX_TRANSACTION_STATUS_FORCED_ABORT = 0x8000, + OLETX_TRANSACTION_STATUS_FORCED_COMMIT = 0x10000, + OLETX_TRANSACTION_STATUS_INDOUBT = 0x20000, + OLETX_TRANSACTION_STATUS_CLOSED = 0x40000, + OLETX_TRANSACTION_STATUS_OPEN = 0x3, + OLETX_TRANSACTION_STATUS_NOTPREPARED = 0x7ffc3, + OLETX_TRANSACTION_STATUS_ALL = 0x7ffff +} + +internal enum OletxTransactionHeuristic : uint +{ + XACTHEURISTIC_ABORT = 1, + XACTHEURISTIC_COMMIT = 2, + XACTHEURISTIC_DAMAGE = 3, + XACTHEURISTIC_DANGER = 4 +} + +internal enum OletxXactStat +{ + XACTSTAT_NONE = 0, + XACTSTAT_OPENNORMAL = 0x1, + XACTSTAT_OPENREFUSED = 0x2, + XACTSTAT_PREPARING = 0x4, + XACTSTAT_PREPARED = 0x8, + XACTSTAT_PREPARERETAINING = 0x10, + XACTSTAT_PREPARERETAINED = 0x20, + XACTSTAT_COMMITTING = 0x40, + XACTSTAT_COMMITRETAINING = 0x80, + XACTSTAT_ABORTING = 0x100, + XACTSTAT_ABORTED = 0x200, + XACTSTAT_COMMITTED = 0x400, + XACTSTAT_HEURISTIC_ABORT = 0x800, + XACTSTAT_HEURISTIC_COMMIT = 0x1000, + XACTSTAT_HEURISTIC_DAMAGE = 0x2000, + XACTSTAT_HEURISTIC_DANGER = 0x4000, + XACTSTAT_FORCED_ABORT = 0x8000, + XACTSTAT_FORCED_COMMIT = 0x10000, + XACTSTAT_INDOUBT = 0x20000, + XACTSTAT_CLOSED = 0x40000, + XACTSTAT_OPEN = 0x3, + XACTSTAT_NOTPREPARED = 0x7ffc3, + XACTSTAT_ALL = 0x7ffff +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/NotificationShimBase.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/NotificationShimBase.cs new file mode 100644 index 00000000000000..bed600488c502c --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/NotificationShimBase.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Transactions.DtcProxyShim; + +internal class NotificationShimBase +{ + public object? EnlistmentIdentifier; + public ShimNotificationType NotificationType; + public bool AbortingHint; + public bool IsSinglePhase; + public byte[]? PrepareInfo; + + protected DtcProxyShimFactory ShimFactory; + + internal NotificationShimBase(DtcProxyShimFactory shimFactory, object? enlistmentIdentifier) + { + ShimFactory = shimFactory; + EnlistmentIdentifier = enlistmentIdentifier; + NotificationType = ShimNotificationType.None; + AbortingHint = false; + IsSinglePhase = false; + } +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/OletxHelper.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/OletxHelper.cs new file mode 100644 index 00000000000000..ce0bb80e059971 --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/OletxHelper.cs @@ -0,0 +1,55 @@ +// 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 System.Threading; + +namespace System.Transactions.DtcProxyShim; + +internal static class OletxHelper +{ + private const int RetryInterval = 50; // in milliseconds + private const int MaxRetryCount = 100; + + internal static int S_OK = 0; + internal static int E_FAIL = -2147467259; // 0x80004005, -2147467259 + internal static int XACT_S_READONLY = 315394; // 0x0004D002, 315394 + internal static int XACT_S_SINGLEPHASE = 315401; // 0x0004D009, 315401 + internal static int XACT_E_ABORTED = -2147168231; // 0x8004D019, -2147168231 + internal static int XACT_E_NOTRANSACTION = -2147168242; // 0x8004D00E, -2147168242 + internal static int XACT_E_CONNECTION_DOWN = -2147168228; // 0x8004D01C, -2147168228 + internal static int XACT_E_REENLISTTIMEOUT = -2147168226; // 0x8004D01E, -2147168226 + internal static int XACT_E_RECOVERYALREADYDONE = -2147167996; // 0x8004D104, -2147167996 + internal static int XACT_E_TMNOTAVAILABLE = -2147168229; // 0x8004d01b, -2147168229 + internal static int XACT_E_INDOUBT = -2147168234; // 0x8004d016, + internal static int XACT_E_ALREADYINPROGRESS = -2147168232; // x08004d018, + internal static int XACT_E_TOOMANY_ENLISTMENTS = -2147167999; // 0x8004d101 + internal static int XACT_E_PROTOCOL = -2147167995; // 8004d105 + internal static int XACT_E_FIRST = -2147168256; // 0x8004D000 + internal static int XACT_E_LAST = -2147168215; // 0x8004D029 + internal static int XACT_E_NOTSUPPORTED = -2147168241; // 0x8004D00F + internal static int XACT_E_NETWORK_TX_DISABLED = -2147168220; // 0x8004D024 + + internal static void Retry(Action action) + { + int nRetries = MaxRetryCount; + + while (true) + { + try + { + action(); + return; + } + catch (COMException e) when (e.ErrorCode == XACT_E_ALREADYINPROGRESS) + { + if (--nRetries == 0) + { + throw; + } + + Thread.Sleep(RetryInterval); + } + } + } +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/OletxXactTransInfo.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/OletxXactTransInfo.cs new file mode 100644 index 00000000000000..1d097d97b78d5e --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/OletxXactTransInfo.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. + +using System.Runtime.InteropServices; + +namespace System.Transactions.DtcProxyShim; + +[ComVisible(false)] +[StructLayout(LayoutKind.Sequential)] +internal struct OletxXactTransInfo +{ + internal Guid Uow; + internal OletxTransactionIsolationLevel IsoLevel; + internal OletxTransactionIsoFlags IsoFlags; + internal int GrfTCSupported; + internal int GrfRMSupported; + internal int GrfTCSupportedRetaining; + internal int GrfRMSupportedRetaining; +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/Phase0NotifyShim.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/Phase0NotifyShim.cs new file mode 100644 index 00000000000000..dc2d19226225cf --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/Phase0NotifyShim.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Transactions.DtcProxyShim.DtcInterfaces; +using System.Transactions.Oletx; + +namespace System.Transactions.DtcProxyShim; + +internal sealed class Phase0NotifyShim : NotificationShimBase, ITransactionPhase0NotifyAsync +{ + internal Phase0NotifyShim(DtcProxyShimFactory shimFactory, object enlistmentIdentifier) + : base(shimFactory, enlistmentIdentifier) + { + } + + public void Phase0Request(bool fAbortHint) + { + AbortingHint = fAbortHint; + NotificationType = ShimNotificationType.Phase0RequestNotify; + ShimFactory.NewNotification(this); + } + + public void EnlistCompleted(int status) + { + // We don't care about these. The managed code waited for the enlistment to be completed. + } +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/Phase0Shim.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/Phase0Shim.cs new file mode 100644 index 00000000000000..511c52a7f7eb36 --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/Phase0Shim.cs @@ -0,0 +1,41 @@ +// 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 System.Transactions.DtcProxyShim.DtcInterfaces; + +namespace System.Transactions.DtcProxyShim; + +internal sealed class Phase0EnlistmentShim +{ + private Phase0NotifyShim _phase0NotifyShim; + + internal ITransactionPhase0EnlistmentAsync? Phase0EnlistmentAsync { get; set; } + + internal Phase0EnlistmentShim(Phase0NotifyShim notifyShim) + => _phase0NotifyShim = notifyShim; + + public void Unenlist() + { + // VSWhidbey 405624 - There is a race between the enlistment and abort of a transaction + // that could cause out proxy interface to already be released when Unenlist is called. + Phase0EnlistmentAsync?.Unenlist(); + } + + public void Phase0Done(bool voteYes) + { + if (voteYes) + { + try + { + Phase0EnlistmentAsync!.Phase0Done(); + } + catch (COMException e) when (e.ErrorCode == OletxHelper.XACT_E_PROTOCOL) + { + // Deal with the proxy bug where we get a Phase0Request(false) on a + // TMDown and the proxy object state is not changed. + return; + } + } + } +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/ResourceManagerNotifyShim.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/ResourceManagerNotifyShim.cs new file mode 100644 index 00000000000000..53ac780b696595 --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/ResourceManagerNotifyShim.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Transactions.Oletx; +using System.Transactions.DtcProxyShim.DtcInterfaces; + +namespace System.Transactions.DtcProxyShim; + +internal sealed class ResourceManagerNotifyShim : NotificationShimBase, IResourceManagerSink +{ + internal ResourceManagerNotifyShim( + DtcProxyShimFactory shimFactory, + object enlistmentIdentifier) + : base(shimFactory, enlistmentIdentifier) + { + } + + public void TMDown() + { + NotificationType = ShimNotificationType.ResourceManagerTmDownNotify; + ShimFactory.NewNotification(this); + } +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/ResourceManagerShim.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/ResourceManagerShim.cs new file mode 100644 index 00000000000000..f9dff75d4b7d7a --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/ResourceManagerShim.cs @@ -0,0 +1,61 @@ +// 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.Transactions.Oletx; +using System.Transactions.DtcProxyShim.DtcInterfaces; + +namespace System.Transactions.DtcProxyShim; + +internal sealed class ResourceManagerShim +{ + private readonly DtcProxyShimFactory _shimFactory; + + internal ResourceManagerShim(DtcProxyShimFactory shimFactory) + => _shimFactory = shimFactory; + + public IResourceManager? ResourceManager { get; set; } + + public void Enlist( + TransactionShim transactionShim, + OletxEnlistment managedIdentifier, + out EnlistmentShim enlistmentShim) + { + var pEnlistmentNotifyShim = new EnlistmentNotifyShim(_shimFactory, managedIdentifier); + var pEnlistmentShim = new EnlistmentShim(pEnlistmentNotifyShim); + + ITransaction transaction = transactionShim.Transaction; + ResourceManager!.Enlist(transaction, pEnlistmentNotifyShim, out Guid txUow, out OletxTransactionIsolationLevel isoLevel, out ITransactionEnlistmentAsync pEnlistmentAsync); + + pEnlistmentNotifyShim.EnlistmentAsync = pEnlistmentAsync; + pEnlistmentShim.EnlistmentAsync = pEnlistmentAsync; + + enlistmentShim = pEnlistmentShim; + } + + public void Reenlist(byte[] prepareInfo, out OletxTransactionOutcome outcome) + { + // Call Reenlist on the proxy, waiting for 5 milliseconds for it to get the outcome. If it doesn't know that outcome in that + // amount of time, tell the caller we don't know the outcome yet. The managed code will reschedule the check by using the + // ReenlistThread. + try + { + ResourceManager!.Reenlist(prepareInfo, (uint)prepareInfo.Length, 5, out OletxXactStat xactStatus); + outcome = xactStatus switch + { + OletxXactStat.XACTSTAT_ABORTED => OletxTransactionOutcome.Aborted, + OletxXactStat.XACTSTAT_COMMITTED => OletxTransactionOutcome.Committed, + _ => OletxTransactionOutcome.Aborted + }; + } + catch (COMException e) when (e.ErrorCode == OletxHelper.XACT_E_REENLISTTIMEOUT) + { + outcome = OletxTransactionOutcome.NotKnownYet; + return; + } + } + + public void ReenlistComplete() + => ResourceManager!.ReenlistmentComplete(); +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/TransactionNotifyShim.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/TransactionNotifyShim.cs new file mode 100644 index 00000000000000..d605f13053a009 --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/TransactionNotifyShim.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Transactions.DtcProxyShim.DtcInterfaces; + +namespace System.Transactions.DtcProxyShim; + +internal sealed class TransactionNotifyShim : NotificationShimBase, ITransactionOutcomeEvents +{ + internal TransactionNotifyShim(DtcProxyShimFactory shimFactory, object? enlistmentIdentifier) + : base(shimFactory, enlistmentIdentifier) + { + } + + public void Committed(bool fRetaining, IntPtr pNewUOW, int hresult) + { + NotificationType = ShimNotificationType.CommittedNotify; + ShimFactory.NewNotification(this); + } + + public void Aborted(IntPtr pboidReason, bool fRetaining, IntPtr pNewUOW, int hresult) + { + NotificationType = ShimNotificationType.AbortedNotify; + ShimFactory.NewNotification(this); + } + + public void HeuristicDecision(OletxTransactionHeuristic dwDecision, IntPtr pboidReason, int hresult) + { + NotificationType = dwDecision switch + { + OletxTransactionHeuristic.XACTHEURISTIC_ABORT => ShimNotificationType.AbortedNotify, + OletxTransactionHeuristic.XACTHEURISTIC_COMMIT => ShimNotificationType.CommittedNotify, + _ => ShimNotificationType.InDoubtNotify + }; + + ShimFactory.NewNotification(this); + } + + public void Indoubt() + { + NotificationType = ShimNotificationType.InDoubtNotify; + ShimFactory.NewNotification(this); + } +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/TransactionOutcome.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/TransactionOutcome.cs new file mode 100644 index 00000000000000..eb7ec1d460edb9 --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/TransactionOutcome.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Transactions.DtcProxyShim; + +internal enum TransactionOutcome +{ + NotKnownYet = 0, + Committed = 1, + Aborted = 2 +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/TransactionShim.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/TransactionShim.cs new file mode 100644 index 00000000000000..349d051999f529 --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/TransactionShim.cs @@ -0,0 +1,98 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Transactions.DtcProxyShim.DtcInterfaces; +using System.Transactions.Oletx; + +namespace System.Transactions.DtcProxyShim; + +internal sealed class TransactionShim +{ + private DtcProxyShimFactory _shimFactory; + private TransactionNotifyShim _transactionNotifyShim; + + internal ITransaction Transaction { get; set; } + + internal TransactionShim(DtcProxyShimFactory shimFactory, TransactionNotifyShim notifyShim, ITransaction transaction) + { + _shimFactory = shimFactory; + _transactionNotifyShim = notifyShim; + Transaction = transaction; + } + + public void Commit() + => Transaction.Commit(false, OletxXacttc.XACTTC_ASYNC, 0); + + public void Abort() + => Transaction.Abort(IntPtr.Zero, false, false); + + public void CreateVoter(OletxPhase1VolatileEnlistmentContainer managedIdentifier, out VoterBallotShim voterBallotShim) + { + var voterNotifyShim = new VoterNotifyShim(_shimFactory, managedIdentifier); + var voterShim = new VoterBallotShim(_shimFactory, voterNotifyShim); + _shimFactory.VoterFactory.Create(Transaction, voterNotifyShim, out ITransactionVoterBallotAsync2 voterBallot); + voterShim.VoterBallotAsync2 = voterBallot; + voterBallotShim = voterShim; + } + + public void Export(byte[] whereabouts, out byte[] cookieBuffer) + { + _shimFactory.ExportFactory.Create((uint)whereabouts.Length, whereabouts, out ITransactionExport export); + + uint cookieSizeULong = 0; + + OletxHelper.Retry(() => export.Export(Transaction, out cookieSizeULong)); + + var cookieSize = (uint)cookieSizeULong; + var buffer = new byte[cookieSize]; + uint bytesUsed = 0; + + OletxHelper.Retry(() => export.GetTransactionCookie(Transaction, cookieSize, buffer, out bytesUsed)); + + cookieBuffer = buffer; + } + + public void GetITransactionNative(out IDtcTransaction transactionNative) + { + var cloner = (ITransactionCloner)Transaction; + cloner.CloneWithCommitDisabled(out ITransaction returnTransaction); + + transactionNative = (IDtcTransaction)returnTransaction; + } + + public unsafe byte[] GetPropagationToken() + { + ITransactionTransmitter transmitter = _shimFactory.GetCachedTransmitter(Transaction); + + try + { + transmitter.GetPropagationTokenSize(out uint propagationTokenSizeULong); + + var propagationTokenSize = (int)propagationTokenSizeULong; + var propagationToken = new byte[propagationTokenSize]; + + transmitter.MarshalPropagationToken((uint)propagationTokenSize, propagationToken, out uint propagationTokenSizeUsed); + + return propagationToken; + } + finally + { + _shimFactory.ReturnCachedTransmitter(transmitter); + } + } + + public void Phase0Enlist(object managedIdentifier, out Phase0EnlistmentShim phase0EnlistmentShim) + { + var phase0Factory = (ITransactionPhase0Factory)Transaction; + var phase0NotifyShim = new Phase0NotifyShim(_shimFactory, managedIdentifier); + var phase0Shim = new Phase0EnlistmentShim(phase0NotifyShim); + + phase0Factory.Create(phase0NotifyShim, out ITransactionPhase0EnlistmentAsync phase0Async); + phase0Shim.Phase0EnlistmentAsync = phase0Async; + + phase0Async.Enable(); + phase0Async.WaitForEnlistment(); + + phase0EnlistmentShim = phase0Shim; + } +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/VoterNotifyShim.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/VoterNotifyShim.cs new file mode 100644 index 00000000000000..fad9958acbef99 --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/VoterNotifyShim.cs @@ -0,0 +1,51 @@ +// 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 System.Transactions.DtcProxyShim.DtcInterfaces; +using System.Transactions.Oletx; + +namespace System.Transactions.DtcProxyShim; + +internal sealed class VoterNotifyShim : NotificationShimBase, ITransactionVoterNotifyAsync2 +{ + internal VoterNotifyShim(DtcProxyShimFactory shimFactory, object enlistmentIdentifier) + : base(shimFactory, enlistmentIdentifier) + { + } + + public void VoteRequest() + { + NotificationType = ShimNotificationType.VoteRequestNotify; + ShimFactory.NewNotification(this); + } + + public void Committed([MarshalAs(UnmanagedType.Bool)] bool fRetaining, IntPtr pNewUOW, uint hresult) + { + NotificationType = ShimNotificationType.CommittedNotify; + ShimFactory.NewNotification(this); + } + + public void Aborted(IntPtr pboidReason, [MarshalAs(UnmanagedType.Bool)] bool fRetaining, IntPtr pNewUOW, uint hresult) + { + NotificationType = ShimNotificationType.AbortedNotify; + ShimFactory.NewNotification(this); + } + + public void HeuristicDecision([MarshalAs(UnmanagedType.U4)] OletxTransactionHeuristic dwDecision, IntPtr pboidReason, uint hresult) + { + NotificationType = dwDecision switch { + OletxTransactionHeuristic.XACTHEURISTIC_ABORT => ShimNotificationType.AbortedNotify, + OletxTransactionHeuristic.XACTHEURISTIC_COMMIT => ShimNotificationType.CommittedNotify, + _ => ShimNotificationType.InDoubtNotify + }; + + ShimFactory.NewNotification(this); + } + + public void Indoubt() + { + NotificationType = ShimNotificationType.InDoubtNotify; + ShimFactory.NewNotification(this); + } +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/VoterShim.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/VoterShim.cs new file mode 100644 index 00000000000000..db354738f7fc1e --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/VoterShim.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.Transactions.DtcProxyShim.DtcInterfaces; + +namespace System.Transactions.DtcProxyShim; + +internal sealed class VoterBallotShim +{ + private VoterNotifyShim _voterNotifyShim; + + internal ITransactionVoterBallotAsync2? VoterBallotAsync2 { get; set; } + + internal VoterBallotShim(DtcProxyShimFactory shimFactory, VoterNotifyShim notifyShim) + => _voterNotifyShim = notifyShim; + + public void Vote(bool voteYes) + { + int voteHr = OletxHelper.S_OK; + + if (!voteYes) + { + voteHr = OletxHelper.E_FAIL; + } + + VoterBallotAsync2!.VoteRequestDone(voteHr, IntPtr.Zero); + } +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/Xactopt.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/Xactopt.cs new file mode 100644 index 00000000000000..2f6bac5637eeb2 --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/Xactopt.cs @@ -0,0 +1,20 @@ +// 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; + +namespace System.Transactions.DtcProxyShim; + +// https://docs.microsoft.com/previous-versions/windows/desktop/ms679195(v=vs.85) +[StructLayout(LayoutKind.Sequential)] +internal struct Xactopt +{ + internal Xactopt(uint ulTimeout, string szDescription) + => (UlTimeout, SzDescription) = (ulTimeout, szDescription); + + public uint UlTimeout; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)] + public string SzDescription; +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DurableEnlistmentState.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DurableEnlistmentState.cs index 8c0a9c3e8464bc..f434ea6a67eab7 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/DurableEnlistmentState.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DurableEnlistmentState.cs @@ -96,7 +96,7 @@ internal override void EnterState(InternalEnlistment enlistment) TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.EnlistmentStatus(enlistment, NotificationCall.Rollback); + etwLog.EnlistmentStatus(TraceSourceType.TraceSourceLtm, enlistment.EnlistmentTraceId, NotificationCall.Rollback); } // Send the Rollback notification to the enlistment @@ -147,7 +147,7 @@ internal override void EnterState(InternalEnlistment enlistment) TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.EnlistmentStatus(enlistment, NotificationCall.SinglePhaseCommit); + etwLog.EnlistmentStatus(TraceSourceType.TraceSourceLtm, enlistment.EnlistmentTraceId, NotificationCall.SinglePhaseCommit); } // Send the Commit notification to the enlistment diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/Enlistment.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/Enlistment.cs index e19dae454de151..7d23de2aad39dc 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/Enlistment.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/Enlistment.cs @@ -29,7 +29,7 @@ internal interface IPromotedEnlistment byte[] GetRecoveryInformation(); - InternalEnlistment InternalEnlistment + InternalEnlistment? InternalEnlistment { get; set; @@ -270,36 +270,28 @@ void ISinglePhaseNotificationInternal.SinglePhaseCommit(IPromotedEnlistment sing } } - void IEnlistmentNotificationInternal.Prepare( - IPromotedEnlistment preparingEnlistment - ) + void IEnlistmentNotificationInternal.Prepare(IPromotedEnlistment preparingEnlistment) { Debug.Assert(_twoPhaseNotifications != null); _promotedEnlistment = preparingEnlistment; _twoPhaseNotifications.Prepare(PreparingEnlistment); } - void IEnlistmentNotificationInternal.Commit( - IPromotedEnlistment enlistment - ) + void IEnlistmentNotificationInternal.Commit(IPromotedEnlistment enlistment) { Debug.Assert(_twoPhaseNotifications != null); _promotedEnlistment = enlistment; _twoPhaseNotifications.Commit(Enlistment); } - void IEnlistmentNotificationInternal.Rollback( - IPromotedEnlistment enlistment - ) + void IEnlistmentNotificationInternal.Rollback(IPromotedEnlistment enlistment) { Debug.Assert(_twoPhaseNotifications != null); _promotedEnlistment = enlistment; _twoPhaseNotifications.Rollback(Enlistment); } - void IEnlistmentNotificationInternal.InDoubt( - IPromotedEnlistment enlistment - ) + void IEnlistmentNotificationInternal.InDoubt(IPromotedEnlistment enlistment) { Debug.Assert(_twoPhaseNotifications != null); _promotedEnlistment = enlistment; diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/IDtcTransaction.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/IDtcTransaction.cs new file mode 100644 index 00000000000000..e0796c1b3c622f --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/IDtcTransaction.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. + +using System.Runtime.InteropServices; + +namespace System.Transactions +{ + [ComImport] + [Guid("0fb15084-af41-11ce-bd2b-204c4f4f5020")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IDtcTransaction + { + void Commit(int retaining, [MarshalAs(UnmanagedType.I4)] int commitType, int reserved); + + void Abort(IntPtr reason, int retaining, int async); + + void GetTransactionInfo(IntPtr transactionInformation); + } +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/InternalTransaction.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/InternalTransaction.cs index 581c9552a60a51..42e1d4c461ed52 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/InternalTransaction.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/InternalTransaction.cs @@ -5,7 +5,8 @@ using System.Diagnostics; using System.Globalization; using System.Threading; -using System.Transactions.Distributed; +using System.Transactions.Oletx; +using OletxTransaction = System.Transactions.Oletx.OletxTransaction; namespace System.Transactions { @@ -93,7 +94,7 @@ internal long CreationTime // These members are used for promoted waves of dependent blocking clones. The Ltm // does not register individually for each blocking clone created in phase 0. Instead // it multiplexes a single phase 0 blocking clone only created after phase 0 has started. - internal DistributedDependentTransaction? _phase0WaveDependentClone; + internal OletxDependentTransaction? _phase0WaveDependentClone; internal int _phase0WaveDependentCloneCount; // These members are used for keeping track of aborting dependent clones if we promote @@ -105,7 +106,7 @@ internal long CreationTime // on the distributed TM takes care of checking to make sure all the aborting dependent // clones have completed as part of its Prepare processing. These are used in conjunction with // phase1volatiles.dependentclones. - internal DistributedDependentTransaction? _abortingDependentClone; + internal OletxDependentTransaction? _abortingDependentClone; internal int _abortingDependentCloneCount; // When the size of the volatile enlistment array grows increase it by this amount. @@ -119,10 +120,10 @@ internal long CreationTime internal TransactionCompletedEventHandler? _transactionCompletedDelegate; // If this transaction get's promoted keep a reference to the promoted transaction - private DistributedTransaction? _promotedTransaction; - internal DistributedTransaction? PromotedTransaction + private OletxTransaction? _promotedTransaction; + internal OletxTransaction? PromotedTransaction { - get { return _promotedTransaction; } + get => _promotedTransaction; set { Debug.Assert(_promotedTransaction == null, "A transaction can only be promoted once!"); @@ -248,7 +249,7 @@ internal InternalTransaction(TimeSpan timeout, CommittableTransaction committabl } // Construct an internal transaction - internal InternalTransaction(Transaction outcomeSource, DistributedTransaction distributedTx) + internal InternalTransaction(Transaction outcomeSource, OletxTransaction distributedTx) { _promotedTransaction = distributedTx; diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/NonWindowsUnsupported.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/NonWindowsUnsupported.cs new file mode 100644 index 00000000000000..01d25a58cc96a0 --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/NonWindowsUnsupported.cs @@ -0,0 +1,154 @@ +// 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.Serialization; +using System.Transactions.Oletx; + +// This files contains non-Windows stubs for Windows-only functionality, so that Sys.Tx can build. The APIs below +// are only ever called when a distributed transaction is needed, and throw PlatformNotSupportedException. + +#pragma warning disable CA1822 + +namespace System.Transactions.Oletx +{ + internal sealed class OletxTransactionManager + { + internal object? NodeName { get; set; } + + internal OletxTransactionManager(string nodeName) + { + } + + internal IPromotedEnlistment ReenlistTransaction( + Guid resourceManagerIdentifier, + byte[] resourceManagerRecoveryInformation, + RecoveringInternalEnlistment internalEnlistment) + => throw NotSupported(); + + internal OletxCommittableTransaction CreateTransaction(TransactionOptions options) + => throw NotSupported(); + + internal void ResourceManagerRecoveryComplete(Guid resourceManagerIdentifier) + => throw NotSupported(); + + internal static byte[] GetWhereabouts() + => throw NotSupported(); + + internal static Transaction GetTransactionFromDtcTransaction(IDtcTransaction transactionNative) + => throw NotSupported(); + + internal static OletxTransaction GetTransactionFromExportCookie(byte[] cookie, Guid txId) + => throw NotSupported(); + + internal static OletxTransaction GetOletxTransactionFromTransmitterPropagationToken(byte[] propagationToken) + => throw NotSupported(); + + internal static Exception NotSupported() + => new PlatformNotSupportedException(SR.DistributedNotSupported); + } + + /// + /// A Transaction object represents a single transaction. It is created by TransactionManager + /// objects through CreateTransaction or through deserialization. Alternatively, the static Create + /// methods provided, which creates a "default" TransactionManager and requests that it create + /// a new transaction with default values. A transaction can only be committed by + /// the client application that created the transaction. If a client application wishes to allow + /// access to the transaction by multiple threads, but wants to prevent those other threads from + /// committing the transaction, the application can make a "clone" of the transaction. Transaction + /// clones have the same capabilities as the original transaction, except for the ability to commit + /// the transaction. + /// + internal class OletxTransaction : ISerializable, IObjectReference + { + internal OletxTransaction() + { + } + + protected OletxTransaction(SerializationInfo serializationInfo, StreamingContext context) + { + //if (serializationInfo == null) + //{ + // throw new ArgumentNullException(nameof(serializationInfo)); + //} + + //throw NotSupported(); + throw new PlatformNotSupportedException(); + } + + internal Exception? InnerException { get; set; } + internal Guid Identifier { get; set; } + internal RealOletxTransaction? RealTransaction { get; set; } + internal TransactionTraceIdentifier TransactionTraceId { get; set; } + internal IsolationLevel IsolationLevel { get; set; } + internal Transaction? SavedLtmPromotedTransaction { get; set; } + + internal IPromotedEnlistment EnlistVolatile( + InternalEnlistment internalEnlistment, + EnlistmentOptions enlistmentOptions) + => throw NotSupported(); + + internal IPromotedEnlistment EnlistDurable( + Guid resourceManagerIdentifier, + DurableInternalEnlistment internalEnlistment, + bool v, + EnlistmentOptions enlistmentOptions) + => throw NotSupported(); + + internal void Rollback() + => throw NotSupported(); + + internal OletxDependentTransaction DependentClone(bool delayCommit) + => throw NotSupported(); + + internal IPromotedEnlistment EnlistVolatile( + VolatileDemultiplexer volatileDemux, + EnlistmentOptions enlistmentOptions) + => throw NotSupported(); + + internal static byte[] GetExportCookie(byte[] whereaboutsCopy) + => throw NotSupported(); + + public object GetRealObject(StreamingContext context) + => throw NotSupported(); + + internal static byte[] GetTransmitterPropagationToken() + => throw NotSupported(); + + internal static IDtcTransaction GetDtcTransaction() + => throw NotSupported(); + + void ISerializable.GetObjectData(SerializationInfo serializationInfo, StreamingContext context) + { + //if (serializationInfo == null) + //{ + // throw new ArgumentNullException(nameof(serializationInfo)); + //} + + //throw NotSupported(); + + throw new PlatformNotSupportedException(); + } + + internal void Dispose() + { + } + + internal static Exception NotSupported() + => new PlatformNotSupportedException(SR.DistributedNotSupported); + + internal sealed class RealOletxTransaction + { + internal InternalTransaction? InternalTransaction { get; set; } + } + } + + internal sealed class OletxDependentTransaction : OletxTransaction + { + internal void Complete() => throw NotSupported(); + } + + internal sealed class OletxCommittableTransaction : OletxTransaction + { + internal void BeginCommit(InternalTransaction tx) => throw NotSupported(); + } +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/DtcTransactionManager.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/DtcTransactionManager.cs new file mode 100644 index 00000000000000..b844f208cccd68 --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/DtcTransactionManager.cs @@ -0,0 +1,132 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using System.Globalization; +using System.Transactions.DtcProxyShim; + +namespace System.Transactions.Oletx; + +internal sealed class DtcTransactionManager +{ + private readonly string? _nodeName; + private readonly OletxTransactionManager _oletxTm; + private readonly DtcProxyShimFactory _proxyShimFactory; + private byte[]? _whereabouts; + + internal DtcTransactionManager(string? nodeName, OletxTransactionManager oletxTm) + { + _nodeName = nodeName; + _oletxTm = oletxTm; + _proxyShimFactory = OletxTransactionManager.ProxyShimFactory; + } + + [MemberNotNull(nameof(_whereabouts))] + private void Initialize() + { + if (_whereabouts is not null) + { + return; + } + + OletxInternalResourceManager internalRM = _oletxTm.InternalResourceManager; + bool nodeNameMatches; + + try + { + _proxyShimFactory.ConnectToProxy( + _nodeName, + internalRM.Identifier, + internalRM, + out nodeNameMatches, + out _whereabouts, + out ResourceManagerShim resourceManagerShim); + + // If the node name does not match, throw. + if (!nodeNameMatches) + { + throw new NotSupportedException(SR.ProxyCannotSupportMultipleNodeNames); + } + + // Give the IResourceManagerShim to the internalRM and tell it to call ReenlistComplete. + internalRM.ResourceManagerShim = resourceManagerShim; + internalRM.CallReenlistComplete(); + } + catch (COMException ex) + { + if (ex.ErrorCode == OletxHelper.XACT_E_NOTSUPPORTED) + { + throw new NotSupportedException(SR.CannotSupportNodeNameSpecification); + } + + OletxTransactionManager.ProxyException(ex); + + // Unfortunately MSDTCPRX may return unknown error codes when attempting to connect to MSDTC + // that error should be propagated back as a TransactionManagerCommunicationException. + throw TransactionManagerCommunicationException.Create(SR.TransactionManagerCommunicationException, ex); + } + } + + internal DtcProxyShimFactory ProxyShimFactory + { + get + { + if (_whereabouts is null) + { + lock (this) + { + Initialize(); + } + } + + return _proxyShimFactory; + } + } + + internal void ReleaseProxy() + { + lock (this) + { + _whereabouts = null; + } + } + + internal byte[] Whereabouts + { + get + { + if (_whereabouts is null) + { + lock (this) + { + Initialize(); + } + } + + return _whereabouts; + } + } + + internal static uint AdjustTimeout(TimeSpan timeout) + { + uint returnTimeout = 0; + + try + { + returnTimeout = Convert.ToUInt32(timeout.TotalMilliseconds, CultureInfo.CurrentCulture); + } + catch (OverflowException caughtEx) + { + // timeout.TotalMilliseconds might be negative, so let's catch overflow exceptions, just in case. + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, caughtEx); + } + + returnTimeout = uint.MaxValue; + } + return returnTimeout; + } +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxCommittableTransaction.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxCommittableTransaction.cs new file mode 100644 index 00000000000000..9e61ccf3689841 --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxCommittableTransaction.cs @@ -0,0 +1,57 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace System.Transactions.Oletx; + +/// +/// A Transaction object represents a single transaction. It is created by TransactionManager +/// objects through CreateTransaction or UnmarshalTransaction. Alternatively, the static Create +/// methodis provided, which creates a "default" TransactionManager and requests that it create +/// a new transaction with default values. A transaction can only be committed by +/// the client application that created the transaction. If a client application wishes to allow +/// access to the transaction by multiple threads, but wants to prevent those other threads from +/// committing the transaction, the application can make a "clone" of the transaction. Transaction +/// clones have the same capabilities as the original transaction, except for the ability to commit +/// the transaction. +/// +[Serializable] +internal sealed class OletxCommittableTransaction : OletxTransaction +{ + private bool _commitCalled; + + /// + /// Constructor for the Transaction object. Specifies the TransactionManager instance that is + /// creating the transaction. + /// + internal OletxCommittableTransaction(RealOletxTransaction realOletxTransaction) + : base(realOletxTransaction) + { + realOletxTransaction.CommittableTransaction = this; + } + + internal bool CommitCalled => _commitCalled; + + internal void BeginCommit(InternalTransaction internalTransaction) + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this); + etwLog.TransactionCommit(TraceSourceType.TraceSourceOleTx, TransactionTraceId, "CommittableTransaction"); + } + + Debug.Assert(0 == Disposed, "OletxTransction object is disposed"); + RealOletxTransaction.InternalTransaction = internalTransaction; + + _commitCalled = true; + + RealOletxTransaction.Commit(); + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxCommittableTransaction)}.{nameof(BeginCommit)}"); + } + } +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxDependentTransaction.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxDependentTransaction.cs new file mode 100644 index 00000000000000..9b2822027a7ab8 --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxDependentTransaction.cs @@ -0,0 +1,64 @@ +// 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.Diagnostics; +using System.Threading; + +namespace System.Transactions.Oletx; + +[Serializable] +internal sealed class OletxDependentTransaction : OletxTransaction +{ + private OletxVolatileEnlistmentContainer _volatileEnlistmentContainer; + + private int _completed; + + internal OletxDependentTransaction(RealOletxTransaction realTransaction, bool delayCommit) + : base(realTransaction) + { + if (realTransaction == null) + { + throw new ArgumentNullException(nameof(realTransaction)); + } + + _volatileEnlistmentContainer = RealOletxTransaction.AddDependentClone(delayCommit); + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.TransactionDependentCloneCreate(TraceSourceType.TraceSourceOleTx, TransactionTraceId, delayCommit + ? DependentCloneOption.BlockCommitUntilComplete + : DependentCloneOption.RollbackIfNotComplete); + } + } + + public void Complete() + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, $"{nameof(DependentTransaction)}.{nameof(Complete)}"); + } + + Debug.Assert(Disposed == 0, "OletxTransction object is disposed"); + + int localCompleted = Interlocked.Exchange(ref _completed, 1); + if (localCompleted == 1) + { + throw TransactionException.CreateTransactionCompletedException(DistributedTxId); + } + + if (etwLog.IsEnabled()) + { + etwLog.TransactionDependentCloneComplete(TraceSourceType.TraceSourceOleTx, TransactionTraceId, "DependentTransaction"); + } + + _volatileEnlistmentContainer.DependentCloneCompleted(); + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"{nameof(DependentTransaction)}.{nameof(Complete)}"); + } + } +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxEnlistment.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxEnlistment.cs new file mode 100644 index 00000000000000..682388828f6c95 --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxEnlistment.cs @@ -0,0 +1,1212 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; +using System.Transactions.DtcProxyShim; + +namespace System.Transactions.Oletx; + +internal sealed class OletxEnlistment : OletxBaseEnlistment, IPromotedEnlistment +{ + internal enum OletxEnlistmentState + { + Active, + Phase0Preparing, + Preparing, + SinglePhaseCommitting, + Prepared, + Committing, + Committed, + Aborting, + Aborted, + InDoubt, + Done + } + + private Phase0EnlistmentShim? _phase0Shim; + private bool _canDoSinglePhase; + private IEnlistmentNotificationInternal? _iEnlistmentNotification; + // The information that comes from/goes to the proxy. + private byte[]? _proxyPrepareInfoByteArray; + + private bool _isSinglePhase; + private Guid _transactionGuid = Guid.Empty; + + // Set to true if we receive an AbortRequest while we still have + // another notification, like prepare, outstanding. It indicates that + // we need to fabricate a rollback to the app after it responds to Prepare. + private bool _fabricateRollback; + + private bool _tmWentDown; + private bool _aborting; + + private byte[]? _prepareInfoByteArray; + + internal Guid TransactionIdentifier => _transactionGuid; + + #region Constructor + + internal OletxEnlistment( + bool canDoSinglePhase, + IEnlistmentNotificationInternal enlistmentNotification, + Guid transactionGuid, + EnlistmentOptions enlistmentOptions, + OletxResourceManager oletxResourceManager, + OletxTransaction oletxTransaction) + : base(oletxResourceManager, oletxTransaction) + { + // This will get set later by the creator of this object after it + // has enlisted with the proxy. + EnlistmentShim = null; + _phase0Shim = null; + + _canDoSinglePhase = canDoSinglePhase; + _iEnlistmentNotification = enlistmentNotification; + State = OletxEnlistmentState.Active; + _transactionGuid = transactionGuid; + + _proxyPrepareInfoByteArray = null; + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.EnlistmentCreated(TraceSourceType.TraceSourceOleTx, InternalTraceIdentifier, EnlistmentType.Durable, enlistmentOptions); + } + + // Always do this last in case anything earlier fails. + AddToEnlistmentTable(); + } + + internal OletxEnlistment( + IEnlistmentNotificationInternal enlistmentNotification, + OletxTransactionStatus xactStatus, + byte[] prepareInfoByteArray, + OletxResourceManager oletxResourceManager) + : base(oletxResourceManager, null) + { + // This will get set later by the creator of this object after it + // has enlisted with the proxy. + EnlistmentShim = null; + _phase0Shim = null; + + _canDoSinglePhase = false; + _iEnlistmentNotification = enlistmentNotification; + State = OletxEnlistmentState.Active; + + // Do this before we do any tracing because it will affect the trace identifiers that we generate. + Debug.Assert(prepareInfoByteArray != null, + "OletxEnlistment.ctor - null oletxTransaction without a prepareInfoByteArray"); + + int prepareInfoLength = prepareInfoByteArray.Length; + _proxyPrepareInfoByteArray = new byte[prepareInfoLength]; + Array.Copy(prepareInfoByteArray, _proxyPrepareInfoByteArray, prepareInfoLength); + + byte[] txGuidByteArray = new byte[16]; + Array.Copy(_proxyPrepareInfoByteArray, txGuidByteArray, 16); + + _transactionGuid = new Guid(txGuidByteArray); + TransactionGuidString = _transactionGuid.ToString(); + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + + // If this is being created as part of a Reenlist and we already know the + // outcome, then tell the application. + switch (xactStatus) + { + case OletxTransactionStatus.OLETX_TRANSACTION_STATUS_ABORTED: + { + State = OletxEnlistmentState.Aborting; + if (etwLog.IsEnabled()) + { + etwLog.EnlistmentStatus(TraceSourceType.TraceSourceOleTx, InternalTraceIdentifier, NotificationCall.Rollback); + } + + _iEnlistmentNotification.Rollback(this); + break; + } + + case OletxTransactionStatus.OLETX_TRANSACTION_STATUS_COMMITTED: + { + State = OletxEnlistmentState.Committing; + // We are going to send the notification to the RM. We need to put the + // enlistment on the reenlistPendingList. We lock the reenlistList because + // we have decided that is the lock that protects both lists. The entry will + // be taken off the reenlistPendingList when the enlistment has + // EnlistmentDone called on it. The enlistment will call + // RemoveFromReenlistPending. + lock (oletxResourceManager.ReenlistList) + { + oletxResourceManager.ReenlistPendingList.Add(this); + } + + if (etwLog.IsEnabled()) + { + etwLog.EnlistmentStatus(TraceSourceType.TraceSourceOleTx, InternalTraceIdentifier, NotificationCall.Commit); + } + + _iEnlistmentNotification.Commit(this); + break; + } + + case OletxTransactionStatus.OLETX_TRANSACTION_STATUS_PREPARED: + { + State = OletxEnlistmentState.Prepared; + lock (oletxResourceManager.ReenlistList) + { + oletxResourceManager.ReenlistList.Add(this); + oletxResourceManager.StartReenlistThread(); + } + break; + } + + default: + { + if (etwLog.IsEnabled()) + { + etwLog.InternalError(SR.OletxEnlistmentUnexpectedTransactionStatus); + } + + throw TransactionException.Create( + SR.OletxEnlistmentUnexpectedTransactionStatus, null, DistributedTxId); + } + } + + if (etwLog.IsEnabled()) + { + etwLog.EnlistmentCreated(TraceSourceType.TraceSourceOleTx, InternalTraceIdentifier, EnlistmentType.Durable, EnlistmentOptions.None); + } + + // Always do this last in case anything prior to this fails. + AddToEnlistmentTable(); + } + #endregion + + internal IEnlistmentNotificationInternal? EnlistmentNotification => _iEnlistmentNotification; + + internal EnlistmentShim? EnlistmentShim { get; set; } + + internal Phase0EnlistmentShim? Phase0EnlistmentShim + { + get => _phase0Shim; + set + { + lock (this) + { + // If this.aborting is set to true, then we must have already received a + // Phase0Request. This could happen if the transaction aborts after the + // enlistment is made, but before we are given the shim. + if (value != null && (_aborting || _tmWentDown)) + { + value.Phase0Done(false); + } + _phase0Shim = value; + } + } + } + + internal OletxEnlistmentState State { get; set; } = OletxEnlistmentState.Active; + + internal byte[]? ProxyPrepareInfoByteArray => _proxyPrepareInfoByteArray; + + internal void FinishEnlistment() + { + lock (this) + { + // If we don't have a wrappedTransactionEnlistmentAsync, we may + // need to remove ourselves from the reenlistPendingList in the + // resource manager. + if (EnlistmentShim == null) + { + OletxResourceManager.RemoveFromReenlistPending(this); + } + _iEnlistmentNotification = null; + + RemoveFromEnlistmentTable(); + } + } + + internal void TMDownFromInternalRM(OletxTransactionManager oletxTm) + { + lock (this) + { + // If we don't have an oletxTransaction or the passed oletxTm matches that of my oletxTransaction, the TM went down. + if (oletxTransaction == null || oletxTm == oletxTransaction.RealOletxTransaction.OletxTransactionManagerInstance) + { + _tmWentDown = true; + } + } + } + + #region ITransactionResourceAsync methods + + // ITranactionResourceAsync.PrepareRequest + public bool PrepareRequest(bool singlePhase, byte[] prepareInfo) + { + EnlistmentShim? localEnlistmentShim; + OletxEnlistmentState localState = OletxEnlistmentState.Active; + IEnlistmentNotificationInternal localEnlistmentNotification; + bool enlistmentDone; + + lock (this) + { + if (OletxEnlistmentState.Active == State) + { + localState = State = OletxEnlistmentState.Preparing; + } + else + { + // We must have done the prepare work in Phase0, so just remember what state we are + // in now. + localState = State; + } + + localEnlistmentNotification = _iEnlistmentNotification!; + + localEnlistmentShim = EnlistmentShim; + + oletxTransaction!.RealOletxTransaction.TooLateForEnlistments = true; + } + + // If we went to Preparing state, send the app + // a prepare request. + if (OletxEnlistmentState.Preparing == localState) + { + _isSinglePhase = singlePhase; + + // Store the prepare info we are given. + Debug.Assert(_proxyPrepareInfoByteArray == null, "Unexpected value in this.proxyPrepareInfoByteArray"); + long arrayLength = prepareInfo.Length; + _proxyPrepareInfoByteArray = new byte[arrayLength]; + Array.Copy(prepareInfo, _proxyPrepareInfoByteArray, arrayLength); + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + + if (_isSinglePhase && _canDoSinglePhase) + { + ISinglePhaseNotificationInternal singlePhaseNotification = (ISinglePhaseNotificationInternal)localEnlistmentNotification; + State = OletxEnlistmentState.SinglePhaseCommitting; + // We don't call DecrementUndecidedEnlistments for Phase1 enlistments. + if (etwLog.IsEnabled()) + { + etwLog.EnlistmentStatus(TraceSourceType.TraceSourceOleTx, InternalTraceIdentifier, NotificationCall.SinglePhaseCommit); + } + + singlePhaseNotification.SinglePhaseCommit(this); + enlistmentDone = true; + } + else + { + State = OletxEnlistmentState.Preparing; + + _prepareInfoByteArray = TransactionManager.GetRecoveryInformation( + OletxResourceManager.OletxTransactionManager.CreationNodeName, + prepareInfo); + + if (etwLog.IsEnabled()) + { + etwLog.EnlistmentStatus(TraceSourceType.TraceSourceOleTx, InternalTraceIdentifier, NotificationCall.Prepare); + } + + localEnlistmentNotification.Prepare(this); + enlistmentDone = false; + } + } + else if (OletxEnlistmentState.Prepared == localState) + { + // We must have done our prepare work during Phase0 so just vote Yes. + try + { + localEnlistmentShim!.PrepareRequestDone(OletxPrepareVoteType.Prepared); + enlistmentDone = false; + } + catch (COMException comException) + { + OletxTransactionManager.ProxyException(comException); + throw; + } + } + else if (OletxEnlistmentState.Done == localState) + { + try + { + // This was an early vote. Respond ReadOnly + try + { + localEnlistmentShim!.PrepareRequestDone(OletxPrepareVoteType.ReadOnly); + enlistmentDone = true; + } + finally + { + FinishEnlistment(); + } + } + catch (COMException comException) + { + OletxTransactionManager.ProxyException(comException); + throw; + } + } + else + { + // Any other state means we should vote NO to the proxy. + try + { + localEnlistmentShim!.PrepareRequestDone(OletxPrepareVoteType.Failed); + } + catch (COMException ex) + { + // No point in rethrowing this. We are not on an app thread and we have already told + // the app that the transaction is aborting. When the app calls EnlistmentDone, we will + // do the final release of the ITransactionEnlistmentAsync. + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, ex); + } + } + + enlistmentDone = true; + } + + return enlistmentDone; + } + + + public void CommitRequest() + { + OletxEnlistmentState localState = OletxEnlistmentState.Active; + IEnlistmentNotificationInternal? localEnlistmentNotification = null; + EnlistmentShim? localEnlistmentShim = null; + bool finishEnlistment = false; + + lock (this) + { + if (OletxEnlistmentState.Prepared == State) + { + localState = State = OletxEnlistmentState.Committing; + localEnlistmentNotification = _iEnlistmentNotification; + } + else + { + // We must have received an EnlistmentDone already. + localState = State; + localEnlistmentShim = EnlistmentShim; + finishEnlistment = true; + } + } + + if (localEnlistmentNotification != null) + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.EnlistmentStatus(TraceSourceType.TraceSourceOleTx, InternalTraceIdentifier, NotificationCall.Commit); + } + + localEnlistmentNotification.Commit(this); + } + else if (localEnlistmentShim != null) + { + // We need to respond to the proxy now. + try + { + localEnlistmentShim.CommitRequestDone(); + } + catch (COMException ex) + { + // If the TM went down during our call, there is nothing special we have to do because + // the App doesn't expect any more notifications. We do want to mark the enlistment + // to finish, however. + if (ex.ErrorCode == OletxHelper.XACT_E_CONNECTION_DOWN || + ex.ErrorCode == OletxHelper.XACT_E_TMNOTAVAILABLE) + { + finishEnlistment = true; + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, ex); + } + } + else + { + throw; + } + } + finally + { + if (finishEnlistment) + { + FinishEnlistment(); + } + } + } + } + + public void AbortRequest() + { + OletxEnlistmentState localState = OletxEnlistmentState.Active; + IEnlistmentNotificationInternal? localEnlistmentNotification = null; + EnlistmentShim? localEnlistmentShim = null; + bool finishEnlistment = false; + + lock (this) + { + if (State is OletxEnlistmentState.Active or OletxEnlistmentState.Prepared) + { + localState = State = OletxEnlistmentState.Aborting; + localEnlistmentNotification = _iEnlistmentNotification; + } + else + { + // We must have received an EnlistmentDone already or we have + // a notification outstanding (Phase0 prepare). + localState = State; + if (OletxEnlistmentState.Phase0Preparing == State) + { + _fabricateRollback = true; + } + else + { + finishEnlistment = true; + } + + localEnlistmentShim = EnlistmentShim; + } + } + + if (localEnlistmentNotification != null) + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.EnlistmentStatus(TraceSourceType.TraceSourceOleTx, InternalTraceIdentifier, NotificationCall.Rollback); + } + + localEnlistmentNotification.Rollback(this); + } + else if (localEnlistmentShim != null) + { + // We need to respond to the proxy now. + try + { + localEnlistmentShim.AbortRequestDone(); + } + catch (COMException ex) + { + // If the TM went down during our call, there is nothing special we have to do because + // the App doesn't expect any more notifications. We do want to mark the enlistment + // to finish, however. + if (ex.ErrorCode == OletxHelper.XACT_E_CONNECTION_DOWN || + ex.ErrorCode == OletxHelper.XACT_E_TMNOTAVAILABLE) + { + finishEnlistment = true; + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, ex); + } + } + else + { + throw; + } + } + finally + { + if (finishEnlistment) + { + FinishEnlistment(); + } + } + } + } + + public void TMDown() + { + // We aren't telling our enlistments about TMDown, only + // resource managers. + // Put this enlistment on the Reenlist list. The Reenlist thread will get + // started when the RMSink gets the TMDown notification. + lock (OletxResourceManager.ReenlistList) + { + lock (this) + { + // Remember that we got the TMDown in case we get a Phase0Request after so we + // can avoid doing a Prepare to the app. + _tmWentDown = true; + + // Only move Prepared and Committing enlistments to the ReenlistList. All others + // do not require a Reenlist to figure out what to do. We save off Committing + // enlistments because the RM has not acknowledged the commit, so we can't + // call RecoveryComplete on the proxy until that has happened. The Reenlist thread + // will loop until the reenlist list is empty and it will leave a Committing + // enlistment on the list until it is done, but will NOT call Reenlist on the proxy. + if (State is OletxEnlistmentState.Prepared or OletxEnlistmentState.Committing) + { + OletxResourceManager.ReenlistList.Add(this); + } + } + } + } + + #endregion + + #region ITransactionPhase0NotifyAsync methods + + // ITransactionPhase0NotifyAsync + public void Phase0Request(bool abortingHint) + { + IEnlistmentNotificationInternal? localEnlistmentNotification = null; + OletxEnlistmentState localState = OletxEnlistmentState.Active; + OletxCommittableTransaction? committableTx; + bool commitNotYetCalled = false; + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxEnlistment)}.{nameof(Phase0Request)}"); + } + + committableTx = oletxTransaction!.RealOletxTransaction.CommittableTransaction; + if (committableTx != null) + { + // We are dealing with the committable transaction. If Commit or BeginCommit has NOT been + // called, then we are dealing with a situation where the TM went down and we are getting + // a bogus Phase0Request with abortHint = false (COMPlus bug 36760/36758). This is an attempt + // to not send the app a Prepare request when we know the transaction is going to abort. + if (!committableTx.CommitCalled) + { + commitNotYetCalled = true; + } + } + + lock (this) + { + _aborting = abortingHint; + + // The app may have already called EnlistmentDone. If this occurs, don't bother sending + // the notification to the app and we don't need to tell the proxy. + if (OletxEnlistmentState.Active == State) + { + // If we got an abort hint or we are the committable transaction and Commit has not yet been called or the TM went down, + // we don't want to do any more work on the transaction. The abort notifications will be sent by the phase 1 + // enlistment + if (_aborting || commitNotYetCalled || _tmWentDown) + { + // There is a possible race where we could get the Phase0Request before we are given the + // shim. In that case, we will vote "no" when we are given the shim. + if (_phase0Shim != null) + { + try + { + _phase0Shim.Phase0Done(false); + } + // I am not going to check for XACT_E_PROTOCOL here because that check is a workaround for a bug + // that only shows up if abortingHint is false. + catch (COMException ex) + { + if (etwLog.IsEnabled()) + { + etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, ex); + } + } + } + } + else + { + localState = State = OletxEnlistmentState.Phase0Preparing; + localEnlistmentNotification = _iEnlistmentNotification; + } + } + } + + // Tell the application to do the work. + if (localEnlistmentNotification != null) + { + if (OletxEnlistmentState.Phase0Preparing == localState) + { + byte[] txGuidArray = _transactionGuid.ToByteArray(); + byte[] rmGuidArray = OletxResourceManager.ResourceManagerIdentifier.ToByteArray(); + + byte[] temp = new byte[txGuidArray.Length + rmGuidArray.Length]; + Thread.MemoryBarrier(); + _proxyPrepareInfoByteArray = temp; + for (int index = 0; index < txGuidArray.Length; index++) + { + _proxyPrepareInfoByteArray[index] = + txGuidArray[index]; + } + + for (int index = 0; index < rmGuidArray.Length; index++) + { + _proxyPrepareInfoByteArray[txGuidArray.Length + index] = + rmGuidArray[index]; + } + + _prepareInfoByteArray = TransactionManager.GetRecoveryInformation( + OletxResourceManager.OletxTransactionManager.CreationNodeName, + _proxyPrepareInfoByteArray); + + if (etwLog.IsEnabled()) + { + etwLog.EnlistmentStatus(TraceSourceType.TraceSourceOleTx, InternalTraceIdentifier, NotificationCall.Prepare); + } + + localEnlistmentNotification.Prepare(this); + } + else + { + // We must have had a race between EnlistmentDone and the proxy telling + // us Phase0Request. Just return. + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxEnlistment)}.{nameof(Phase0Request)}"); + } + + return; + } + + } + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxEnlistment)}.{nameof(Phase0Request)}"); + } + } + + #endregion + + public void EnlistmentDone() + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxEnlistment)}.{nameof(EnlistmentDone)}"); + etwLog.EnlistmentCallbackPositive(InternalTraceIdentifier, EnlistmentCallback.Done); + } + + EnlistmentShim? localEnlistmentShim = null; + Phase0EnlistmentShim? localPhase0Shim = null; + OletxEnlistmentState localState = OletxEnlistmentState.Active; + bool finishEnlistment; + bool localFabricateRollback; + + lock (this) + { + localState = State; + if (OletxEnlistmentState.Active == State) + { + // Early vote. If we are doing Phase0, we need to unenlist. Otherwise, just + // remember. + localPhase0Shim = Phase0EnlistmentShim; + if (localPhase0Shim != null) + { + // We are a Phase0 enlistment and we have a vote - decrement the undecided enlistment count. + // We only do this for Phase0 because we don't count Phase1 durable enlistments. + oletxTransaction!.RealOletxTransaction.DecrementUndecidedEnlistments(); + } + finishEnlistment = false; + } + else if (OletxEnlistmentState.Preparing == State) + { + // Read only vote. Tell the proxy and go to the Done state. + localEnlistmentShim = EnlistmentShim; + // We don't decrement the undecided enlistment count for Preparing because we only count + // Phase0 enlistments and we are in Phase1 in Preparing state. + finishEnlistment = true; + } + else if (OletxEnlistmentState.Phase0Preparing == State) + { + // Read only vote to Phase0. Tell the proxy okay and go to the Done state. + localPhase0Shim = Phase0EnlistmentShim; + // We are a Phase0 enlistment and we have a vote - decrement the undecided enlistment count. + // We only do this for Phase0 because we don't count Phase1 durable enlistments. + oletxTransaction!.RealOletxTransaction.DecrementUndecidedEnlistments(); + + // If we would have fabricated a rollback then we have already received an abort request + // from proxy and will not receive any more notifications. Otherwise more notifications + // will be coming. + if (_fabricateRollback) + { + finishEnlistment = true; + } + else + { + finishEnlistment = false; + } + } + else if (State is OletxEnlistmentState.Committing + or OletxEnlistmentState.Aborting + or OletxEnlistmentState.SinglePhaseCommitting) + { + localEnlistmentShim = EnlistmentShim; + finishEnlistment = true; + // We don't decrement the undecided enlistment count for SinglePhaseCommitting because we only + // do it for Phase0 enlistments. + } + else + { + throw TransactionException.CreateEnlistmentStateException(null, DistributedTxId); + } + + // If this.fabricateRollback is true, it means that we are fabricating this + // AbortRequest, rather than having the proxy tell us. So we don't need + // to respond to the proxy with AbortRequestDone. + localFabricateRollback = _fabricateRollback; + + State = OletxEnlistmentState.Done; + } + + try + { + if (localEnlistmentShim != null) + { + if (OletxEnlistmentState.Preparing == localState) + { + localEnlistmentShim.PrepareRequestDone(OletxPrepareVoteType.ReadOnly); + } + else if (OletxEnlistmentState.Committing == localState) + { + localEnlistmentShim.CommitRequestDone(); + } + else if (OletxEnlistmentState.Aborting == localState) + { + // If localFabricatRollback is true, it means that we are fabricating this + // AbortRequest, rather than having the proxy tell us. So we don't need + // to respond to the proxy with AbortRequestDone. + if (!localFabricateRollback) + { + localEnlistmentShim.AbortRequestDone(); + } + } + else if (OletxEnlistmentState.SinglePhaseCommitting == localState) + { + localEnlistmentShim.PrepareRequestDone(OletxPrepareVoteType.SinglePhase); + } + else + { + throw TransactionException.CreateEnlistmentStateException(null, DistributedTxId); + } + } + else if (localPhase0Shim != null) + { + if (localState == OletxEnlistmentState.Active) + { + localPhase0Shim.Unenlist(); + } + else if (localState == OletxEnlistmentState.Phase0Preparing) + { + localPhase0Shim.Phase0Done(true); + } + else + { + throw TransactionException.CreateEnlistmentStateException(null, DistributedTxId); + } + } + } + catch (COMException ex) + { + // If we get an error talking to the proxy, there is nothing special we have to do because + // the App doesn't expect any more notifications. We do want to mark the enlistment + // to finish, however. + finishEnlistment = true; + + if (etwLog.IsEnabled()) + { + etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, ex); + } + } + finally + { + if (finishEnlistment) + { + FinishEnlistment(); + } + } + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxEnlistment)}.{nameof(EnlistmentDone)}"); + } + } + + public EnlistmentTraceIdentifier EnlistmentTraceId + { + get + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxEnlistment)}.{nameof(EnlistmentTraceId)}"); + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxEnlistment)}.{nameof(EnlistmentTraceId)}"); + } + + return InternalTraceIdentifier; + } + } + + public void Prepared() + { + int hrResult = OletxHelper.S_OK; + EnlistmentShim? localEnlistmentShim = null; + Phase0EnlistmentShim? localPhase0Shim = null; + bool localFabricateRollback = false; + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, $"OletxPreparingEnlistment.{nameof(Prepared)}"); + etwLog.EnlistmentCallbackPositive(InternalTraceIdentifier, EnlistmentCallback.Prepared); + } + + lock (this) + { + if (State == OletxEnlistmentState.Preparing) + { + localEnlistmentShim = EnlistmentShim; + } + else if (OletxEnlistmentState.Phase0Preparing == State) + { + // If the transaction is doomed or we have fabricateRollback is true because the + // transaction aborted while the Phase0 Prepare request was outstanding, + // release the WrappedTransactionPhase0EnlistmentAsync and remember that + // we have a pending rollback. + localPhase0Shim = Phase0EnlistmentShim; + if (oletxTransaction!.RealOletxTransaction.Doomed || _fabricateRollback) + { + // Set fabricateRollback in case we got here because the transaction is doomed. + _fabricateRollback = true; + localFabricateRollback = _fabricateRollback; + } + } + else + { + throw TransactionException.CreateEnlistmentStateException(null, DistributedTxId); + } + + State = OletxEnlistmentState.Prepared; + } + + try + { + if (localEnlistmentShim != null) + { + localEnlistmentShim.PrepareRequestDone(OletxPrepareVoteType.Prepared); + } + else if (localPhase0Shim != null) + { + // We have a vote - decrement the undecided enlistment count. We do + // this after checking Doomed because ForceRollback will decrement also. + // We also do this only for Phase0 enlistments. + oletxTransaction!.RealOletxTransaction.DecrementUndecidedEnlistments(); + + localPhase0Shim.Phase0Done(!localFabricateRollback); + } + else + { + // The TM must have gone down, thus causing our interface pointer to be + // invalidated. So we need to drive abort of the enlistment as if we + // received an AbortRequest. + localFabricateRollback = true; + } + + if (localFabricateRollback) + { + AbortRequest(); + } + } + catch (COMException ex) + { + // If the TM went down during our call, the TMDown notification to the enlistment + // and RM will put this enlistment on the ReenlistList, if appropriate. The outcome + // will be obtained by the ReenlistThread. + if ((ex.ErrorCode == OletxHelper.XACT_E_CONNECTION_DOWN || ex.ErrorCode == OletxHelper.XACT_E_TMNOTAVAILABLE) && etwLog.IsEnabled()) + { + etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, ex); + } + // In the case of Phase0, there is a bug in the proxy that causes an XACT_E_PROTOCOL + // error if the TM goes down while the enlistment is still active. The Phase0Request is + // sent out with abortHint false, but the state of the proxy object is not changed, causing + // Phase0Done request to fail with XACT_E_PROTOCOL. + // For Prepared, we want to make sure the proxy aborts the transaction. We don't need + // to drive the abort to the application here because the Phase1 enlistment will do that. + // In other words, treat this as if the proxy said Phase0Request( abortingHint = true ). + else if (ex.ErrorCode == OletxHelper.XACT_E_PROTOCOL) + { + Phase0EnlistmentShim = null; + + if (etwLog.IsEnabled()) + { + etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, ex); + } + } + else + { + throw; + } + } + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"OletxPreparingEnlistment.{nameof(Prepared)}"); + } + } + + public void ForceRollback() + => ForceRollback(null); + + public void ForceRollback(Exception? e) + { + EnlistmentShim? localEnlistmentShim = null; + Phase0EnlistmentShim? localPhase0Shim = null; + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, $"OletxPreparingEnlistment.{nameof(ForceRollback)}"); + etwLog.EnlistmentCallbackNegative(InternalTraceIdentifier, EnlistmentCallback.ForceRollback); + } + + lock (this) + { + if (OletxEnlistmentState.Preparing == State) + { + localEnlistmentShim = EnlistmentShim; + } + else if (OletxEnlistmentState.Phase0Preparing == State) + { + localPhase0Shim = Phase0EnlistmentShim; + if (localPhase0Shim != null) + { + // We have a vote - decrement the undecided enlistment count. We only do this + // if we are Phase0 enlistment. + oletxTransaction!.RealOletxTransaction.DecrementUndecidedEnlistments(); + } + } + else + { + throw TransactionException.CreateEnlistmentStateException(null, DistributedTxId); + } + + State = OletxEnlistmentState.Aborted; + } + + Interlocked.CompareExchange(ref oletxTransaction!.RealOletxTransaction.InnerException, e, null); + + try + { + if (localEnlistmentShim != null) + { + localEnlistmentShim.PrepareRequestDone(OletxPrepareVoteType.Failed); + } + } + catch (COMException ex) + { + // If the TM went down during our call, there is nothing special we have to do because + // the App doesn't expect any more notifications. + if (ex.ErrorCode == OletxHelper.XACT_E_CONNECTION_DOWN || + ex.ErrorCode == OletxHelper.XACT_E_TMNOTAVAILABLE) + { + if (etwLog.IsEnabled()) + { + etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, ex); + } + } + else + { + throw; + } + } + finally + { + FinishEnlistment(); + } + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"OletxPreparingEnlistment.{nameof(ForceRollback)}"); + } + } + + public void Committed() + { + EnlistmentShim? localEnlistmentShim = null; + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, $"OletxSinglePhaseEnlistment.{nameof(Committed)}"); + etwLog.EnlistmentCallbackPositive(InternalTraceIdentifier, EnlistmentCallback.Committed); + } + + lock (this) + { + if (!_isSinglePhase || OletxEnlistmentState.SinglePhaseCommitting != State) + { + throw TransactionException.CreateEnlistmentStateException(null, DistributedTxId); + } + State = OletxEnlistmentState.Committed; + localEnlistmentShim = EnlistmentShim; + } + + try + { + // This may be the result of a reenlist, which means we don't have a + // reference to the proxy. + if (localEnlistmentShim != null) + { + localEnlistmentShim.PrepareRequestDone(OletxPrepareVoteType.SinglePhase); + } + } + catch (COMException ex) + { + // If the TM went down during our call, there is nothing special we have to do because + // the App doesn't expect any more notifications. + if (ex.ErrorCode == OletxHelper.XACT_E_CONNECTION_DOWN || + ex.ErrorCode == OletxHelper.XACT_E_TMNOTAVAILABLE) + { + if (etwLog.IsEnabled()) + { + etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, ex); + } + } + else + { + throw; + } + } + finally + { + FinishEnlistment(); + } + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"OletxSinglePhaseEnlistment.{nameof(Committed)}"); + } + } + + public void Aborted() + => Aborted(null); + + public void Aborted(Exception? e) + { + EnlistmentShim? localEnlistmentShim = null; + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, $"OletxSinglePhaseEnlistment.{nameof(Aborted)}"); + etwLog.EnlistmentCallbackNegative(InternalTraceIdentifier, EnlistmentCallback.Aborted); + } + + lock (this) + { + if (!_isSinglePhase || OletxEnlistmentState.SinglePhaseCommitting != State) + { + throw TransactionException.CreateEnlistmentStateException(null, DistributedTxId); + } + State = OletxEnlistmentState.Aborted; + + localEnlistmentShim = EnlistmentShim; + } + + Interlocked.CompareExchange(ref oletxTransaction!.RealOletxTransaction.InnerException, e, null); + + try + { + if (localEnlistmentShim != null) + { + localEnlistmentShim.PrepareRequestDone(OletxPrepareVoteType.Failed); + } + } + // If the TM went down during our call, there is nothing special we have to do because + // the App doesn't expect any more notifications. + catch (COMException ex) when ( + (ex.ErrorCode == OletxHelper.XACT_E_CONNECTION_DOWN || ex.ErrorCode == OletxHelper.XACT_E_TMNOTAVAILABLE) && etwLog.IsEnabled()) + { + etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, ex); + } + finally + { + FinishEnlistment(); + } + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"OletxSinglePhaseEnlistment.{nameof(Aborted)}"); + } + } + + public void InDoubt() + => InDoubt(null); + + public void InDoubt(Exception? e) + { + EnlistmentShim? localEnlistmentShim = null; + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, $"OletxSinglePhaseEnlistment.{nameof(InDoubt)}"); + etwLog.EnlistmentCallbackNegative(InternalTraceIdentifier, EnlistmentCallback.InDoubt); + } + + lock (this) + { + if (!_isSinglePhase || OletxEnlistmentState.SinglePhaseCommitting != State) + { + throw TransactionException.CreateEnlistmentStateException(null, DistributedTxId); + } + State = OletxEnlistmentState.InDoubt; + localEnlistmentShim = EnlistmentShim; + } + + lock (oletxTransaction!.RealOletxTransaction) + { + oletxTransaction.RealOletxTransaction.InnerException ??= e; + } + + try + { + if (localEnlistmentShim != null) + { + localEnlistmentShim.PrepareRequestDone(OletxPrepareVoteType.InDoubt); + } + } + // If the TM went down during our call, there is nothing special we have to do because + // the App doesn't expect any more notifications. + catch (COMException ex) when ( + (ex.ErrorCode == OletxHelper.XACT_E_CONNECTION_DOWN || ex.ErrorCode == OletxHelper.XACT_E_TMNOTAVAILABLE) && etwLog.IsEnabled()) + { + etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, ex); + } + finally + { + FinishEnlistment(); + } + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"OletxSinglePhaseEnlistment.{nameof(InDoubt)}"); + } + } + + public byte[] GetRecoveryInformation() + { + if (_prepareInfoByteArray == null) + { + Debug.Fail(string.Format(null, "this.prepareInfoByteArray == null in RecoveryInformation()")); + throw TransactionException.CreateEnlistmentStateException(null, DistributedTxId); + } + + return _prepareInfoByteArray; + } + + InternalEnlistment? IPromotedEnlistment.InternalEnlistment + { + get => base.InternalEnlistment; + set => base.InternalEnlistment = value; + } +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxResourceManager.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxResourceManager.cs new file mode 100644 index 00000000000000..be9a099184c488 --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxResourceManager.cs @@ -0,0 +1,896 @@ +// 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; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; +using System.Transactions.DtcProxyShim; + +namespace System.Transactions.Oletx; + +internal sealed class OletxResourceManager +{ + internal Guid ResourceManagerIdentifier; + + internal ResourceManagerShim? resourceManagerShim; + internal Hashtable EnlistmentHashtable; + internal static Hashtable VolatileEnlistmentHashtable = new Hashtable(); + internal OletxTransactionManager OletxTransactionManager; + + // reenlistList is a simple ArrayList of OletxEnlistment objects that are either in the + // Preparing or Prepared state when we receive a TMDown notification or have had + // ReenlistTransaction called for them. The ReenlistThread is responsible for traversing this + // list trying to obtain the outcome for the enlistments. All access, read or write, to this + // list should get a lock on the list. + // Special Note: If you are going to lock both the OletxResourceManager object AND the + // reenlistList, lock the reenlistList FIRST. + internal ArrayList ReenlistList; + + // reenlistPendingList is also a simple ArrayList of OletxEnlistment objects. But for these + // we have received the outcome from the proxy and have called into the RM to deliver the + // notification, but the RM has not yet called EnlistmentDone to let us know that the outcome + // has been processed. This list must be empty, in addition to the reenlistList, in order for + // the ReenlistThread to call RecoveryComplete and not be rescheduled. Access to this list + // should be protected by locking the reenlistList. The lists are always accessed together, + // so there is no reason to grab two locks. + internal ArrayList ReenlistPendingList; + + // This is where we keep the reenlistThread and thread timer values. If there is a reenlist thread running, + // reenlistThread will be non-null. If reenlistThreadTimer is non-null, we have a timer scheduled which will + // fire off a reenlist thread when it expires. Only one or the other should be non-null at a time. However, they + // could both be null, which means that there is no reenlist thread running and there is no timer scheduled to + // create one. Access to these members should be done only after obtaining a lock on the OletxResourceManager object. + internal Timer? ReenlistThreadTimer; + internal Thread? reenlistThread; + + // This boolean is set to true if the resource manager application has called RecoveryComplete. + // A lock on the OletxResourceManager instance will be obtained when retrieving or modifying + // this value. Before calling ReenlistComplete on the DTC proxy, this value must be true. + internal bool RecoveryCompleteCalledByApplication { get; set; } + + internal OletxResourceManager(OletxTransactionManager transactionManager, Guid resourceManagerIdentifier) + { + Debug.Assert(transactionManager != null, "Argument is null"); + + // This will get set later, after the resource manager is created with the proxy. + resourceManagerShim = null; + OletxTransactionManager = transactionManager; + ResourceManagerIdentifier = resourceManagerIdentifier; + + EnlistmentHashtable = new Hashtable(); + ReenlistList = new ArrayList(); + ReenlistPendingList = new ArrayList(); + + ReenlistThreadTimer = null; + reenlistThread = null; + RecoveryCompleteCalledByApplication = false; + } + + internal ResourceManagerShim? ResourceManagerShim + { + get + { + ResourceManagerShim? localResourceManagerShim = null; + + if (resourceManagerShim == null) + { + lock (this) + { + if (resourceManagerShim == null) + { + OletxTransactionManager.DtcTransactionManagerLock.AcquireReaderLock( -1 ); + try + { + Guid rmGuid = ResourceManagerIdentifier; + + OletxTransactionManager.DtcTransactionManager.ProxyShimFactory.CreateResourceManager( + rmGuid, + this, + out localResourceManagerShim); + } + catch (COMException ex) + { + if (ex.ErrorCode == OletxHelper.XACT_E_CONNECTION_DOWN || + ex.ErrorCode == OletxHelper.XACT_E_TMNOTAVAILABLE) + { + // Just to make sure... + localResourceManagerShim = null; + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, ex); + } + } + else + { + throw; + } + } + catch (TransactionException ex) + { + if (ex.InnerException is COMException comEx) + { + // Tolerate TM down. + if (comEx.ErrorCode == OletxHelper.XACT_E_CONNECTION_DOWN || + comEx.ErrorCode == OletxHelper.XACT_E_TMNOTAVAILABLE) + { + // Just to make sure... + localResourceManagerShim = null; + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, ex); + } + } + else + { + throw; + } + } + else + { + throw; + } + } + finally + { + OletxTransactionManager.DtcTransactionManagerLock.ReleaseReaderLock(); + } + Thread.MemoryBarrier(); + resourceManagerShim = localResourceManagerShim; + } + } + } + return resourceManagerShim; + } + + set + { + Debug.Assert(value == null, "set_ResourceManagerShim, value not null"); + resourceManagerShim = value; + } + } + + internal bool CallProxyReenlistComplete() + { + bool success = false; + if (RecoveryCompleteCalledByApplication) + { + ResourceManagerShim? localResourceManagerShim; + try + { + localResourceManagerShim = ResourceManagerShim; + if (localResourceManagerShim != null) + { + localResourceManagerShim.ReenlistComplete(); + success = true; + } + // If we don't have an iResourceManagerOletx, just tell the caller that + // we weren't successful and it will schedule a retry. + } + catch (COMException ex) + { + // If we get a TMDown error, eat it and indicate that we were unsuccessful. + if (ex.ErrorCode == OletxHelper.XACT_E_CONNECTION_DOWN || + ex.ErrorCode == OletxHelper.XACT_E_TMNOTAVAILABLE) + { + success = false; + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, ex); + } + } + + // We might get an XACT_E_RECOVERYALREADYDONE if there are multiple OletxTransactionManager + // objects for the same backend TM. We can safely ignore this error. + else if (ex.ErrorCode != OletxHelper.XACT_E_RECOVERYALREADYDONE) + { + OletxTransactionManager.ProxyException(ex); + throw; + } + // Getting XACT_E_RECOVERYALREADYDONE is considered success. + else + { + success = true; + } + } + finally + { + localResourceManagerShim = null; + } + } + else // The application has not yet called RecoveryComplete, so lie just a little. + { + success = true; + } + + return success; + } + + // This is called by the internal RM when it gets a TM Down notification. This routine will + // tell the enlistments about the TMDown from the internal RM. The enlistments will then + // decide what to do, based on their state. This is mainly to work around COMPlus bug 36760/36758, + // where Phase0 enlistments get Phase0Request( abortHint = false ) when the TM goes down. We want + // to try to avoid telling the application to prepare when we know the transaction will abort. + // We can't do this out of the normal TMDown notification to the RM because it is too late. The + // Phase0Request gets sent before the TMDown notification. + internal void TMDownFromInternalRM(OletxTransactionManager oletxTM) + { + Hashtable localEnlistmentHashtable; + IDictionaryEnumerator enlistEnum; + OletxEnlistment? enlistment; + + // If the internal RM got a TMDown, we will shortly, so null out our ResourceManagerShim now. + ResourceManagerShim = null; + + // Make our own copy of the hashtable of enlistments. + lock (EnlistmentHashtable.SyncRoot) + { + localEnlistmentHashtable = (Hashtable)EnlistmentHashtable.Clone(); + } + + // Tell all of our enlistments that the TM went down. The proxy only + // tells enlistments that are in the Prepared state, but we want our Phase0 + // enlistments to know so they can avoid sending Prepare when they get a + // Phase0Request - COMPlus bug 36760/36758. + enlistEnum = localEnlistmentHashtable.GetEnumerator(); + while (enlistEnum.MoveNext()) + { + enlistment = enlistEnum.Value as OletxEnlistment; + enlistment?.TMDownFromInternalRM(oletxTM); + } + } + + public void TMDown() + { + // The ResourceManagerShim was already set to null by TMDownFromInternalRM, so we don't need to do it again here. + // Just start the ReenlistThread. + StartReenlistThread(); + } + + internal OletxEnlistment EnlistDurable( + OletxTransaction oletxTransaction, + bool canDoSinglePhase, + IEnlistmentNotificationInternal enlistmentNotification, + EnlistmentOptions enlistmentOptions) + { + ResourceManagerShim? localResourceManagerShim; + + Debug.Assert(oletxTransaction != null, "Argument is null" ); + Debug.Assert(enlistmentNotification != null, "Argument is null" ); + + EnlistmentShim enlistmentShim; + Phase0EnlistmentShim phase0Shim; + Guid txUow = Guid.Empty; + bool undecidedEnlistmentsIncremented = false; + + // Create our enlistment object. + OletxEnlistment enlistment = new( + canDoSinglePhase, + enlistmentNotification, + oletxTransaction.RealTransaction.TxGuid, + enlistmentOptions, + this, + oletxTransaction); + + bool enlistmentSucceeded = false; + + try + { + if ((enlistmentOptions & EnlistmentOptions.EnlistDuringPrepareRequired) != 0) + { + oletxTransaction.RealTransaction.IncrementUndecidedEnlistments(); + undecidedEnlistmentsIncremented = true; + } + + // This entire sequence needs to be executed before we can go on. + lock (enlistment) + { + try + { + // Do the enlistment on the proxy. + localResourceManagerShim = ResourceManagerShim; + if (localResourceManagerShim == null) + { + // The TM must be down. Throw the appropriate exception. + throw TransactionManagerCommunicationException.Create(SR.TraceSourceOletx, null); + } + + if ((enlistmentOptions & EnlistmentOptions.EnlistDuringPrepareRequired) != 0) + { + oletxTransaction.RealTransaction.TransactionShim.Phase0Enlist(enlistment, out phase0Shim); + enlistment.Phase0EnlistmentShim = phase0Shim; + } + + localResourceManagerShim.Enlist(oletxTransaction.RealTransaction.TransactionShim, enlistment, out enlistmentShim); + + enlistment.EnlistmentShim = enlistmentShim; + } + catch (COMException comException) + { + // There is no string mapping for XACT_E_TOOMANY_ENLISTMENTS, so we need to do it here. + if (comException.ErrorCode == OletxHelper.XACT_E_TOOMANY_ENLISTMENTS) + { + throw TransactionException.Create( + SR.OletxTooManyEnlistments, + comException, + enlistment == null ? Guid.Empty : enlistment.DistributedTxId); + } + + OletxTransactionManager.ProxyException(comException); + + throw; + } + } + + enlistmentSucceeded = true; + } + finally + { + if (!enlistmentSucceeded && + (enlistmentOptions & EnlistmentOptions.EnlistDuringPrepareRequired) != 0 && + undecidedEnlistmentsIncremented) + { + oletxTransaction.RealTransaction.DecrementUndecidedEnlistments(); + } + } + + return enlistment; + } + + internal OletxEnlistment Reenlist(byte[] prepareInfo, IEnlistmentNotificationInternal enlistmentNotification) + { + OletxTransactionOutcome outcome = OletxTransactionOutcome.NotKnownYet; + OletxTransactionStatus xactStatus = OletxTransactionStatus.OLETX_TRANSACTION_STATUS_NONE; + + if (prepareInfo == null) + { + throw new ArgumentException(SR.InvalidArgument, nameof(prepareInfo)); + } + + // Verify that the resource manager guid in the recovery info matches that of the calling resource manager. + byte[] rmGuidArray = new byte[16]; + for (int i = 0; i < 16; i++) + { + rmGuidArray[i] = prepareInfo[i + 16]; + } + Guid rmGuid = new(rmGuidArray); + if (rmGuid != ResourceManagerIdentifier) + { + throw TransactionException.Create(TraceSourceType.TraceSourceOleTx, SR.ResourceManagerIdDoesNotMatchRecoveryInformation, null); + } + + // Ask the proxy resource manager to reenlist. + ResourceManagerShim? localResourceManagerShim = null; + try + { + localResourceManagerShim = ResourceManagerShim; + if (localResourceManagerShim == null) + { + // The TM must be down. Throw the exception that will get caught below and will cause + // the enlistment to start the ReenlistThread. The TMDown thread will be trying to reestablish + // connection with the TM and will start the reenlist thread when it does. + throw new COMException(SR.DtcTransactionManagerUnavailable, OletxHelper.XACT_E_CONNECTION_DOWN); + } + + // Only wait for 5 milliseconds. If the TM doesn't have the outcome now, we will + // put the enlistment on the reenlistList for later processing. + localResourceManagerShim.Reenlist(prepareInfo, out outcome); + + if (OletxTransactionOutcome.Committed == outcome) + { + xactStatus = OletxTransactionStatus.OLETX_TRANSACTION_STATUS_COMMITTED; + } + else if (OletxTransactionOutcome.Aborted == outcome) + { + xactStatus = OletxTransactionStatus.OLETX_TRANSACTION_STATUS_ABORTED; + } + else // we must not know the outcome yet. + { + xactStatus = OletxTransactionStatus.OLETX_TRANSACTION_STATUS_PREPARED; + StartReenlistThread(); + } + } + catch (COMException ex) when (ex.ErrorCode == OletxHelper.XACT_E_CONNECTION_DOWN ) + { + xactStatus = OletxTransactionStatus.OLETX_TRANSACTION_STATUS_PREPARED; + ResourceManagerShim = null; + StartReenlistThread(); + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, ex); + } + } + finally + { + localResourceManagerShim = null; + } + + // Now create our enlistment to tell the client the outcome. + return new OletxEnlistment(enlistmentNotification, xactStatus, prepareInfo, this); + } + + internal void RecoveryComplete() + { + Timer? localTimer = null; + + // Remember that the application has called RecoveryComplete. + RecoveryCompleteCalledByApplication = true; + + try + { + // Remove the OletxEnlistment objects from the reenlist list because the RM says it doesn't + // have any unresolved transactions, so we don't need to keep asking and the reenlist thread can exit. + // Leave the reenlistPendingList alone. If we have notifications outstanding, we still can't remove those. + lock (ReenlistList) + { + // If the ReenlistThread is not running and there are no reenlistPendingList entries, we need to call ReenlistComplete ourself. + lock (this) + { + if (ReenlistList.Count == 0 && ReenlistPendingList.Count == 0) + { + if (ReenlistThreadTimer != null) + { + // If we have a pending reenlistThreadTimer, cancel it. We do the cancel + // in the finally block to satisfy FXCop. + localTimer = ReenlistThreadTimer; + ReenlistThreadTimer = null; + } + + // Try to tell the proxy ReenlistmentComplete. + bool success = CallProxyReenlistComplete(); + if (!success) + { + // We are now responsible for calling RecoveryComplete. Fire up the ReenlistThread + // to do it for us. + StartReenlistThread(); + } + } + else + { + StartReenlistThread(); + } + } + } + } + finally + { + if (localTimer != null) + { + localTimer.Dispose(); + } + } + } + + internal void StartReenlistThread() + { + // We are not going to check the reenlistList.Count. Just always start the thread. We do this because + // if we get a COMException from calling ReenlistComplete, we start the reenlistThreadTimer to retry it for us + // in the background. + lock (this) + { + // We don't need a MemoryBarrier here because all access to the reenlistThreadTimer member is done while + // holding a lock on the OletxResourceManager object. + if (ReenlistThreadTimer == null && reenlistThread == null) + { + ReenlistThreadTimer = new Timer(ReenlistThread, this, 10, Timeout.Infinite); + } + } + } + + // This routine searches the reenlistPendingList for the specified enlistment and if it finds + // it, removes it from the list. An enlistment calls this routine when it is "finishing" because + // the RM has called EnlistmentDone or it was InDoubt. But it only calls it if the enlistment does NOT + // have a WrappedTransactionEnlistmentAsync value, indicating that it is a recovery enlistment. + internal void RemoveFromReenlistPending(OletxEnlistment enlistment) + { + // We lock the reenlistList because we have decided to lock that list when accessing either + // the reenlistList or the reenlistPendingList. + lock (ReenlistList) + { + // This will do a linear search of the list, but that is what we need to do because + // the enlistments may change indicies while notifications are outstanding. Also, + // this does not throw if the enlistment isn't on the list. + ReenlistPendingList.Remove(enlistment); + + lock (this) + { + // If we have a ReenlistThread timer and both the reenlistList and the reenlistPendingList + // are empty, kick the ReenlistThread now. + if (ReenlistThreadTimer != null && ReenlistList.Count == 0 && ReenlistPendingList.Count == 0) + { + if (!ReenlistThreadTimer.Change( 0, Timeout.Infinite)) + { + throw TransactionException.CreateInvalidOperationException( + TraceSourceType.TraceSourceOleTx, + SR.UnexpectedTimerFailure, + null); + } + } + } + } + } + + internal void ReenlistThread(object? state) + { + int localLoopCount; + bool done; + OletxEnlistment? localEnlistment; + ResourceManagerShim? localResourceManagerShim; + bool success; + Timer? localTimer = null; + bool disposeLocalTimer = false; + + OletxResourceManager resourceManager = (OletxResourceManager)state!; + + try + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxResourceManager)}.{nameof(ReenlistThread)}"); + } + + lock (resourceManager) + { + localResourceManagerShim = resourceManager.ResourceManagerShim; + localTimer = resourceManager.ReenlistThreadTimer; + resourceManager.ReenlistThreadTimer = null; + resourceManager.reenlistThread = Thread.CurrentThread; + } + + // We only want to do work if we have a resourceManagerShim. + if (localResourceManagerShim != null) + { + lock (resourceManager.ReenlistList) + { + // Get the current count on the list. + localLoopCount = resourceManager.ReenlistList.Count; + } + + done = false; + while (!done && localLoopCount > 0 && localResourceManagerShim != null) + { + lock (resourceManager.ReenlistList) + { + localEnlistment = null; + localLoopCount--; + if (resourceManager.ReenlistList.Count == 0) + { + done = true; + } + else + { + localEnlistment = resourceManager.ReenlistList[0] as OletxEnlistment; + if (localEnlistment == null) + { + if (etwLog.IsEnabled()) + { + etwLog.InternalError(); + } + + throw TransactionException.Create(SR.InternalError, null); + } + + resourceManager.ReenlistList.RemoveAt(0); + object syncRoot = localEnlistment; + lock (syncRoot) + { + if (OletxEnlistment.OletxEnlistmentState.Done == localEnlistment.State) + { + // We may be racing with a RecoveryComplete here. Just forget about this + // enlistment. + localEnlistment = null; + } + + else if (OletxEnlistment.OletxEnlistmentState.Prepared != localEnlistment.State) + { + // The app hasn't yet responded to Prepare, so we don't know + // if it is indoubt or not yet. So just re-add it to the end + // of the list. + resourceManager.ReenlistList.Add(localEnlistment); + localEnlistment = null; + } + } + } + } + + if (localEnlistment != null) + { + OletxTransactionOutcome localOutcome = OletxTransactionOutcome.NotKnownYet; + try + { + Debug.Assert(localResourceManagerShim != null, "ReenlistThread - localResourceManagerShim is null" ); + + // Make sure we have a prepare info. + if (localEnlistment.ProxyPrepareInfoByteArray == null) + { + Debug.Assert(false, string.Format(null, "this.prepareInfoByteArray == null in RecoveryInformation()")); + if (etwLog.IsEnabled()) + { + etwLog.InternalError(); + } + + throw TransactionException.Create(SR.InternalError, null); + } + + localResourceManagerShim.Reenlist(localEnlistment.ProxyPrepareInfoByteArray, out localOutcome); + + if (localOutcome == OletxTransactionOutcome.NotKnownYet) + { + object syncRoot = localEnlistment; + lock (syncRoot) + { + if (OletxEnlistment.OletxEnlistmentState.Done == localEnlistment.State) + { + // We may be racing with a RecoveryComplete here. Just forget about this + // enlistment. + localEnlistment = null; + } + else + { + // Put the enlistment back on the end of the list for retry later. + lock (resourceManager.ReenlistList) + { + resourceManager.ReenlistList.Add(localEnlistment); + localEnlistment = null; + } + } + } + } + } + catch (COMException ex) when (ex.ErrorCode == OletxHelper.XACT_E_CONNECTION_DOWN) + { + if (etwLog.IsEnabled()) + { + etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, ex); + } + + // Release the resource manager so we can create a new one. + resourceManager.ResourceManagerShim = null; + + // Now create a new resource manager with the proxy. + localResourceManagerShim = resourceManager.ResourceManagerShim; + } + + // If we get here and we still have localEnlistment, then we got the outcome. + if (localEnlistment != null) + { + object syncRoot = localEnlistment; + lock (syncRoot) + { + if (OletxEnlistment.OletxEnlistmentState.Done == localEnlistment.State) + { + // We may be racing with a RecoveryComplete here. Just forget about this + // enlistment. + localEnlistment = null; + } + else + { + // We are going to send the notification to the RM. We need to put the + // enlistment on the reenlistPendingList. We lock the reenlistList because + // we have decided that is the lock that protects both lists. The entry will + // be taken off the reenlistPendingList when the enlistment has + // EnlistmentDone called on it. The enlistment will call + // RemoveFromReenlistPending. + lock (resourceManager.ReenlistList) + { + resourceManager.ReenlistPendingList.Add(localEnlistment); + } + + if (localOutcome == OletxTransactionOutcome.Committed) + { + localEnlistment.State = OletxEnlistment.OletxEnlistmentState.Committing; + + if (etwLog.IsEnabled()) + { + etwLog.EnlistmentStatus(TraceSourceType.TraceSourceOleTx, localEnlistment.EnlistmentTraceId, NotificationCall.Commit); + } + + localEnlistment.EnlistmentNotification!.Commit(localEnlistment); + } + else if (localOutcome == OletxTransactionOutcome.Aborted) + { + localEnlistment.State = OletxEnlistment.OletxEnlistmentState.Aborting; + + if (etwLog.IsEnabled()) + { + etwLog.EnlistmentStatus(TraceSourceType.TraceSourceOleTx, localEnlistment.EnlistmentTraceId, NotificationCall.Rollback); + } + + localEnlistment.EnlistmentNotification!.Rollback(localEnlistment); + } + else + { + if (etwLog.IsEnabled()) + { + etwLog.InternalError(); + } + + throw TransactionException.Create(SR.InternalError, null); + } + } + } + } // end of if null != localEnlistment + } // end of if null != localEnlistment + } + } + + localResourceManagerShim = null; + + // Check to see if there is more work to do. + lock (resourceManager.ReenlistList) + { + lock (resourceManager) + { + // Get the current count on the list. + localLoopCount = resourceManager.ReenlistList.Count; + if (localLoopCount <= 0 && resourceManager.ReenlistPendingList.Count <= 0) + { + // No more entries on the list. Try calling ReenlistComplete on the proxy, if + // appropriate. + // If the application has called RecoveryComplete, + // we are responsible for calling ReenlistComplete on the + // proxy. + success = resourceManager.CallProxyReenlistComplete(); + if (success) + { + // Okay, the reenlist thread is done and we don't need to schedule another one. + disposeLocalTimer = true; + } + else + { + // We couldn't talk to the proxy to do ReenlistComplete, so schedule + // the thread again for 10 seconds from now. + resourceManager.ReenlistThreadTimer = localTimer; + if (!localTimer!.Change(10000, Timeout.Infinite)) + { + throw TransactionException.CreateInvalidOperationException( + TraceSourceType.TraceSourceLtm, + SR.UnexpectedTimerFailure, + null); + } + } + } + else + { + // There are still entries on the list, so they must not be + // resovled, yet. Schedule the thread again in 10 seconds. + resourceManager.ReenlistThreadTimer = localTimer; + if (!localTimer!.Change(10000, Timeout.Infinite)) + { + throw TransactionException.CreateInvalidOperationException( + TraceSourceType.TraceSourceLtm, + SR.UnexpectedTimerFailure, + null); + } + } + + resourceManager.reenlistThread = null; + } + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxResourceManager)}.{nameof(ReenlistThread)}"); + } + } + } // end of outer-most try + finally + { + localResourceManagerShim = null; + if (disposeLocalTimer && localTimer != null) + { + localTimer.Dispose(); + } + } + } // end of ReenlistThread method; +} + +// This is the base class for all enlistment objects. The enlistment objects provide the callback +// that is made from the application and pass it through to the proxy. +internal abstract class OletxBaseEnlistment +{ + protected Guid EnlistmentGuid; + protected OletxResourceManager OletxResourceManager; + protected OletxTransaction? oletxTransaction; + internal OletxTransaction? OletxTransaction => oletxTransaction; + + internal Guid DistributedTxId + { + get + { + Guid returnValue = Guid.Empty; + + if (OletxTransaction != null) + { + returnValue = OletxTransaction.DistributedTxId; + } + return returnValue; + } + } + + protected string TransactionGuidString; + protected int EnlistmentId; + // this needs to be internal so it can be set from the recovery information during Reenlist. + internal EnlistmentTraceIdentifier TraceIdentifier; + + // Owning public Enlistment object + protected InternalEnlistment? InternalEnlistment; + + public OletxBaseEnlistment(OletxResourceManager oletxResourceManager, OletxTransaction? oletxTransaction) + { + Guid resourceManagerId = Guid.Empty; + + EnlistmentGuid = Guid.NewGuid(); + OletxResourceManager = oletxResourceManager; + this.oletxTransaction = oletxTransaction; + if (oletxTransaction != null) + { + EnlistmentId = oletxTransaction.RealOletxTransaction._enlistmentCount++; + TransactionGuidString = oletxTransaction.RealOletxTransaction.TxGuid.ToString(); + } + else + { + TransactionGuidString = Guid.Empty.ToString(); + } + TraceIdentifier = EnlistmentTraceIdentifier.Empty; + } + + protected EnlistmentTraceIdentifier InternalTraceIdentifier + { + get + { + if (EnlistmentTraceIdentifier.Empty == TraceIdentifier ) + { + lock (this) + { + if (EnlistmentTraceIdentifier.Empty == TraceIdentifier ) + { + Guid rmId = Guid.Empty; + if (OletxResourceManager != null) + { + rmId = OletxResourceManager.ResourceManagerIdentifier; + } + EnlistmentTraceIdentifier temp; + if (oletxTransaction != null) + { + temp = new EnlistmentTraceIdentifier(rmId, oletxTransaction.TransactionTraceId, EnlistmentId); + } + else + { + TransactionTraceIdentifier txTraceId = new(TransactionGuidString, 0); + temp = new EnlistmentTraceIdentifier( rmId, txTraceId, EnlistmentId); + } + Thread.MemoryBarrier(); + TraceIdentifier = temp; + } + } + } + + return TraceIdentifier; + } + } + + protected void AddToEnlistmentTable() + { + lock (OletxResourceManager.EnlistmentHashtable.SyncRoot) + { + OletxResourceManager.EnlistmentHashtable.Add(EnlistmentGuid, this); + } + } + + protected void RemoveFromEnlistmentTable() + { + lock (OletxResourceManager.EnlistmentHashtable.SyncRoot) + { + OletxResourceManager.EnlistmentHashtable.Remove(EnlistmentGuid); + } + } +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxTransaction.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxTransaction.cs new file mode 100644 index 00000000000000..6f18c18cc8143f --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxTransaction.cs @@ -0,0 +1,1373 @@ +// 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; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Threading; +using System.Transactions.DtcProxyShim; + +namespace System.Transactions.Oletx +{ + /// + /// A Transaction object represents a single transaction. It is created by TransactionManager + /// objects through CreateTransaction or through deserialization. Alternatively, the static Create + /// method is provided, which creates a "default" TransactionManager and requests that it create + /// a new transaction with default values. A transaction can only be committed by + /// the client application that created the transaction. If a client application wishes to allow + /// access to the transaction by multiple threads, but wants to prevent those other threads from + /// committing the transaction, the application can make a "clone" of the transaction. Transaction + /// clones have the same capabilities as the original transaction, except for the ability to commit + /// the transaction. + /// + [Serializable] + internal class OletxTransaction : ISerializable, IObjectReference + { + // We have a strong reference on realOletxTransaction which does the real work + internal RealOletxTransaction RealOletxTransaction; + + // String that is used as a name for the propagationToken + // while serializing and deserializing this object + protected const string PropagationTokenString = "OletxTransactionPropagationToken"; + + // When an OletxTransaction is being created via deserialization, this member is + // filled with the propagation token from the serialization info. Later, when + // GetRealObject is called, this array is used to decide whether or not a new + // transation needs to be created and if so, to create the transaction. + private byte[]? _propagationTokenForDeserialize; + + protected int Disposed; + + // In GetRealObject, we ask LTM if it has a promoted transaction with the same ID. If it does, + // we need to remember that transaction because GetRealObject is called twice during + // deserialization. In this case, GetRealObject returns the LTM transaction, not this OletxTransaction. + // The OletxTransaction will get GC'd because there will be no references to it. + internal Transaction? SavedLtmPromotedTransaction; + + private TransactionTraceIdentifier _traceIdentifier = TransactionTraceIdentifier.Empty; + + // Property + internal RealOletxTransaction RealTransaction + => RealOletxTransaction; + + internal Guid Identifier + { + get + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxTransaction)}.{nameof(Identifier)}"); + } + + Guid returnValue = RealOletxTransaction.Identifier; + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxTransaction)}.{nameof(Identifier)}"); + } + + return returnValue; + } + } + + internal Guid DistributedTxId + { + get + { + Guid returnValue = Guid.Empty; + + if (RealOletxTransaction != null && RealOletxTransaction.InternalTransaction != null) + { + returnValue = RealOletxTransaction.InternalTransaction.DistributedTxId; + } + + return returnValue; + } + } + + internal TransactionStatus Status + { + get + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxTransaction)}.{nameof(Status)}"); + } + + TransactionStatus returnValue = RealOletxTransaction.Status; + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxTransaction)}.{nameof(Status)}"); + } + + return returnValue; + } + } + + internal Exception? InnerException + => RealOletxTransaction.InnerException; + + internal OletxTransaction(RealOletxTransaction realOletxTransaction) + { + RealOletxTransaction = realOletxTransaction; + + // Tell the realOletxTransaction that we are here. + RealOletxTransaction.OletxTransactionCreated(); + } + + protected OletxTransaction(SerializationInfo? serializationInfo, StreamingContext context) + { + if (serializationInfo == null) + { + throw new ArgumentNullException(nameof(serializationInfo)); + } + + // Simply store the propagation token from the serialization info. GetRealObject will + // decide whether or not we will use it. + _propagationTokenForDeserialize = (byte[])serializationInfo.GetValue(PropagationTokenString, typeof(byte[]))!; + + if (_propagationTokenForDeserialize.Length < 24) + { + throw new ArgumentException(SR.InvalidArgument, nameof(serializationInfo)); + } + + RealOletxTransaction = null!; + } + + public object GetRealObject(StreamingContext context) + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, $"{nameof(IObjectReference)}.{nameof(GetRealObject)}"); + } + + if (_propagationTokenForDeserialize == null) + { + if (etwLog.IsEnabled()) + { + etwLog.InternalError(SR.UnableToDeserializeTransaction); + } + + throw TransactionException.Create(SR.UnableToDeserializeTransactionInternalError, null); + } + + // This may be a second call. If so, just return. + if (SavedLtmPromotedTransaction != null) + { + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"{nameof(IObjectReference)}.{nameof(GetRealObject)}"); + } + + return SavedLtmPromotedTransaction; + } + + Transaction returnValue = TransactionInterop.GetTransactionFromTransmitterPropagationToken(_propagationTokenForDeserialize); + Debug.Assert(returnValue != null, "OletxTransaction.GetRealObject - GetTxFromPropToken returned null"); + + SavedLtmPromotedTransaction = returnValue; + + if (etwLog.IsEnabled()) + { + etwLog.TransactionDeserialized(returnValue._internalTransaction.PromotedTransaction!.TransactionTraceId); + } + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"{nameof(IObjectReference)}.{nameof(GetRealObject)}"); + } + + return returnValue; + } + + /// + /// Implementation of IDisposable.Dispose. Releases managed, and unmanaged resources + /// associated with the Transaction object. + /// + internal void Dispose() + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, $"{nameof(IDisposable)}.{nameof(Dispose)}"); + } + + int localDisposed = Interlocked.CompareExchange(ref Disposed, 1, 0); + if (localDisposed == 0) + { + RealOletxTransaction.OletxTransactionDisposed(); + } + GC.SuppressFinalize(this); + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"{nameof(IDisposable)}.{nameof(Dispose)}"); + } + } + + // Specific System.Transactions implementation + + /// + /// Initiates commit processing of the transaction. The caller must have created the transaction + /// as a new transaction through TransactionManager.CreateTransaction or Transaction.Create. + /// + /// If the transaction is already aborted due to some other participant making a Rollback call, + /// the transaction timeout period expiring, or some sort of network failure, an exception will + /// be raised. + /// + /// + /// Initiates rollback processing of the transaction. This method can be called on any instance + /// of a Transaction class, regardless of how the Transaction was obtained. It is possible for this + /// method to be called "too late", after the outcome of the transaction has already been determined. + /// In this case, an exception is raised. + /// + internal void Rollback() + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxTransaction)}.{nameof(Rollback)}"); + etwLog.TransactionRollback(TraceSourceType.TraceSourceOleTx, TransactionTraceId, "Transaction"); + } + + Debug.Assert(Disposed == 0, "OletxTransction object is disposed"); + + RealOletxTransaction.Rollback(); + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxTransaction)}.{nameof(Rollback)}"); + } + } + + internal IPromotedEnlistment EnlistVolatile( + ISinglePhaseNotificationInternal singlePhaseNotification, + EnlistmentOptions enlistmentOptions) + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxEnlistment)}.{nameof(EnlistVolatile)}(({nameof(ISinglePhaseNotificationInternal)}"); + } + + Debug.Assert(singlePhaseNotification != null, "Argument is null"); + Debug.Assert(Disposed == 0, "OletxTransction object is disposed"); + + if (RealOletxTransaction == null || RealOletxTransaction.TooLateForEnlistments) + { + throw TransactionException.Create(SR.TooLate, null, DistributedTxId); + } + + IPromotedEnlistment enlistment = RealOletxTransaction.EnlistVolatile( + singlePhaseNotification, + enlistmentOptions, + this); + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxEnlistment)}.{nameof(EnlistVolatile)}(({nameof(ISinglePhaseNotificationInternal)}"); + } + + return enlistment; + } + + internal IPromotedEnlistment EnlistVolatile( + IEnlistmentNotificationInternal enlistmentNotification, + EnlistmentOptions enlistmentOptions) + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxTransaction)}.{nameof(EnlistVolatile)}({nameof(IEnlistmentNotificationInternal)}"); + } + + Debug.Assert(enlistmentNotification != null, "Argument is null"); + Debug.Assert(Disposed == 0, "OletxTransction object is disposed"); + + if (RealOletxTransaction == null || RealOletxTransaction.TooLateForEnlistments ) + { + throw TransactionException.Create(SR.TooLate, null, DistributedTxId); + } + + IPromotedEnlistment enlistment = RealOletxTransaction.EnlistVolatile( + enlistmentNotification, + enlistmentOptions, + this); + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxTransaction)}.{nameof(EnlistVolatile)}({nameof(IEnlistmentNotificationInternal)}"); + } + + return enlistment; + } + + internal IPromotedEnlistment EnlistDurable( + Guid resourceManagerIdentifier, + ISinglePhaseNotificationInternal singlePhaseNotification, + bool canDoSinglePhase, + EnlistmentOptions enlistmentOptions) + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter( + TraceSourceType.TraceSourceOleTx, + this, + $"{nameof(OletxTransaction)}.{nameof(EnlistDurable)}({nameof(ISinglePhaseNotificationInternal)})"); + } + + Debug.Assert(Disposed == 0, "OletxTransction object is disposed"); + + if (RealOletxTransaction == null || RealOletxTransaction.TooLateForEnlistments) + { + throw TransactionException.Create(SR.TooLate, null, DistributedTxId); + } + + // get the Oletx TM from the real class + OletxTransactionManager oletxTM = RealOletxTransaction.OletxTransactionManagerInstance; + + // get the resource manager from the Oletx TM + OletxResourceManager rm = oletxTM.FindOrRegisterResourceManager(resourceManagerIdentifier); + + // ask the rm to do the durable enlistment + OletxEnlistment enlistment = rm.EnlistDurable( + this, + canDoSinglePhase, + singlePhaseNotification, + enlistmentOptions); + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit( + TraceSourceType.TraceSourceOleTx, + this, + $"{nameof(OletxTransaction)}.{nameof(EnlistDurable)}({nameof(ISinglePhaseNotificationInternal)})"); + } + + return enlistment; + } + + + internal OletxDependentTransaction DependentClone(bool delayCommit) + { + OletxDependentTransaction dependentClone; + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxTransaction)}.{nameof(DependentClone)}"); + } + + Debug.Assert(Disposed == 0, "OletxTransction object is disposed"); + + if (TransactionStatus.Aborted == Status) + { + throw TransactionAbortedException.Create( + SR.TransactionAborted, RealOletxTransaction.InnerException, DistributedTxId); + } + if (TransactionStatus.InDoubt == Status) + { + throw TransactionInDoubtException.Create( + SR.TransactionIndoubt, RealOletxTransaction.InnerException, DistributedTxId); + } + if (TransactionStatus.Active != Status) + { + throw TransactionException.Create(SR.TransactionAlreadyOver, null, DistributedTxId); + } + + dependentClone = new OletxDependentTransaction(RealOletxTransaction, delayCommit); + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxTransaction)}.{nameof(DependentClone)}"); + } + + return dependentClone; + + } + + internal TransactionTraceIdentifier TransactionTraceId + { + get + { + if (_traceIdentifier == TransactionTraceIdentifier.Empty) + { + lock (RealOletxTransaction) + { + if (_traceIdentifier == TransactionTraceIdentifier.Empty) + { + try + { + TransactionTraceIdentifier temp = new(RealOletxTransaction.Identifier.ToString(), 0); + Thread.MemoryBarrier(); + _traceIdentifier = temp; + } + catch (TransactionException ex) + { + // realOletxTransaction.Identifier throws a TransactionException if it can't determine the guid of the + // transaction because the transaction was already committed or aborted before the RealOletxTransaction was + // created. If that happens, we don't want to throw just because we are trying to trace. So just use + // the TransactionTraceIdentifier.Empty. + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, ex); + } + } + + } + } + } + return _traceIdentifier; + } + } + + public void GetObjectData(SerializationInfo serializationInfo, StreamingContext context) + { + if (serializationInfo == null) + { + throw new ArgumentNullException(nameof(serializationInfo)); + } + + byte[] propagationToken; + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxTransaction)}.{nameof(GetObjectData)}"); + } + + Debug.Assert(Disposed == 0, "OletxTransction object is disposed"); + + propagationToken = TransactionInterop.GetTransmitterPropagationToken(this); + + serializationInfo.SetType(typeof(OletxTransaction)); + serializationInfo.AddValue(PropagationTokenString, propagationToken); + + if (etwLog.IsEnabled()) + { + etwLog.TransactionSerialized(TransactionTraceId); + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxTransaction)}.{nameof(GetObjectData)}"); + } + } + + public virtual IsolationLevel IsolationLevel + => RealOletxTransaction.TransactionIsolationLevel; + } + + // Internal class used by OletxTransaction class which is public + internal sealed class RealOletxTransaction + { + // Transaction manager + internal OletxTransactionManager OletxTransactionManagerInstance { get; } + + private TransactionShim? _transactionShim; + + // guid related to transaction + internal Guid TxGuid { get; private set; } + + // Isolation level of the transaction + internal IsolationLevel TransactionIsolationLevel { get; private set; } + + // Record the exception that caused the transaction to abort. + internal Exception? InnerException; + + // Store status + internal TransactionStatus Status { get; private set; } + + // This is the count of undisposed OletxTransaction objects that reference + // this RealOletxTransaction. This is incremented when an OletxTransaction is created + // and decremented when OletxTransactionDisposed is + // called. When it is decremented to zero, the transactionShim + // field is "released", thus releasing the unmanged proxy interface + // pointer. + private int _undisposedOletxTransactionCount; + + // The list of containers for phase0 volatile enlistment multiplexing so we only enlist with the proxy once per wave. + // The last one on the list is the "current" one. + internal ArrayList? Phase0EnlistVolatilementContainerList; + + // The container for phase1 volatile enlistment multiplexing so we only enlist with the proxy once. + internal OletxPhase1VolatileEnlistmentContainer? Phase1EnlistVolatilementContainer; + + // Used to get outcomes of transactions with a voter. + private OutcomeEnlistment? _outcomeEnlistment; + + // This is a count of volatile and Phase0 durable enlistments on this transaction that have not yet voted. + // This is incremented when an enlistment is made and decremented when the + // enlistment votes. It is checked in Rollback. If the count is greater than 0, + // then the doomed field is set to true and the Rollback is allowed. If the count + // is zero in Rollback, the rollback is rejected with a "too late" exception. + // All checking and modification of this field needs to be done under a lock( this ). + private int _undecidedEnlistmentCount; + + // If true, indicates that the transaction should NOT commit. This is set to + // true if Rollback is called when there are outstanding enlistments. This is + // checked when enlistments vote Prepared. If true, then the enlistment's vote + // is turned into a ForceRollback. All checking and modification of this field + // needs to be done under a lock (this). + internal bool Doomed { get; private set; } + + // This property is used to allocate enlistment identifiers for enlistment trace identifiers. + // It is only incremented when a new enlistment is created for this instance of RealOletxTransaction. + // Enlistments on all clones of this Real transaction use this value. + internal int _enlistmentCount; + + private DateTime _creationTime; + private DateTime _lastStateChangeTime; + private TransactionTraceIdentifier _traceIdentifier = TransactionTraceIdentifier.Empty; + + // This field is set directly from the OletxCommittableTransaction constructor. It will be null + // for non-root RealOletxTransactions. + internal OletxCommittableTransaction? CommittableTransaction; + + // This is an internal OletxTransaction. It is created as part of the RealOletxTransaction constructor. + // It is used by the DependentCloneEnlistments when creating their volatile enlistments. + internal OletxTransaction InternalClone; + + // This is set initialized to false. It is set to true when the OletxPhase1VolatileContainer gets a VoteRequest or + // when any OletxEnlistment attached to this transaction gets a PrepareRequest. At that point, it is too late for any + // more enlistments. + internal bool TooLateForEnlistments { get; set; } + + // This is the InternalTransaction that instigated creation of this RealOletxTransaction. When we get the outcome + // of the transaction, we use this to notify the InternalTransaction of the outcome. We do this to avoid the LTM + // always creating a volatile enlistment just to get the outcome. + internal InternalTransaction? InternalTransaction { get; set; } + + internal Guid Identifier + { + get + { + // The txGuid will be empty if the oletx transaction was already committed or aborted when we + // tried to create the RealOletxTransaction. We still allow creation of the RealOletxTransaction + // for COM+ interop purposes, but we can't get the guid or the status of the transaction. + if (TxGuid.Equals(Guid.Empty)) + { + throw TransactionException.Create(SR.GetResourceString(SR.CannotGetTransactionIdentifier), null); + } + + return TxGuid; + } + } + + internal Guid DistributedTxId + { + get + { + Guid returnValue = Guid.Empty; + + if (InternalTransaction != null) + { + returnValue = InternalTransaction.DistributedTxId; + } + + return returnValue; + } + } + + internal void IncrementUndecidedEnlistments() + { + // Avoid taking a lock on the transaction here. Decrement + // will be called by a thread owning a lock on enlistment + // containers. When creating new enlistments the transaction + // will attempt to get a lock on the container when it + // already holds a lock on the transaction. This can result + // in a deadlock. + Interlocked.Increment(ref _undecidedEnlistmentCount); + } + + internal void DecrementUndecidedEnlistments() + { + // Avoid taking a lock on the transaction here. Decrement + // will be called by a thread owning a lock on enlistment + // containers. When creating new enlistments the transaction + // will attempt to get a lock on the container when it + // already holds a lock on the transaction. This can result + // in a deadlock. + Interlocked.Decrement(ref _undecidedEnlistmentCount); + } + + internal int UndecidedEnlistments + => _undecidedEnlistmentCount; + + internal TransactionShim TransactionShim + { + get + { + TransactionShim? shim = _transactionShim; + if (shim == null) + { + throw TransactionInDoubtException.Create(SR.TransactionIndoubt, null, DistributedTxId); + } + + return shim; + } + } + + // Common constructor used by all types of constructors + // Create a clean and fresh transaction. + internal RealOletxTransaction( + OletxTransactionManager transactionManager, + TransactionShim? transactionShim, + OutcomeEnlistment? outcomeEnlistment, + Guid identifier, + OletxTransactionIsolationLevel oletxIsoLevel, + bool isRoot) + { + bool successful = false; + + try + { + // initialize the member fields + OletxTransactionManagerInstance = transactionManager; + _transactionShim = transactionShim; + _outcomeEnlistment = outcomeEnlistment; + TxGuid = identifier; + TransactionIsolationLevel = OletxTransactionManager.ConvertIsolationLevelFromProxyValue(oletxIsoLevel); + Status = TransactionStatus.Active; + _undisposedOletxTransactionCount = 0; + Phase0EnlistVolatilementContainerList = null; + Phase1EnlistVolatilementContainer = null; + TooLateForEnlistments = false; + InternalTransaction = null; + + _creationTime = DateTime.UtcNow; + _lastStateChangeTime = _creationTime; + + // Connect this object with the OutcomeEnlistment. + InternalClone = new OletxTransaction( this ); + + // We have have been created without an outcome enlistment if it was too late to create + // a clone from the ITransactionNative that we were created from. + if (_outcomeEnlistment != null) + { + _outcomeEnlistment.SetRealTransaction(this); + } + else + { + Status = TransactionStatus.InDoubt; + } + + successful = true; + } + finally + { + if (!successful) + { + if (_outcomeEnlistment != null) + { + _outcomeEnlistment.UnregisterOutcomeCallback(); + _outcomeEnlistment = null; + } + } + } + } + + internal OletxVolatileEnlistmentContainer AddDependentClone(bool delayCommit) + { + Phase0EnlistmentShim? phase0Shim = null; + VoterBallotShim? voterShim = null; + bool needVoterEnlistment = false; + bool needPhase0Enlistment = false; + OletxVolatileEnlistmentContainer? returnValue = null; + OletxPhase0VolatileEnlistmentContainer? localPhase0VolatileContainer = null; + OletxPhase1VolatileEnlistmentContainer? localPhase1VolatileContainer = null; + bool phase0ContainerLockAcquired = false; + + // Yes, we are talking to the proxy while holding the lock on the RealOletxTransaction. + // If we don't then things get real sticky with other threads allocating containers. + // We only do this the first time we get a depenent clone of a given type (delay vs. non-delay). + // After that, we don't create a new container, except for Phase0 if we need to create one + // for a second wave. + try + { + lock (this) + { + if (delayCommit) + { + // Not using a MemoryBarrier because all access to this member variable is under a lock of the + // object. + Phase0EnlistVolatilementContainerList ??= new ArrayList(1); + + // We may have failed the proxy enlistment for the first container, but we would have + // allocated the list. That is why we have this check here. + if (Phase0EnlistVolatilementContainerList.Count == 0) + { + localPhase0VolatileContainer = new OletxPhase0VolatileEnlistmentContainer(this); + needPhase0Enlistment = true; + } + else + { + localPhase0VolatileContainer = Phase0EnlistVolatilementContainerList[^1] as OletxPhase0VolatileEnlistmentContainer; + + if (localPhase0VolatileContainer != null) + { + TakeContainerLock(localPhase0VolatileContainer, ref phase0ContainerLockAcquired); + } + + if (!localPhase0VolatileContainer!.NewEnlistmentsAllowed) + { + //It is OK to release the lock at this time because we are creating a new container that has not yet + //been enlisted with DTC. So there is no race to worry about + ReleaseContainerLock(localPhase0VolatileContainer, ref phase0ContainerLockAcquired); + + localPhase0VolatileContainer = new OletxPhase0VolatileEnlistmentContainer( this ); + needPhase0Enlistment = true; + } + else + { + needPhase0Enlistment = false; + } + } + } + else // ! delayCommit + { + if (Phase1EnlistVolatilementContainer == null) + { + localPhase1VolatileContainer = new OletxPhase1VolatileEnlistmentContainer(this); + needVoterEnlistment = true; + } + else + { + needVoterEnlistment = false; + localPhase1VolatileContainer = Phase1EnlistVolatilementContainer; + } + } + + try + { + //At this point, we definitely need the lock on the phase0 container so that it doesnt race with shim notifications from unmanaged code + //corrupting state while we are in the middle of an AddDependentClone processing + if (localPhase0VolatileContainer != null) + { + TakeContainerLock(localPhase0VolatileContainer, ref phase0ContainerLockAcquired); + } + + // If enlistDuringPrepareRequired is true, we need to ask the proxy to create a Phase0 enlistment. + if (needPhase0Enlistment) + { + _transactionShim!.Phase0Enlist(localPhase0VolatileContainer!, out phase0Shim); + localPhase0VolatileContainer!.Phase0EnlistmentShim = phase0Shim; + } + + if (needVoterEnlistment) + { + // We need to use shims if native threads are not allowed to enter managed code. + OletxTransactionManagerInstance.DtcTransactionManagerLock.AcquireReaderLock(-1); + try + { + _transactionShim!.CreateVoter(localPhase1VolatileContainer!, out voterShim); + } + finally + { + OletxTransactionManagerInstance.DtcTransactionManagerLock.ReleaseReaderLock(); + } + + localPhase1VolatileContainer!.VoterBallotShim = voterShim; + } + + if (delayCommit) + { + // if we needed a Phase0 enlistment, we need to add the container to the + // list. + if (needPhase0Enlistment) + { + Phase0EnlistVolatilementContainerList!.Add(localPhase0VolatileContainer); + } + localPhase0VolatileContainer!.AddDependentClone(); + returnValue = localPhase0VolatileContainer; + } + else + { + // If we needed a voter enlistment, we need to save the container as THE + // phase1 container for this transaction. + if (needVoterEnlistment) + { + Debug.Assert(Phase1EnlistVolatilementContainer == null, + "RealOletxTransaction.AddDependentClone - phase1VolContainer not null when expected" ); + Phase1EnlistVolatilementContainer = localPhase1VolatileContainer; + } + localPhase1VolatileContainer!.AddDependentClone(); + returnValue = localPhase1VolatileContainer; + } + + } + catch (COMException comException) + { + OletxTransactionManager.ProxyException(comException); + throw; + } + } + } + finally + { + //First release the lock on the phase 0 container if it was acquired. Any work on localPhase0VolatileContainer + //that needs its state to be consistent while processing should do so before this statement is executed. + if (localPhase0VolatileContainer != null) + { + ReleaseContainerLock(localPhase0VolatileContainer, ref phase0ContainerLockAcquired); + } + } + return returnValue; + } + + private static void ReleaseContainerLock(OletxPhase0VolatileEnlistmentContainer localPhase0VolatileContainer, ref bool phase0ContainerLockAcquired) + { + if (phase0ContainerLockAcquired) + { + Monitor.Exit(localPhase0VolatileContainer); + phase0ContainerLockAcquired = false; + } + } + + private static void TakeContainerLock(OletxPhase0VolatileEnlistmentContainer localPhase0VolatileContainer, ref bool phase0ContainerLockAcquired) + { + if (!phase0ContainerLockAcquired) + { + Monitor.Enter(localPhase0VolatileContainer); + phase0ContainerLockAcquired = true; + } + } + + internal IPromotedEnlistment CommonEnlistVolatile( + IEnlistmentNotificationInternal enlistmentNotification, + EnlistmentOptions enlistmentOptions, + OletxTransaction oletxTransaction) + { + OletxVolatileEnlistment? enlistment = null; + bool needVoterEnlistment = false; + bool needPhase0Enlistment = false; + OletxPhase0VolatileEnlistmentContainer? localPhase0VolatileContainer = null; + OletxPhase1VolatileEnlistmentContainer? localPhase1VolatileContainer = null; + VoterBallotShim? voterShim = null; + Phase0EnlistmentShim? phase0Shim = null; + + // Yes, we are talking to the proxy while holding the lock on the RealOletxTransaction. + // If we don't then things get real sticky with other threads allocating containers. + // We only do this the first time we get a depenent clone of a given type (delay vs. non-delay). + // After that, we don't create a new container, except for Phase0 if we need to create one + // for a second wave. + lock (this) + { + enlistment = new OletxVolatileEnlistment( + enlistmentNotification, + enlistmentOptions, + oletxTransaction); + + if ((enlistmentOptions & EnlistmentOptions.EnlistDuringPrepareRequired) != 0) + { + if (Phase0EnlistVolatilementContainerList == null) + { + // Not using a MemoryBarrier because all access to this member variable is done when holding + // a lock on the object. + Phase0EnlistVolatilementContainerList = new ArrayList(1); + } + // We may have failed the proxy enlistment for the first container, but we would have + // allocated the list. That is why we have this check here. + if (Phase0EnlistVolatilementContainerList.Count == 0) + { + localPhase0VolatileContainer = new OletxPhase0VolatileEnlistmentContainer(this); + needPhase0Enlistment = true; + } + else + { + localPhase0VolatileContainer = Phase0EnlistVolatilementContainerList[^1] as OletxPhase0VolatileEnlistmentContainer; + if (!localPhase0VolatileContainer!.NewEnlistmentsAllowed) + { + localPhase0VolatileContainer = new OletxPhase0VolatileEnlistmentContainer(this); + needPhase0Enlistment = true; + } + else + { + needPhase0Enlistment = false; + } + } + } + else // not EDPR = TRUE - may need a voter... + { + if (Phase1EnlistVolatilementContainer == null) + { + needVoterEnlistment = true; + localPhase1VolatileContainer = new OletxPhase1VolatileEnlistmentContainer(this); + } + else + { + needVoterEnlistment = false; + localPhase1VolatileContainer = Phase1EnlistVolatilementContainer; + } + } + + try + { + // If enlistDuringPrepareRequired is true, we need to ask the proxy to create a Phase0 enlistment. + if (needPhase0Enlistment) + { + lock (localPhase0VolatileContainer!) + { + _transactionShim!.Phase0Enlist(localPhase0VolatileContainer, out phase0Shim); + + localPhase0VolatileContainer.Phase0EnlistmentShim = phase0Shim; + } + } + + if (needVoterEnlistment) + { + _transactionShim!.CreateVoter(localPhase1VolatileContainer!, out voterShim); + + localPhase1VolatileContainer!.VoterBallotShim = voterShim; + } + + if ((enlistmentOptions & EnlistmentOptions.EnlistDuringPrepareRequired) != 0) + { + localPhase0VolatileContainer!.AddEnlistment(enlistment); + if (needPhase0Enlistment) + { + Phase0EnlistVolatilementContainerList!.Add(localPhase0VolatileContainer); + } + } + else + { + localPhase1VolatileContainer!.AddEnlistment(enlistment); + + if (needVoterEnlistment) + { + Debug.Assert(Phase1EnlistVolatilementContainer == null, + "RealOletxTransaction.CommonEnlistVolatile - phase1VolContainer not null when expected."); + Phase1EnlistVolatilementContainer = localPhase1VolatileContainer; + } + } + } + catch (COMException comException) + { + OletxTransactionManager.ProxyException(comException); + throw; + } + } + + return enlistment; + } + + internal IPromotedEnlistment EnlistVolatile( + ISinglePhaseNotificationInternal enlistmentNotification, + EnlistmentOptions enlistmentOptions, + OletxTransaction oletxTransaction) + => CommonEnlistVolatile( + enlistmentNotification, + enlistmentOptions, + oletxTransaction); + + internal IPromotedEnlistment EnlistVolatile( + IEnlistmentNotificationInternal enlistmentNotification, + EnlistmentOptions enlistmentOptions, + OletxTransaction oletxTransaction) + => CommonEnlistVolatile( + enlistmentNotification, + enlistmentOptions, + oletxTransaction); + + internal void Commit() + { + try + { + _transactionShim!.Commit(); + } + catch (COMException comException) + { + if (comException.ErrorCode == OletxHelper.XACT_E_ABORTED || + comException.ErrorCode == OletxHelper.XACT_E_INDOUBT) + { + Interlocked.CompareExchange(ref InnerException, comException, null); + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, comException); + } + } + else if (comException.ErrorCode == OletxHelper.XACT_E_ALREADYINPROGRESS) + { + throw TransactionException.Create(SR.TransactionAlreadyOver, comException); + } + else + { + OletxTransactionManager.ProxyException(comException); + throw; + } + } + } + + internal void Rollback() + { + Guid tempGuid = Guid.Empty; + + lock (this) + { + // if status is not active and not aborted, then throw an exception + if (TransactionStatus.Aborted != Status && + TransactionStatus.Active != Status) + { + throw TransactionException.Create(SR.TransactionAlreadyOver, null, DistributedTxId); + } + + // If the transaciton is already aborted, we can get out now. Calling Rollback on an already aborted transaction + // is legal. + if (TransactionStatus.Aborted == Status) + { + return; + } + + // If there are still undecided enlistments, we can doom the transaction. + // We can safely make this check because we ALWAYS have a Phase1 Volatile enlistment to + // get the outcome. If we didn't have that enlistment, we would not be able to do this + // because not all instances of RealOletxTransaction would have enlistments. + if (_undecidedEnlistmentCount > 0) + { + Doomed = true; + } + else if (TooLateForEnlistments ) + { + // It's too late for rollback to be called here. + throw TransactionException.Create(SR.TransactionAlreadyOver, null, DistributedTxId); + } + + // Tell the volatile enlistment containers to vote no now if they have outstanding + // notifications. + if (Phase0EnlistVolatilementContainerList != null) + { + foreach (OletxPhase0VolatileEnlistmentContainer phase0VolatileContainer in Phase0EnlistVolatilementContainerList) + { + phase0VolatileContainer.RollbackFromTransaction(); + } + } + if (Phase1EnlistVolatilementContainer != null) + { + Phase1EnlistVolatilementContainer.RollbackFromTransaction(); + } + } + + try + { + _transactionShim!.Abort(); + } + catch (COMException comException) + { + // If the ErrorCode is XACT_E_ALREADYINPROGRESS and the transaciton is already doomed, we must be + // the root transaction and we have already called Commit - ignore the exception. The + // Rollback is allowed and one of the enlistments that hasn't voted yet will make sure it is + // aborted. + if (comException.ErrorCode == OletxHelper.XACT_E_ALREADYINPROGRESS) + { + if (Doomed) + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, comException); + } + } + else + { + throw TransactionException.Create(SR.TransactionAlreadyOver, comException, DistributedTxId); + } + } + else + { + // Otherwise, throw the exception out to the app. + OletxTransactionManager.ProxyException(comException); + + throw; + } + } + } + + internal void OletxTransactionCreated() + => Interlocked.Increment(ref _undisposedOletxTransactionCount); + + internal void OletxTransactionDisposed() + { + int localCount = Interlocked.Decrement(ref _undisposedOletxTransactionCount); + Debug.Assert(localCount >= 0, "RealOletxTransction.undisposedOletxTransationCount < 0"); + } + + internal void FireOutcome(TransactionStatus statusArg) + { + lock (this) + { + if (statusArg == TransactionStatus.Committed) + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.TransactionCommitted(TraceSourceType.TraceSourceOleTx, TransactionTraceId); + } + + Status = TransactionStatus.Committed; + } + else if (statusArg == TransactionStatus.Aborted) + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.TransactionAborted(TraceSourceType.TraceSourceOleTx, TransactionTraceId); + } + + Status = TransactionStatus.Aborted; + } + else + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.TransactionInDoubt(TraceSourceType.TraceSourceOleTx, TransactionTraceId); + } + + Status = TransactionStatus.InDoubt; + } + } + + // Let the InternalTransaciton know about the outcome. + if (InternalTransaction != null) + { + InternalTransaction.DistributedTransactionOutcome(InternalTransaction, Status); + } + + } + + internal TransactionTraceIdentifier TransactionTraceId + { + get + { + if (TransactionTraceIdentifier.Empty == _traceIdentifier) + { + lock (this) + { + if (_traceIdentifier == TransactionTraceIdentifier.Empty) + { + if (TxGuid != Guid.Empty) + { + TransactionTraceIdentifier temp = new(TxGuid.ToString(), 0); + Thread.MemoryBarrier(); + _traceIdentifier = temp; + } + else + { + // We don't have a txGuid if we couldn't determine the guid of the + // transaction because the transaction was already committed or aborted before the RealOletxTransaction was + // created. If that happens, we don't want to throw just because we are trying to trace. So just use the + // TransactionTraceIdentifier.Empty. + } + } + } + } + return _traceIdentifier; + } + } + + internal void TMDown() + { + lock (this) + { + // Tell the volatile enlistment containers that the TM went down. + if (Phase0EnlistVolatilementContainerList != null) + { + foreach (OletxPhase0VolatileEnlistmentContainer phase0VolatileContainer in Phase0EnlistVolatilementContainerList) + { + phase0VolatileContainer.TMDown(); + } + } + } + // Tell the outcome enlistment the TM went down. We are doing this outside the lock + // because this may end up making calls out to user code through enlistments. + _outcomeEnlistment!.TMDown(); + } + } + + internal sealed class OutcomeEnlistment + { + private WeakReference? _weakRealTransaction; + + internal Guid TransactionIdentifier { get; private set; } + + private bool _haveIssuedOutcome; + + private TransactionStatus _savedStatus; + + internal OutcomeEnlistment() + { + _haveIssuedOutcome = false; + _savedStatus = TransactionStatus.InDoubt; + } + + internal void SetRealTransaction(RealOletxTransaction realTx) + { + bool localHaveIssuedOutcome = false; + TransactionStatus localStatus = TransactionStatus.InDoubt; + + lock (this) + { + localHaveIssuedOutcome = _haveIssuedOutcome; + localStatus = _savedStatus; + + // We want to do this while holding the lock. + if (!localHaveIssuedOutcome) + { + // We don't use MemoryBarrier here because all access to these member variables is done while holding + // a lock on the object. + + // We are going to use a weak reference so the transaction object can get garbage + // collected before we receive the outcome. + _weakRealTransaction = new WeakReference(realTx); + + // Save the transaction guid so that the transaction can be removed from the + // TransactionTable + TransactionIdentifier = realTx.TxGuid; + } + } + + // We want to do this outside the lock because we are potentially calling out to user code. + if (localHaveIssuedOutcome) + { + realTx.FireOutcome(localStatus); + + // We may be getting this notification while there are still volatile prepare notifications outstanding. Tell the + // container to drive the aborted notification in that case. + if ( localStatus is TransactionStatus.Aborted or TransactionStatus.InDoubt && + realTx.Phase1EnlistVolatilementContainer != null) + { + realTx.Phase1EnlistVolatilementContainer.OutcomeFromTransaction(localStatus); + } + } + } + + internal void UnregisterOutcomeCallback() + { + _weakRealTransaction = null; + } + + private void InvokeOutcomeFunction(TransactionStatus status) + { + WeakReference? localTxWeakRef; + + // In the face of TMDown notifications, we may have already issued + // the outcome of the transaction. + lock (this) + { + if (_haveIssuedOutcome) + { + return; + } + _haveIssuedOutcome = true; + _savedStatus = status; + localTxWeakRef = _weakRealTransaction; + } + + // It is possible for the weakRealTransaction member to be null if some exception was thrown + // during the RealOletxTransaction constructor after the OutcomeEnlistment object was created. + // In the finally block of the constructor, it calls UnregisterOutcomeCallback, which will + // null out weakRealTransaction. If this is the case, there is nothing to do. + if (localTxWeakRef != null) + { + if (localTxWeakRef.Target is RealOletxTransaction realOletxTransaction) + { + realOletxTransaction.FireOutcome(status); + + // The container list won't be changing on us now because the transaction status has changed such that + // new enlistments will not be created. + // Tell the Phase0Volatile containers, if any, about the outcome of the transaction. + // I am not protecting the access to phase0EnlistVolatilementContainerList with a lock on "this" + // because it is too late for these to be allocated anyway. + if (realOletxTransaction.Phase0EnlistVolatilementContainerList != null) + { + foreach (OletxPhase0VolatileEnlistmentContainer phase0VolatileContainer in realOletxTransaction.Phase0EnlistVolatilementContainerList) + { + phase0VolatileContainer.OutcomeFromTransaction( status ); + } + } + + // We may be getting this notification while there are still volatile prepare notifications outstanding. Tell the + // container to drive the aborted notification in that case. + if ( status is TransactionStatus.Aborted or TransactionStatus.InDoubt && + realOletxTransaction.Phase1EnlistVolatilementContainer != null) + { + realOletxTransaction.Phase1EnlistVolatilementContainer.OutcomeFromTransaction(status); + } + } + + localTxWeakRef.Target = null; + } + } + + // + // We need to figure out if the transaction is InDoubt as a result of TMDown. This + // can happen for a number of reasons. For instance we have responded prepared + // to all of our enlistments or we have no enlistments. + // + internal static bool TransactionIsInDoubt(RealOletxTransaction realTx) + { + if (realTx.CommittableTransaction is { CommitCalled: false } ) + { + // If this is a committable transaction and commit has not been called + // then we know the outcome. + return false; + } + + return realTx.UndecidedEnlistments == 0; + } + + internal void TMDown() + { + // Assume that we don't know because that is the safest answer. + bool transactionIsInDoubt = true; + RealOletxTransaction? realOletxTransaction = null; + lock (this) + { + if (_weakRealTransaction != null) + { + realOletxTransaction = _weakRealTransaction.Target as RealOletxTransaction; + } + } + + if (realOletxTransaction != null) + { + lock (realOletxTransaction) + { + transactionIsInDoubt = TransactionIsInDoubt(realOletxTransaction); + } + } + + // If we have already voted, then we can't tell what the outcome + // is. We do this outside the lock because it may end up invoking user + // code when it calls into the enlistments later on the stack. + if (transactionIsInDoubt) + { + InDoubt(); + } + // We have not yet voted, so just say it aborted. + else + { + Aborted(); + } + } + + #region ITransactionOutcome Members + + public void Committed() + => InvokeOutcomeFunction(TransactionStatus.Committed); + + public void Aborted() + => InvokeOutcomeFunction(TransactionStatus.Aborted); + + public void InDoubt() + => InvokeOutcomeFunction(TransactionStatus.InDoubt); + + #endregion + } +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxTransactionManager.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxTransactionManager.cs new file mode 100644 index 00000000000000..86329f235b424d --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxTransactionManager.cs @@ -0,0 +1,804 @@ +// 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; +using System.Runtime.InteropServices; +using System.Threading; +using System.Transactions.DtcProxyShim; + +namespace System.Transactions.Oletx; + +internal sealed class OletxTransactionManager +{ + private IsolationLevel _isolationLevelProperty; + + private TimeSpan _timeoutProperty; + + private TransactionOptions _configuredTransactionOptions = default; + + // Object for synchronizing access to the entire class( avoiding lock( typeof( ... )) ) + private static object? _classSyncObject; + + // These have to be static because we can only add an RM with the proxy once, even if we + // have multiple OletxTransactionManager instances. + internal static Hashtable? _resourceManagerHashTable; + public static ReaderWriterLock ResourceManagerHashTableLock = null!; + + internal static volatile bool ProcessingTmDown; + + internal ReaderWriterLock DtcTransactionManagerLock; + private DtcTransactionManager _dtcTransactionManager; + internal OletxInternalResourceManager InternalResourceManager; + + internal static DtcProxyShimFactory ProxyShimFactory = null!; // Late initialization + + // Double-checked locking pattern requires volatile for read/write synchronization + internal static volatile EventWaitHandle? _shimWaitHandle; + internal static EventWaitHandle ShimWaitHandle + { + get + { + if (_shimWaitHandle == null) + { + lock (ClassSyncObject) + { + _shimWaitHandle ??= new EventWaitHandle(false, EventResetMode.AutoReset); + } + } + + return _shimWaitHandle; + } + } + + private string? _nodeNameField; + + internal static void ShimNotificationCallback(object? state, bool timeout) + { + // First we need to get the notification from the shim factory. + object? enlistment2 = null; + ShimNotificationType shimNotificationType = ShimNotificationType.None; + bool isSinglePhase; + bool abortingHint; + + byte[]? prepareInfoBuffer = null; + + bool holdingNotificationLock = false; + + DtcProxyShimFactory localProxyShimFactory; + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, $"{nameof(OletxTransactionManager)}.{nameof(ShimNotificationCallback)}"); + } + + // This lock doesn't really protect any of our data. It is here so that if an exception occurs + // while calling out to the app, we get an escalation to AppDomainUnload. + Thread.BeginCriticalRegion(); + try + { + do + { + // Take a local copy of the proxyShimFactory because if we get an RM TMDown notification, + // we will still hold the critical section in that factory, but processing of the TMDown will + // cause replacement of the OletxTransactionManager.proxyShimFactory. + localProxyShimFactory = ProxyShimFactory; + try + { + Thread.BeginThreadAffinity(); + try + { + localProxyShimFactory.GetNotification( + out enlistment2, + out shimNotificationType, + out isSinglePhase, + out abortingHint, + out holdingNotificationLock, + out prepareInfoBuffer); + } + finally + { + if (holdingNotificationLock) + { + if (enlistment2 is OletxInternalResourceManager) + { + // In this case we know that the TM has gone down and we need to exchange + // the native lock for a managed lock. + ProcessingTmDown = true; + Monitor.Enter(ProxyShimFactory); + } + else + { + holdingNotificationLock = false; + } + localProxyShimFactory.ReleaseNotificationLock(); + } + Thread.EndThreadAffinity(); + } + + // If a TM down is being processed it is possible that the native lock + // has been exchanged for a managed lock. In that case we need to attempt + // to take a lock to hold up processing more events until the TM down + // processing is complete. + if (ProcessingTmDown) + { + lock (ProxyShimFactory) + { + // We don't do any work under this lock just make sure that we + // can take it. + } + } + + if (shimNotificationType != ShimNotificationType.None) + { + // Next, based on the notification type, cast the Handle accordingly and make + // the appropriate call on the enlistment. + switch (shimNotificationType) + { + case ShimNotificationType.Phase0RequestNotify: + { + if (enlistment2 is OletxPhase0VolatileEnlistmentContainer ph0VolEnlistContainer) + { + ph0VolEnlistContainer.Phase0Request(abortingHint); + } + else + { + if (enlistment2 is OletxEnlistment oletxEnlistment) + { + oletxEnlistment.Phase0Request(abortingHint); + } + else + { + Environment.FailFast(SR.InternalError); + } + } + + break; + } + + case ShimNotificationType.VoteRequestNotify: + { + if (enlistment2 is OletxPhase1VolatileEnlistmentContainer ph1VolEnlistContainer) + { + ph1VolEnlistContainer.VoteRequest(); + } + else + { + Environment.FailFast(SR.InternalError); + } + + break; + } + + case ShimNotificationType.CommittedNotify: + { + if (enlistment2 is OutcomeEnlistment outcomeEnlistment) + { + outcomeEnlistment.Committed(); + } + else + { + if (enlistment2 is OletxPhase1VolatileEnlistmentContainer ph1VolEnlistContainer) + { + ph1VolEnlistContainer.Committed(); + } + else + { + Environment.FailFast(SR.InternalError); + } + } + + break; + } + + case ShimNotificationType.AbortedNotify: + { + if (enlistment2 is OutcomeEnlistment outcomeEnlistment) + { + outcomeEnlistment.Aborted(); + } + else + { + if (enlistment2 is OletxPhase1VolatileEnlistmentContainer ph1VolEnlistContainer) + { + ph1VolEnlistContainer.Aborted(); + } + // else + // Voters may receive notifications even + // in cases where they therwise respond + // negatively to the vote request. It is + // also not guaranteed that we will get a + // notification if we do respond negatively. + // The only safe thing to do is to free the + // Handle when we abort the transaction + // with a voter. These two things together + // mean that we cannot guarantee that this + // Handle will be alive when we get this + // notification. + } + + break; + } + + case ShimNotificationType.InDoubtNotify: + { + if (enlistment2 is OutcomeEnlistment outcomeEnlistment) + { + outcomeEnlistment.InDoubt(); + } + else + { + if (enlistment2 is OletxPhase1VolatileEnlistmentContainer ph1VolEnlistContainer) + { + ph1VolEnlistContainer.InDoubt(); + } + else + { + Environment.FailFast(SR.InternalError); + } + } + + break; + } + + case ShimNotificationType.PrepareRequestNotify: + { + bool enlistmentDone = true; + + if (enlistment2 is OletxEnlistment enlistment) + { + enlistmentDone = enlistment.PrepareRequest(isSinglePhase, prepareInfoBuffer!); + } + else + { + Environment.FailFast(SR.InternalError); + } + + break; + } + + case ShimNotificationType.CommitRequestNotify: + { + if (enlistment2 is OletxEnlistment enlistment) + { + enlistment.CommitRequest(); + } + else + { + Environment.FailFast(SR.InternalError); + } + + break; + } + + case ShimNotificationType.AbortRequestNotify: + { + if (enlistment2 is OletxEnlistment enlistment) + { + enlistment.AbortRequest(); + } + else + { + Environment.FailFast(SR.InternalError); + } + + break; + } + + case ShimNotificationType.EnlistmentTmDownNotify: + { + if (enlistment2 is OletxEnlistment enlistment) + { + enlistment.TMDown(); + } + else + { + Environment.FailFast(SR.InternalError); + } + + break; + } + + case ShimNotificationType.ResourceManagerTmDownNotify: + { + switch (enlistment2) + { + case OletxResourceManager resourceManager: + resourceManager.TMDown(); + break; + + case OletxInternalResourceManager internalResourceManager: + internalResourceManager.TMDown(); + break; + + default: + Environment.FailFast(SR.InternalError); + break; + } + + break; + } + + default: + { + Environment.FailFast(SR.InternalError); + break; + } + } + } + } + finally + { + if (holdingNotificationLock) + { + holdingNotificationLock = false; + ProcessingTmDown = false; + Monitor.Exit(ProxyShimFactory); + } + } + } + while (shimNotificationType != ShimNotificationType.None); + } + finally + { + if (holdingNotificationLock) + { + holdingNotificationLock = false; + ProcessingTmDown = false; + Monitor.Exit(ProxyShimFactory); + } + + Thread.EndCriticalRegion(); + } + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, $"{nameof(OletxTransactionManager)}.{nameof(ShimNotificationCallback)}"); + } + } + + internal OletxTransactionManager(string nodeName) + { + lock (ClassSyncObject) + { + // If we have not already initialized the shim factory and started the notification + // thread, do so now. + if (ProxyShimFactory == null) + { + ProxyShimFactory = new DtcProxyShimFactory(ShimWaitHandle); + + ThreadPool.UnsafeRegisterWaitForSingleObject( + ShimWaitHandle, + ShimNotificationCallback, + null, + -1, + false); + } + } + + DtcTransactionManagerLock = new ReaderWriterLock(); + + _nodeNameField = nodeName; + + // The DTC proxy doesn't like an empty string for node name on 64-bit platforms when + // running as WOW64. It treats any non-null node name as a "remote" node and turns off + // the WOW64 bit, causing problems when reading the registry. So if we got on empty + // string for the node name, just treat it as null. + if (_nodeNameField is { Length: 0 }) + { + _nodeNameField = null; + } + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.OleTxTransactionManagerCreate(GetType(), _nodeNameField); + } + + // Initialize the properties from config. + _configuredTransactionOptions.IsolationLevel = _isolationLevelProperty = TransactionManager.DefaultIsolationLevel; + _configuredTransactionOptions.Timeout = _timeoutProperty = TransactionManager.DefaultTimeout; + + InternalResourceManager = new OletxInternalResourceManager( this ); + + DtcTransactionManagerLock.AcquireWriterLock(-1); + try + { + _dtcTransactionManager = new DtcTransactionManager(_nodeNameField, this); + } + finally + { + DtcTransactionManagerLock.ReleaseWriterLock(); + } + + if (_resourceManagerHashTable == null) + { + _resourceManagerHashTable = new Hashtable(2); + ResourceManagerHashTableLock = new ReaderWriterLock(); + } + } + + internal OletxCommittableTransaction CreateTransaction(TransactionOptions properties) + { + OletxCommittableTransaction tx; + RealOletxTransaction realTransaction; + TransactionShim? transactionShim = null; + Guid txIdentifier = Guid.Empty; + OutcomeEnlistment outcomeEnlistment; + + TransactionManager.ValidateIsolationLevel(properties.IsolationLevel); + + // Never create a transaction with an IsolationLevel of Unspecified. + if (IsolationLevel.Unspecified == properties.IsolationLevel) + { + properties.IsolationLevel = _configuredTransactionOptions.IsolationLevel; + } + + properties.Timeout = TransactionManager.ValidateTimeout(properties.Timeout); + + DtcTransactionManagerLock.AcquireReaderLock(-1); + try + { + OletxTransactionIsolationLevel oletxIsoLevel = ConvertIsolationLevel(properties.IsolationLevel); + uint oletxTimeout = DtcTransactionManager.AdjustTimeout(properties.Timeout); + + outcomeEnlistment = new OutcomeEnlistment(); + try + { + _dtcTransactionManager.ProxyShimFactory.BeginTransaction( + oletxTimeout, + oletxIsoLevel, + outcomeEnlistment, + out txIdentifier, + out transactionShim); + } + catch (COMException ex) + { + ProxyException(ex); + throw; + } + + realTransaction = new RealOletxTransaction( + this, + transactionShim, + outcomeEnlistment, + txIdentifier, + oletxIsoLevel, + true); + tx = new OletxCommittableTransaction(realTransaction); + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.TransactionCreated(TraceSourceType.TraceSourceOleTx, tx.TransactionTraceId, "OletxTransaction"); + } + } + finally + { + DtcTransactionManagerLock.ReleaseReaderLock(); + } + + return tx; + } + + internal OletxEnlistment ReenlistTransaction( + Guid resourceManagerIdentifier, + byte[] recoveryInformation, + IEnlistmentNotificationInternal enlistmentNotification) + { + if (recoveryInformation == null) + { + throw new ArgumentNullException(nameof(recoveryInformation)); + } + + if (enlistmentNotification == null) + { + throw new ArgumentNullException(nameof(enlistmentNotification)); + } + + // Now go find the resource manager in the collection. + OletxResourceManager oletxResourceManager = RegisterResourceManager(resourceManagerIdentifier); + if (oletxResourceManager == null) + { + throw new ArgumentException(SR.InvalidArgument, nameof(resourceManagerIdentifier)); + } + + if (oletxResourceManager.RecoveryCompleteCalledByApplication) + { + throw new InvalidOperationException(SR.ReenlistAfterRecoveryComplete); + } + + // Now ask the resource manager to reenlist. + OletxEnlistment returnValue = oletxResourceManager.Reenlist(recoveryInformation, enlistmentNotification); + + return returnValue; + } + + internal void ResourceManagerRecoveryComplete(Guid resourceManagerIdentifier) + { + OletxResourceManager oletxRm = RegisterResourceManager(resourceManagerIdentifier); + + if (oletxRm.RecoveryCompleteCalledByApplication) + { + throw new InvalidOperationException(SR.DuplicateRecoveryComplete); + } + + oletxRm.RecoveryComplete(); + } + + internal OletxResourceManager RegisterResourceManager(Guid resourceManagerIdentifier) + { + OletxResourceManager? oletxResourceManager; + + ResourceManagerHashTableLock.AcquireWriterLock(-1); + + try + { + // If this resource manager has already been registered, don't register it again. + oletxResourceManager = _resourceManagerHashTable![resourceManagerIdentifier] as OletxResourceManager; + if (oletxResourceManager != null) + { + return oletxResourceManager; + } + + oletxResourceManager = new OletxResourceManager(this, resourceManagerIdentifier); + + _resourceManagerHashTable.Add(resourceManagerIdentifier, oletxResourceManager); + } + finally + { + ResourceManagerHashTableLock.ReleaseWriterLock(); + } + + return oletxResourceManager; + } + + internal string? CreationNodeName + => _nodeNameField; + + internal OletxResourceManager FindOrRegisterResourceManager(Guid resourceManagerIdentifier) + { + if (resourceManagerIdentifier == Guid.Empty) + { + throw new ArgumentException(SR.BadResourceManagerId, nameof(resourceManagerIdentifier)); + } + + OletxResourceManager? oletxResourceManager; + + ResourceManagerHashTableLock.AcquireReaderLock(-1); + try + { + oletxResourceManager = _resourceManagerHashTable![resourceManagerIdentifier] as OletxResourceManager; + } + finally + { + ResourceManagerHashTableLock.ReleaseReaderLock(); + } + + if (oletxResourceManager == null) + { + return RegisterResourceManager(resourceManagerIdentifier); + } + + return oletxResourceManager; + } + + internal DtcTransactionManager DtcTransactionManager + { + get + { + if (DtcTransactionManagerLock.IsReaderLockHeld ||DtcTransactionManagerLock.IsWriterLockHeld) + { + if (_dtcTransactionManager == null) + { + throw TransactionException.Create(SR.DtcTransactionManagerUnavailable, null); + } + + return _dtcTransactionManager; + } + + // Internal programming error. A reader or writer lock should be held when this property is invoked. + throw TransactionException.Create(SR.InternalError, null); + } + } + + internal string? NodeName + => _nodeNameField; + + internal static void ProxyException(COMException comException) + { + if (comException.ErrorCode == OletxHelper.XACT_E_CONNECTION_DOWN || + comException.ErrorCode == OletxHelper.XACT_E_TMNOTAVAILABLE) + { + throw TransactionManagerCommunicationException.Create( + SR.TransactionManagerCommunicationException, + comException); + } + if (comException.ErrorCode == OletxHelper.XACT_E_NETWORK_TX_DISABLED) + { + throw TransactionManagerCommunicationException.Create( + SR.NetworkTransactionsDisabled, + comException); + } + // Else if the error is a transaction oriented error, throw a TransactionException + if (comException.ErrorCode >= OletxHelper.XACT_E_FIRST && + comException.ErrorCode <= OletxHelper.XACT_E_LAST) + { + // Special casing XACT_E_NOTRANSACTION + throw TransactionException.Create( + OletxHelper.XACT_E_NOTRANSACTION == comException.ErrorCode + ? SR.TransactionAlreadyOver + : comException.Message, + comException); + } + } + + internal void ReinitializeProxy() + { + // This is created by the static constructor. + DtcTransactionManagerLock.AcquireWriterLock(-1); + + try + { + _dtcTransactionManager?.ReleaseProxy(); + } + finally + { + DtcTransactionManagerLock.ReleaseWriterLock(); + } + } + + internal static OletxTransactionIsolationLevel ConvertIsolationLevel(IsolationLevel isolationLevel) + => isolationLevel switch + { + IsolationLevel.Serializable => OletxTransactionIsolationLevel.ISOLATIONLEVEL_SERIALIZABLE, + IsolationLevel.RepeatableRead => OletxTransactionIsolationLevel.ISOLATIONLEVEL_REPEATABLEREAD, + IsolationLevel.ReadCommitted => OletxTransactionIsolationLevel.ISOLATIONLEVEL_READCOMMITTED, + IsolationLevel.ReadUncommitted => OletxTransactionIsolationLevel.ISOLATIONLEVEL_READUNCOMMITTED, + IsolationLevel.Chaos => OletxTransactionIsolationLevel.ISOLATIONLEVEL_CHAOS, + IsolationLevel.Unspecified => OletxTransactionIsolationLevel.ISOLATIONLEVEL_UNSPECIFIED, + _ => OletxTransactionIsolationLevel.ISOLATIONLEVEL_SERIALIZABLE + }; + + internal static IsolationLevel ConvertIsolationLevelFromProxyValue(OletxTransactionIsolationLevel proxyIsolationLevel) + => proxyIsolationLevel switch + { + OletxTransactionIsolationLevel.ISOLATIONLEVEL_SERIALIZABLE => IsolationLevel.Serializable, + OletxTransactionIsolationLevel.ISOLATIONLEVEL_REPEATABLEREAD => IsolationLevel.RepeatableRead, + OletxTransactionIsolationLevel.ISOLATIONLEVEL_READCOMMITTED => IsolationLevel.ReadCommitted, + OletxTransactionIsolationLevel.ISOLATIONLEVEL_READUNCOMMITTED => IsolationLevel.ReadUncommitted, + OletxTransactionIsolationLevel.ISOLATIONLEVEL_UNSPECIFIED => IsolationLevel.Unspecified, + OletxTransactionIsolationLevel.ISOLATIONLEVEL_CHAOS => IsolationLevel.Chaos, + _ => IsolationLevel.Serializable + }; + + // Helper object for static synchronization + internal static object ClassSyncObject + { + get + { + if (_classSyncObject == null) + { + object o = new(); + Interlocked.CompareExchange(ref _classSyncObject, o, null); + } + + return _classSyncObject; + } + } +} + +internal sealed class OletxInternalResourceManager +{ + private OletxTransactionManager _oletxTm; + + internal Guid Identifier { get; } + + internal ResourceManagerShim? ResourceManagerShim; + + internal OletxInternalResourceManager(OletxTransactionManager oletxTm) + { + _oletxTm = oletxTm; + Identifier = Guid.NewGuid(); + } + + public void TMDown() + { + // Let's set ourselves up for reinitialization with the proxy by releasing our + // reference to the resource manager shim, which will release its reference + // to the proxy when it destructs. + ResourceManagerShim = null; + + // We need to look through all the transactions and tell them about + // the TMDown so they can tell their Phase0VolatileEnlistmentContainers. + Transaction? tx; + RealOletxTransaction realTx; + IDictionaryEnumerator tableEnum; + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxInternalResourceManager)}.{nameof(TMDown)}"); + } + + // make a local copy of the hash table to avoid possible deadlocks when we lock both the global hash table + // and the transaction object. + Hashtable txHashTable; + lock (TransactionManager.PromotedTransactionTable.SyncRoot) + { + txHashTable = (Hashtable)TransactionManager.PromotedTransactionTable.Clone(); + } + + // No need to lock my hashtable, nobody is going to change it. + tableEnum = txHashTable.GetEnumerator(); + while (tableEnum.MoveNext()) + { + WeakReference? txWeakRef = (WeakReference?)tableEnum.Value; + if (txWeakRef != null) + { + tx = (Transaction?)txWeakRef.Target; + if (tx != null) + { + realTx = tx._internalTransaction.PromotedTransaction!.RealOletxTransaction; + // Only deal with transactions owned by my OletxTm. + if (realTx.OletxTransactionManagerInstance == _oletxTm) + { + realTx.TMDown(); + } + } + } + } + + // Now make a local copy of the hash table of resource managers and tell each of them. This is to + // deal with Durable EDPR=true (phase0) enlistments. Each RM will also get a TMDown, but it will + // come AFTER the "buggy" Phase0Request with abortHint=true - COMPlus bug 36760/36758. + Hashtable? rmHashTable = null; + if (OletxTransactionManager._resourceManagerHashTable != null) + { + OletxTransactionManager.ResourceManagerHashTableLock.AcquireReaderLock(Timeout.Infinite); + try + { + rmHashTable = (Hashtable)OletxTransactionManager._resourceManagerHashTable.Clone(); + } + finally + { + OletxTransactionManager.ResourceManagerHashTableLock.ReleaseReaderLock(); + } + } + + if (rmHashTable != null) + { + // No need to lock my hashtable, nobody is going to change it. + tableEnum = rmHashTable.GetEnumerator(); + while (tableEnum.MoveNext()) + { + OletxResourceManager? oletxRM = (OletxResourceManager?)tableEnum.Value; + if (oletxRM != null) + { + // When the RM spins through its enlistments, it will need to make sure that + // the enlistment is for this particular TM. + oletxRM.TMDownFromInternalRM(_oletxTm); + } + } + } + + // Now let's reinitialize the shim. + _oletxTm.DtcTransactionManagerLock.AcquireWriterLock(-1); + try + { + _oletxTm.ReinitializeProxy(); + } + finally + { + _oletxTm.DtcTransactionManagerLock.ReleaseWriterLock(); + } + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxInternalResourceManager)}.{nameof(TMDown)}"); + } + } + + internal void CallReenlistComplete() + => ResourceManagerShim!.ReenlistComplete(); +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxVolatileEnlistment.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxVolatileEnlistment.cs new file mode 100644 index 00000000000000..82240b5f5ddf3c --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxVolatileEnlistment.cs @@ -0,0 +1,1508 @@ +// 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; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Threading; +using System.Transactions.DtcProxyShim; + +namespace System.Transactions.Oletx; + +internal abstract class OletxVolatileEnlistmentContainer +{ + protected OletxVolatileEnlistmentContainer(RealOletxTransaction realOletxTransaction) + { + Debug.Assert(realOletxTransaction != null, "Argument is null"); + + RealOletxTransaction = realOletxTransaction; + } + + protected RealOletxTransaction RealOletxTransaction; + protected ArrayList EnlistmentList = new(); + protected int Phase; + protected int OutstandingNotifications; + protected bool CollectedVoteYes; + protected int IncompleteDependentClones; + protected bool AlreadyVoted; + + internal abstract void DecrementOutstandingNotifications(bool voteYes); + + internal abstract void AddDependentClone(); + + internal abstract void DependentCloneCompleted(); + + internal abstract void RollbackFromTransaction(); + + internal abstract void OutcomeFromTransaction(TransactionStatus outcome); + + internal abstract void Committed(); + + internal abstract void Aborted(); + + internal abstract void InDoubt(); + + internal Guid TransactionIdentifier + => RealOletxTransaction.Identifier; +} + +internal sealed class OletxPhase0VolatileEnlistmentContainer : OletxVolatileEnlistmentContainer +{ + private Phase0EnlistmentShim? _phase0EnlistmentShim; + private bool _aborting; + private bool _tmWentDown; + + internal OletxPhase0VolatileEnlistmentContainer(RealOletxTransaction realOletxTransaction) + : base(realOletxTransaction) + { + // This will be set later, after the caller creates the enlistment with the proxy. + _phase0EnlistmentShim = null; + + Phase = -1; + _aborting = false; + _tmWentDown = false; + OutstandingNotifications = 0; + IncompleteDependentClones = 0; + AlreadyVoted = false; + // If anybody votes false, this will get set to false. + CollectedVoteYes = true; + + // This is a new undecided enlistment on the transaction. Do this last since it has side affects. + realOletxTransaction.IncrementUndecidedEnlistments(); + } + + internal void TMDown() + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxPhase0VolatileEnlistmentContainer)}.{nameof(TMDown)}"); + } + + _tmWentDown = true; + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxPhase0VolatileEnlistmentContainer)}.{nameof(TMDown)}"); + } + } + + // Be sure to lock this object before calling this. + internal bool NewEnlistmentsAllowed + => Phase == -1; + + internal void AddEnlistment(OletxVolatileEnlistment enlistment) + { + Debug.Assert(enlistment != null, "Argument is null"); + + lock (this) + { + if (Phase != -1) + { + throw TransactionException.Create(SR.TooLate, null); + } + + EnlistmentList.Add(enlistment); + } + } + + internal override void AddDependentClone() + { + lock (this) + { + if (Phase != -1) + { + throw TransactionException.CreateTransactionStateException(null); + } + + IncompleteDependentClones++; + } + } + + internal override void DependentCloneCompleted() + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + + bool doDecrement = false; + lock (this) + { + if (etwLog.IsEnabled()) + { + string description = "OletxPhase0VolatileEnlistmentContainer.DependentCloneCompleted, outstandingNotifications = " + + OutstandingNotifications.ToString(CultureInfo.CurrentCulture) + + ", incompleteDependentClones = " + + IncompleteDependentClones.ToString(CultureInfo.CurrentCulture) + + ", phase = " + Phase.ToString(CultureInfo.CurrentCulture); + + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, description); + } + + IncompleteDependentClones--; + Debug.Assert(IncompleteDependentClones >= 0, "OletxPhase0VolatileEnlistmentContainer.DependentCloneCompleted - incompleteDependentClones < 0"); + + // If we have not more incomplete dependent clones and we are in Phase 0, we need to "fake out" a notification completion. + if (IncompleteDependentClones == 0 && Phase == 0) + { + OutstandingNotifications++; + doDecrement = true; + } + } + if (doDecrement) + { + DecrementOutstandingNotifications(true); + } + + if (etwLog.IsEnabled()) + { + string description = "OletxPhase0VolatileEnlistmentContainer.DependentCloneCompleted"; + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, description); + } + } + + internal override void RollbackFromTransaction() + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + + lock (this) + { + if (etwLog.IsEnabled()) + { + string description = "OletxPhase0VolatileEnlistmentContainer.RollbackFromTransaction, outstandingNotifications = " + + OutstandingNotifications.ToString(CultureInfo.CurrentCulture) + + ", incompleteDependentClones = " + IncompleteDependentClones.ToString(CultureInfo.CurrentCulture); + + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, description); + } + + if (Phase == 0 && (OutstandingNotifications > 0 || IncompleteDependentClones > 0)) + { + AlreadyVoted = true; + // All we are going to do is release the Phase0Enlistment interface because there + // is no negative vote to Phase0Request. + if (Phase0EnlistmentShim != null) + { + Phase0EnlistmentShim.Phase0Done(false); + } + } + } + + if (etwLog.IsEnabled()) + { + string description = "OletxPhase0VolatileEnlistmentContainer.RollbackFromTransaction"; + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, description); + } + } + + + internal Phase0EnlistmentShim? Phase0EnlistmentShim + { + get + { + lock (this) + { + return _phase0EnlistmentShim; + } + } + set + { + lock (this) + { + // If this.aborting is set to true, then we must have already received a + // Phase0Request. This could happen if the transaction aborts after the + // enlistment is made, but before we are given the shim. + if (_aborting || _tmWentDown) + { + value!.Phase0Done(false); + } + _phase0EnlistmentShim = value; + } + } + } + + internal override void DecrementOutstandingNotifications(bool voteYes) + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + bool respondToProxy = false; + Phase0EnlistmentShim? localPhase0Shim = null; + + lock (this) + { + if (etwLog.IsEnabled()) + { + string description = "OletxPhase0VolatileEnlistmentContainer.DecrementOutstandingNotifications, outstandingNotifications = " + + OutstandingNotifications.ToString(CultureInfo.CurrentCulture) + + ", incompleteDependentClones = " + + IncompleteDependentClones.ToString(CultureInfo.CurrentCulture); + + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, description); + } + OutstandingNotifications--; + Debug.Assert(OutstandingNotifications >= 0, "OletxPhase0VolatileEnlistmentContainer.DecrementOutstandingNotifications - outstandingNotifications < 0"); + + CollectedVoteYes = CollectedVoteYes && voteYes; + if (OutstandingNotifications == 0 && IncompleteDependentClones == 0) + { + if (Phase == 0 && !AlreadyVoted) + { + respondToProxy = true; + AlreadyVoted = true; + localPhase0Shim = _phase0EnlistmentShim; + } + RealOletxTransaction.DecrementUndecidedEnlistments(); + } + } + + try + { + if (respondToProxy) + { + if (localPhase0Shim != null) + { + localPhase0Shim.Phase0Done(CollectedVoteYes && !RealOletxTransaction.Doomed); + } + } + } + catch (COMException ex) + { + if ((ex.ErrorCode == OletxHelper.XACT_E_CONNECTION_DOWN || ex.ErrorCode == OletxHelper.XACT_E_TMNOTAVAILABLE) && etwLog.IsEnabled()) + { + etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, ex); + } + + // In the case of Phase0, there is a bug in the proxy that causes an XACT_E_PROTOCOL + // error if the TM goes down while the enlistment is still active. The Phase0Request is + // sent out with abortHint false, but the state of the proxy object is not changed, causing + // Phase0Done request to fail with XACT_E_PROTOCOL. + // For Prepared, we want to make sure the proxy aborts the transaction. We don't need + // to drive the abort to the application here because the Phase1 enlistment will do that. + // In other words, treat this as if the proxy said Phase0Request( abortingHint = true ). + else if (OletxHelper.XACT_E_PROTOCOL == ex.ErrorCode) + { + _phase0EnlistmentShim = null; + + if (etwLog.IsEnabled()) + { + etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, ex); + } + } + else + { + throw; + } + } + + if (etwLog.IsEnabled()) + { + string description = "OletxPhase0VolatileEnlistmentContainer.DecrementOutstandingNotifications"; + + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, description); + } + } + + internal override void OutcomeFromTransaction(TransactionStatus outcome) + { + switch (outcome) + { + case TransactionStatus.Committed: + Committed(); + break; + case TransactionStatus.Aborted: + Aborted(); + break; + case TransactionStatus.InDoubt: + InDoubt(); + break; + default: + Debug.Assert(false, "OletxPhase0VolatileEnlistmentContainer.OutcomeFromTransaction, outcome is not Commited or Aborted or InDoubt"); + break; + } + } + + internal override void Committed() + { + OletxVolatileEnlistment? enlistment; + int localCount; + + lock (this) + { + Debug.Assert(Phase == 0 && OutstandingNotifications == 0); + Phase = 2; + localCount = EnlistmentList.Count; + } + + for (int i = 0; i < localCount; i++) + { + enlistment = EnlistmentList[i] as OletxVolatileEnlistment; + if (enlistment == null) + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.InternalError(); + } + + Debug.Assert(false, "OletxPhase1VolatileEnlistmentContainer.Committed, enlistmentList element is not an OletxVolatileEnlistment."); + throw new InvalidOperationException(SR.InternalError); + } + + enlistment.Commit(); + } + } + + internal override void Aborted() + { + OletxVolatileEnlistment? enlistment; + int localCount; + + lock (this) + { + // Tell all the enlistments that the transaction aborted and let the enlistment + // state determine if the notification should be delivered. + Phase = 2; + localCount = EnlistmentList.Count; + } + + for (int i = 0; i < localCount; i++) + { + enlistment = EnlistmentList[i] as OletxVolatileEnlistment; + if (enlistment == null) + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.InternalError(); + } + + Debug.Assert(false, "OletxPhase1VolatileEnlistmentContainer.Aborted, enlistmentList element is not an OletxVolatileEnlistment."); + throw new InvalidOperationException(SR.InternalError); + } + + enlistment.Rollback(); + } + } + + internal override void InDoubt() + { + OletxVolatileEnlistment? enlistment; + int localCount; + + lock (this) + { + // Tell all the enlistments that the transaction is InDoubt and let the enlistment + // state determine if the notification should be delivered. + Phase = 2; + localCount = EnlistmentList.Count; + } + + for (int i = 0; i < localCount; i++ ) + { + enlistment = EnlistmentList[i] as OletxVolatileEnlistment; + if (enlistment == null) + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.InternalError(); + } + + Debug.Fail("OletxPhase1VolatileEnlistmentContainer.InDoubt, enlistmentList element is not an OletxVolatileEnlistment."); + throw new InvalidOperationException( SR.InternalError); + } + + enlistment.InDoubt(); + } + } + + internal void Phase0Request(bool abortHint) + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + OletxVolatileEnlistment? enlistment; + int localCount; + OletxCommittableTransaction? committableTx; + bool commitNotYetCalled = false; + + lock (this) + { + if (etwLog.IsEnabled()) + { + string description = "OletxPhase0VolatileEnlistmentContainer.Phase0Request, abortHint = " + + abortHint.ToString(CultureInfo.CurrentCulture) + + ", phase = " + Phase.ToString(CultureInfo.CurrentCulture); + + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, description); + } + + _aborting = abortHint; + committableTx = RealOletxTransaction.CommittableTransaction; + if (committableTx != null) + { + // We are dealing with the committable transaction. If Commit or BeginCommit has NOT been + // called, then we are dealing with a situation where the TM went down and we are getting + // a bogus Phase0Request with abortHint = false (COMPlus bug 36760/36758). This is an attempt + // to not send the app a Prepare request when we know the transaction is going to abort. + if (!committableTx.CommitCalled) + { + commitNotYetCalled = true; + _aborting = true; + } + } + // It's possible that we are in phase 2 if we got an Aborted outcome from the transaction before we got the + // Phase0Request. In both cases, we just respond to the proxy and don't bother telling the enlistments. + // They have either already heard about the abort or will soon. + if (Phase == 2 || Phase == -1) + { + if (Phase == -1) + { + Phase = 0; + } + + // If we got an abort hint or we are the committable transaction and Commit has not yet been called or the TM went down, + // we don't want to do any more work on the transaction. The abort notifications will be sent by the phase 1 + // enlistment + if (_aborting || _tmWentDown || commitNotYetCalled || Phase == 2) + { + // There is a possible race where we could get the Phase0Request before we are given the + // shim. In that case, we will vote "no" when we are given the shim. + if (_phase0EnlistmentShim != null) + { + try + { + _phase0EnlistmentShim.Phase0Done(false); + // We need to set the alreadyVoted flag to true once we successfully voted, so later we don't vote again when OletxDependentTransaction::Complete is called + // Otherwise, in OletxPhase0VolatileEnlistmentContainer::DecrementOutstandingNotifications code path, we are going to call Phase0Done( true ) again + // and result in an access violation while accessing the pPhase0EnlistmentAsync member variable of the Phase0Shim object. + AlreadyVoted = true; + } + // I am not going to check for XACT_E_PROTOCOL here because that check is a workaround for a bug + // that only shows up if abortingHint is false. + catch (COMException ex) + { + if (etwLog.IsEnabled()) + { + etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, ex); + } + } + } + return; + } + OutstandingNotifications = EnlistmentList.Count; + localCount = EnlistmentList.Count; + // If we don't have any enlistments, then we must have created this container for + // delay commit dependent clones only. So we need to fake a notification. + if (localCount == 0) + { + OutstandingNotifications = 1; + } + } + else // any other phase is bad news. + { + if (etwLog.IsEnabled()) + { + etwLog.InternalError("OletxPhase0VolatileEnlistmentContainer.Phase0Request, phase != -1"); + } + + Debug.Fail("OletxPhase0VolatileEnlistmentContainer.Phase0Request, phase != -1"); + throw new InvalidOperationException( SR.InternalError); + } + } + + // We may not have any Phase0 volatile enlistments, which means that this container + // got created solely for delay commit dependent transactions. We need to fake out a + // notification completion. + if (localCount == 0) + { + DecrementOutstandingNotifications(true); + } + else + { + for (int i = 0; i < localCount; i++) + { + enlistment = EnlistmentList[i] as OletxVolatileEnlistment; + if (enlistment == null) + { + if (etwLog.IsEnabled()) + { + etwLog.InternalError(); + } + + Debug.Fail("OletxPhase0VolatileEnlistmentContainer.Phase0Request, enlistmentList element is not an OletxVolatileEnlistment."); + throw new InvalidOperationException( SR.InternalError); + } + + // Do the notification outside any locks. + Debug.Assert(enlistment.EnlistDuringPrepareRequired, "OletxPhase0VolatileEnlistmentContainer.Phase0Request, enlistmentList element not marked as EnlistmentDuringPrepareRequired."); + Debug.Assert(!abortHint, "OletxPhase0VolatileEnlistmentContainer.Phase0Request, abortingHint is true just before sending Prepares."); + + enlistment.Prepare(this); + } + } + + if (etwLog.IsEnabled()) + { + string description = "OletxPhase0VolatileEnlistmentContainer.Phase0Request, abortHint = " + abortHint.ToString(CultureInfo.CurrentCulture); + + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, description); + } + } +} + +internal sealed class OletxPhase1VolatileEnlistmentContainer : OletxVolatileEnlistmentContainer +{ + private VoterBallotShim? _voterBallotShim; + + internal OletxPhase1VolatileEnlistmentContainer(RealOletxTransaction realOletxTransaction) + : base(realOletxTransaction) + { + // This will be set later, after the caller creates the enlistment with the proxy. + _voterBallotShim = null; + + Phase = -1; + OutstandingNotifications = 0; + IncompleteDependentClones = 0; + AlreadyVoted = false; + + // If anybody votes false, this will get set to false. + CollectedVoteYes = true; + + // This is a new undecided enlistment on the transaction. Do this last since it has side affects. + realOletxTransaction.IncrementUndecidedEnlistments(); + } + + // Returns true if this container is enlisted for Phase 0. + internal void AddEnlistment(OletxVolatileEnlistment enlistment) + { + Debug.Assert(enlistment != null, "Argument is null"); + + lock (this) + { + if (Phase != -1) + { + throw TransactionException.Create(SR.TooLate, null); + } + + EnlistmentList.Add( enlistment ); + } + } + + internal override void AddDependentClone() + { + lock (this) + { + if (Phase != -1) + { + throw TransactionException.CreateTransactionStateException(null, Guid.Empty); + } + + // We simply need to block the response to the proxy until all clone is completed. + IncompleteDependentClones++; + } + } + + internal override void DependentCloneCompleted() + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + string description = "OletxPhase1VolatileEnlistmentContainer.DependentCloneCompleted, outstandingNotifications = " + + OutstandingNotifications.ToString(CultureInfo.CurrentCulture) + + ", incompleteDependentClones = " + + IncompleteDependentClones.ToString(CultureInfo.CurrentCulture) + + ", phase = " + Phase.ToString(CultureInfo.CurrentCulture); + + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, description); + } + + // This is to synchronize with the corresponding AddDependentClone which takes the container lock while incrementing the incompleteDependentClone count + lock (this) + { + IncompleteDependentClones--; + } + + Debug.Assert(OutstandingNotifications >= 0, "OletxPhase1VolatileEnlistmentContainer.DependentCloneCompleted - DependentCloneCompleted < 0"); + + if (etwLog.IsEnabled()) + { + string description = "OletxPhase1VolatileEnlistmentContainer.DependentCloneCompleted"; + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, description); + } + } + + internal override void RollbackFromTransaction() + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + bool voteNo = false; + VoterBallotShim? localVoterShim = null; + + lock (this) + { + if (etwLog.IsEnabled()) + { + string description = "OletxPhase1VolatileEnlistmentContainer.RollbackFromTransaction, outstandingNotifications = " + + OutstandingNotifications.ToString(CultureInfo.CurrentCulture) + + ", incompleteDependentClones = " + IncompleteDependentClones.ToString(CultureInfo.CurrentCulture); + + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, description); + } + + if (Phase == 1 && OutstandingNotifications > 0) + { + AlreadyVoted = true; + voteNo = true; + localVoterShim = _voterBallotShim; + } + } + + if (voteNo) + { + try + { + localVoterShim?.Vote(false); + + // We are not going to hear anymore from the proxy if we voted no, so we need to tell the + // enlistments to rollback. The state of the OletxVolatileEnlistment will determine whether or + // not the notification actually goes out to the app. + Aborted(); + } + catch (COMException ex) when (ex.ErrorCode == OletxHelper.XACT_E_CONNECTION_DOWN || ex.ErrorCode == OletxHelper.XACT_E_TMNOTAVAILABLE) + { + lock (this) + { + // If we are in phase 1, we need to tell the enlistments that the transaction is InDoubt. + if (Phase == 1) + { + InDoubt(); + } + } + + if (etwLog.IsEnabled()) + { + etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, ex); + } + } + } + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, "OletxPhase1VolatileEnlistmentContainer.RollbackFromTransaction"); + } + } + + internal VoterBallotShim? VoterBallotShim + { + get + { + lock (this) + { + return _voterBallotShim; + } + } + set + { + lock (this) + { + _voterBallotShim = value; + } + } + } + + internal override void DecrementOutstandingNotifications(bool voteYes) + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + bool respondToProxy = false; + VoterBallotShim? localVoterShim = null; + + lock (this) + { + if (etwLog.IsEnabled()) + { + string description = "OletxPhase1VolatileEnlistmentContainer.DecrementOutstandingNotifications, outstandingNotifications = " + + OutstandingNotifications.ToString(CultureInfo.CurrentCulture) + + ", incompleteDependentClones = " + + IncompleteDependentClones.ToString(CultureInfo.CurrentCulture); + + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, description); + } + + OutstandingNotifications--; + Debug.Assert(OutstandingNotifications >= 0, "OletxPhase1VolatileEnlistmentContainer.DecrementOutstandingNotifications - outstandingNotifications < 0"); + CollectedVoteYes = CollectedVoteYes && voteYes; + if (OutstandingNotifications == 0) + { + if (Phase == 1 && !AlreadyVoted) + { + respondToProxy = true; + AlreadyVoted = true; + localVoterShim = VoterBallotShim; + } + RealOletxTransaction.DecrementUndecidedEnlistments(); + } + } + + try + { + if (respondToProxy) + { + if (CollectedVoteYes && !RealOletxTransaction.Doomed) + { + localVoterShim?.Vote(true); + } + else // we need to vote no. + { + localVoterShim?.Vote(false); + + // We are not going to hear anymore from the proxy if we voted no, so we need to tell the + // enlistments to rollback. The state of the OletxVolatileEnlistment will determine whether or + // not the notification actually goes out to the app. + Aborted(); + } + } + } + catch (COMException ex) when (ex.ErrorCode == OletxHelper.XACT_E_CONNECTION_DOWN || ex.ErrorCode == OletxHelper.XACT_E_TMNOTAVAILABLE) + { + lock (this) + { + // If we are in phase 1, we need to tell the enlistments that the transaction is InDoubt. + if (Phase == 1) + { + InDoubt(); + } + + // There is nothing special to do for phase 2. + } + + if (etwLog.IsEnabled()) + { + etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, ex); + } + } + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, "OletxPhase1VolatileEnlistmentContainer.DecrementOutstandingNotifications"); + } + } + + internal override void OutcomeFromTransaction(TransactionStatus outcome) + { + bool driveAbort = false; + bool driveInDoubt = false; + + lock (this) + { + // If we are in Phase 1 and still have outstanding notifications, we need + // to drive sending of the outcome to the enlistments. If we are in any + // other phase, or we don't have outstanding notifications, we will eventually + // get the outcome notification on our OWN voter enlistment, so we will just + // wait for that. + if (Phase == 1 && OutstandingNotifications > 0) + { + switch (outcome) + { + case TransactionStatus.Aborted: + driveAbort = true; + break; + case TransactionStatus.InDoubt: + driveInDoubt = true; + break; + default: + Debug.Fail("OletxPhase1VolatileEnlistmentContainer.OutcomeFromTransaction, outcome is not Aborted or InDoubt"); + break; + } + } + } + + if (driveAbort) + { + Aborted(); + } + + if (driveInDoubt) + { + InDoubt(); + } + } + + internal override void Committed() + { + OletxVolatileEnlistment? enlistment; + int localPhase1Count; + + lock (this) + { + Phase = 2; + localPhase1Count = EnlistmentList.Count; + } + + for ( int i = 0; i < localPhase1Count; i++ ) + { + enlistment = EnlistmentList[i] as OletxVolatileEnlistment; + if (enlistment == null) + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.InternalError(); + } + + Debug.Fail("OletxPhase1VolatileEnlistmentContainer.Committed, enlistmentList element is not an OletxVolatileEnlistment."); + throw new InvalidOperationException(SR.InternalError); + } + + enlistment.Commit(); + } + } + + internal override void Aborted() + { + OletxVolatileEnlistment? enlistment; + int localPhase1Count; + + lock (this) + { + Phase = 2; + localPhase1Count = EnlistmentList.Count; + } + + for (int i = 0; i < localPhase1Count; i++) + { + enlistment = EnlistmentList[i] as OletxVolatileEnlistment; + if (enlistment == null) + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.InternalError(); + } + + Debug.Fail("OletxPhase1VolatileEnlistmentContainer.Aborted, enlistmentList element is not an OletxVolatileEnlistment."); + throw new InvalidOperationException( SR.InternalError); + } + + enlistment.Rollback(); + } + } + + internal override void InDoubt() + { + OletxVolatileEnlistment? enlistment; + int localPhase1Count; + + lock (this) + { + Phase = 2; + localPhase1Count = EnlistmentList.Count; + } + + for (int i = 0; i < localPhase1Count; i++) + { + enlistment = EnlistmentList[i] as OletxVolatileEnlistment; + if (enlistment == null) + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.InternalError(); + } + + Debug.Fail("OletxPhase1VolatileEnlistmentContainer.InDoubt, enlistmentList element is not an OletxVolatileEnlistment."); + throw new InvalidOperationException( SR.InternalError); + } + + enlistment.InDoubt(); + } + } + + internal void VoteRequest() + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + OletxVolatileEnlistment? enlistment; + int localPhase1Count = 0; + bool voteNo = false; + + lock (this) + { + if (etwLog.IsEnabled()) + { + string description = "OletxPhase1VolatileEnlistmentContainer.VoteRequest"; + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, description); + } + + Phase = 1; + + // If we still have incomplete dependent clones, vote no now. + if (IncompleteDependentClones > 0) + { + voteNo = true; + OutstandingNotifications = 1; + } + else + { + OutstandingNotifications = EnlistmentList.Count; + localPhase1Count = EnlistmentList.Count; + // We may not have an volatile phase 1 enlistments, which means that this + // container was created only for non-delay commit dependent clones. If that + // is the case, fake out a notification and response. + if (localPhase1Count == 0) + { + OutstandingNotifications = 1; + } + } + + RealOletxTransaction.TooLateForEnlistments = true; + } + + if (voteNo) + { + DecrementOutstandingNotifications( false ); + } + else if (localPhase1Count == 0) + { + DecrementOutstandingNotifications( true ); + } + else + { + for (int i = 0; i < localPhase1Count; i++) + { + enlistment = EnlistmentList[i] as OletxVolatileEnlistment; + if (enlistment == null) + { + if (etwLog.IsEnabled()) + { + etwLog.InternalError(); + } + + Debug.Fail("OletxPhase1VolatileEnlistmentContainer.VoteRequest, enlistmentList element is not an OletxVolatileEnlistment."); + throw new InvalidOperationException( SR.InternalError); + } + + enlistment.Prepare(this); + } + } + + if (etwLog.IsEnabled()) + { + string description = "OletxPhase1VolatileEnlistmentContainer.VoteRequest"; + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, description); + } + } +} + +internal sealed class OletxVolatileEnlistment : OletxBaseEnlistment, IPromotedEnlistment +{ + private enum OletxVolatileEnlistmentState + { + Active, + Preparing, + Committing, + Aborting, + Prepared, + Aborted, + InDoubt, + Done + } + + private IEnlistmentNotificationInternal _iEnlistmentNotification; + private OletxVolatileEnlistmentState _state = OletxVolatileEnlistmentState.Active; + private OletxVolatileEnlistmentContainer? _container; + internal bool EnlistDuringPrepareRequired; + + // This is used if the transaction outcome is received while a prepare request + // is still outstanding to an app. Active means no outcome, yet. Aborted means + // we should tell the app Aborted. InDoubt means tell the app InDoubt. This + // should never be Committed because we shouldn't receive a Committed notification + // from the proxy while we have a Prepare outstanding. + private TransactionStatus _pendingOutcome; + + internal OletxVolatileEnlistment( + IEnlistmentNotificationInternal enlistmentNotification, + EnlistmentOptions enlistmentOptions, + OletxTransaction oletxTransaction) + : base(null!, oletxTransaction) + { + _iEnlistmentNotification = enlistmentNotification; + EnlistDuringPrepareRequired = (enlistmentOptions & EnlistmentOptions.EnlistDuringPrepareRequired) != 0; + + // We get a container when we are asked to vote. + _container = null; + + _pendingOutcome = TransactionStatus.Active; + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.EnlistmentCreated(TraceSourceType.TraceSourceOleTx, InternalTraceIdentifier, EnlistmentType.Volatile, enlistmentOptions); + } + } + + internal void Prepare(OletxVolatileEnlistmentContainer container) + { + OletxVolatileEnlistmentState localState = OletxVolatileEnlistmentState.Active; + IEnlistmentNotificationInternal localEnlistmentNotification; + + lock (this) + { + localEnlistmentNotification = _iEnlistmentNotification; + + // The app may have already called EnlistmentDone. If this occurs, don't bother sending + // the notification to the app. + if (OletxVolatileEnlistmentState.Active == _state) + { + localState = _state = OletxVolatileEnlistmentState.Preparing; + } + else + { + localState = _state; + } + _container = container; + } + + // Tell the application to do the work. + if (localState == OletxVolatileEnlistmentState.Preparing) + { + if (localEnlistmentNotification != null) + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.EnlistmentStatus(TraceSourceType.TraceSourceOleTx, InternalTraceIdentifier, NotificationCall.Prepare); + } + + localEnlistmentNotification.Prepare(this); + } + else + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.InternalError(); + } + + Debug.Fail("OletxVolatileEnlistment.Prepare, no enlistmentNotification member."); + throw new InvalidOperationException(SR.InternalError); + } + } + else if (localState == OletxVolatileEnlistmentState.Done) + { + // Voting yes because it was an early read-only vote. + container.DecrementOutstandingNotifications( true ); + + // We must have had a race between EnlistmentDone and the proxy telling + // us Phase0Request. Just return. + return; + } + // It is okay to be in Prepared state if we are edpr=true because we already + // did our prepare in Phase0. + else if (localState == OletxVolatileEnlistmentState.Prepared && EnlistDuringPrepareRequired) + { + container.DecrementOutstandingNotifications(true); + return; + } + else if (localState is OletxVolatileEnlistmentState.Aborting or OletxVolatileEnlistmentState.Aborted) + { + // An abort has raced with this volatile Prepare + // decrement the outstanding notifications making sure to vote no. + container.DecrementOutstandingNotifications(false); + return; + } + else + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.InternalError(); + } + + Debug.Fail("OletxVolatileEnlistment.Prepare, invalid state."); + throw new InvalidOperationException( SR.InternalError); + } + } + + internal void Commit() + { + OletxVolatileEnlistmentState localState = OletxVolatileEnlistmentState.Active; + IEnlistmentNotificationInternal? localEnlistmentNotification = null; + + lock (this) + { + // The app may have already called EnlistmentDone. If this occurs, don't bother sending + // the notification to the app and we don't need to tell the proxy. + if (_state == OletxVolatileEnlistmentState.Prepared) + { + localState = _state = OletxVolatileEnlistmentState.Committing; + localEnlistmentNotification = _iEnlistmentNotification; + } + else + { + localState = _state; + } + } + + // Tell the application to do the work. + if (OletxVolatileEnlistmentState.Committing == localState) + { + if (localEnlistmentNotification != null) + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.EnlistmentStatus(TraceSourceType.TraceSourceOleTx, InternalTraceIdentifier, NotificationCall.Commit); + } + + localEnlistmentNotification.Commit(this); + } + else + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.InternalError(); + } + + Debug.Fail("OletxVolatileEnlistment.Commit, no enlistmentNotification member."); + throw new InvalidOperationException(SR.InternalError); + } + } + else if (localState == OletxVolatileEnlistmentState.Done) + { + // Early Exit - state was Done + } + else + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.InternalError(); + } + + Debug.Fail("OletxVolatileEnlistment.Commit, invalid state."); + throw new InvalidOperationException(SR.InternalError); + } + } + + internal void Rollback() + { + OletxVolatileEnlistmentState localState = OletxVolatileEnlistmentState.Active; + IEnlistmentNotificationInternal? localEnlistmentNotification = null; + + lock (this) + { + // The app may have already called EnlistmentDone. If this occurs, don't bother sending + // the notification to the app and we don't need to tell the proxy. + if ( _state is OletxVolatileEnlistmentState.Prepared or OletxVolatileEnlistmentState.Active) + { + localState = _state = OletxVolatileEnlistmentState.Aborting; + localEnlistmentNotification = _iEnlistmentNotification; + } + else + { + if (_state == OletxVolatileEnlistmentState.Preparing) + { + _pendingOutcome = TransactionStatus.Aborted; + } + + localState = _state; + } + } + + switch (localState) + { + // Tell the application to do the work. + case OletxVolatileEnlistmentState.Aborting: + { + if (localEnlistmentNotification != null) + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.EnlistmentStatus(TraceSourceType.TraceSourceOleTx, InternalTraceIdentifier, NotificationCall.Rollback); + } + + localEnlistmentNotification.Rollback(this); + } + + // There is a small race where Rollback could be called when the enlistment is already + // aborting the transaciton, so just ignore that call. When the app enlistment + // finishes responding to its Rollback notification with EnlistmentDone, things will get + // cleaned up. + break; + } + case OletxVolatileEnlistmentState.Preparing: + // We need to tolerate this state, but we have already marked the + // enlistment as pendingRollback, so there is nothing else to do here. + break; + case OletxVolatileEnlistmentState.Done: + // Early Exit - state was Done + break; + default: + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.InternalError(); + } + + Debug.Fail("OletxVolatileEnlistment.Rollback, invalid state."); + throw new InvalidOperationException(SR.InternalError); + } + } + } + + internal void InDoubt() + { + OletxVolatileEnlistmentState localState = OletxVolatileEnlistmentState.Active; + IEnlistmentNotificationInternal? localEnlistmentNotification = null; + + lock (this) + { + // The app may have already called EnlistmentDone. If this occurs, don't bother sending + // the notification to the app and we don't need to tell the proxy. + if (_state == OletxVolatileEnlistmentState.Prepared) + { + localState = _state = OletxVolatileEnlistmentState.InDoubt; + localEnlistmentNotification = _iEnlistmentNotification; + } + else + { + if (_state == OletxVolatileEnlistmentState.Preparing) + { + _pendingOutcome = TransactionStatus.InDoubt; + } + localState = _state; + } + } + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + + switch (localState) + { + // Tell the application to do the work. + case OletxVolatileEnlistmentState.InDoubt when localEnlistmentNotification != null: + { + if (etwLog.IsEnabled()) + { + etwLog.EnlistmentStatus(TraceSourceType.TraceSourceOleTx, InternalTraceIdentifier, NotificationCall.InDoubt); + } + + localEnlistmentNotification.InDoubt(this); + break; + } + case OletxVolatileEnlistmentState.InDoubt: + { + if (etwLog.IsEnabled()) + { + etwLog.InternalError(); + } + + Debug.Fail("OletxVolatileEnlistment.InDoubt, no enlistmentNotification member."); + throw new InvalidOperationException(SR.InternalError); + } + case OletxVolatileEnlistmentState.Preparing: + // We have already set pendingOutcome, so there is nothing else to do. + break; + case OletxVolatileEnlistmentState.Done: + // Early Exit - state was Done + break; + default: + { + if (etwLog.IsEnabled()) + { + etwLog.InternalError(); + } + + Debug.Fail("OletxVolatileEnlistment.InDoubt, invalid state."); + throw new InvalidOperationException( SR.InternalError); + } + } + } + + void IPromotedEnlistment.EnlistmentDone() + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxEnlistment)}.{nameof(IPromotedEnlistment.EnlistmentDone)}"); + etwLog.EnlistmentCallbackPositive(InternalTraceIdentifier, EnlistmentCallback.Done); + } + + OletxVolatileEnlistmentState localState = OletxVolatileEnlistmentState.Active; + OletxVolatileEnlistmentContainer? localContainer; + + lock (this) + { + localState = _state; + localContainer = _container; + + if (_state != OletxVolatileEnlistmentState.Active && + _state != OletxVolatileEnlistmentState.Preparing && + _state != OletxVolatileEnlistmentState.Aborting && + _state != OletxVolatileEnlistmentState.Committing && + _state != OletxVolatileEnlistmentState.InDoubt) + { + throw TransactionException.CreateEnlistmentStateException(null, DistributedTxId); + } + + _state = OletxVolatileEnlistmentState.Done; + } + + // For the Preparing state, we need to decrement the outstanding + // count with the container. If the state is Active, it is an early vote so we + // just stay in the Done state and when we get the Prepare, we will vote appropriately. + if (localState == OletxVolatileEnlistmentState.Preparing) + { + if (localContainer != null) + { + // Specify true. If aborting, it is okay because the transaction is already + // aborting. + localContainer.DecrementOutstandingNotifications(true); + } + } + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxEnlistment)}.{nameof(IPromotedEnlistment.EnlistmentDone)}"); + } + } + + void IPromotedEnlistment.Prepared() + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, $"OletxPreparingEnlistment.{nameof(IPromotedEnlistment.Prepared)}"); + etwLog.EnlistmentCallbackPositive(InternalTraceIdentifier, EnlistmentCallback.Prepared); + } + + OletxVolatileEnlistmentContainer localContainer; + TransactionStatus localPendingOutcome = TransactionStatus.Active; + + lock (this) + { + if (_state != OletxVolatileEnlistmentState.Preparing) + { + throw TransactionException.CreateEnlistmentStateException(null, DistributedTxId); + } + + _state = OletxVolatileEnlistmentState.Prepared; + localPendingOutcome = _pendingOutcome; + + if (_container == null) + { + if (etwLog.IsEnabled()) + { + etwLog.InternalError(); + } + + Debug.Fail("OletxVolatileEnlistment.Prepared, no container member."); + throw new InvalidOperationException(SR.InternalError); + } + + localContainer = _container; + } + + // Vote yes. + localContainer.DecrementOutstandingNotifications(true); + + switch (localPendingOutcome) + { + case TransactionStatus.Active: + // nothing to do. Everything is proceeding as normal. + break; + + case TransactionStatus.Aborted: + // The transaction aborted while the Prepare was outstanding. + // We need to tell the app to rollback. + Rollback(); + break; + + case TransactionStatus.InDoubt: + // The transaction went InDoubt while the Prepare was outstanding. + // We need to tell the app. + InDoubt(); + break; + + default: + // This shouldn't happen. + if (etwLog.IsEnabled()) + { + etwLog.InternalError(); + } + + Debug.Fail("OletxVolatileEnlistment.Prepared, invalid pending outcome value."); + throw new InvalidOperationException(SR.InternalError); + } + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"OletxPreparingEnlistment.{nameof(IPromotedEnlistment.Prepared)}"); + } + } + + void IPromotedEnlistment.ForceRollback() + => ((IPromotedEnlistment)this).ForceRollback(null); + + void IPromotedEnlistment.ForceRollback(Exception? e) + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, $"OletxPreparingEnlistment.{nameof(IPromotedEnlistment.ForceRollback)}"); + etwLog.EnlistmentCallbackNegative(InternalTraceIdentifier, EnlistmentCallback.ForceRollback); + } + + OletxVolatileEnlistmentContainer localContainer; + + lock (this) + { + if (_state != OletxVolatileEnlistmentState.Preparing) + { + throw TransactionException.CreateEnlistmentStateException(null, DistributedTxId); + } + + // There are no more notifications that need to happen on this enlistment. + _state = OletxVolatileEnlistmentState.Done; + + if (_container == null) + { + if (etwLog.IsEnabled()) + { + etwLog.InternalError(); + } + + Debug.Fail("OletxVolatileEnlistment.ForceRollback, no container member."); + throw new InvalidOperationException(SR.InternalError); + } + + localContainer = _container; + } + + Interlocked.CompareExchange(ref oletxTransaction!.RealOletxTransaction.InnerException, e, null); + + // Vote no. + localContainer.DecrementOutstandingNotifications(false); + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"OletxPreparingEnlistment.{nameof(IPromotedEnlistment.ForceRollback)}"); + } + } + + void IPromotedEnlistment.Committed() => throw new InvalidOperationException(); + void IPromotedEnlistment.Aborted() => throw new InvalidOperationException(); + void IPromotedEnlistment.Aborted(Exception? e) => throw new InvalidOperationException(); + void IPromotedEnlistment.InDoubt() => throw new InvalidOperationException(); + void IPromotedEnlistment.InDoubt(Exception? e) => throw new InvalidOperationException(); + + byte[] IPromotedEnlistment.GetRecoveryInformation() + => throw TransactionException.CreateInvalidOperationException( + TraceSourceType.TraceSourceOleTx, + SR.VolEnlistNoRecoveryInfo, + null, + DistributedTxId); + + InternalEnlistment? IPromotedEnlistment.InternalEnlistment + { + get => InternalEnlistment; + set => InternalEnlistment = value; + } +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/Transaction.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/Transaction.cs index ba6d5af2f3d66f..10eac90c862d37 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/Transaction.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/Transaction.cs @@ -6,7 +6,7 @@ using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Threading; -using System.Transactions.Distributed; +using System.Transactions.Oletx; namespace System.Transactions { @@ -276,7 +276,7 @@ internal Transaction(IsolationLevel isoLevel, InternalTransaction? internalTrans } } - internal Transaction(DistributedTransaction distributedTransaction) + internal Transaction(OletxTransaction distributedTransaction) { _isoLevel = distributedTransaction.IsolationLevel; _internalTransaction = new InternalTransaction(this, distributedTransaction); @@ -566,7 +566,7 @@ public void Rollback() if (etwLog.IsEnabled()) { etwLog.MethodEnter(TraceSourceType.TraceSourceLtm, this); - etwLog.TransactionRollback(this, "Transaction"); + etwLog.TransactionRollback(TraceSourceType.TraceSourceLtm, TransactionTraceId, "Transaction"); } ObjectDisposedException.ThrowIf(Disposed, this); @@ -590,7 +590,7 @@ public void Rollback(Exception? e) if (etwLog.IsEnabled()) { etwLog.MethodEnter(TraceSourceType.TraceSourceLtm, this); - etwLog.TransactionRollback(this, "Transaction"); + etwLog.TransactionRollback(TraceSourceType.TraceSourceLtm, TransactionTraceId, "Transaction"); } ObjectDisposedException.ThrowIf(Disposed, this); @@ -965,7 +965,7 @@ public Enlistment PromoteAndEnlistDurable(Guid resourceManagerIdentifier, TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.MethodEnter(TraceSourceType.TraceSourceDistributed, this); + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this); } ObjectDisposedException.ThrowIf(Disposed, this); @@ -996,7 +996,7 @@ public Enlistment PromoteAndEnlistDurable(Guid resourceManagerIdentifier, if (etwLog.IsEnabled()) { - etwLog.MethodExit(TraceSourceType.TraceSourceDistributed, this); + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this); } return enlistment; @@ -1041,7 +1041,7 @@ public void SetDistributedTransactionIdentifier(IPromotableSinglePhaseNotificati } } - internal DistributedTransaction? Promote() + internal OletxTransaction? Promote() { lock (_internalTransaction) { diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionInterop.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionInterop.cs index a65f8c24b9a90a..a8983b5afad2bc 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionInterop.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionInterop.cs @@ -1,26 +1,17 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Runtime.InteropServices; -using System.Transactions.Distributed; +using System.Transactions.DtcProxyShim; +using System.Transactions.DtcProxyShim.DtcInterfaces; +using System.Transactions.Oletx; namespace System.Transactions { - [ComImport] - [Guid("0fb15084-af41-11ce-bd2b-204c4f4f5020")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - public interface IDtcTransaction - { - void Commit(int retaining, [MarshalAs(UnmanagedType.I4)] int commitType, int reserved); - - void Abort(IntPtr reason, int retaining, int async); - - void GetTransactionInfo(IntPtr transactionInformation); - } - public static class TransactionInterop { - internal static DistributedTransaction ConvertToDistributedTransaction(Transaction transaction) + internal static OletxTransaction ConvertToOletxTransaction(Transaction transaction) { ArgumentNullException.ThrowIfNull(transaction); @@ -31,12 +22,10 @@ internal static DistributedTransaction ConvertToDistributedTransaction(Transacti throw TransactionException.CreateTransactionCompletedException(transaction.DistributedTxId); } - DistributedTransaction? distributedTx = transaction.Promote(); - if (distributedTx == null) - { - throw DistributedTransaction.NotSupported(); - } - return distributedTx; + OletxTransaction? oletxTx = transaction.Promote(); + Debug.Assert(oletxTx != null, "transaction.Promote returned null instead of throwing."); + + return oletxTx; } /// @@ -62,19 +51,39 @@ public static byte[] GetExportCookie(Transaction transaction, byte[] whereabouts TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.MethodEnter(TraceSourceType.TraceSourceDistributed, "TransactionInterop.GetExportCookie"); + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, $"{nameof(TransactionInterop)}.{nameof(GetExportCookie)}"); } + byte[] cookie; + // Copy the whereabouts so that it cannot be modified later. var whereaboutsCopy = new byte[whereabouts.Length]; Buffer.BlockCopy(whereabouts, 0, whereaboutsCopy, 0, whereabouts.Length); - ConvertToDistributedTransaction(transaction); - byte[] cookie = DistributedTransaction.GetExportCookie(whereaboutsCopy); + // First, make sure we are working with an OletxTransaction. + OletxTransaction oletxTx = ConvertToOletxTransaction(transaction); + + try + { + oletxTx.RealOletxTransaction.TransactionShim.Export(whereabouts, out cookie); + } + catch (COMException comException) + { + OletxTransactionManager.ProxyException(comException); + + // We are unsure of what the exception may mean. It is possible that + // we could get E_FAIL when trying to contact a transaction manager that is + // being blocked by a fire wall. On the other hand we may get a COMException + // based on bad data. The more common situation is that the data is fine + // (since it is generated by Microsoft code) and the problem is with + // communication. So in this case we default for unknown exceptions to + // assume that the problem is with communication. + throw TransactionManagerCommunicationException.Create(null, comException); + } if (etwLog.IsEnabled()) { - etwLog.MethodExit(TraceSourceType.TraceSourceDistributed, "TransactionInterop.GetExportCookie"); + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, $"{nameof(TransactionInterop)}.{nameof(GetExportCookie)}"); } return cookie; @@ -92,13 +101,20 @@ public static Transaction GetTransactionFromExportCookie(byte[] cookie) TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.MethodEnter(TraceSourceType.TraceSourceDistributed, "TransactionInterop.GetTransactionFromExportCookie"); + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, $"{nameof(TransactionInterop)}.{nameof(GetTransactionFromExportCookie)}"); } var cookieCopy = new byte[cookie.Length]; Buffer.BlockCopy(cookie, 0, cookieCopy, 0, cookie.Length); cookie = cookieCopy; + Transaction? transaction; + TransactionShim? transactionShim = null; + Guid txIdentifier = Guid.Empty; + OletxTransactionIsolationLevel oletxIsoLevel = OletxTransactionIsolationLevel.ISOLATIONLEVEL_SERIALIZABLE; + OutcomeEnlistment? outcomeEnlistment; + OletxTransaction? oleTx; + // Extract the transaction guid from the propagation token to see if we already have a // transaction object for the transaction. // In a cookie, the transaction guid is preceded by a signature guid. @@ -106,24 +122,64 @@ public static Transaction GetTransactionFromExportCookie(byte[] cookie) // First check to see if there is a promoted LTM transaction with the same ID. If there // is, just return that. - Transaction? transaction = TransactionManager.FindPromotedTransaction(txId); + transaction = TransactionManager.FindPromotedTransaction(txId); if (transaction != null) { if (etwLog.IsEnabled()) { - etwLog.MethodExit(TraceSourceType.TraceSourceDistributed, "TransactionInterop.GetTransactionFromExportCookie"); + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, "TransactionInterop.GetTransactionFromExportCookie"); } return transaction; } - // Find or create the promoted transaction. - DistributedTransaction dTx = DistributedTransactionManager.GetTransactionFromExportCookie(cookieCopy, txId); - transaction = TransactionManager.FindOrCreatePromotedTransaction(txId, dTx); + // We need to create a new transaction + RealOletxTransaction? realTx = null; + OletxTransactionManager oletxTm = TransactionManager.DistributedTransactionManager; + + oletxTm.DtcTransactionManagerLock.AcquireReaderLock(-1); + try + { + outcomeEnlistment = new OutcomeEnlistment(); + oletxTm.DtcTransactionManager.ProxyShimFactory.Import(cookie, outcomeEnlistment, out txIdentifier, out oletxIsoLevel, out transactionShim); + } + catch (COMException comException) + { + OletxTransactionManager.ProxyException(comException); + + // We are unsure of what the exception may mean. It is possible that + // we could get E_FAIL when trying to contact a transaction manager that is + // being blocked by a fire wall. On the other hand we may get a COMException + // based on bad data. The more common situation is that the data is fine + // (since it is generated by Microsoft code) and the problem is with + // communication. So in this case we default for unknown exceptions to + // assume that the problem is with communication. + throw TransactionManagerCommunicationException.Create(SR.TraceSourceOletx, comException); + } + finally + { + oletxTm.DtcTransactionManagerLock.ReleaseReaderLock(); + } + + // We need to create a new RealOletxTransaction. + realTx = new RealOletxTransaction( + oletxTm, + transactionShim, + outcomeEnlistment, + txIdentifier, + oletxIsoLevel, + false); + + // Now create the associated OletxTransaction. + oleTx = new OletxTransaction(realTx); + + // If a transaction is found then FindOrCreate will Dispose the oletx + // created. + transaction = TransactionManager.FindOrCreatePromotedTransaction(txId, oleTx); if (etwLog.IsEnabled()) { - etwLog.MethodExit(TraceSourceType.TraceSourceDistributed, "TransactionInterop.GetTransactionFromExportCookie"); + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, $"{nameof(TransactionInterop)}.{nameof(GetTransactionFromExportCookie)}"); } return transaction; @@ -136,20 +192,39 @@ public static byte[] GetTransmitterPropagationToken(Transaction transaction) TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.MethodEnter(TraceSourceType.TraceSourceDistributed, "TransactionInterop.GetTransmitterPropagationToken"); + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, $"{nameof(TransactionInterop)}.{nameof(GetTransmitterPropagationToken)}"); } - ConvertToDistributedTransaction(transaction); - byte[] token = DistributedTransaction.GetTransmitterPropagationToken(); + // First, make sure we are working with an OletxTransaction. + OletxTransaction oletxTx = ConvertToOletxTransaction(transaction); + + byte[] token = GetTransmitterPropagationToken(oletxTx); if (etwLog.IsEnabled()) { - etwLog.MethodExit(TraceSourceType.TraceSourceDistributed, "TransactionInterop.GetTransmitterPropagationToken"); + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, $"{nameof(TransactionInterop)}.{nameof(GetTransmitterPropagationToken)}"); } return token; } + internal static byte[] GetTransmitterPropagationToken(OletxTransaction oletxTx) + { + byte[]? propagationToken = null; + + try + { + propagationToken = oletxTx.RealOletxTransaction.TransactionShim.GetPropagationToken(); + } + catch (COMException comException) + { + OletxTransactionManager.ProxyException(comException); + throw; + } + + return propagationToken; + } + public static Transaction GetTransactionFromTransmitterPropagationToken(byte[] propagationToken) { ArgumentNullException.ThrowIfNull(propagationToken); @@ -162,7 +237,7 @@ public static Transaction GetTransactionFromTransmitterPropagationToken(byte[] p TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.MethodEnter(TraceSourceType.TraceSourceDistributed, "TransactionInterop.GetTransactionFromTransmitterPropagationToken"); + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, "TransactionInterop.GetTransactionFromTransmitterPropagationToken"); } // Extract the transaction guid from the propagation token to see if we already have a @@ -176,20 +251,20 @@ public static Transaction GetTransactionFromTransmitterPropagationToken(byte[] p { if (etwLog.IsEnabled()) { - etwLog.MethodExit(TraceSourceType.TraceSourceDistributed, "TransactionInterop.GetTransactionFromTransmitterPropagationToken"); + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, "TransactionInterop.GetTransactionFromTransmitterPropagationToken"); } return tx; } - DistributedTransaction dTx = GetDistributedTransactionFromTransmitterPropagationToken(propagationToken); + OletxTransaction dTx = GetOletxTransactionFromTransmitterPropagationToken(propagationToken); // If a transaction is found then FindOrCreate will Dispose the distributed transaction created. Transaction returnValue = TransactionManager.FindOrCreatePromotedTransaction(txId, dTx); if (etwLog.IsEnabled()) { - etwLog.MethodExit(TraceSourceType.TraceSourceDistributed, "TransactionInterop.GetTransactionFromTransmitterPropagationToken"); + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, "TransactionInterop.GetTransactionFromTransmitterPropagationToken"); } return returnValue; } @@ -201,15 +276,27 @@ public static IDtcTransaction GetDtcTransaction(Transaction transaction) TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.MethodEnter(TraceSourceType.TraceSourceDistributed, "TransactionInterop.GetDtcTransaction"); + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, $"{nameof(TransactionInterop)}.{nameof(GetDtcTransaction)}"); } - ConvertToDistributedTransaction(transaction); - IDtcTransaction transactionNative = DistributedTransaction.GetDtcTransaction(); + IDtcTransaction? transactionNative; + + // First, make sure we are working with an OletxTransaction. + OletxTransaction oletxTx = ConvertToOletxTransaction(transaction); + + try + { + oletxTx.RealOletxTransaction.TransactionShim.GetITransactionNative(out transactionNative); + } + catch (COMException comException) + { + OletxTransactionManager.ProxyException(comException); + throw; + } if (etwLog.IsEnabled()) { - etwLog.MethodExit(TraceSourceType.TraceSourceDistributed, "TransactionInterop.GetDtcTransaction"); + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, $"{nameof(TransactionInterop)}.{nameof(GetDtcTransaction)}"); } return transactionNative; @@ -217,20 +304,121 @@ public static IDtcTransaction GetDtcTransaction(Transaction transaction) public static Transaction GetTransactionFromDtcTransaction(IDtcTransaction transactionNative) { - ArgumentNullException.ThrowIfNull(transactionNative); + ArgumentNullException.ThrowIfNull(transactionNative, nameof(transactionNative)); TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.MethodEnter(TraceSourceType.TraceSourceDistributed, "TransactionInterop.GetTransactionFromDtcTransaction"); + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, $"{nameof(TransactionInterop)}.{nameof(GetTransactionFromDtcTransaction)}"); + } + + Transaction? transaction = null; + bool tooLate = false; + TransactionShim? transactionShim = null; + Guid txIdentifier = Guid.Empty; + OletxTransactionIsolationLevel oletxIsoLevel = OletxTransactionIsolationLevel.ISOLATIONLEVEL_SERIALIZABLE; + OutcomeEnlistment? outcomeEnlistment = null; + RealOletxTransaction? realTx = null; + OletxTransaction? oleTx = null; + + // Let's get the guid of the transaction from the proxy to see if we already have an object. + if (transactionNative is not ITransaction myTransactionNative) + { + throw new ArgumentException(SR.InvalidArgument, nameof(transactionNative)); + } + + OletxXactTransInfo xactInfo; + try + { + myTransactionNative.GetTransactionInfo(out xactInfo); + } + catch (COMException ex) when (ex.ErrorCode == OletxHelper.XACT_E_NOTRANSACTION) + { + // If we get here, the transaction has appraently already been committed or aborted. Allow creation of the + // OletxTransaction, but it will be marked with a status of InDoubt and attempts to get its Identifier + // property will result in a TransactionException. + tooLate = true; + xactInfo.Uow = Guid.Empty; } - Transaction transaction = DistributedTransactionManager.GetTransactionFromDtcTransaction(transactionNative); + OletxTransactionManager oletxTm = TransactionManager.DistributedTransactionManager; + if (!tooLate) + { + // First check to see if there is a promoted LTM transaction with the same ID. If there + // is, just return that. + transaction = TransactionManager.FindPromotedTransaction(xactInfo.Uow); + if (transaction != null) + { + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, $"{nameof(TransactionInterop)}.{nameof(GetTransactionFromDtcTransaction)}"); + } + + return transaction; + } + + // We need to create a new RealOletxTransaction... + oletxTm.DtcTransactionManagerLock.AcquireReaderLock(-1); + try + { + outcomeEnlistment = new OutcomeEnlistment(); + oletxTm.DtcTransactionManager.ProxyShimFactory.CreateTransactionShim( + transactionNative, + outcomeEnlistment, + out txIdentifier, + out oletxIsoLevel, + out transactionShim); + } + catch (COMException comException) + { + OletxTransactionManager.ProxyException(comException); + throw; + } + finally + { + oletxTm.DtcTransactionManagerLock.ReleaseReaderLock(); + } + + // We need to create a new RealOletxTransaction. + realTx = new RealOletxTransaction( + oletxTm, + transactionShim, + outcomeEnlistment, + txIdentifier, + oletxIsoLevel, + false); + + oleTx = new OletxTransaction(realTx); + + // If a transaction is found then FindOrCreate will Dispose the oletx + // created. + transaction = TransactionManager.FindOrCreatePromotedTransaction(xactInfo.Uow, oleTx); + } + else + { + // It was too late to do a clone of the provided ITransactionNative, so we are just going to + // create a RealOletxTransaction without a transaction shim or outcome enlistment. + realTx = new RealOletxTransaction( + oletxTm, + null, + null, + txIdentifier, + OletxTransactionIsolationLevel.ISOLATIONLEVEL_SERIALIZABLE, + false); + + oleTx = new OletxTransaction(realTx); + transaction = new Transaction(oleTx); + TransactionManager.FireDistributedTransactionStarted(transaction); + oleTx.SavedLtmPromotedTransaction = transaction; + + InternalTransaction.DistributedTransactionOutcome(transaction._internalTransaction, TransactionStatus.InDoubt); + } if (etwLog.IsEnabled()) { - etwLog.MethodExit(TraceSourceType.TraceSourceDistributed, "TransactionInterop.GetTransactionFromDtcTransaction"); + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, $"{nameof(TransactionInterop)}.{nameof(GetTransactionFromDtcTransaction)}"); } + return transaction; } @@ -239,19 +427,36 @@ public static byte[] GetWhereabouts() TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.MethodEnter(TraceSourceType.TraceSourceDistributed, "TransactionInterop.GetWhereabouts"); + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, $"{nameof(TransactionInterop)}.${nameof(GetWhereabouts)}"); + } + + OletxTransactionManager oletxTm = TransactionManager.DistributedTransactionManager; + if (oletxTm == null) + { + throw new ArgumentException(SR.InvalidArgument, "transactionManager"); } - byte[] returnValue = DistributedTransactionManager.GetWhereabouts(); + byte[]? returnValue; + + oletxTm.DtcTransactionManagerLock.AcquireReaderLock(-1); + try + { + returnValue = oletxTm.DtcTransactionManager.Whereabouts; + } + finally + { + oletxTm.DtcTransactionManagerLock.ReleaseReaderLock(); + } if (etwLog.IsEnabled()) { - etwLog.MethodExit(TraceSourceType.TraceSourceDistributed, "TransactionInterop.GetWhereabouts"); + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, $"{nameof(TransactionInterop)}.${nameof(GetWhereabouts)}"); } + return returnValue; } - internal static DistributedTransaction GetDistributedTransactionFromTransmitterPropagationToken(byte[] propagationToken) + internal static OletxTransaction GetOletxTransactionFromTransmitterPropagationToken(byte[] propagationToken) { ArgumentNullException.ThrowIfNull(propagationToken); @@ -260,10 +465,56 @@ internal static DistributedTransaction GetDistributedTransactionFromTransmitterP throw new ArgumentException(SR.InvalidArgument, nameof(propagationToken)); } + Guid identifier; + OletxTransactionIsolationLevel oletxIsoLevel; + OutcomeEnlistment outcomeEnlistment; + TransactionShim? transactionShim = null; + byte[] propagationTokenCopy = new byte[propagationToken.Length]; Array.Copy(propagationToken, propagationTokenCopy, propagationToken.Length); + propagationToken = propagationTokenCopy; + + // First we need to create an OletxTransactionManager from Config. + OletxTransactionManager oletxTm = TransactionManager.DistributedTransactionManager; + + oletxTm.DtcTransactionManagerLock.AcquireReaderLock(-1); + try + { + outcomeEnlistment = new OutcomeEnlistment(); + oletxTm.DtcTransactionManager.ProxyShimFactory.ReceiveTransaction( + propagationToken, + outcomeEnlistment, + out identifier, + out oletxIsoLevel, + out transactionShim); + } + catch (COMException comException) + { + OletxTransactionManager.ProxyException(comException); + + // We are unsure of what the exception may mean. It is possible that + // we could get E_FAIL when trying to contact a transaction manager that is + // being blocked by a fire wall. On the other hand we may get a COMException + // based on bad data. The more common situation is that the data is fine + // (since it is generated by Microsoft code) and the problem is with + // communication. So in this case we default for unknown exceptions to + // assume that the problem is with communication. + throw TransactionManagerCommunicationException.Create(SR.TraceSourceOletx, comException); + } + finally + { + oletxTm.DtcTransactionManagerLock.ReleaseReaderLock(); + } + + var realTx = new RealOletxTransaction( + oletxTm, + transactionShim, + outcomeEnlistment, + identifier, + oletxIsoLevel, + false); - return DistributedTransactionManager.GetDistributedTransactionFromTransmitterPropagationToken(propagationTokenCopy); + return new OletxTransaction(realTx); } } } diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionInteropNonWindows.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionInteropNonWindows.cs new file mode 100644 index 00000000000000..d656c1a6458d59 --- /dev/null +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionInteropNonWindows.cs @@ -0,0 +1,257 @@ +// 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 System.Transactions.Oletx; + +namespace System.Transactions +{ + public static class TransactionInterop + { + internal static OletxTransaction ConvertToOletxTransaction(Transaction transaction) + { + ArgumentNullException.ThrowIfNull(transaction); + + ObjectDisposedException.ThrowIf(transaction.Disposed, transaction); + + if (transaction._complete) + { + throw TransactionException.CreateTransactionCompletedException(transaction.DistributedTxId); + } + + OletxTransaction? distributedTx = transaction.Promote(); + if (distributedTx == null) + { + throw OletxTransaction.NotSupported(); + } + return distributedTx; + } + + /// + /// This is the PromoterType value that indicates that the transaction is promoting to MSDTC. + /// + /// If using the variation of Transaction.EnlistPromotableSinglePhase that takes a PromoterType and the + /// ITransactionPromoter being used promotes to MSDTC, then this is the value that should be + /// specified for the PromoterType parameter to EnlistPromotableSinglePhase. + /// + /// If using the variation of Transaction.EnlistPromotableSinglePhase that assumes promotion to MSDTC and + /// it that returns false, the caller can compare this value with Transaction.PromoterType to + /// verify that the transaction promoted, or will promote, to MSDTC. If the Transaction.PromoterType + /// matches this value, then the caller can continue with its enlistment with MSDTC. But if it + /// does not match, the caller will not be able to enlist with MSDTC. + /// + public static readonly Guid PromoterTypeDtc = new Guid("14229753-FFE1-428D-82B7-DF73045CB8DA"); + + public static byte[] GetExportCookie(Transaction transaction, byte[] whereabouts) + { + ArgumentNullException.ThrowIfNull(transaction); + ArgumentNullException.ThrowIfNull(whereabouts); + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, "TransactionInterop.GetExportCookie"); + } + + // Copy the whereabouts so that it cannot be modified later. + var whereaboutsCopy = new byte[whereabouts.Length]; + Buffer.BlockCopy(whereabouts, 0, whereaboutsCopy, 0, whereabouts.Length); + + ConvertToOletxTransaction(transaction); + byte[] cookie = OletxTransaction.GetExportCookie(whereaboutsCopy); + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, "TransactionInterop.GetExportCookie"); + } + + return cookie; + } + + public static Transaction GetTransactionFromExportCookie(byte[] cookie) + { + ArgumentNullException.ThrowIfNull(cookie); + + if (cookie.Length < 32) + { + throw new ArgumentException(SR.InvalidArgument, nameof(cookie)); + } + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, "TransactionInterop.GetTransactionFromExportCookie"); + } + + var cookieCopy = new byte[cookie.Length]; + Buffer.BlockCopy(cookie, 0, cookieCopy, 0, cookie.Length); + cookie = cookieCopy; + + // Extract the transaction guid from the propagation token to see if we already have a + // transaction object for the transaction. + // In a cookie, the transaction guid is preceded by a signature guid. + var txId = new Guid(cookie.AsSpan(16, 16)); + + // First check to see if there is a promoted LTM transaction with the same ID. If there + // is, just return that. + Transaction? transaction = TransactionManager.FindPromotedTransaction(txId); + if (transaction != null) + { + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, "TransactionInterop.GetTransactionFromExportCookie"); + } + + return transaction; + } + + // Find or create the promoted transaction. + OletxTransaction dTx = OletxTransactionManager.GetTransactionFromExportCookie(cookieCopy, txId); + transaction = TransactionManager.FindOrCreatePromotedTransaction(txId, dTx); + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, "TransactionInterop.GetTransactionFromExportCookie"); + } + + return transaction; + } + + public static byte[] GetTransmitterPropagationToken(Transaction transaction) + { + ArgumentNullException.ThrowIfNull(transaction); + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, "TransactionInterop.GetTransmitterPropagationToken"); + } + + ConvertToOletxTransaction(transaction); + byte[] token = OletxTransaction.GetTransmitterPropagationToken(); + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, "TransactionInterop.GetTransmitterPropagationToken"); + } + + return token; + } + + public static Transaction GetTransactionFromTransmitterPropagationToken(byte[] propagationToken) + { + ArgumentNullException.ThrowIfNull(propagationToken); + + if (propagationToken.Length < 24) + { + throw new ArgumentException(SR.InvalidArgument, nameof(propagationToken)); + } + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, "TransactionInterop.GetTransactionFromTransmitterPropagationToken"); + } + + // Extract the transaction guid from the propagation token to see if we already have a + // transaction object for the transaction. + // In a propagation token, the transaction guid is preceded by two version DWORDs. + var txId = new Guid(propagationToken.AsSpan(8, 16)); + + // First check to see if there is a promoted LTM transaction with the same ID. If there is, just return that. + Transaction? tx = TransactionManager.FindPromotedTransaction(txId); + if (null != tx) + { + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, "TransactionInterop.GetTransactionFromTransmitterPropagationToken"); + } + + return tx; + } + + OletxTransaction dTx = GetOletxTransactionFromTransmitterPropagationToken(propagationToken); + + // If a transaction is found then FindOrCreate will Dispose the distributed transaction created. + Transaction returnValue = TransactionManager.FindOrCreatePromotedTransaction(txId, dTx); + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, "TransactionInterop.GetTransactionFromTransmitterPropagationToken"); + } + return returnValue; + } + + public static IDtcTransaction GetDtcTransaction(Transaction transaction) + { + ArgumentNullException.ThrowIfNull(transaction); + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, "TransactionInterop.GetDtcTransaction"); + } + + ConvertToOletxTransaction(transaction); + IDtcTransaction transactionNative = OletxTransaction.GetDtcTransaction(); + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, "TransactionInterop.GetDtcTransaction"); + } + + return transactionNative; + } + + public static Transaction GetTransactionFromDtcTransaction(IDtcTransaction transactionNative) + { + ArgumentNullException.ThrowIfNull(transactionNative); + + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, "TransactionInterop.GetTransactionFromDtcTransaction"); + } + + Transaction transaction = OletxTransactionManager.GetTransactionFromDtcTransaction(transactionNative); + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, "TransactionInterop.GetTransactionFromDtcTransaction"); + } + return transaction; + } + + public static byte[] GetWhereabouts() + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, "TransactionInterop.GetWhereabouts"); + } + + byte[] returnValue = OletxTransactionManager.GetWhereabouts(); + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, "TransactionInterop.GetWhereabouts"); + } + return returnValue; + } + + internal static OletxTransaction GetOletxTransactionFromTransmitterPropagationToken(byte[] propagationToken) + { + ArgumentNullException.ThrowIfNull(propagationToken); + + if (propagationToken.Length < 24) + { + throw new ArgumentException(SR.InvalidArgument, nameof(propagationToken)); + } + + byte[] propagationTokenCopy = new byte[propagationToken.Length]; + Array.Copy(propagationToken, propagationTokenCopy, propagationToken.Length); + + return OletxTransactionManager.GetOletxTransactionFromTransmitterPropagationToken(propagationTokenCopy); + } + } +} diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionManager.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionManager.cs index ff811ad32a6de8..7ce21c51bad238 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionManager.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionManager.cs @@ -6,7 +6,7 @@ using System.IO; using System.Threading; using System.Transactions.Configuration; -using System.Transactions.Distributed; +using System.Transactions.Oletx; namespace System.Transactions { @@ -18,6 +18,7 @@ public static class TransactionManager { // Revovery Information Version private const int RecoveryInformationVersion1 = 1; + private const int CurrentRecoveryVersion = RecoveryInformationVersion1; // Hashtable of promoted transactions, keyed by identifier guid. This is used by // FindPromotedTransaction to support transaction equivalence when a transaction is @@ -215,9 +216,9 @@ public static Enlistment Reenlist( } - private static DistributedTransactionManager CheckTransactionManager(string? nodeName) + private static OletxTransactionManager CheckTransactionManager(string? nodeName) { - DistributedTransactionManager tm = DistributedTransactionManager; + OletxTransactionManager tm = DistributedTransactionManager; if (!((tm.NodeName == null && (nodeName == null || nodeName.Length == 0)) || (tm.NodeName != null && tm.NodeName.Equals(nodeName)))) { @@ -390,6 +391,55 @@ public static TimeSpan MaximumTimeout } } + // This routine writes the "header" for the recovery information, based on the + // type of the calling object and its provided parameter collection. This information + // we be read back by the static Reenlist method to create the necessary transaction + // manager object with the right parameters in order to do a ReenlistTransaction call. + internal static byte[] GetRecoveryInformation( + string? startupInfo, + byte[] resourceManagerRecoveryInformation + ) + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, $"{nameof(TransactionManager)}.{nameof(GetRecoveryInformation)}"); + } + + MemoryStream stream = new MemoryStream(); + byte[]? returnValue = null; + + try + { + // Manually write the recovery information + BinaryWriter writer = new BinaryWriter(stream); + + writer.Write(CurrentRecoveryVersion); + if (startupInfo != null) + { + writer.Write(startupInfo); + } + else + { + writer.Write(""); + } + writer.Write(resourceManagerRecoveryInformation); + writer.Flush(); + returnValue = stream.ToArray(); + } + finally + { + stream.Close(); + } + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, $"{nameof(TransactionManager)}.{nameof(GetRecoveryInformation)}"); + } + + return returnValue; + } + /// /// This static function throws an ArgumentOutOfRange if the specified IsolationLevel is not within /// the range of valid values. @@ -414,7 +464,6 @@ internal static void ValidateIsolationLevel(IsolationLevel transactionIsolationL } } - /// /// This static function throws an ArgumentOutOfRange if the specified TimeSpan does not meet /// requirements of a valid transaction timeout. Timeout values must be positive. @@ -462,7 +511,7 @@ internal static TimeSpan ValidateTimeout(TimeSpan transactionTimeout) return null; } - internal static Transaction FindOrCreatePromotedTransaction(Guid transactionIdentifier, DistributedTransaction dtx) + internal static Transaction FindOrCreatePromotedTransaction(Guid transactionIdentifier, OletxTransaction dtx) { Transaction? tx = null; Hashtable promotedTransactionTable = PromotedTransactionTable; @@ -511,9 +560,10 @@ internal static Transaction FindOrCreatePromotedTransaction(Guid transactionIden LazyInitializer.EnsureInitialized(ref s_transactionTable, ref s_classSyncObject, () => new TransactionTable()); // Fault in a DistributedTransactionManager if one has not already been created. - internal static DistributedTransactionManager? distributedTransactionManager; - internal static DistributedTransactionManager DistributedTransactionManager => + internal static OletxTransactionManager? distributedTransactionManager; + internal static OletxTransactionManager DistributedTransactionManager => // If the distributed transaction manager is not configured, throw an exception - LazyInitializer.EnsureInitialized(ref distributedTransactionManager, ref s_classSyncObject, () => new DistributedTransactionManager()); + LazyInitializer.EnsureInitialized(ref distributedTransactionManager, ref s_classSyncObject, + () => new OletxTransactionManager(DefaultSettingsSection.DistributedTransactionManagerName)); } } diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionScope.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionScope.cs index 938a4c0735beb5..0ca05d76d4bb7b 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionScope.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionScope.cs @@ -854,7 +854,7 @@ private static void TimerCallback(object? state) TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.TransactionScopeInternalError("TransactionScopeTimerObjectInvalid"); + etwLog.InternalError("TransactionScopeTimerObjectInvalid"); } throw TransactionException.Create(TraceSourceType.TraceSourceBase, SR.InternalError + SR.TransactionScopeTimerObjectInvalid, null); diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionState.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionState.cs index f6a99cb47d940b..331e679db04ea1 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionState.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionState.cs @@ -5,7 +5,7 @@ using System.Diagnostics; using System.Runtime.Serialization; using System.Threading; -using System.Transactions.Distributed; +using System.Transactions.Oletx; namespace System.Transactions { @@ -1532,7 +1532,7 @@ internal override void EnterState(InternalTransaction tx) TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.TransactionCommitted(tx.TransactionTraceId); + etwLog.TransactionCommitted(TraceSourceType.TraceSourceLtm, tx.TransactionTraceId); } // Fire Completion for anyone listening @@ -1595,7 +1595,7 @@ internal override void EnterState(InternalTransaction tx) TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.TransactionInDoubt(tx.TransactionTraceId); + etwLog.TransactionInDoubt(TraceSourceType.TraceSourceLtm, tx.TransactionTraceId); } // Fire Completion for anyone listening @@ -1676,7 +1676,7 @@ Transaction atomicTransaction EnlistmentState.EnlistmentStatePromoted.EnterState(en.InternalEnlistment); en.InternalEnlistment.PromotedEnlistment = - DistributedTransaction.EnlistVolatile( + tx.PromotedTransaction.EnlistVolatile( en.InternalEnlistment, enlistmentOptions); return en; } @@ -1705,7 +1705,7 @@ Transaction atomicTransaction EnlistmentState.EnlistmentStatePromoted.EnterState(en.InternalEnlistment); en.InternalEnlistment.PromotedEnlistment = - DistributedTransaction.EnlistVolatile( + tx.PromotedTransaction.EnlistVolatile( en.InternalEnlistment, enlistmentOptions); return en; } @@ -1742,7 +1742,7 @@ Transaction atomicTransaction EnlistmentState.EnlistmentStatePromoted.EnterState(en.InternalEnlistment); en.InternalEnlistment.PromotedEnlistment = - DistributedTransaction.EnlistDurable( + tx.PromotedTransaction.EnlistDurable( resourceManagerIdentifier, (DurableInternalEnlistment)en.InternalEnlistment, false, @@ -1783,7 +1783,7 @@ Transaction atomicTransaction EnlistmentState.EnlistmentStatePromoted.EnterState(en.InternalEnlistment); en.InternalEnlistment.PromotedEnlistment = - DistributedTransaction.EnlistDurable( + tx.PromotedTransaction.EnlistDurable( resourceManagerIdentifier, (DurableInternalEnlistment)en.InternalEnlistment, true, @@ -1809,7 +1809,7 @@ internal override void Rollback(InternalTransaction tx, Exception? e) Monitor.Exit(tx); try { - DistributedTransaction.Rollback(); + tx.PromotedTransaction.Rollback(); } finally { @@ -1894,16 +1894,17 @@ internal override void CompleteBlockingClone(InternalTransaction tx) Debug.Assert(tx._phase0WaveDependentCloneCount >= 0); if (tx._phase0WaveDependentCloneCount == 0) { + OletxDependentTransaction dtx = tx._phase0WaveDependentClone!; tx._phase0WaveDependentClone = null; Monitor.Exit(tx); try { - DistributedDependentTransaction.Complete(); + dtx.Complete(); } finally { - Monitor.Enter(tx); + dtx.Dispose(); } } } @@ -1930,22 +1931,22 @@ internal override void CompleteAbortingClone(InternalTransaction tx) { // We need to complete our dependent clone on the promoted transaction and null it out // so if we get a new one, a new one will be created on the promoted transaction. + OletxDependentTransaction dtx = tx._abortingDependentClone!; tx._abortingDependentClone = null; Monitor.Exit(tx); try { - DistributedDependentTransaction.Complete(); + dtx.Complete(); } finally { - Monitor.Enter(tx); + dtx.Dispose(); } } } } - internal override void CreateBlockingClone(InternalTransaction tx) { // Once the transaction is promoted leverage the distributed @@ -1954,7 +1955,7 @@ internal override void CreateBlockingClone(InternalTransaction tx) if (tx._phase0WaveDependentClone == null) { Debug.Assert(tx.PromotedTransaction != null); - tx._phase0WaveDependentClone = DistributedTransaction.DependentClone(true); + tx._phase0WaveDependentClone = tx.PromotedTransaction.DependentClone(true); } tx._phase0WaveDependentCloneCount++; @@ -1976,7 +1977,7 @@ internal override void CreateAbortingClone(InternalTransaction tx) if (null == tx._abortingDependentClone) { Debug.Assert(tx.PromotedTransaction != null); - tx._abortingDependentClone = DistributedTransaction.DependentClone(false); + tx._abortingDependentClone = tx.PromotedTransaction.DependentClone(false); } tx._abortingDependentCloneCount++; } @@ -2050,7 +2051,7 @@ internal override void Timeout(InternalTransaction tx) { tx._innerException ??= new TimeoutException(SR.TraceTransactionTimeout); Debug.Assert(tx.PromotedTransaction != null); - DistributedTransaction.Rollback(); + tx.PromotedTransaction.Rollback(); TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) @@ -2143,7 +2144,7 @@ internal override void EnterState(InternalTransaction tx) CommonEnterState(tx); // Create a transaction with the distributed transaction manager - DistributedCommittableTransaction? distributedTx = null; + OletxCommittableTransaction? distributedTx = null; try { TimeSpan newTimeout; @@ -2169,7 +2170,7 @@ internal override void EnterState(InternalTransaction tx) // Create a new distributed transaction. distributedTx = - DistributedTransactionManager.CreateTransaction(options); + TransactionManager.DistributedTransactionManager.CreateTransaction(options); distributedTx.SavedLtmPromotedTransaction = tx._outcomeSource; TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; @@ -2240,7 +2241,7 @@ protected static bool PromotePhaseVolatiles( } Debug.Assert(tx.PromotedTransaction != null); - volatiles.VolatileDemux._promotedEnlistment = DistributedTransaction.EnlistVolatile(volatiles.VolatileDemux, + volatiles.VolatileDemux._promotedEnlistment = tx.PromotedTransaction.EnlistVolatile(volatiles.VolatileDemux, phase0 ? EnlistmentOptions.EnlistDuringPrepareRequired : EnlistmentOptions.None); } @@ -2256,7 +2257,7 @@ internal virtual bool PromoteDurable(InternalTransaction tx) // Directly enlist the durable enlistment with the resource manager. InternalEnlistment enlistment = tx._durableEnlistment; Debug.Assert(tx.PromotedTransaction != null); - IPromotedEnlistment promotedEnlistment = DistributedTransaction.EnlistDurable( + IPromotedEnlistment promotedEnlistment = tx.PromotedTransaction.EnlistDurable( enlistment.ResourceManagerIdentifier, (DurableInternalEnlistment)enlistment, enlistment.SinglePhaseNotification != null, @@ -2306,7 +2307,7 @@ internal virtual void PromoteEnlistmentsAndOutcome(InternalTransaction tx) { if (!enlistmentsPromoted) { - DistributedTransaction.Rollback(); + tx.PromotedTransaction.Rollback(); // Now abort this transaction. tx.State.ChangeStateAbortedDuringPromotion(tx); @@ -2335,7 +2336,7 @@ internal virtual void PromoteEnlistmentsAndOutcome(InternalTransaction tx) { if (!enlistmentsPromoted) { - DistributedTransaction.Rollback(); + tx.PromotedTransaction.Rollback(); // Now abort this transaction. tx.State.ChangeStateAbortedDuringPromotion(tx); @@ -2365,7 +2366,7 @@ internal virtual void PromoteEnlistmentsAndOutcome(InternalTransaction tx) { if (!enlistmentsPromoted) { - DistributedTransaction.Rollback(); + tx.PromotedTransaction.Rollback(); // Now abort this transaction. tx.State.ChangeStateAbortedDuringPromotion(tx); @@ -2450,7 +2451,8 @@ internal override void EnterState(InternalTransaction tx) CommonEnterState(tx); // Use the asynchronous commit provided by the promoted transaction - DistributedCommittableTransaction.BeginCommit(tx); + OletxCommittableTransaction ctx = (OletxCommittableTransaction)tx.PromotedTransaction!; + ctx.BeginCommit(tx); } @@ -2762,7 +2764,7 @@ internal override void EnterState(InternalTransaction tx) { Debug.Assert(tx.PromotedTransaction != null); // Otherwise make sure that the transaction rolls back. - DistributedTransaction.Rollback(); + tx.PromotedTransaction.Rollback(); } } @@ -2932,7 +2934,7 @@ internal override void EnterState(InternalTransaction tx) TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.TransactionAborted(tx.TransactionTraceId); + etwLog.TransactionAborted(TraceSourceType.TraceSourceLtm, tx.TransactionTraceId); } } @@ -3076,7 +3078,7 @@ internal override void EnterState(InternalTransaction tx) TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.TransactionCommitted(tx.TransactionTraceId); + etwLog.TransactionCommitted(TraceSourceType.TraceSourceLtm, tx.TransactionTraceId); } } @@ -3146,7 +3148,7 @@ internal override void EnterState(InternalTransaction tx) TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.TransactionInDoubt(tx.TransactionTraceId); + etwLog.TransactionInDoubt(TraceSourceType.TraceSourceLtm, tx.TransactionTraceId); } } @@ -3247,7 +3249,7 @@ internal override void EnterState(InternalTransaction tx) CommonEnterState(tx); // Create a transaction with the distributed transaction manager - DistributedTransaction? distributedTx = null; + Oletx.OletxTransaction? distributedTx = null; try { // Ask the delegation interface to promote the transaction. @@ -3256,7 +3258,7 @@ internal override void EnterState(InternalTransaction tx) TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.EnlistmentStatus(tx._durableEnlistment, NotificationCall.Promote); + etwLog.EnlistmentStatus(TraceSourceType.TraceSourceLtm, tx._durableEnlistment.EnlistmentTraceId, NotificationCall.Promote); } } @@ -3834,7 +3836,7 @@ internal override void EnterState(InternalTransaction tx) TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.EnlistmentStatus(tx._durableEnlistment, NotificationCall.SinglePhaseCommit); + etwLog.EnlistmentStatus(TraceSourceType.TraceSourceLtm, tx._durableEnlistment.EnlistmentTraceId, NotificationCall.SinglePhaseCommit); } // We are about to tell the PSPE to do the SinglePhaseCommit. It is too late for us to timeout the transaction. @@ -4036,7 +4038,7 @@ internal override void EnterState(InternalTransaction tx) TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.TransactionAborted(tx.TransactionTraceId); + etwLog.TransactionAborted(TraceSourceType.TraceSourceLtm, tx.TransactionTraceId); } } @@ -4129,7 +4131,7 @@ internal override void EnterState(InternalTransaction tx) TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.TransactionCommitted(tx.TransactionTraceId); + etwLog.TransactionCommitted(TraceSourceType.TraceSourceLtm, tx.TransactionTraceId); } } @@ -4173,7 +4175,7 @@ internal override void EnterState(InternalTransaction tx) TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.TransactionInDoubt(tx.TransactionTraceId); + etwLog.TransactionInDoubt(TraceSourceType.TraceSourceLtm, tx.TransactionTraceId); } } @@ -4233,7 +4235,7 @@ internal override void EnterState(InternalTransaction tx) CommonEnterState(tx); // We are never going to have an DistributedTransaction for this one. - DistributedTransaction? distributedTx; + OletxTransaction? distributedTx; try { // Ask the delegation interface to promote the transaction. @@ -4242,7 +4244,7 @@ internal override void EnterState(InternalTransaction tx) TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.EnlistmentStatus(tx._durableEnlistment, NotificationCall.Promote); + etwLog.EnlistmentStatus(TraceSourceType.TraceSourceLtm, tx._durableEnlistment.EnlistmentTraceId, NotificationCall.Promote); } } @@ -4291,7 +4293,7 @@ internal override void Rollback(InternalTransaction tx, Exception? e) tx._innerException ??= e; Debug.Assert(tx.PromotedTransaction != null); - DistributedTransaction.Rollback(); + tx.PromotedTransaction.Rollback(); TransactionStatePromotedAborted.EnterState(tx); } @@ -4379,7 +4381,7 @@ internal void Phase0PSPEInitialize( } } - internal DistributedTransaction? PSPEPromote(InternalTransaction tx) + internal Oletx.OletxTransaction? PSPEPromote(InternalTransaction tx) { bool changeToReturnState = true; @@ -4390,7 +4392,7 @@ internal void Phase0PSPEInitialize( "PSPEPromote called from state other than TransactionStateDelegated[NonMSDTC]"); CommonEnterState(tx); - DistributedTransaction? distributedTx = null; + Oletx.OletxTransaction? distributedTx = null; try { if (tx._attemptingPSPEPromote) @@ -4457,7 +4459,7 @@ internal void Phase0PSPEInitialize( { try { - distributedTx = TransactionInterop.GetDistributedTransactionFromTransmitterPropagationToken( + distributedTx = TransactionInterop.GetOletxTransactionFromTransmitterPropagationToken( propagationToken! ); } @@ -4603,13 +4605,12 @@ internal override void EnterState(InternalTransaction tx) TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.EnlistmentStatus(tx._durableEnlistment, NotificationCall.SinglePhaseCommit); + etwLog.EnlistmentStatus(TraceSourceType.TraceSourceLtm, tx._durableEnlistment.EnlistmentTraceId, NotificationCall.SinglePhaseCommit); } try { - tx._durableEnlistment.PromotableSinglePhaseNotification.SinglePhaseCommit( - tx._durableEnlistment.SinglePhaseEnlistment); + tx._durableEnlistment.PromotableSinglePhaseNotification.SinglePhaseCommit(tx._durableEnlistment.SinglePhaseEnlistment); } finally { @@ -4640,7 +4641,7 @@ internal override void EnterState(InternalTransaction tx) TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.EnlistmentStatus(tx._durableEnlistment, NotificationCall.Rollback); + etwLog.EnlistmentStatus(TraceSourceType.TraceSourceLtm, tx._durableEnlistment.EnlistmentTraceId, NotificationCall.Rollback); } tx._durableEnlistment.PromotableSinglePhaseNotification.Rollback( diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionsEtwProvider.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionsEtwProvider.cs index f6fd5f6b87e2d7..ec230efce89728 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionsEtwProvider.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionsEtwProvider.cs @@ -11,6 +11,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Collections; +using System.Transactions.Oletx; namespace System.Transactions { @@ -34,6 +35,16 @@ internal enum NotificationCall Promote = 5 } + internal enum EnlistmentCallback + { + Done = 0, + Prepared = 1, + ForceRollback = 2, + Committed = 3, + Aborted = 4, + InDoubt = 5 + } + internal enum TransactionScopeResult { CreatedTransaction = 0, @@ -57,7 +68,7 @@ internal enum TraceSourceType { TraceSourceBase = 0, TraceSourceLtm = 1, - TraceSourceDistributed = 2 + TraceSourceOleTx = 2 } /// Provides an event source for tracing Transactions information. [EventSource( @@ -94,7 +105,7 @@ private TransactionsEtwProvider() { } /// The event ID for the enlistment done event. private const int ENLISTMENT_DONE_EVENTID = 4; /// The event ID for the enlistment status. - private const int ENLISTMENT_EVENTID = 5; + private const int ENLISTMENT_LTM_EVENTID = 5; /// The event ID for the enlistment forcerollback event. private const int ENLISTMENT_FORCEROLLBACK_EVENTID = 6; /// The event ID for the enlistment indoubt event. @@ -114,33 +125,33 @@ private TransactionsEtwProvider() { } /// The event ID for method exit event. private const int METHOD_EXIT_BASE_EVENTID = 14; /// The event ID for method enter event. - private const int METHOD_ENTER_DISTRIBUTED_EVENTID = 15; + private const int METHOD_ENTER_OLETX_EVENTID = 15; /// The event ID for method exit event. - private const int METHOD_EXIT_DISTRIBUTED_EVENTID = 16; + private const int METHOD_EXIT_OLETX_EVENTID = 16; /// The event ID for transaction aborted event. - private const int TRANSACTION_ABORTED_EVENTID = 17; + private const int TRANSACTION_ABORTED_LTM_EVENTID = 17; /// The event ID for the transaction clone create event. private const int TRANSACTION_CLONECREATE_EVENTID = 18; /// The event ID for the transaction commit event. - private const int TRANSACTION_COMMIT_EVENTID = 19; + private const int TRANSACTION_COMMIT_LTM_EVENTID = 19; /// The event ID for transaction committed event. - private const int TRANSACTION_COMMITTED_EVENTID = 20; + private const int TRANSACTION_COMMITTED_LTM_EVENTID = 20; /// The event ID for when we encounter a new Transactions object that hasn't had its name traced to the trace file. - private const int TRANSACTION_CREATED_EVENTID = 21; + private const int TRANSACTION_CREATED_LTM_EVENTID = 21; /// The event ID for the transaction dependent clone complete event. - private const int TRANSACTION_DEPENDENT_CLONE_COMPLETE_EVENTID = 22; + private const int TRANSACTION_DEPENDENT_CLONE_COMPLETE_LTM_EVENTID = 22; /// The event ID for the transaction exception event. private const int TRANSACTION_EXCEPTION_LTM_EVENTID = 23; /// The event ID for the transaction exception event. private const int TRANSACTION_EXCEPTION_BASE_EVENTID = 24; /// The event ID for transaction indoubt event. - private const int TRANSACTION_INDOUBT_EVENTID = 25; + private const int TRANSACTION_INDOUBT_LTM_EVENTID = 25; /// The event ID for the transaction invalid operation event. private const int TRANSACTION_INVALID_OPERATION_EVENTID = 26; /// The event ID for transaction promoted event. private const int TRANSACTION_PROMOTED_EVENTID = 27; /// The event ID for the transaction rollback event. - private const int TRANSACTION_ROLLBACK_EVENTID = 28; + private const int TRANSACTION_ROLLBACK_LTM_EVENTID = 28; /// The event ID for the transaction serialized event. private const int TRANSACTION_SERIALIZED_EVENTID = 29; /// The event ID for transaction timeout event. @@ -158,7 +169,7 @@ private TransactionsEtwProvider() { } /// The event ID for transactionscope incomplete event. private const int TRANSACTIONSCOPE_INCOMPLETE_EVENTID = 36; /// The event ID for transactionscope internal error event. - private const int TRANSACTIONSCOPE_INTERNAL_ERROR_EVENTID = 37; + private const int INTERNAL_ERROR_EVENTID = 37; /// The event ID for transactionscope nested incorrectly event. private const int TRANSACTIONSCOPE_NESTED_INCORRECTLY_EVENTID = 38; /// The event ID for transactionscope timeout event. @@ -166,6 +177,43 @@ private TransactionsEtwProvider() { } /// The event ID for enlistment event. private const int TRANSACTIONSTATE_ENLIST_EVENTID = 40; + /// The event ID for the transaction commit event. + private const int TRANSACTION_COMMIT_OLETX_EVENTID = 41; + /// The event ID for the transaction rollback event. + private const int TRANSACTION_ROLLBACK_OLETX_EVENTID = 42; + /// The event ID for exception consumed event. + private const int EXCEPTION_CONSUMED_OLETX_EVENTID = 43; + /// The event ID for transaction committed event. + private const int TRANSACTION_COMMITTED_OLETX_EVENTID = 44; + /// The event ID for transaction aborted event. + private const int TRANSACTION_ABORTED_OLETX_EVENTID = 45; + /// The event ID for transaction indoubt event. + private const int TRANSACTION_INDOUBT_OLETX_EVENTID = 46; + /// The event ID for the transaction dependent clone complete event. + private const int TRANSACTION_DEPENDENT_CLONE_COMPLETE_OLETX_EVENTID = 47; + /// The event ID for the transaction dependent clone complete event. + private const int TRANSACTION_DEPENDENT_CLONE_CREATE_LTM_EVENTID = 48; + /// The event ID for the transaction dependent clone complete event. + private const int TRANSACTION_DEPENDENT_CLONE_CREATE_OLETX_EVENTID = 49; + /// The event ID for the transaction deserialized event. + private const int TRANSACTION_DESERIALIZED_EVENTID = 50; + /// The event ID for when we encounter a new Transactions object that hasn't had its name traced to the trace file. + private const int TRANSACTION_CREATED_OLETX_EVENTID = 11; + + /// The event ID for the enlistment status. + private const int ENLISTMENT_OLETX_EVENTID = 52; + /// The event ID for the enlistment callback positive event. + private const int ENLISTMENT_CALLBACK_POSITIVE_EVENTID = 53; + /// The event ID for the enlistment callback positive event. + private const int ENLISTMENT_CALLBACK_NEGATIVE_EVENTID = 54; + /// The event ID for when we create an enlistment. + private const int ENLISTMENT_CREATED_LTM_EVENTID = 55; + /// The event ID for when we create an enlistment. + private const int ENLISTMENT_CREATED_OLETX_EVENTID = 56; + + /// The event ID for transactionmanager reenlist event. + private const int TRANSACTIONMANAGER_CREATE_OLETX_EVENTID = 57; + //----------------------------------------------------------------------------------- // // Transactions Events @@ -183,28 +231,34 @@ private TransactionsEtwProvider() { } public static int GetHashCode(object? value) => value?.GetHashCode() ?? 0; #region Transaction Creation - /// Trace an event when a new transaction is created. - /// The transaction that was created. - /// The type of transaction.Method [NonEvent] - internal void TransactionCreated(Transaction transaction, string? type) + internal void TransactionCreated(TraceSourceType traceSource, TransactionTraceIdentifier txTraceId, string? type) { - Debug.Assert(transaction != null, "Transaction needed for the ETW event."); - if (IsEnabled(EventLevel.Informational, ALL_KEYWORDS)) { - if (transaction != null && transaction.TransactionTraceId.TransactionIdentifier != null) - TransactionCreated(transaction.TransactionTraceId.TransactionIdentifier, type); - else - TransactionCreated(string.Empty, type); + if (traceSource == TraceSourceType.TraceSourceLtm) + { + TransactionCreatedLtm(txTraceId.TransactionIdentifier, type); + } + else if (traceSource == TraceSourceType.TraceSourceOleTx) + { + TransactionCreatedOleTx(txTraceId.TransactionIdentifier, type); + } } } - [Event(TRANSACTION_CREATED_EVENTID, Keywords = Keywords.TraceLtm, Level = EventLevel.Informational, Task = Tasks.Transaction, Opcode = Opcodes.Create, Message = "Transaction Created. ID is {0}, type is {1}")] - private void TransactionCreated(string transactionIdentifier, string? type) + [Event(TRANSACTION_CREATED_LTM_EVENTID, Keywords = Keywords.TraceLtm, Level = EventLevel.Informational, Task = Tasks.Transaction, Opcode = Opcodes.Create, Message = "Transaction Created (LTM). ID is {0}, type is {1}")] + private void TransactionCreatedLtm(string transactionIdentifier, string? type) + { + SetActivityId(transactionIdentifier); + WriteEvent(TRANSACTION_CREATED_LTM_EVENTID, transactionIdentifier, type); + } + + [Event(TRANSACTION_CREATED_OLETX_EVENTID, Keywords = Keywords.TraceOleTx, Level = EventLevel.Informational, Task = Tasks.Transaction, Opcode = Opcodes.Create, Message = "Transaction Created (OLETX). ID is {0}, type is {1}")] + private void TransactionCreatedOleTx(string transactionIdentifier, string? type) { SetActivityId(transactionIdentifier); - WriteEvent(TRANSACTION_CREATED_EVENTID, transactionIdentifier, type); + WriteEvent(TRANSACTION_CREATED_OLETX_EVENTID, transactionIdentifier, type); } #endregion @@ -235,28 +289,38 @@ private void TransactionCloneCreate(string transactionIdentifier, string type) #endregion #region Transaction Serialized - /// Trace an event when a transaction is serialized. - /// The transaction that was serialized. - /// The type of transaction. [NonEvent] - internal void TransactionSerialized(Transaction transaction, string type) + internal void TransactionSerialized(TransactionTraceIdentifier transactionTraceId) { - Debug.Assert(transaction != null, "Transaction needed for the ETW event."); - if (IsEnabled(EventLevel.Informational, ALL_KEYWORDS)) { - if (transaction != null && transaction.TransactionTraceId.TransactionIdentifier != null) - TransactionSerialized(transaction.TransactionTraceId.TransactionIdentifier, type); - else - TransactionSerialized(string.Empty, type); + TransactionSerialized(transactionTraceId.TransactionIdentifier); + } + } + + [Event(TRANSACTION_SERIALIZED_EVENTID, Keywords = Keywords.TraceOleTx, Level = EventLevel.Informational, Task = Tasks.Transaction, Opcode = Opcodes.Serialized, Message = "Transaction Serialized. ID is {0}")] + private void TransactionSerialized(string transactionIdentifier) + { + SetActivityId(transactionIdentifier); + WriteEvent(TRANSACTION_SERIALIZED_EVENTID, transactionIdentifier); + } + #endregion + + #region Transaction Deserialized + [NonEvent] + internal void TransactionDeserialized(TransactionTraceIdentifier transactionTraceId) + { + if (IsEnabled(EventLevel.Verbose, ALL_KEYWORDS)) + { + TransactionDeserialized(transactionTraceId.TransactionIdentifier); } } - [Event(TRANSACTION_SERIALIZED_EVENTID, Keywords = Keywords.TraceLtm, Level = EventLevel.Informational, Task = Tasks.Transaction, Opcode = Opcodes.Serialized, Message = "Transaction Serialized. ID is {0}, type is {1}")] - private void TransactionSerialized(string transactionIdentifier, string type) + [Event(TRANSACTION_DESERIALIZED_EVENTID, Keywords = Keywords.TraceOleTx, Level = EventLevel.Verbose, Task = Tasks.Transaction, Opcode = Opcodes.Serialized, Message = "Transaction Deserialized. ID is {0}")] + private void TransactionDeserialized(string transactionIdentifier) { SetActivityId(transactionIdentifier); - WriteEvent(TRANSACTION_SERIALIZED_EVENTID, transactionIdentifier, type); + WriteEvent(TRANSACTION_DESERIALIZED_EVENTID, transactionIdentifier); } #endregion @@ -332,106 +396,196 @@ private void TransactionInvalidOperation(string? transactionIdentifier, string? #endregion #region Transaction Rollback - /// Trace an event when rollback on a transaction. - /// The transaction to rollback. - /// The type of transaction. [NonEvent] - internal void TransactionRollback(Transaction transaction, string? type) + internal void TransactionRollback(TraceSourceType traceSource, TransactionTraceIdentifier txTraceId, string? type) { - Debug.Assert(transaction != null, "Transaction needed for the ETW event."); - if (IsEnabled(EventLevel.Warning, ALL_KEYWORDS)) { - if (transaction != null && transaction.TransactionTraceId.TransactionIdentifier != null) - TransactionRollback(transaction.TransactionTraceId.TransactionIdentifier, type); - else - TransactionRollback(string.Empty, type); + if (traceSource == TraceSourceType.TraceSourceLtm) + { + TransactionRollbackLtm(txTraceId.TransactionIdentifier, type); + } + else if (traceSource == TraceSourceType.TraceSourceOleTx) + { + TransactionRollbackOleTx(txTraceId.TransactionIdentifier, type); + } } } - [Event(TRANSACTION_ROLLBACK_EVENTID, Keywords = Keywords.TraceLtm, Level = EventLevel.Warning, Task = Tasks.Transaction, Opcode = Opcodes.Rollback, Message = "Transaction Rollback. ID is {0}, type is {1}")] - private void TransactionRollback(string transactionIdentifier, string? type) + [Event(TRANSACTION_ROLLBACK_LTM_EVENTID, Keywords = Keywords.TraceLtm, Level = EventLevel.Warning, Task = Tasks.Transaction, Opcode = Opcodes.Rollback, Message = "Transaction LTM Rollback. ID is {0}, type is {1}")] + private void TransactionRollbackLtm(string transactionIdentifier, string? type) { SetActivityId(transactionIdentifier); - WriteEvent(TRANSACTION_ROLLBACK_EVENTID, transactionIdentifier, type); + WriteEvent(TRANSACTION_ROLLBACK_LTM_EVENTID, transactionIdentifier, type); + } + + [Event(TRANSACTION_ROLLBACK_OLETX_EVENTID, Keywords = Keywords.TraceOleTx, Level = EventLevel.Warning, Task = Tasks.Transaction, Opcode = Opcodes.Rollback, Message = "Transaction OleTx Rollback. ID is {0}, type is {1}")] + private void TransactionRollbackOleTx(string transactionIdentifier, string? type) + { + SetActivityId(transactionIdentifier); + WriteEvent(TRANSACTION_ROLLBACK_OLETX_EVENTID, transactionIdentifier, type); } #endregion - #region Transaction Dependent Clone Complete - /// Trace an event when transaction dependent clone complete. - /// The transaction that do dependent clone. - /// The type of transaction. + #region Transaction Dependent Clone Create [NonEvent] - internal void TransactionDependentCloneComplete(Transaction transaction, string? type) + internal void TransactionDependentCloneCreate(TraceSourceType traceSource, TransactionTraceIdentifier txTraceId, DependentCloneOption option) { - Debug.Assert(transaction != null, "Transaction needed for the ETW event."); + if (IsEnabled(EventLevel.Informational, ALL_KEYWORDS)) + { + if (traceSource == TraceSourceType.TraceSourceLtm) + { + TransactionDependentCloneCreateLtm(txTraceId.TransactionIdentifier, option.ToString()); + } + else if (traceSource == TraceSourceType.TraceSourceOleTx) + { + TransactionDependentCloneCreateOleTx(txTraceId.TransactionIdentifier, option.ToString()); + } + } + } + + [Event(TRANSACTION_DEPENDENT_CLONE_CREATE_LTM_EVENTID, Keywords = Keywords.TraceLtm, Level = EventLevel.Informational, Task = Tasks.Transaction, Opcode = Opcodes.DependentCloneComplete, Message = "Transaction Dependent Clone Created (LTM). ID is {0}, option is {1}")] + private void TransactionDependentCloneCreateLtm(string transactionIdentifier, string? option) + { + SetActivityId(transactionIdentifier); + WriteEvent(TRANSACTION_DEPENDENT_CLONE_CREATE_LTM_EVENTID, transactionIdentifier, option); + } + + [Event(TRANSACTION_DEPENDENT_CLONE_CREATE_OLETX_EVENTID, Keywords = Keywords.TraceOleTx, Level = EventLevel.Informational, Task = Tasks.Transaction, Opcode = Opcodes.DependentCloneComplete, Message = "Transaction Dependent Clone Created (OLETX). ID is {0}, option is {1}")] + private void TransactionDependentCloneCreateOleTx(string transactionIdentifier, string? option) + { + SetActivityId(transactionIdentifier); + WriteEvent(TRANSACTION_DEPENDENT_CLONE_CREATE_OLETX_EVENTID, transactionIdentifier, option); + } + #endregion + #region Transaction Dependent Clone Complete + [NonEvent] + internal void TransactionDependentCloneComplete(TraceSourceType traceSource, TransactionTraceIdentifier txTraceId, string? type) + { if (IsEnabled(EventLevel.Informational, ALL_KEYWORDS)) { - if (transaction != null && transaction.TransactionTraceId.TransactionIdentifier != null) - TransactionDependentCloneComplete(transaction.TransactionTraceId.TransactionIdentifier, type); - else - TransactionDependentCloneComplete(string.Empty, type); + if (traceSource == TraceSourceType.TraceSourceLtm) + { + TransactionDependentCloneCompleteLtm(txTraceId.TransactionIdentifier, type); + } + else if (traceSource == TraceSourceType.TraceSourceOleTx) + { + TransactionDependentCloneCompleteOleTx(txTraceId.TransactionIdentifier, type); + } } } - [Event(TRANSACTION_DEPENDENT_CLONE_COMPLETE_EVENTID, Keywords = Keywords.TraceLtm, Level = EventLevel.Informational, Task = Tasks.Transaction, Opcode = Opcodes.DependentCloneComplete, Message = "Transaction Dependent Clone Completed. ID is {0}, type is {1}")] - private void TransactionDependentCloneComplete(string transactionIdentifier, string? type) + [Event(TRANSACTION_DEPENDENT_CLONE_COMPLETE_LTM_EVENTID, Keywords = Keywords.TraceLtm, Level = EventLevel.Informational, Task = Tasks.Transaction, Opcode = Opcodes.DependentCloneComplete, Message = "Transaction Dependent Clone Completed (LTM). ID is {0}, type is {1}")] + private void TransactionDependentCloneCompleteLtm(string transactionIdentifier, string? type) + { + SetActivityId(transactionIdentifier); + WriteEvent(TRANSACTION_DEPENDENT_CLONE_COMPLETE_LTM_EVENTID, transactionIdentifier, type); + } + + [Event(TRANSACTION_DEPENDENT_CLONE_COMPLETE_OLETX_EVENTID, Keywords = Keywords.TraceOleTx, Level = EventLevel.Informational, Task = Tasks.Transaction, Opcode = Opcodes.DependentCloneComplete, Message = "Transaction Dependent Clone Completed (OLETX). ID is {0}, type is {1}")] + private void TransactionDependentCloneCompleteOleTx(string transactionIdentifier, string? type) { SetActivityId(transactionIdentifier); - WriteEvent(TRANSACTION_DEPENDENT_CLONE_COMPLETE_EVENTID, transactionIdentifier, type); + WriteEvent(TRANSACTION_DEPENDENT_CLONE_COMPLETE_OLETX_EVENTID, transactionIdentifier, type); } #endregion #region Transaction Commit - /// Trace an event when there is commit on that transaction. - /// The transaction to commit. - /// The type of transaction. [NonEvent] - internal void TransactionCommit(Transaction transaction, string? type) + internal void TransactionCommit(TraceSourceType traceSource, TransactionTraceIdentifier txTraceId, string? type) { - Debug.Assert(transaction != null, "Transaction needed for the ETW event."); - if (IsEnabled(EventLevel.Verbose, ALL_KEYWORDS)) { - if (transaction != null && transaction.TransactionTraceId.TransactionIdentifier != null) - TransactionCommit(transaction.TransactionTraceId.TransactionIdentifier, type); - else - TransactionCommit(string.Empty, type); + if (traceSource == TraceSourceType.TraceSourceLtm) + { + TransactionCommitLtm(txTraceId.TransactionIdentifier, type); + } + else if (traceSource == TraceSourceType.TraceSourceOleTx) + { + TransactionCommitOleTx(txTraceId.TransactionIdentifier, type); + } } } - [Event(TRANSACTION_COMMIT_EVENTID, Keywords = Keywords.TraceLtm, Level = EventLevel.Verbose, Task = Tasks.Transaction, Opcode = Opcodes.Commit, Message = "Transaction Commit: ID is {0}, type is {1}")] - private void TransactionCommit(string transactionIdentifier, string? type) + [Event(TRANSACTION_COMMIT_LTM_EVENTID, Keywords = Keywords.TraceLtm, Level = EventLevel.Verbose, Task = Tasks.Transaction, Opcode = Opcodes.Commit, Message = "Transaction LTM Commit: ID is {0}, type is {1}")] + private void TransactionCommitLtm(string transactionIdentifier, string? type) + { + SetActivityId(transactionIdentifier); + WriteEvent(TRANSACTION_COMMIT_LTM_EVENTID, transactionIdentifier, type); + } + + [Event(TRANSACTION_COMMIT_OLETX_EVENTID, Keywords = Keywords.TraceOleTx, Level = EventLevel.Verbose, Task = Tasks.Transaction, Opcode = Opcodes.Commit, Message = "Transaction OleTx Commit: ID is {0}, type is {1}")] + private void TransactionCommitOleTx(string transactionIdentifier, string? type) { SetActivityId(transactionIdentifier); - WriteEvent(TRANSACTION_COMMIT_EVENTID, transactionIdentifier, type); + WriteEvent(TRANSACTION_COMMIT_OLETX_EVENTID, transactionIdentifier, type); } #endregion #region Enlistment - /// Trace an event for enlistment status. - /// The enlistment to report status. - /// The notification call on the enlistment. [NonEvent] - internal void EnlistmentStatus(InternalEnlistment enlistment, NotificationCall notificationCall) + internal void EnlistmentStatus(TraceSourceType traceSource, EnlistmentTraceIdentifier enlistmentTraceId, NotificationCall notificationCall) { - Debug.Assert(enlistment != null, "Enlistment needed for the ETW event."); - if (IsEnabled(EventLevel.Verbose, ALL_KEYWORDS)) { - if (enlistment != null && enlistment.EnlistmentTraceId.EnlistmentIdentifier != 0) - EnlistmentStatus(enlistment.EnlistmentTraceId.EnlistmentIdentifier, notificationCall.ToString()); - else - EnlistmentStatus(0, notificationCall.ToString()); + if (traceSource == TraceSourceType.TraceSourceLtm) + { + EnlistmentStatusLtm(enlistmentTraceId.EnlistmentIdentifier, notificationCall.ToString()); + } + else if (traceSource == TraceSourceType.TraceSourceOleTx) + { + EnlistmentStatusOleTx(enlistmentTraceId.EnlistmentIdentifier, notificationCall.ToString()); + } + } + } + + [Event(ENLISTMENT_LTM_EVENTID, Keywords = Keywords.TraceLtm, Level = EventLevel.Verbose, Task = Tasks.Enlistment, Message = "Enlistment status (LTM): ID is {0}, notificationcall is {1}")] + private void EnlistmentStatusLtm(int enlistmentIdentifier, string notificationCall) + { + SetActivityId(string.Empty); + WriteEvent(ENLISTMENT_LTM_EVENTID, enlistmentIdentifier, notificationCall); + } + + [Event(ENLISTMENT_OLETX_EVENTID, Keywords = Keywords.TraceOleTx, Level = EventLevel.Verbose, Task = Tasks.Enlistment, Message = "Enlistment status (OLETX): ID is {0}, notificationcall is {1}")] + private void EnlistmentStatusOleTx(int enlistmentIdentifier, string notificationCall) + { + SetActivityId(string.Empty); + WriteEvent(ENLISTMENT_OLETX_EVENTID, enlistmentIdentifier, notificationCall); + } + #endregion + + #region Enlistment Creation + [NonEvent] + internal void EnlistmentCreated(TraceSourceType traceSource, EnlistmentTraceIdentifier enlistmentTraceId, EnlistmentType enlistmentType, EnlistmentOptions enlistmentOptions) + { + if (IsEnabled(EventLevel.Informational, ALL_KEYWORDS)) + { + if (traceSource == TraceSourceType.TraceSourceLtm) + { + EnlistmentCreatedLtm(enlistmentTraceId.EnlistmentIdentifier, enlistmentType.ToString(), enlistmentOptions.ToString()); + } + else if (traceSource == TraceSourceType.TraceSourceOleTx) + { + EnlistmentCreatedOleTx(enlistmentTraceId.EnlistmentIdentifier, enlistmentType.ToString(), enlistmentOptions.ToString()); + } } } - [Event(ENLISTMENT_EVENTID, Keywords = Keywords.TraceLtm, Level = EventLevel.Verbose, Task = Tasks.Enlistment, Message = "Enlistment status: ID is {0}, notificationcall is {1}")] - private void EnlistmentStatus(int enlistmentIdentifier, string notificationCall) + [Event(ENLISTMENT_CREATED_LTM_EVENTID, Keywords = Keywords.TraceLtm, Level = EventLevel.Informational, Task = Tasks.Enlistment, Opcode = Opcodes.Create, Message = "Enlistment Created (LTM). ID is {0}, type is {1}, options is {2}")] + [UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL2026", Justification = "Only string/int are passed")] + private void EnlistmentCreatedLtm(int enlistmentIdentifier, string enlistmentType, string enlistmentOptions) + { + SetActivityId(string.Empty); + WriteEvent(ENLISTMENT_CREATED_LTM_EVENTID, enlistmentIdentifier, enlistmentType, enlistmentOptions); + } + + [Event(ENLISTMENT_CREATED_OLETX_EVENTID, Keywords = Keywords.TraceOleTx, Level = EventLevel.Informational, Task = Tasks.Enlistment, Opcode = Opcodes.Create, Message = "Enlistment Created (OLETX). ID is {0}, type is {1}, options is {2}")] + [UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL2026", Justification = "Only string/int are passed")] + private void EnlistmentCreatedOleTx(int enlistmentIdentifier, string enlistmentType, string enlistmentOptions) { SetActivityId(string.Empty); - WriteEvent(ENLISTMENT_EVENTID, enlistmentIdentifier, notificationCall); + WriteEvent(ENLISTMENT_CREATED_OLETX_EVENTID, enlistmentIdentifier, enlistmentType, enlistmentOptions); } #endregion @@ -586,6 +740,42 @@ private void EnlistmentInDoubt(int enlistmentIdentifier) } #endregion + #region Enlistment Callback Positive + [NonEvent] + internal void EnlistmentCallbackPositive(EnlistmentTraceIdentifier enlistmentTraceIdentifier, EnlistmentCallback callback) + { + if (IsEnabled(EventLevel.Verbose, ALL_KEYWORDS)) + { + EnlistmentCallbackPositive(enlistmentTraceIdentifier.EnlistmentIdentifier, callback.ToString()); + } + } + + [Event(ENLISTMENT_CALLBACK_POSITIVE_EVENTID, Keywords = Keywords.TraceOleTx, Level = EventLevel.Verbose, Task = Tasks.Enlistment, Opcode = Opcodes.CallbackPositive, Message = "Enlistment callback positive: ID is {0}, callback is {1}")] + private void EnlistmentCallbackPositive(int enlistmentIdentifier, string? callback) + { + SetActivityId(string.Empty); + WriteEvent(ENLISTMENT_CALLBACK_POSITIVE_EVENTID, enlistmentIdentifier, callback); + } + #endregion + + #region Enlistment Callback Negative + [NonEvent] + internal void EnlistmentCallbackNegative(EnlistmentTraceIdentifier enlistmentTraceIdentifier, EnlistmentCallback callback) + { + if (IsEnabled(EventLevel.Warning, ALL_KEYWORDS)) + { + EnlistmentCallbackNegative(enlistmentTraceIdentifier.EnlistmentIdentifier, callback.ToString()); + } + } + + [Event(ENLISTMENT_CALLBACK_NEGATIVE_EVENTID, Keywords = Keywords.TraceOleTx, Level = EventLevel.Warning, Task = Tasks.Enlistment, Opcode = Opcodes.CallbackNegative, Message = "Enlistment callback negative: ID is {0}, callback is {1}")] + private void EnlistmentCallbackNegative(int enlistmentIdentifier, string? callback) + { + SetActivityId(string.Empty); + WriteEvent(ENLISTMENT_CALLBACK_NEGATIVE_EVENTID, enlistmentIdentifier, callback); + } + #endregion + #region Method Enter /// Trace an event when enter a method. /// trace source @@ -604,7 +794,7 @@ internal void MethodEnter(TraceSourceType traceSource, object? thisOrContextObje { MethodEnterTraceBase(IdOf(thisOrContextObject), methodname); } - else if (traceSource == TraceSourceType.TraceSourceDistributed) + else if (traceSource == TraceSourceType.TraceSourceOleTx) { MethodEnterTraceDistributed(IdOf(thisOrContextObject), methodname); } @@ -627,7 +817,7 @@ internal void MethodEnter(TraceSourceType traceSource, [CallerMemberName] string { MethodEnterTraceBase(string.Empty, methodname); } - else if (traceSource == TraceSourceType.TraceSourceDistributed) + else if (traceSource == TraceSourceType.TraceSourceOleTx) { MethodEnterTraceDistributed(string.Empty, methodname); } @@ -646,11 +836,11 @@ private void MethodEnterTraceBase(string thisOrContextObject, string? methodname SetActivityId(string.Empty); WriteEvent(METHOD_ENTER_BASE_EVENTID, thisOrContextObject, methodname); } - [Event(METHOD_ENTER_DISTRIBUTED_EVENTID, Keywords = Keywords.TraceDistributed, Level = EventLevel.Verbose, Task = Tasks.Method, Opcode = Opcodes.Enter, Message = "Enter method : {0}.{1}")] + [Event(METHOD_ENTER_OLETX_EVENTID, Keywords = Keywords.TraceOleTx, Level = EventLevel.Verbose, Task = Tasks.Method, Opcode = Opcodes.Enter, Message = "Enter method : {0}.{1}")] private void MethodEnterTraceDistributed(string thisOrContextObject, string? methodname) { SetActivityId(string.Empty); - WriteEvent(METHOD_ENTER_DISTRIBUTED_EVENTID, thisOrContextObject, methodname); + WriteEvent(METHOD_ENTER_OLETX_EVENTID, thisOrContextObject, methodname); } #endregion @@ -672,7 +862,7 @@ internal void MethodExit(TraceSourceType traceSource, object? thisOrContextObjec { MethodExitTraceBase(IdOf(thisOrContextObject), methodname); } - else if (traceSource == TraceSourceType.TraceSourceDistributed) + else if (traceSource == TraceSourceType.TraceSourceOleTx) { MethodExitTraceDistributed(IdOf(thisOrContextObject), methodname); } @@ -695,7 +885,7 @@ internal void MethodExit(TraceSourceType traceSource, [CallerMemberName] string? { MethodExitTraceBase(string.Empty, methodname); } - else if (traceSource == TraceSourceType.TraceSourceDistributed) + else if (traceSource == TraceSourceType.TraceSourceOleTx) { MethodExitTraceDistributed(string.Empty, methodname); } @@ -714,11 +904,11 @@ private void MethodExitTraceBase(string thisOrContextObject, string? methodname) SetActivityId(string.Empty); WriteEvent(METHOD_EXIT_BASE_EVENTID, thisOrContextObject, methodname); } - [Event(METHOD_EXIT_DISTRIBUTED_EVENTID, Keywords = Keywords.TraceDistributed, Level = EventLevel.Verbose, Task = Tasks.Method, Opcode = Opcodes.Exit, Message = "Exit method: {0}.{1}")] + [Event(METHOD_EXIT_OLETX_EVENTID, Keywords = Keywords.TraceOleTx, Level = EventLevel.Verbose, Task = Tasks.Method, Opcode = Opcodes.Exit, Message = "Exit method: {0}.{1}")] private void MethodExitTraceDistributed(string thisOrContextObject, string? methodname) { SetActivityId(string.Empty); - WriteEvent(METHOD_EXIT_DISTRIBUTED_EVENTID, thisOrContextObject, methodname); + WriteEvent(METHOD_EXIT_OLETX_EVENTID, thisOrContextObject, methodname); } #endregion @@ -732,13 +922,17 @@ internal void ExceptionConsumed(TraceSourceType traceSource, Exception exception { if (IsEnabled(EventLevel.Verbose, ALL_KEYWORDS)) { - if (traceSource == TraceSourceType.TraceSourceBase) - { - ExceptionConsumedBase(exception.ToString()); - } - else + switch (traceSource) { - ExceptionConsumedLtm(exception.ToString()); + case TraceSourceType.TraceSourceBase: + ExceptionConsumedBase(exception.ToString()); + return; + case TraceSourceType.TraceSourceLtm: + ExceptionConsumedLtm(exception.ToString()); + return; + case TraceSourceType.TraceSourceOleTx: + ExceptionConsumedOleTx(exception.ToString()); + return; } } } @@ -765,6 +959,30 @@ private void ExceptionConsumedLtm(string exceptionStr) SetActivityId(string.Empty); WriteEvent(EXCEPTION_CONSUMED_LTM_EVENTID, exceptionStr); } + [Event(EXCEPTION_CONSUMED_OLETX_EVENTID, Keywords = Keywords.TraceOleTx, Level = EventLevel.Verbose, Opcode = Opcodes.ExceptionConsumed, Message = "Exception consumed: {0}")] + private void ExceptionConsumedOleTx(string exceptionStr) + { + SetActivityId(string.Empty); + WriteEvent(EXCEPTION_CONSUMED_OLETX_EVENTID, exceptionStr); + } + #endregion + + #region OleTx TransactionManager Create + [NonEvent] + internal void OleTxTransactionManagerCreate(Type tmType, string? nodeName) + { + if (IsEnabled(EventLevel.Verbose, ALL_KEYWORDS)) + { + OleTxTransactionManagerCreate(tmType.ToString(), nodeName); + } + } + + [Event(TRANSACTIONMANAGER_CREATE_OLETX_EVENTID, Keywords = Keywords.TraceOleTx, Level = EventLevel.Verbose, Task = Tasks.TransactionManager, Opcode = Opcodes.Created, Message = "Created OleTx transaction manager, type is {0}, node name is {1}")] + private void OleTxTransactionManagerCreate(string tmType, string? nodeName) + { + SetActivityId(string.Empty); + WriteEvent(TRANSACTIONMANAGER_CREATE_OLETX_EVENTID, tmType, nodeName); + } #endregion #region TransactionManager Reenlist @@ -940,26 +1158,6 @@ private void TransactionScopeIncomplete(string transactionID) } #endregion - #region Transactionscope Internal Error - /// Trace an event when there is an internal error on transactionscope. - /// The error information. - [NonEvent] - internal void TransactionScopeInternalError(string? error) - { - if (IsEnabled(EventLevel.Critical, ALL_KEYWORDS)) - { - TransactionScopeInternalErrorTrace(error); - } - } - - [Event(TRANSACTIONSCOPE_INTERNAL_ERROR_EVENTID, Keywords = Keywords.TraceBase, Level = EventLevel.Critical, Task = Tasks.TransactionScope, Opcode = Opcodes.InternalError, Message = "Transactionscope internal error: {0}")] - private void TransactionScopeInternalErrorTrace(string? error) - { - SetActivityId(string.Empty); - WriteEvent(TRANSACTIONSCOPE_INTERNAL_ERROR_EVENTID, error); - } - #endregion - #region Transactionscope Timeout /// Trace an event when there is timeout on transactionscope. /// The transaction ID. @@ -1000,7 +1198,7 @@ private void TransactionTimeout(string transactionID) } #endregion - #region Transactionstate Enlist + #region Transaction Enlist /// Trace an event when there is enlist. /// The enlistment ID. /// The enlistment type. @@ -1025,43 +1223,65 @@ private void TransactionstateEnlist(string enlistmentID, string type, string opt } #endregion - #region Transactionstate committed - /// Trace an event when transaction is committed. - /// The transaction ID. + #region Transaction committed [NonEvent] - internal void TransactionCommitted(TransactionTraceIdentifier transactionID) + internal void TransactionCommitted(TraceSourceType traceSource, TransactionTraceIdentifier transactionID) { if (IsEnabled(EventLevel.Verbose, ALL_KEYWORDS)) { - TransactionCommitted(transactionID.TransactionIdentifier ?? string.Empty); + if (traceSource == TraceSourceType.TraceSourceLtm) + { + TransactionCommittedLtm(transactionID.TransactionIdentifier ?? string.Empty); + } + else if (traceSource == TraceSourceType.TraceSourceOleTx) + { + TransactionCommittedOleTx(transactionID.TransactionIdentifier ?? string.Empty); + } } } - [Event(TRANSACTION_COMMITTED_EVENTID, Keywords = Keywords.TraceLtm, Level = EventLevel.Verbose, Task = Tasks.Transaction, Opcode = Opcodes.Committed, Message = "Transaction committed: transaction ID is {0}")] - private void TransactionCommitted(string transactionID) + [Event(TRANSACTION_COMMITTED_LTM_EVENTID, Keywords = Keywords.TraceLtm, Level = EventLevel.Verbose, Task = Tasks.Transaction, Opcode = Opcodes.Committed, Message = "Transaction committed LTM: transaction ID is {0}")] + private void TransactionCommittedLtm(string transactionID) { SetActivityId(transactionID); - WriteEvent(TRANSACTION_COMMITTED_EVENTID, transactionID); + WriteEvent(TRANSACTION_COMMITTED_LTM_EVENTID, transactionID); + } + [Event(TRANSACTION_COMMITTED_OLETX_EVENTID, Keywords = Keywords.TraceOleTx, Level = EventLevel.Verbose, Task = Tasks.Transaction, Opcode = Opcodes.Committed, Message = "Transaction committed OleTx: transaction ID is {0}")] + private void TransactionCommittedOleTx(string transactionID) + { + SetActivityId(transactionID); + WriteEvent(TRANSACTION_COMMITTED_OLETX_EVENTID, transactionID); } #endregion - #region Transactionstate indoubt - /// Trace an event when transaction is indoubt. - /// The transaction ID. + #region Transaction indoubt [NonEvent] - internal void TransactionInDoubt(TransactionTraceIdentifier transactionID) + internal void TransactionInDoubt(TraceSourceType traceSource, TransactionTraceIdentifier transactionID) { if (IsEnabled(EventLevel.Warning, ALL_KEYWORDS)) { - TransactionInDoubt(transactionID.TransactionIdentifier ?? string.Empty); + if (traceSource == TraceSourceType.TraceSourceLtm) + { + TransactionInDoubtLtm(transactionID.TransactionIdentifier ?? string.Empty); + } + else if (traceSource == TraceSourceType.TraceSourceOleTx) + { + TransactionInDoubtOleTx(transactionID.TransactionIdentifier ?? string.Empty); + } } } - [Event(TRANSACTION_INDOUBT_EVENTID, Keywords = Keywords.TraceLtm, Level = EventLevel.Warning, Task = Tasks.Transaction, Opcode = Opcodes.InDoubt, Message = "Transaction indoubt: transaction ID is {0}")] - private void TransactionInDoubt(string transactionID) + [Event(TRANSACTION_INDOUBT_LTM_EVENTID, Keywords = Keywords.TraceLtm, Level = EventLevel.Warning, Task = Tasks.Transaction, Opcode = Opcodes.InDoubt, Message = "Transaction indoubt LTM: transaction ID is {0}")] + private void TransactionInDoubtLtm(string transactionID) { SetActivityId(transactionID); - WriteEvent(TRANSACTION_INDOUBT_EVENTID, transactionID); + WriteEvent(TRANSACTION_INDOUBT_LTM_EVENTID, transactionID); + } + [Event(TRANSACTION_INDOUBT_OLETX_EVENTID, Keywords = Keywords.TraceOleTx, Level = EventLevel.Warning, Task = Tasks.Transaction, Opcode = Opcodes.InDoubt, Message = "Transaction indoubt OleTx: transaction ID is {0}")] + private void TransactionInDoubtOleTx(string transactionID) + { + SetActivityId(transactionID); + WriteEvent(TRANSACTION_INDOUBT_OLETX_EVENTID, transactionID); } #endregion @@ -1086,25 +1306,57 @@ private void TransactionPromoted(string transactionID, string distributedTxID) } #endregion - #region Transactionstate aborted - /// Trace an event when transaction is aborted. - /// The transaction ID. + #region Transaction aborted [NonEvent] - internal void TransactionAborted(TransactionTraceIdentifier transactionID) + internal void TransactionAborted(TraceSourceType traceSource, TransactionTraceIdentifier transactionID) { if (IsEnabled(EventLevel.Warning, ALL_KEYWORDS)) { - TransactionAborted(transactionID.TransactionIdentifier ?? string.Empty); + if (traceSource == TraceSourceType.TraceSourceLtm) + { + TransactionAbortedLtm(transactionID.TransactionIdentifier ?? string.Empty); + } + else if (traceSource == TraceSourceType.TraceSourceOleTx) + { + TransactionAbortedOleTx(transactionID.TransactionIdentifier ?? string.Empty); + } } } - [Event(TRANSACTION_ABORTED_EVENTID, Keywords = Keywords.TraceLtm, Level = EventLevel.Warning, Task = Tasks.Transaction, Opcode = Opcodes.Aborted, Message = "Transaction aborted: transaction ID is {0}")] - private void TransactionAborted(string transactionID) + [Event(TRANSACTION_ABORTED_LTM_EVENTID, Keywords = Keywords.TraceLtm, Level = EventLevel.Warning, Task = Tasks.Transaction, Opcode = Opcodes.Aborted, Message = "Transaction aborted LTM: transaction ID is {0}")] + private void TransactionAbortedLtm(string transactionID) + { + SetActivityId(transactionID); + WriteEvent(TRANSACTION_ABORTED_LTM_EVENTID, transactionID); + } + [Event(TRANSACTION_ABORTED_OLETX_EVENTID, Keywords = Keywords.TraceOleTx, Level = EventLevel.Warning, Task = Tasks.Transaction, Opcode = Opcodes.Aborted, Message = "Transaction aborted OleTx: transaction ID is {0}")] + private void TransactionAbortedOleTx(string transactionID) { SetActivityId(transactionID); - WriteEvent(TRANSACTION_ABORTED_EVENTID, transactionID); + WriteEvent(TRANSACTION_ABORTED_OLETX_EVENTID, transactionID); } #endregion + + #region Internal Error + /// Trace an event when there is an internal error. + /// The error information. + [NonEvent] + internal void InternalError(string? error = null) + { + if (IsEnabled(EventLevel.Critical, ALL_KEYWORDS)) + { + InternalErrorTrace(error); + } + } + + [Event(INTERNAL_ERROR_EVENTID, Keywords = Keywords.TraceBase, Level = EventLevel.Critical, Task = Tasks.TransactionScope, Opcode = Opcodes.InternalError, Message = "Transactionscope internal error: {0}")] + private void InternalErrorTrace(string? error) + { + SetActivityId(string.Empty); + WriteEvent(INTERNAL_ERROR_EVENTID, error); + } + #endregion + public static class Opcodes { public const EventOpcode Aborted = (EventOpcode)100; @@ -1136,6 +1388,8 @@ public static class Opcodes public const EventOpcode Rollback = (EventOpcode)126; public const EventOpcode Serialized = (EventOpcode)127; public const EventOpcode Timeout = (EventOpcode)128; + public const EventOpcode CallbackPositive = (EventOpcode)129; + public const EventOpcode CallbackNegative = (EventOpcode)130; } public static class Tasks @@ -1155,7 +1409,7 @@ public static class Keywords { public const EventKeywords TraceBase = (EventKeywords)0x0001; public const EventKeywords TraceLtm = (EventKeywords)0x0002; - public const EventKeywords TraceDistributed = (EventKeywords)0x0004; + public const EventKeywords TraceOleTx = (EventKeywords)0x0004; } private static void SetActivityId(string str) diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/VolatileEnlistmentState.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/VolatileEnlistmentState.cs index 360dc3e3a80e77..a0bf1387b3ab5c 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/VolatileEnlistmentState.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/VolatileEnlistmentState.cs @@ -141,7 +141,7 @@ internal override void EnterState(InternalEnlistment enlistment) TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.EnlistmentStatus(enlistment, NotificationCall.Prepare); + etwLog.EnlistmentStatus(TraceSourceType.TraceSourceLtm, enlistment.EnlistmentTraceId, NotificationCall.Prepare); } Debug.Assert(enlistment.EnlistmentNotification != null); @@ -213,7 +213,7 @@ internal override void EnterState(InternalEnlistment enlistment) TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.EnlistmentStatus(enlistment, NotificationCall.SinglePhaseCommit); + etwLog.EnlistmentStatus(TraceSourceType.TraceSourceLtm, enlistment.EnlistmentTraceId, NotificationCall.SinglePhaseCommit); } Monitor.Exit(enlistment.Transaction); @@ -366,7 +366,7 @@ internal override void EnterState(InternalEnlistment enlistment) TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.EnlistmentStatus(enlistment, NotificationCall.Rollback); + etwLog.EnlistmentStatus(TraceSourceType.TraceSourceLtm, enlistment.EnlistmentTraceId, NotificationCall.Rollback); } Debug.Assert(enlistment.EnlistmentNotification != null); @@ -409,7 +409,7 @@ internal override void EnterState(InternalEnlistment enlistment) TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.EnlistmentStatus(enlistment, NotificationCall.Commit); + etwLog.EnlistmentStatus(TraceSourceType.TraceSourceLtm, enlistment.EnlistmentTraceId, NotificationCall.Commit); } Debug.Assert(enlistment.EnlistmentNotification != null); @@ -443,7 +443,7 @@ internal override void EnterState(InternalEnlistment enlistment) TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { - etwLog.EnlistmentStatus(enlistment, NotificationCall.InDoubt); + etwLog.EnlistmentStatus(TraceSourceType.TraceSourceLtm, enlistment.EnlistmentTraceId, NotificationCall.InDoubt); } Debug.Assert(enlistment.EnlistmentNotification != null); diff --git a/src/libraries/System.Transactions.Local/tests/LTMEnlistmentTests.cs b/src/libraries/System.Transactions.Local/tests/LTMEnlistmentTests.cs index a5fd3047fa64a1..c686049d742b5a 100644 --- a/src/libraries/System.Transactions.Local/tests/LTMEnlistmentTests.cs +++ b/src/libraries/System.Transactions.Local/tests/LTMEnlistmentTests.cs @@ -36,83 +36,28 @@ public void SinglePhaseDurable(int volatileCount, EnlistmentOptions volatileEnli Transaction tx = null; try { - using (TransactionScope ts = new TransactionScope()) - { - tx = Transaction.Current.Clone(); - - if (volatileCount > 0) - { - TestSinglePhaseEnlistment[] volatiles = new TestSinglePhaseEnlistment[volatileCount]; - for (int i = 0; i < volatileCount; i++) - { - // It doesn't matter what we specify for SinglePhaseVote. - volatiles[i] = new TestSinglePhaseEnlistment(volatilePhase1Vote, SinglePhaseVote.InDoubt, expectedVolatileOutcome); - tx.EnlistVolatile(volatiles[i], volatileEnlistmentOption); - } - } + using var ts = new TransactionScope(); - // Doesn't really matter what we specify for EnlistmentOutcome here. This is an SPC, so Phase2 won't happen for this enlistment. - TestSinglePhaseEnlistment durable = new TestSinglePhaseEnlistment(Phase1Vote.Prepared, singlePhaseVote, EnlistmentOutcome.Committed); - tx.EnlistDurable(Guid.NewGuid(), durable, EnlistmentOptions.None); + tx = Transaction.Current!.Clone(); - if (commit) + if (volatileCount > 0) + { + TestSinglePhaseEnlistment[] volatiles = new TestSinglePhaseEnlistment[volatileCount]; + for (int i = 0; i < volatileCount; i++) { - ts.Complete(); + // It doesn't matter what we specify for SinglePhaseVote. + volatiles[i] = new TestSinglePhaseEnlistment(volatilePhase1Vote, SinglePhaseVote.InDoubt, expectedVolatileOutcome); + tx.EnlistVolatile(volatiles[i], volatileEnlistmentOption); } } - } - catch (TransactionInDoubtException) - { - Assert.Equal(TransactionStatus.InDoubt, expectedTxStatus); - } - catch (TransactionAbortedException) - { - Assert.Equal(TransactionStatus.Aborted, expectedTxStatus); - } - - Assert.NotNull(tx); - Assert.Equal(expectedTxStatus, tx.TransactionInformation.Status); - } + // Doesn't really matter what we specify for EnlistmentOutcome here. This is an SPC, so Phase2 won't happen for this enlistment. + TestSinglePhaseEnlistment durable = new TestSinglePhaseEnlistment(Phase1Vote.Prepared, singlePhaseVote, EnlistmentOutcome.Committed); + tx.EnlistDurable(Guid.NewGuid(), durable, EnlistmentOptions.None); - [Theory] - // This test needs to change once we have promotion support. - // Right now any attempt to create a two phase durable enlistment will attempt to promote and will fail because promotion is not supported. This results in the transaction being - // aborted. - [InlineData(0, EnlistmentOptions.None, EnlistmentOptions.None, Phase1Vote.Prepared, true, EnlistmentOutcome.Aborted, EnlistmentOutcome.Aborted, TransactionStatus.Aborted)] - [InlineData(1, EnlistmentOptions.None, EnlistmentOptions.None, Phase1Vote.Prepared, true, EnlistmentOutcome.Aborted, EnlistmentOutcome.Aborted, TransactionStatus.Aborted)] - [InlineData(2, EnlistmentOptions.None, EnlistmentOptions.None, Phase1Vote.Prepared, true, EnlistmentOutcome.Aborted, EnlistmentOutcome.Aborted, TransactionStatus.Aborted)] - public void TwoPhaseDurable(int volatileCount, EnlistmentOptions volatileEnlistmentOption, EnlistmentOptions durableEnlistmentOption, Phase1Vote volatilePhase1Vote, bool commit, EnlistmentOutcome expectedVolatileOutcome, EnlistmentOutcome expectedDurableOutcome, TransactionStatus expectedTxStatus) - { - Transaction tx = null; - try - { - using (TransactionScope ts = new TransactionScope()) + if (commit) { - tx = Transaction.Current.Clone(); - - if (volatileCount > 0) - { - TestEnlistment[] volatiles = new TestEnlistment[volatileCount]; - for (int i = 0; i < volatileCount; i++) - { - // It doesn't matter what we specify for SinglePhaseVote. - volatiles[i] = new TestEnlistment(volatilePhase1Vote, expectedVolatileOutcome); - tx.EnlistVolatile(volatiles[i], volatileEnlistmentOption); - } - } - - TestEnlistment durable = new TestEnlistment(Phase1Vote.Prepared, expectedDurableOutcome); - // This needs to change once we have promotion support. - Assert.Throws(() => // Creation of two phase durable enlistment attempts to promote to MSDTC - { - tx.EnlistDurable(Guid.NewGuid(), durable, durableEnlistmentOption); - }); - - if (commit) - { - ts.Complete(); - } + ts.Complete(); } } catch (TransactionInDoubtException) @@ -137,17 +82,16 @@ public void EnlistDuringPhase0(EnlistmentOptions enlistmentOption, Phase1Vote ph AutoResetEvent outcomeEvent = null; try { - using (TransactionScope ts = new TransactionScope()) - { - tx = Transaction.Current.Clone(); - outcomeEvent = new AutoResetEvent(false); - TestEnlistment enlistment = new TestEnlistment(phase1Vote, expectedOutcome, true, expectPhase0EnlistSuccess, outcomeEvent); - tx.EnlistVolatile(enlistment, enlistmentOption); + using var ts = new TransactionScope(); - if (commit) - { - ts.Complete(); - } + tx = Transaction.Current!.Clone(); + outcomeEvent = new AutoResetEvent(false); + var enlistment = new TestEnlistment(phase1Vote, expectedOutcome, true, expectPhase0EnlistSuccess, outcomeEvent); + tx.EnlistVolatile(enlistment, enlistmentOption); + + if (commit) + { + ts.Complete(); } } catch (TransactionInDoubtException) @@ -173,30 +117,29 @@ public void EnlistVolatile(int volatileCount, EnlistmentOptions enlistmentOption Transaction tx = null; try { - using (TransactionScope ts = new TransactionScope()) - { - tx = Transaction.Current.Clone(); + using var ts = new TransactionScope(); - if (volatileCount > 0) - { - TestEnlistment[] volatiles = new TestEnlistment[volatileCount]; - outcomeEvents = new AutoResetEvent[volatileCount]; - for (int i = 0; i < volatileCount-1; i++) - { - outcomeEvents[i] = new AutoResetEvent(false); - volatiles[i] = new TestEnlistment(volatilePhase1Vote, expectedEnlistmentOutcome, false, true, outcomeEvents[i]); - tx.EnlistVolatile(volatiles[i], enlistmentOption); - } - - outcomeEvents[volatileCount-1] = new AutoResetEvent(false); - volatiles[volatileCount - 1] = new TestEnlistment(lastPhase1Vote, expectedEnlistmentOutcome, false, true, outcomeEvents[volatileCount-1]); - tx.EnlistVolatile(volatiles[volatileCount - 1], enlistmentOption); - } + tx = Transaction.Current!.Clone(); - if (commit) + if (volatileCount > 0) + { + TestEnlistment[] volatiles = new TestEnlistment[volatileCount]; + outcomeEvents = new AutoResetEvent[volatileCount]; + for (int i = 0; i < volatileCount-1; i++) { - ts.Complete(); + outcomeEvents[i] = new AutoResetEvent(false); + volatiles[i] = new TestEnlistment(volatilePhase1Vote, expectedEnlistmentOutcome, false, true, outcomeEvents[i]); + tx.EnlistVolatile(volatiles[i], enlistmentOption); } + + outcomeEvents[volatileCount-1] = new AutoResetEvent(false); + volatiles[volatileCount - 1] = new TestEnlistment(lastPhase1Vote, expectedEnlistmentOutcome, false, true, outcomeEvents[volatileCount-1]); + tx.EnlistVolatile(volatiles[volatileCount - 1], enlistmentOption); + } + + if (commit) + { + ts.Complete(); } } catch (TransactionInDoubtException) @@ -216,6 +159,5 @@ public void EnlistVolatile(int volatileCount, EnlistmentOptions enlistmentOption Assert.NotNull(tx); Assert.Equal(expectedTxStatus, tx.TransactionInformation.Status); } - } } diff --git a/src/libraries/System.Transactions.Local/tests/NonMsdtcPromoterTests.cs b/src/libraries/System.Transactions.Local/tests/NonMsdtcPromoterTests.cs index 747d33a280d8c0..dc2ba01f63bcbb 100644 --- a/src/libraries/System.Transactions.Local/tests/NonMsdtcPromoterTests.cs +++ b/src/libraries/System.Transactions.Local/tests/NonMsdtcPromoterTests.cs @@ -724,106 +724,6 @@ public void Rollback(Enlistment enlistment) } #endregion - // This class is used in conjunction with SubordinateTransaction. When asked via the Promote - // method, it needs to create a DTC transaction and return the propagation token. Since we - // can't just create another CommittableTransaction and promote it and return it's propagation - // token in the same AppDomain, we spin up another AppDomain and do it there. - private class MySimpleTransactionSuperior : ISimpleTransactionSuperior - { - private DtcTxCreator _dtcTxCreator = new DtcTxCreator() { TraceEnabled = false }; - private PromotedTx _promotedTx; - - public byte[] Promote() - { - byte[] propagationToken = null; - - Trace("MySimpleTransactionSuperior.Promote"); - propagationToken = _dtcTxCreator.CreatePromotedTx(ref _promotedTx); - - return propagationToken; - } - - public void Rollback() - { - Trace("MySimpleTransactionSuperior.Rollback"); - _promotedTx.Rollback(); - } - - public void Commit() - { - Trace("MySimpleTransactionSuperior.Commit"); - _promotedTx.Commit(); - } - } - - public class DtcTxCreator // : MarshalByRefObject - { - private static bool s_trace = false; - - public bool TraceEnabled - { - get { return s_trace; } - set { s_trace = value; } - } - public static void Trace(string stringToTrace, params object[] args) - { - if (s_trace) - { - Debug.WriteLine(stringToTrace, args); - } - } - - public byte[] CreatePromotedTx(ref PromotedTx promotedTx) - { - DtcTxCreator.Trace("DtcTxCreator.CreatePromotedTx"); - byte[] propagationToken; - CommittableTransaction commitTx = new CommittableTransaction(); - promotedTx = new PromotedTx(commitTx); - propagationToken = TransactionInterop.GetTransmitterPropagationToken(commitTx); - return propagationToken; - } - } - - // This is the class that is created in the "other" AppDomain to create a - // CommittableTransaction, promote it to DTC, and return the propagation token. - // It also commits or aborts the transaction. Used by MySimpleTransactionSuperior - // to create a DTC transaction when asked to promote. - public class PromotedTx // : MarshalByRefObject - { - private CommittableTransaction _commitTx; - - public PromotedTx(CommittableTransaction commitTx) - { - DtcTxCreator.Trace("PromotedTx constructor"); - _commitTx = commitTx; - } - - ~PromotedTx() - { - DtcTxCreator.Trace("PromotedTx destructor"); - if (_commitTx != null) - { - DtcTxCreator.Trace("PromotedTx destructor calling Rollback"); - _commitTx.Rollback(); - _commitTx = null; - } - } - - public void Commit() - { - DtcTxCreator.Trace("PromotedTx.Commit"); - _commitTx.Commit(); - _commitTx = null; - } - - public void Rollback() - { - DtcTxCreator.Trace("PromotedTx.Rollback"); - _commitTx.Rollback(); - _commitTx = null; - } - } - #region TestCase_ methods private static void TestCase_VolatileEnlistments( int count, @@ -2062,7 +1962,6 @@ private static void TestCase_SetDistributedIdWithWrongNotificationObject() #endregion - /// /// This test case is very basic Volatile Enlistment test. /// @@ -2273,29 +2172,5 @@ public void PSPENonMsdtcSetDistributedTransactionIdentifierCallWithWrongNotifica // Call SetDistributedTransactionIdentifier at the wrong time. TestCase_SetDistributedIdWithWrongNotificationObject(); } - - [Fact] - public void SimpleTransactionSuperior() - { - MySimpleTransactionSuperior superior = new MySimpleTransactionSuperior(); - SubordinateTransaction subTx = new SubordinateTransaction(IsolationLevel.Serializable, superior); - - AutoResetEvent durableCompleted = new AutoResetEvent(false); - MyEnlistment durable = null; - - durable = new MyEnlistment( - durableCompleted, - true, - false, - EnlistmentOptions.None, - /*expectSuccessfulEnlist=*/ false, - /*secondEnlistmentCompleted=*/ null); - durable.TransactionToEnlist = Transaction.Current; - - Assert.Throws(() => // SubordinateTransaction promotes to MSDTC - { - subTx.EnlistDurable(Guid.NewGuid(), durable, EnlistmentOptions.None); - }); - } } } diff --git a/src/libraries/System.Transactions.Local/tests/OleTxNonWindowsUnsupportedTests.cs b/src/libraries/System.Transactions.Local/tests/OleTxNonWindowsUnsupportedTests.cs new file mode 100644 index 00000000000000..fc753cef0e2d0a --- /dev/null +++ b/src/libraries/System.Transactions.Local/tests/OleTxNonWindowsUnsupportedTests.cs @@ -0,0 +1,64 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.Transactions.Tests; + +#nullable enable + +[SkipOnPlatform(TestPlatforms.Windows, "These tests assert that OleTx operations properly throw PlatformNotSupportedException on non-Windows platforms")] +public class OleTxNonWindowsUnsupportedTests +{ + [Fact] + public void Durable_enlistment() + { + var tx = new CommittableTransaction(); + + // Votes and outcomes don't matter, the 2nd enlistment fails in non-Windows + var enlistment1 = new TestEnlistment(Phase1Vote.Prepared, EnlistmentOutcome.Aborted); + + Assert.Throws(() => tx.EnlistDurable(Guid.NewGuid(), enlistment1, EnlistmentOptions.None)); + Assert.Equal(TransactionStatus.Aborted, tx.TransactionInformation.Status); + } + + [Fact] + public void Promotable_enlistments() + { + var tx = new CommittableTransaction(); + + var promotableEnlistment1 = new TestPromotableSinglePhaseEnlistment(() => new byte[24], EnlistmentOutcome.Aborted); + var promotableEnlistment2 = new TestPromotableSinglePhaseEnlistment(null, EnlistmentOutcome.Aborted); + + // 1st promotable enlistment - no distributed transaction yet. + Assert.True(tx.EnlistPromotableSinglePhase(promotableEnlistment1)); + Assert.True(promotableEnlistment1.InitializedCalled); + + // 2nd promotable enlistment returns false. + tx.EnlistPromotableSinglePhase(promotableEnlistment2); + Assert.False(promotableEnlistment2.InitializedCalled); + + // Now enlist a durable enlistment, this will cause the escalation to a distributed transaction and fail on non-Windows. + var durableEnlistment = new TestEnlistment(Phase1Vote.Prepared, EnlistmentOutcome.Aborted); + Assert.Throws(() => tx.EnlistDurable(Guid.NewGuid(), durableEnlistment, EnlistmentOptions.None)); + + Assert.True(promotableEnlistment1.PromoteCalled); + Assert.False(promotableEnlistment2.PromoteCalled); + + Assert.Equal(TransactionStatus.Aborted, tx.TransactionInformation.Status); + } + + [Fact] + public void TransmitterPropagationToken() + => Assert.Throws(() => + TransactionInterop.GetTransmitterPropagationToken(new CommittableTransaction())); + + [Fact] + public void GetWhereabouts() + => Assert.Throws(() => TransactionInterop.GetWhereabouts()); + + [Fact] + public void GetExportCookie() + => Assert.Throws(() => TransactionInterop.GetExportCookie( + new CommittableTransaction(), new byte[200])); +} diff --git a/src/libraries/System.Transactions.Local/tests/OleTxTests.cs b/src/libraries/System.Transactions.Local/tests/OleTxTests.cs new file mode 100644 index 00000000000000..fd89ef40714591 --- /dev/null +++ b/src/libraries/System.Transactions.Local/tests/OleTxTests.cs @@ -0,0 +1,477 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; +using Microsoft.DotNet.RemoteExecutor; +using Xunit; +using Xunit.Sdk; + +namespace System.Transactions.Tests; + +#nullable enable + +[PlatformSpecific(TestPlatforms.Windows)] +public class OleTxTests +{ + //private static readonly TimeSpan Timeout = TimeSpan.FromMinutes(3); + private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(10); + + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] + [InlineData(Phase1Vote.Prepared, Phase1Vote.Prepared, EnlistmentOutcome.Committed, EnlistmentOutcome.Committed, TransactionStatus.Committed)] + [InlineData(Phase1Vote.Prepared, Phase1Vote.ForceRollback, EnlistmentOutcome.Aborted, EnlistmentOutcome.Aborted, TransactionStatus.Aborted)] + [InlineData(Phase1Vote.ForceRollback, Phase1Vote.Prepared, EnlistmentOutcome.Aborted, EnlistmentOutcome.Aborted, TransactionStatus.Aborted)] + public void Two_durable_enlistments_commit(Phase1Vote vote1, Phase1Vote vote2, EnlistmentOutcome expectedOutcome1, EnlistmentOutcome expectedOutcome2, TransactionStatus expectedTxStatus) + { + if (RuntimeInformation.ProcessArchitecture == Architecture.X86) + { + return; // Temporarily skip on 32-bit where we have an issue + } + + var tx = new CommittableTransaction(); + + try + { + var enlistment1 = new TestEnlistment(vote1, expectedOutcome1); + var enlistment2 = new TestEnlistment(vote2, expectedOutcome2); + + tx.EnlistDurable(Guid.NewGuid(), enlistment1, EnlistmentOptions.None); + tx.EnlistDurable(Guid.NewGuid(), enlistment2, EnlistmentOptions.None); + + Assert.Equal(TransactionStatus.Active, tx.TransactionInformation.Status); + tx.Commit(); + } + catch (TransactionInDoubtException) + { + Assert.Equal(TransactionStatus.InDoubt, expectedTxStatus); + } + catch (TransactionAbortedException) + { + Assert.Equal(TransactionStatus.Aborted, expectedTxStatus); + } + + Retry(() => Assert.Equal(expectedTxStatus, tx.TransactionInformation.Status)); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] + public void Two_durable_enlistments_rollback() + { + if (RuntimeInformation.ProcessArchitecture == Architecture.X86) + { + return; // Temporarily skip on 32-bit where we have an issue + } + + var tx = new CommittableTransaction(); + + var enlistment1 = new TestEnlistment(Phase1Vote.Prepared, EnlistmentOutcome.Aborted); + var enlistment2 = new TestEnlistment(Phase1Vote.Prepared, EnlistmentOutcome.Aborted); + + tx.EnlistDurable(Guid.NewGuid(), enlistment1, EnlistmentOptions.None); + tx.EnlistDurable(Guid.NewGuid(), enlistment2, EnlistmentOptions.None); + + tx.Rollback(); + + Assert.False(enlistment1.WasPreparedCalled); + Assert.False(enlistment2.WasPreparedCalled); + + // This matches the .NET Framework behavior + Retry(() => Assert.Equal(TransactionStatus.Aborted, tx.TransactionInformation.Status)); + } + + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + public void Volatile_and_durable_enlistments(int volatileCount) + { + if (RuntimeInformation.ProcessArchitecture == Architecture.X86) + { + return; // Temporarily skip on 32-bit where we have an issue + } + + var tx = new CommittableTransaction(); + + if (volatileCount > 0) + { + TestEnlistment[] volatiles = new TestEnlistment[volatileCount]; + for (int i = 0; i < volatileCount; i++) + { + // It doesn't matter what we specify for SinglePhaseVote. + volatiles[i] = new TestEnlistment(Phase1Vote.Prepared, EnlistmentOutcome.Committed); + tx.EnlistVolatile(volatiles[i], EnlistmentOptions.None); + } + } + + var durable = new TestEnlistment(Phase1Vote.Prepared, EnlistmentOutcome.Committed); + + // Creation of two phase durable enlistment attempts to promote to MSDTC + tx.EnlistDurable(Guid.NewGuid(), durable, EnlistmentOptions.None); + + tx.Commit(); + + Retry(() => Assert.Equal(TransactionStatus.Committed, tx.TransactionInformation.Status)); + } + + protected static bool IsRemoteExecutorSupportedAndNotNano => RemoteExecutor.IsSupported && PlatformDetection.IsNotWindowsNanoServer; + + [ConditionalFact(nameof(IsRemoteExecutorSupportedAndNotNano))] + public void Promotion() + { + if (RuntimeInformation.ProcessArchitecture == Architecture.X86) + { + return; // Temporarily skip on 32-bit where we have an issue + } + + // This simulates the full promotable flow, as implemented for SQL Server. + + // We are going to spin up two external processes. + // 1. The 1st external process will create the transaction and save its propagation token to disk. + // 2. The main process will read that, and propagate the transaction to the 2nd external process. + // 3. The main process will then notify the 1st external process to commit (as the main's transaction is delegated to it). + // 4. At that point the MSDTC Commit will be triggered; enlistments on both the 1st and 2nd processes will be notified + // to commit, and the transaction status will reflect the committed status in the main process. + var tx = new CommittableTransaction(); + + string propagationTokenFilePath = Path.GetTempFileName(); + string exportCookieFilePath = Path.GetTempFileName(); + using var waitHandle1 = new EventWaitHandle(initialState: false, EventResetMode.ManualReset, "System.Transactions.Tests.OleTxTests.Promotion1"); + using var waitHandle2 = new EventWaitHandle(initialState: false, EventResetMode.ManualReset, "System.Transactions.Tests.OleTxTests.Promotion2"); + using var waitHandle3 = new EventWaitHandle(initialState: false, EventResetMode.ManualReset, "System.Transactions.Tests.OleTxTests.Promotion3"); + + RemoteInvokeHandle? remote1 = null, remote2 = null; + + try + { + remote1 = RemoteExecutor.Invoke(Remote1, propagationTokenFilePath, new RemoteInvokeOptions { ExpectedExitCode = 42 }); + + // Wait for the external process to start a transaction and save its propagation token + Assert.True(waitHandle1.WaitOne(Timeout)); + + // Enlist the first PSPE. No escalation happens yet, since its the only enlistment. + var pspe1 = new TestPromotableSinglePhaseNotification(propagationTokenFilePath); + Assert.True(tx.EnlistPromotableSinglePhase(pspe1)); + Assert.True(pspe1.WasInitializedCalled); + Assert.False(pspe1.WasPromoteCalled); + Assert.False(pspe1.WasRollbackCalled); + Assert.False(pspe1.WasSinglePhaseCommitCalled); + + // Enlist the second PSPE. This returns false and does nothing, since there's already an enlistment. + var pspe2 = new TestPromotableSinglePhaseNotification(propagationTokenFilePath); + Assert.False(tx.EnlistPromotableSinglePhase(pspe2)); + Assert.False(pspe2.WasInitializedCalled); + Assert.False(pspe2.WasPromoteCalled); + Assert.False(pspe2.WasRollbackCalled); + Assert.False(pspe2.WasSinglePhaseCommitCalled); + + // Now generate an export cookie for the 2nd external process. This causes escalation and promotion. + byte[] whereabouts = TransactionInterop.GetWhereabouts(); + byte[] exportCookie = TransactionInterop.GetExportCookie(tx, whereabouts); + + Assert.True(pspe1.WasPromoteCalled); + Assert.False(pspe1.WasRollbackCalled); + Assert.False(pspe1.WasSinglePhaseCommitCalled); + + // Write the export cookie and start the 2nd external process, which will read the cookie and enlist in the transaction. + // Wait for it to complete. + File.WriteAllBytes(exportCookieFilePath, exportCookie); + remote2 = RemoteExecutor.Invoke(Remote2, exportCookieFilePath, new RemoteInvokeOptions { ExpectedExitCode = 42 }); + Assert.True(waitHandle2.WaitOne(Timeout)); + + // We now have two external processes with enlistments to our distributed transaction. Commit. + // Since our transaction is delegated to the 1st PSPE enlistment, Sys.Tx will call SinglePhaseCommit on it. + // In SQL Server this contacts the 1st DB to actually commit the transaction with MSDTC. In this simulation we'll just use a wait handle to trigger this. + tx.Commit(); + Assert.True(pspe1.WasSinglePhaseCommitCalled); + waitHandle3.Set(); + + Retry(() => Assert.Equal(TransactionStatus.Committed, tx.TransactionInformation.Status)); + } + catch + { + try + { + remote1?.Process.Kill(); + remote2?.Process.Kill(); + } + catch + { + } + + throw; + } + finally + { + File.Delete(propagationTokenFilePath); + } + + // Disposal of the RemoteExecutor handles will wait for the external processes to exit with the right exit code, + // which will happen when their enlistments receive the commit. + remote1?.Dispose(); + remote2?.Dispose(); + + static void Remote1(string propagationTokenFilePath) + { + var tx = new CommittableTransaction(); + + var outcomeEvent = new AutoResetEvent(false); + var enlistment = new TestEnlistment(Phase1Vote.Prepared, EnlistmentOutcome.Committed, outcomeReceived: outcomeEvent); + tx.EnlistDurable(Guid.NewGuid(), enlistment, EnlistmentOptions.None); + + // We now have an OleTx transaction. Save its propagation token to disk so that the main process can read it when promoting. + byte[] propagationToken = TransactionInterop.GetTransmitterPropagationToken(tx); + File.WriteAllBytes(propagationTokenFilePath, propagationToken); + + // Signal to the main process that the propagation token is ready to be read + using var waitHandle1 = new EventWaitHandle(initialState: false, EventResetMode.ManualReset, "System.Transactions.Tests.OleTxTests.Promotion1"); + waitHandle1.Set(); + + // The main process will now import our transaction via the propagation token, and propagate it to a 2nd process. + // In the main process the transaction is delegated; we're the one who started it, and so we're the one who need to Commit. + // When Commit() is called in the main process, that will trigger a SinglePhaseCommit on the PSPE which represents us. In SQL Server this + // contacts the DB to actually commit the transaction with MSDTC. In this simulation we'll just use the wait handle again to trigger this. + using var waitHandle3 = new EventWaitHandle(initialState: false, EventResetMode.ManualReset, "System.Transactions.Tests.OleTxTests.Promotion3"); + Assert.True(waitHandle3.WaitOne(Timeout)); + + tx.Commit(); + + // Wait for the commit to occur on our enlistment, then exit successfully. + Assert.True(outcomeEvent.WaitOne(Timeout)); + Environment.Exit(42); // 42 is error code expected by RemoteExecutor + } + + static void Remote2(string exportCookieFilePath) + { + // Load the export cookie and enlist durably + byte[] exportCookie = File.ReadAllBytes(exportCookieFilePath); + var tx = TransactionInterop.GetTransactionFromExportCookie(exportCookie); + + // Now enlist durably. This triggers promotion of the first PSPE, reading the propagation token. + var outcomeEvent = new AutoResetEvent(false); + var enlistment = new TestEnlistment(Phase1Vote.Prepared, EnlistmentOutcome.Committed, outcomeReceived: outcomeEvent); + tx.EnlistDurable(Guid.NewGuid(), enlistment, EnlistmentOptions.None); + + // Signal to the main process that we're enlisted and ready to commit + using var waitHandle = new EventWaitHandle(initialState: false, EventResetMode.ManualReset, "System.Transactions.Tests.OleTxTests.Promotion2"); + waitHandle.Set(); + + // Wait for the main process to commit the transaction + Assert.True(outcomeEvent.WaitOne(Timeout)); + Environment.Exit(42); // 42 is error code expected by RemoteExecutor + } + } + + public class TestPromotableSinglePhaseNotification : IPromotableSinglePhaseNotification + { + private string _propagationTokenFilePath; + + public TestPromotableSinglePhaseNotification(string propagationTokenFilePath) + => _propagationTokenFilePath = propagationTokenFilePath; + + public bool WasInitializedCalled { get; private set; } + public bool WasPromoteCalled { get; private set; } + public bool WasRollbackCalled { get; private set; } + public bool WasSinglePhaseCommitCalled { get; private set; } + + public void Initialize() + => WasInitializedCalled = true; + + public byte[] Promote() + { + WasPromoteCalled = true; + + return File.ReadAllBytes(_propagationTokenFilePath); + } + + public void Rollback(SinglePhaseEnlistment singlePhaseEnlistment) + => WasRollbackCalled = true; + + public void SinglePhaseCommit(SinglePhaseEnlistment singlePhaseEnlistment) + { + WasSinglePhaseCommitCalled = true; + + singlePhaseEnlistment.Committed(); + } + } + + [ConditionalFact(nameof(IsRemoteExecutorSupportedAndNotNano))] + public void Recovery() + { + if (RuntimeInformation.ProcessArchitecture == Architecture.X86) + { + return; // Temporarily skip on 32-bit where we have an issue + } + + // We are going to spin up an external process to also enlist in the transaction, and then to crash when it + // receives the commit notification. We will then initiate the recovery flow. + + var tx = new CommittableTransaction(); + + var outcomeEvent1 = new AutoResetEvent(false); + var enlistment1 = new TestEnlistment(Phase1Vote.Prepared, EnlistmentOutcome.Committed, outcomeReceived: outcomeEvent1); + var guid1 = Guid.NewGuid(); + tx.EnlistDurable(guid1, enlistment1, EnlistmentOptions.None); + + // The propagation token is used to propagate the transaction to that process so it can enlist to our + // transaction. We also provide the resource manager identifier GUID, and a path where the external process will + // write the recovery information it will receive from the MSDTC when preparing. + // We'll need these two elements later in order to Reenlist and trigger recovery. + byte[] propagationToken = TransactionInterop.GetTransmitterPropagationToken(tx); + string propagationTokenText = Convert.ToBase64String(propagationToken); + var guid2 = Guid.NewGuid(); + string secondEnlistmentRecoveryFilePath = Path.GetTempFileName(); + + using var waitHandle = new EventWaitHandle( + initialState: false, + EventResetMode.ManualReset, + "System.Transactions.Tests.OleTxTests.Recovery"); + + try + { + using (RemoteExecutor.Invoke( + EnlistAndCrash, + propagationTokenText, guid2.ToString(), secondEnlistmentRecoveryFilePath, + new RemoteInvokeOptions { ExpectedExitCode = 42 })) + { + // Wait for the external process to enlist in the transaction, it will signal this EventWaitHandle. + Assert.True(waitHandle.WaitOne(Timeout)); + + tx.Commit(); + } + + // The other has crashed when the MSDTC notified it to commit. + // Load the recovery information the other process has written to disk for us and reenlist with + // the failed RM's Guid to commit. + var outcomeEvent3 = new AutoResetEvent(false); + var enlistment3 = new TestEnlistment(Phase1Vote.Prepared, EnlistmentOutcome.Committed, outcomeReceived: outcomeEvent3); + byte[] secondRecoveryInformation = File.ReadAllBytes(secondEnlistmentRecoveryFilePath); + _ = TransactionManager.Reenlist(guid2, secondRecoveryInformation, enlistment3); + TransactionManager.RecoveryComplete(guid2); + + Assert.True(outcomeEvent1.WaitOne(Timeout)); + Assert.True(outcomeEvent3.WaitOne(Timeout)); + Assert.Equal(EnlistmentOutcome.Committed, enlistment1.Outcome); + Assert.Equal(EnlistmentOutcome.Committed, enlistment3.Outcome); + Assert.Equal(TransactionStatus.Committed, tx.TransactionInformation.Status); + + // Note: verify manually in the MSDTC console that the distributed transaction is gone + // (i.e. successfully committed), + // (Start -> Component Services -> Computers -> My Computer -> Distributed Transaction Coordinator -> + // Local DTC -> Transaction List) + } + finally + { + File.Delete(secondEnlistmentRecoveryFilePath); + } + + static void EnlistAndCrash(string propagationTokenText, string resourceManagerIdentifierGuid, string recoveryInformationFilePath) + { + byte[] propagationToken = Convert.FromBase64String(propagationTokenText); + var tx = TransactionInterop.GetTransactionFromTransmitterPropagationToken(propagationToken); + + var crashingEnlistment = new CrashingEnlistment(recoveryInformationFilePath); + tx.EnlistDurable(Guid.Parse(resourceManagerIdentifierGuid), crashingEnlistment, EnlistmentOptions.None); + + // Signal to the main process that we've enlisted and are ready to accept prepare/commit. + using var waitHandle = new EventWaitHandle(initialState: false, EventResetMode.ManualReset, "System.Transactions.Tests.OleTxTests.Recovery"); + waitHandle.Set(); + + // We've enlisted, and set it up so that when the MSDTC tells us to commit, the process will crash. + Thread.Sleep(Timeout); + } + } + + public class CrashingEnlistment : IEnlistmentNotification + { + private string _recoveryInformationFilePath; + + public CrashingEnlistment(string recoveryInformationFilePath) + => _recoveryInformationFilePath = recoveryInformationFilePath; + + public void Prepare(PreparingEnlistment preparingEnlistment) + { + // Received a prepare notification from MSDTC, persist the recovery information so that the main process can perform recovery for it. + File.WriteAllBytes(_recoveryInformationFilePath, preparingEnlistment.RecoveryInformation()); + + preparingEnlistment.Prepared(); + } + + public void Commit(Enlistment enlistment) + => Environment.Exit(42); // 42 is error code expected by RemoteExecutor + + public void Rollback(Enlistment enlistment) + => Environment.Exit(1); + + public void InDoubt(Enlistment enlistment) + => Environment.Exit(1); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] + public void TransmitterPropagationToken() + { + if (RuntimeInformation.ProcessArchitecture == Architecture.X86) + { + return; // Temporarily skip on 32-bit where we have an issue + } + + var tx = new CommittableTransaction(); + + Assert.Equal(Guid.Empty, tx.TransactionInformation.DistributedIdentifier); + + var propagationToken = TransactionInterop.GetTransmitterPropagationToken(tx); + + Assert.NotEqual(Guid.Empty, tx.TransactionInformation.DistributedIdentifier); + + var tx2 = TransactionInterop.GetTransactionFromTransmitterPropagationToken(propagationToken); + + Assert.Equal(tx.TransactionInformation.DistributedIdentifier, tx2.TransactionInformation.DistributedIdentifier); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] + public void GetExportCookie() + { + if (RuntimeInformation.ProcessArchitecture == Architecture.X86) + { + return; // Temporarily skip on 32-bit where we have an issue + } + + var tx = new CommittableTransaction(); + + var whereabouts = TransactionInterop.GetWhereabouts(); + + Assert.Equal(Guid.Empty, tx.TransactionInformation.DistributedIdentifier); + + var exportCookie = TransactionInterop.GetExportCookie(tx, whereabouts); + + Assert.NotEqual(Guid.Empty, tx.TransactionInformation.DistributedIdentifier); + + var tx2 = TransactionInterop.GetTransactionFromExportCookie(exportCookie); + + Assert.Equal(tx.TransactionInformation.DistributedIdentifier, tx2.TransactionInformation.DistributedIdentifier); + } + + // MSDTC is aynchronous, i.e. Commit/Rollback may return before the transaction has actually completed; + // so allow some time for assertions to succeed. + private static void Retry(Action action) + { + const int Retries = 50; + + for (var i = 0; i < Retries; i++) + { + try + { + action(); + return; + } + catch (EqualException) + { + if (i == Retries - 1) + { + throw; + } + + Thread.Sleep(100); + } + } + } +} diff --git a/src/libraries/System.Transactions.Local/tests/System.Transactions.Local.Tests.csproj b/src/libraries/System.Transactions.Local/tests/System.Transactions.Local.Tests.csproj index a5dda3d5689350..2f5841b9f63d97 100644 --- a/src/libraries/System.Transactions.Local/tests/System.Transactions.Local.Tests.csproj +++ b/src/libraries/System.Transactions.Local/tests/System.Transactions.Local.Tests.csproj @@ -8,6 +8,8 @@ + + @@ -17,4 +19,7 @@ + + + \ No newline at end of file diff --git a/src/libraries/System.Transactions.Local/tests/TestEnlistments.cs b/src/libraries/System.Transactions.Local/tests/TestEnlistments.cs index 800b4aedd8fdbc..039a7cc296c79f 100644 --- a/src/libraries/System.Transactions.Local/tests/TestEnlistments.cs +++ b/src/libraries/System.Transactions.Local/tests/TestEnlistments.cs @@ -2,11 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.Reflection; +using System.IO; using System.Threading; -using System.Threading.Tasks; using Xunit; +#nullable enable + namespace System.Transactions.Tests { public enum Phase1Vote { Prepared, ForceRollback, Done }; @@ -91,25 +92,35 @@ public void InDoubt(Enlistment enlistment) public class TestEnlistment : IEnlistmentNotification { - Phase1Vote _phase1Vote; - EnlistmentOutcome _expectedOutcome; - bool _volatileEnlistDuringPrepare; - bool _expectEnlistToSucceed; - AutoResetEvent _outcomeReceived; - Transaction _txToEnlist; + readonly Phase1Vote _phase1Vote; + readonly EnlistmentOutcome _expectedOutcome; + readonly bool _volatileEnlistDuringPrepare; + readonly bool _expectEnlistToSucceed; + readonly AutoResetEvent? _outcomeReceived; + readonly Transaction _txToEnlist; - public TestEnlistment(Phase1Vote phase1Vote, EnlistmentOutcome expectedOutcome, bool volatileEnlistDuringPrepare = false, bool expectEnlistToSucceed = true, AutoResetEvent outcomeReceived = null) + public TestEnlistment( + Phase1Vote phase1Vote, + EnlistmentOutcome expectedOutcome, + bool volatileEnlistDuringPrepare = false, + bool expectEnlistToSucceed = true, + AutoResetEvent? outcomeReceived = null) { _phase1Vote = phase1Vote; _expectedOutcome = expectedOutcome; _volatileEnlistDuringPrepare = volatileEnlistDuringPrepare; _expectEnlistToSucceed = expectEnlistToSucceed; _outcomeReceived = outcomeReceived; - _txToEnlist = Transaction.Current; + _txToEnlist = Transaction.Current!; } + public EnlistmentOutcome? Outcome { get; private set; } + public bool WasPreparedCalled { get; private set; } + public void Prepare(PreparingEnlistment preparingEnlistment) { + WasPreparedCalled = true; + switch (_phase1Vote) { case Phase1Vote.Prepared: @@ -132,19 +143,13 @@ public void Prepare(PreparingEnlistment preparingEnlistment) } case Phase1Vote.ForceRollback: { - if (_outcomeReceived != null) - { - _outcomeReceived.Set(); - } + _outcomeReceived?.Set(); preparingEnlistment.ForceRollback(); break; } case Phase1Vote.Done: { - if (_outcomeReceived != null) - { - _outcomeReceived.Set(); - } + _outcomeReceived?.Set(); preparingEnlistment.Done(); break; } @@ -153,32 +158,76 @@ public void Prepare(PreparingEnlistment preparingEnlistment) public void Commit(Enlistment enlistment) { + Outcome = EnlistmentOutcome.Committed; Assert.Equal(EnlistmentOutcome.Committed, _expectedOutcome); - if (_outcomeReceived != null) - { - _outcomeReceived.Set(); - } + _outcomeReceived?.Set(); enlistment.Done(); } public void Rollback(Enlistment enlistment) { + Outcome = EnlistmentOutcome.Aborted; Assert.Equal(EnlistmentOutcome.Aborted, _expectedOutcome); - if (_outcomeReceived != null) - { - _outcomeReceived.Set(); - } + _outcomeReceived?.Set(); enlistment.Done(); } public void InDoubt(Enlistment enlistment) { + Outcome = EnlistmentOutcome.InDoubt; Assert.Equal(EnlistmentOutcome.InDoubt, _expectedOutcome); - if (_outcomeReceived != null) + _outcomeReceived?.Set(); + enlistment.Done(); + } + } + + public class TestPromotableSinglePhaseEnlistment : IPromotableSinglePhaseNotification + { + private readonly Func? _promoteDelegate; + private EnlistmentOutcome _expectedOutcome; + private AutoResetEvent? _outcomeReceived; + + public bool InitializedCalled { get; private set; } + public bool PromoteCalled { get; private set; } + + public TestPromotableSinglePhaseEnlistment(Func? promoteDelegate, EnlistmentOutcome expectedOutcome, AutoResetEvent? outcomeReceived = null) + { + _promoteDelegate = promoteDelegate; + _expectedOutcome = expectedOutcome; + _outcomeReceived = outcomeReceived; + } + + public void Initialize() + => InitializedCalled = true; + + public byte[]? Promote() + { + PromoteCalled = true; + + if (_promoteDelegate is null) { - _outcomeReceived.Set(); + Assert.Fail("Promote called but no promotion delegate was provided"); } - enlistment.Done(); + + return _promoteDelegate(); + } + + public void SinglePhaseCommit(SinglePhaseEnlistment singlePhaseEnlistment) + { + Assert.Equal(EnlistmentOutcome.Committed, _expectedOutcome); + + _outcomeReceived?.Set(); + + singlePhaseEnlistment.Done(); + } + + public void Rollback(SinglePhaseEnlistment singlePhaseEnlistment) + { + Assert.Equal(EnlistmentOutcome.Aborted, _expectedOutcome); + + _outcomeReceived?.Set(); + + singlePhaseEnlistment.Done(); } } } From 55f5c7c0e0eb1d2a610b600da93c6415d7d46c98 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Fri, 12 Aug 2022 04:05:27 -0700 Subject: [PATCH 05/56] Introduce a default synchronization context for wasm (#72652) Introduce a JSSynchronizationContext that automatically remotes function calls back to the browser thread and queues them as background jobs. Exercise sync context in threads sample to display the complete progress indicator Clean up an old copy-paste error in the typescript --- ....Runtime.InteropServices.JavaScript.csproj | 4 + .../JavaScript/Interop/JavaScriptExports.cs | 15 ++ .../JavaScript/JSSynchronizationContext.cs | 143 ++++++++++++++++++ .../sample/wasm/browser-threads/Program.cs | 21 ++- src/mono/wasm/runtime/driver.c | 6 +- src/mono/wasm/runtime/managed-exports.ts | 36 +++-- src/mono/wasm/runtime/startup.ts | 1 - src/mono/wasm/runtime/types.ts | 3 + 8 files changed, 200 insertions(+), 29 deletions(-) create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSSynchronizationContext.cs 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 0605e250b9cea1..353071ad0c5a0c 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 @@ -61,6 +61,8 @@ + + @@ -69,6 +71,8 @@ + + diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs index 7434974ae1319f..3c7ba82f162641 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs @@ -192,6 +192,21 @@ public static void CompleteTask(JSMarshalerArgument* arguments_buffer) } } + [MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 + // the marshaled signature is: + // void InstallSynchronizationContext() + public static void InstallSynchronizationContext (JSMarshalerArgument* arguments_buffer) { + ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame() + try + { + JSSynchronizationContext.Install(); + } + catch (Exception ex) + { + arg_exc.ToJS(ex); + } + } + [MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 public static void StopProfile() { diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSSynchronizationContext.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSSynchronizationContext.cs new file mode 100644 index 00000000000000..9c87e4ec351408 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSSynchronizationContext.cs @@ -0,0 +1,143 @@ +// 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.Threading; +using System.Threading.Channels; +using System.Runtime; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using QueueType = System.Threading.Channels.Channel; + +namespace System.Runtime.InteropServices.JavaScript { + /// + /// Provides a thread-safe default SynchronizationContext for the browser that will automatically + /// route callbacks to the main browser thread where they can interact with the DOM and other + /// thread-affinity-having APIs like WebSockets, fetch, WebGL, etc. + /// Callbacks are processed during event loop turns via the runtime's background job system. + /// + internal sealed unsafe class JSSynchronizationContext : SynchronizationContext { + public readonly Thread MainThread; + + internal readonly struct WorkItem { + public readonly SendOrPostCallback Callback; + public readonly object? Data; + public readonly ManualResetEventSlim? Signal; + + public WorkItem (SendOrPostCallback callback, object? data, ManualResetEventSlim? signal) { + Callback = callback; + Data = data; + Signal = signal; + } + } + + private static JSSynchronizationContext? MainThreadSynchronizationContext; + private readonly QueueType Queue; + private readonly Action _DataIsAvailable; + + private JSSynchronizationContext (Thread mainThread) + : this ( + mainThread, + Channel.CreateUnbounded( + new UnboundedChannelOptions { SingleWriter = false, SingleReader = true, AllowSynchronousContinuations = true } + ) + ) + { + } + + private JSSynchronizationContext (Thread mainThread, QueueType queue) { + MainThread = mainThread; + Queue = queue; + _DataIsAvailable = DataIsAvailable; + } + + public override SynchronizationContext CreateCopy () { + return new JSSynchronizationContext(MainThread, Queue); + } + + private void AwaitNewData () { + var vt = Queue.Reader.WaitToReadAsync(); + if (vt.IsCompleted) { + DataIsAvailable(); + return; + } + + // Once data is added to the queue, vt will be completed on the thread that added the data + // because we created the channel with AllowSynchronousContinuations = true. We want to + // fire a callback that will schedule a background job to pump the queue on the main thread. + var awaiter = vt.AsTask().ConfigureAwait(false).GetAwaiter(); + // UnsafeOnCompleted avoids spending time flowing the execution context (we don't need it.) + awaiter.UnsafeOnCompleted(_DataIsAvailable); + } + + private void DataIsAvailable () { + // While we COULD pump here, we don't want to. We want the pump to happen on the next event loop turn. + // Otherwise we could get a chain where a pump generates a new work item and that makes us pump again, forever. + ScheduleBackgroundJob((void*)(delegate* unmanaged[Cdecl])&BackgroundJobHandler); + } + + public override void Post (SendOrPostCallback d, object? state) { + var workItem = new WorkItem(d, state, null); + if (!Queue.Writer.TryWrite(workItem)) + throw new Exception("Internal error"); + } + + // This path can only run when threading is enabled + #pragma warning disable CA1416 + + public override void Send (SendOrPostCallback d, object? state) { + if (Thread.CurrentThread == MainThread) { + d(state); + return; + } + + using (var signal = new ManualResetEventSlim(false)) { + var workItem = new WorkItem(d, state, signal); + if (!Queue.Writer.TryWrite(workItem)) + throw new Exception("Internal error"); + + signal.Wait(); + } + } + + internal static void Install () { + if (MainThreadSynchronizationContext == null) + MainThreadSynchronizationContext = new JSSynchronizationContext(Thread.CurrentThread); + + SynchronizationContext.SetSynchronizationContext(MainThreadSynchronizationContext); + MainThreadSynchronizationContext.AwaitNewData(); + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern unsafe void ScheduleBackgroundJob(void* callback); + +#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] + private static unsafe void BackgroundJobHandler () { + MainThreadSynchronizationContext!.Pump(); + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] + private static unsafe void RequestPumpCallback () { + ScheduleBackgroundJob((void*)(delegate* unmanaged[Cdecl])&BackgroundJobHandler); + } + + private void Pump () { + try { + while (Queue.Reader.TryRead(out var item)) { + try { + item.Callback(item.Data); + // While we would ideally have a catch block here and do something to dispatch/forward unhandled + // exceptions, the standard threadpool (and thus standard synchronizationcontext) have zero + // error handling, so for consistency with them we do nothing. Don't throw in SyncContext callbacks. + } finally { + item.Signal?.Set(); + } + } + } finally { + // If an item throws, we want to ensure that the next pump gets scheduled appropriately regardless. + AwaitNewData(); + } + } + } +} diff --git a/src/mono/sample/wasm/browser-threads/Program.cs b/src/mono/sample/wasm/browser-threads/Program.cs index e3ce64b19cc38f..f836fdff4fcf3c 100644 --- a/src/mono/sample/wasm/browser-threads/Program.cs +++ b/src/mono/sample/wasm/browser-threads/Program.cs @@ -30,9 +30,18 @@ public static void Start(int n) { var comp = new ExpensiveComputation(n); comp.Start(); + #pragma warning disable CS4014 + WaitForCompletion(comp); _demo = new Demo(UpdateProgress, comp); } + public static async Task WaitForCompletion (ExpensiveComputation comp) { + Console.WriteLine($"WaitForCompletion started on thread {Thread.CurrentThread.ManagedThreadId}"); + await comp.Completion; + Console.WriteLine($"WaitForCompletion completed on thread {Thread.CurrentThread.ManagedThreadId}"); + UpdateProgress("✌︎"); + } + [JSExport] public static int Progress() { @@ -135,16 +144,12 @@ public Demo(Action updateProgress, ExpensiveComputation comp) public bool Progress() { - _animation.Step($"{_expensiveComputation.CallCounter} calls"); - if (_expensiveComputation.Completion.IsCompleted) - { - _updateProgress("✌︎"); - return true; - } - else + if (!_expensiveComputation.Completion.IsCompleted) { - return false; + _animation.Step($"{_expensiveComputation.CallCounter} calls"); } + + return _expensiveComputation.Completion.IsCompleted; } public int Result => _expensiveComputation.Completion.Result; diff --git a/src/mono/wasm/runtime/driver.c b/src/mono/wasm/runtime/driver.c index 0ddc467ae5966f..e2db123f7ab5f2 100644 --- a/src/mono/wasm/runtime/driver.c +++ b/src/mono/wasm/runtime/driver.c @@ -449,6 +449,9 @@ get_native_to_interp (MonoMethod *method, void *extra_arg) return addr; } +typedef void (*background_job_cb)(void); +void mono_threads_schedule_background_job (background_job_cb cb); + void mono_initialize_internals () { // Blazor specific custom routines - see dotnet_support.js for backing code @@ -458,6 +461,7 @@ void mono_initialize_internals () core_initialize_internals(); #endif + mono_add_internal_call ("System.Runtime.InteropServices.JavaScript.JSSynchronizationContext::ScheduleBackgroundJob", mono_threads_schedule_background_job); } EMSCRIPTEN_KEEPALIVE void @@ -542,7 +546,7 @@ mono_wasm_load_runtime (const char *unused, int debug_level) mono_dl_fallback_register (wasm_dl_load, wasm_dl_symbol, NULL, NULL); mono_wasm_install_get_native_to_interp_tramp (get_native_to_interp); - + #ifdef GEN_PINVOKE mono_wasm_install_interp_to_native_callback (mono_wasm_interp_to_native_callback); #endif diff --git a/src/mono/wasm/runtime/managed-exports.ts b/src/mono/wasm/runtime/managed-exports.ts index 8934a2844b77a0..bbd48aa9e30140 100644 --- a/src/mono/wasm/runtime/managed-exports.ts +++ b/src/mono/wasm/runtime/managed-exports.ts @@ -3,28 +3,12 @@ import { GCHandle, MarshalerToCs, MarshalerToJs, MonoMethod, mono_assert } from "./types"; import cwraps from "./cwraps"; -import { Module, runtimeHelpers } from "./imports"; +import { Module, runtimeHelpers, ENVIRONMENT_IS_PTHREAD } from "./imports"; import { alloc_stack_frame, get_arg, get_arg_gc_handle, MarshalerType, set_arg_type, set_gc_handle } from "./marshal"; import { invoke_method_and_handle_exception } from "./invoke-cs"; import { marshal_array_to_cs_impl, marshal_exception_to_cs, marshal_intptr_to_cs } from "./marshal-to-cs"; import { marshal_int32_to_js, marshal_task_to_js } from "./marshal-to-js"; -// in all the exported internals methods, we use the same data structures for stack frame as normal full blow interop -// see src\libraries\System.Runtime.InteropServices.JavaScript\src\System\Runtime\InteropServices\JavaScript\Interop\JavaScriptExports.cs -export interface JavaScriptExports { - // the marshaled signature is: void ReleaseJSOwnedObjectByGCHandle(GCHandle gcHandle) - release_js_owned_object_by_gc_handle(gc_handle: GCHandle): void; - // the marshaled signature is: GCHandle CreateTaskCallback() - create_task_callback(): GCHandle; - // the marshaled signature is: void CompleteTask(GCHandle holder, Exception? exceptionResult, T? result) - complete_task(holder_gc_handle: GCHandle, error?: any, data?: any, res_converter?: MarshalerToCs): void; - // the marshaled signature is: TRes? CallDelegate(GCHandle callback, T1? arg1, T2? arg2, T3? arg3) - call_delegate(callback_gc_handle: GCHandle, arg1_js: any, arg2_js: any, arg3_js: any, - res_converter?: MarshalerToJs, arg1_converter?: MarshalerToCs, arg2_converter?: MarshalerToCs, arg3_converter?: MarshalerToCs): any; - // the marshaled signature is: Task? CallEntrypoint(MonoMethod* entrypointPtr, string[] args) - call_entry_point(entry_point: MonoMethod, args?: string[]): Promise; -} - export function init_managed_exports(): void { const anyModule = Module as any; const exports_fqn_asm = "System.Runtime.InteropServices.JavaScript"; @@ -38,7 +22,8 @@ export function init_managed_exports(): void { if (!runtimeHelpers.runtime_interop_exports_class) throw "Can't find " + runtimeHelpers.runtime_interop_namespace + "." + runtimeHelpers.runtime_interop_exports_classname + " class"; - + const install_sync_context = get_method("InstallSynchronizationContext"); + mono_assert(install_sync_context, "Can't find InstallSynchronizationContext method"); const call_entry_point = get_method("CallEntrypoint"); mono_assert(call_entry_point, "Can't find CallEntrypoint method"); const release_js_owned_object_by_gc_handle_method = get_method("ReleaseJSOwnedObjectByGCHandle"); @@ -149,6 +134,19 @@ export function init_managed_exports(): void { anyModule.stackRestore(sp); } }; + runtimeHelpers.javaScriptExports.install_synchronization_context = () => { + const sp = anyModule.stackSave(); + try { + const args = alloc_stack_frame(2); + invoke_method_and_handle_exception(install_sync_context, args); + } finally { + anyModule.stackRestore(sp); + } + }; + + if (!ENVIRONMENT_IS_PTHREAD) + // Install our sync context so that async continuations will migrate back to this thread (the main thread) automatically + runtimeHelpers.javaScriptExports.install_synchronization_context(); } export function get_method(method_name: string): MonoMethod { @@ -156,4 +154,4 @@ export function get_method(method_name: string): MonoMethod { if (!res) throw "Can't find method " + runtimeHelpers.runtime_interop_namespace + "." + runtimeHelpers.runtime_interop_exports_classname + "." + method_name; return res; -} \ No newline at end of file +} diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts index 9ee66858a7ea75..f97655bf6b3a57 100644 --- a/src/mono/wasm/runtime/startup.ts +++ b/src/mono/wasm/runtime/startup.ts @@ -454,7 +454,6 @@ export function bindings_init(): void { initialize_marshalers_to_js(); initialize_marshalers_to_cs(); runtimeHelpers._i52_error_scratch_buffer = Module._malloc(4); - } catch (err) { _print_error("MONO_WASM: Error in bindings_init", err); throw err; diff --git a/src/mono/wasm/runtime/types.ts b/src/mono/wasm/runtime/types.ts index b7082b21f41500..a42f7a1f81d9e0 100644 --- a/src/mono/wasm/runtime/types.ts +++ b/src/mono/wasm/runtime/types.ts @@ -361,6 +361,9 @@ export interface JavaScriptExports { // the marshaled signature is: Task? CallEntrypoint(MonoMethod* entrypointPtr, string[] args) call_entry_point(entry_point: MonoMethod, args?: string[]): Promise; + + // the marshaled signature is: void InstallSynchronizationContext() + install_synchronization_context(): void; } export type MarshalerToJs = (arg: JSMarshalerArgument, sig?: JSMarshalerType, res_converter?: MarshalerToJs, arg1_converter?: MarshalerToCs, arg2_converter?: MarshalerToCs) => any; From 0a173d12930122c07a326c7bd3457b22c0fa48b0 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Fri, 12 Aug 2022 07:58:52 -0400 Subject: [PATCH 06/56] [wasm] Disable some tests on NodeJS/Windows (#73834) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Disable some tests on NodeJS/Windows - Introduce NodeJS platform in PlatformDetection. Co-authored-by: Marek Fišera --- .../tests/TestUtilities/System/PlatformDetection.cs | 9 +++++++++ .../tests/System/CodeDom/Compiler/CodeCompilerTests.cs | 8 ++++++++ .../tests/CtorsDelimiterTests.cs | 1 + .../tests/XmlWriterTraceListenerTests.cs | 1 + .../tests/BasicScenarioTests.cs | 1 + src/mono/wasm/test-main.js | 3 +++ 6 files changed, 23 insertions(+) diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index 8851446e6b2b5f..449f9900c33d3a 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -106,6 +106,7 @@ public static partial class PlatformDetection public static bool IsWebSocketSupported => IsEnvironmentVariableTrue("IsWebSocketSupported"); public static bool IsNodeJS => IsEnvironmentVariableTrue("IsNodeJS"); public static bool IsNotNodeJS => !IsNodeJS; + public static bool IsNodeJSOnWindows => GetNodeJSPlatform() == "win32"; public static bool LocalEchoServerIsNotAvailable => !LocalEchoServerIsAvailable; public static bool LocalEchoServerIsAvailable => IsBrowser; @@ -593,6 +594,14 @@ private static bool IsEnvironmentVariableTrue(string variableName) return (val != null && val == "true"); } + private static string GetNodeJSPlatform() + { + if (!IsNodeJS) + return null; + + return Environment.GetEnvironmentVariable("NodeJSPlatform"); + } + private static bool AssemblyConfigurationEquals(string configuration) { AssemblyConfigurationAttribute assemblyConfigurationAttribute = typeof(string).Assembly.GetCustomAttribute(); diff --git a/src/libraries/System.CodeDom/tests/System/CodeDom/Compiler/CodeCompilerTests.cs b/src/libraries/System.CodeDom/tests/System/CodeDom/Compiler/CodeCompilerTests.cs index 7a53cd95b1d9eb..244931f1f5876b 100644 --- a/src/libraries/System.CodeDom/tests/System/CodeDom/Compiler/CodeCompilerTests.cs +++ b/src/libraries/System.CodeDom/tests/System/CodeDom/Compiler/CodeCompilerTests.cs @@ -55,6 +55,7 @@ public void CompileAssemblyFromDom_NullOptions_ThrowsArgumentNullException() [Theory] [MemberData(nameof(CodeCompileUnit_TestData))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/73721", typeof(PlatformDetection), nameof(PlatformDetection.IsNodeJSOnWindows))] public void FromDom_ValidCodeCompileUnit_ReturnsExpected(CodeCompileUnit compilationUnit) { var compiler = new StubCompiler(); @@ -66,6 +67,7 @@ public void FromDom_ValidCodeCompileUnit_ReturnsExpected(CodeCompileUnit compila [Theory] [MemberData(nameof(CodeCompileUnit_TestData))] [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/73721", typeof(PlatformDetection), nameof(PlatformDetection.IsNodeJSOnWindows))] public void FromDom_ValidCodeCompileUnit_ThrowsPlatformNotSupportedException(CodeCompileUnit compilationUnit) { var compiler = new Compiler(); @@ -361,6 +363,7 @@ public void CompileAssemblyFromSource_NullOptions_ThrowsArgumentNullException() } [Theory] + [ActiveIssue("https://github.com/dotnet/runtime/issues/73721", typeof(PlatformDetection), nameof(PlatformDetection.IsNodeJSOnWindows))] [MemberData(nameof(Source_TestData))] public void FromSource_ValidSource_ReturnsExpected(string source) { @@ -371,6 +374,7 @@ public void FromSource_ValidSource_ReturnsExpected(string source) [Theory] [MemberData(nameof(Source_TestData))] [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/73721", typeof(PlatformDetection), nameof(PlatformDetection.IsNodeJSOnWindows))] public void FromSource_ValidSource_ThrowsPlatformNotSupportedException(string source) { var compiler = new Compiler(); @@ -394,6 +398,7 @@ public static IEnumerable Sources_TestData() [Theory] [MemberData(nameof(Sources_TestData))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/73721", typeof(PlatformDetection), nameof(PlatformDetection.IsNodeJSOnWindows))] public void CompileAssemblyFromSourceBatch_ValidSources_ReturnsExpected(string[] sources) { ICodeCompiler compiler = new StubCompiler(); @@ -425,6 +430,7 @@ public void CompileAssemblyFromSourceBatch_NullSources_ThrowsArgumentNullExcepti [Theory] [MemberData(nameof(Sources_TestData))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/73721", typeof(PlatformDetection), nameof(PlatformDetection.IsNodeJSOnWindows))] public void FromSourceBatch_ValidSources_ReturnsExpected(string[] sources) { var compiler = new StubCompiler(); @@ -434,6 +440,7 @@ public void FromSourceBatch_ValidSources_ReturnsExpected(string[] sources) [Theory] [MemberData(nameof(Sources_TestData))] [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/73721", typeof(PlatformDetection), nameof(PlatformDetection.IsNodeJSOnWindows))] public void FromSourceBatch_ValidSources_ThrowsPlatformNotSupportedException(string[] sources) { var compiler = new Compiler(); @@ -459,6 +466,7 @@ public void FromSourceBatch_NullSources_ThrowsArgumentNullException() [InlineData("")] [InlineData("cmdArgs")] [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Occasionally fails in .NET framework, probably caused from a very edge case bug in .NET Framework which we will not be fixing")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/73721", typeof(PlatformDetection), nameof(PlatformDetection.IsNodeJSOnWindows))] public void GetResponseFileCmdArgs_ValidCmdArgs_ReturnsExpected(string cmdArgs) { var compiler = new Compiler(); diff --git a/src/libraries/System.Diagnostics.TextWriterTraceListener/tests/CtorsDelimiterTests.cs b/src/libraries/System.Diagnostics.TextWriterTraceListener/tests/CtorsDelimiterTests.cs index 352d0d1a6b29cb..cb4059bc43e72c 100644 --- a/src/libraries/System.Diagnostics.TextWriterTraceListener/tests/CtorsDelimiterTests.cs +++ b/src/libraries/System.Diagnostics.TextWriterTraceListener/tests/CtorsDelimiterTests.cs @@ -114,6 +114,7 @@ public void TestConstructorWithFileName() [Theory] [MemberData(nameof(TestNames))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/73721", typeof(PlatformDetection), nameof(PlatformDetection.IsNodeJSOnWindows))] public void TestConstructorWithFileNameAndName(string testName) { var target = new DelimitedListTraceListener(Path.GetTempFileName(), testName); diff --git a/src/libraries/System.Diagnostics.TextWriterTraceListener/tests/XmlWriterTraceListenerTests.cs b/src/libraries/System.Diagnostics.TextWriterTraceListener/tests/XmlWriterTraceListenerTests.cs index 20576cc9190b91..46638320c0cdb0 100644 --- a/src/libraries/System.Diagnostics.TextWriterTraceListener/tests/XmlWriterTraceListenerTests.cs +++ b/src/libraries/System.Diagnostics.TextWriterTraceListener/tests/XmlWriterTraceListenerTests.cs @@ -44,6 +44,7 @@ public static IEnumerable ConstructorsTestData() [Theory] [MemberData(nameof(ConstructorsTestData))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/73721", typeof(PlatformDetection), nameof(PlatformDetection.IsNodeJSOnWindows))] public void SingleArgumentConstructorTest(XmlWriterTraceListener listener, string expectedName) { Assert.Equal(expectedName, listener.Name); diff --git a/src/libraries/System.ServiceModel.Syndication/tests/BasicScenarioTests.cs b/src/libraries/System.ServiceModel.Syndication/tests/BasicScenarioTests.cs index f1269d75e0a2d5..92fbf89181261d 100644 --- a/src/libraries/System.ServiceModel.Syndication/tests/BasicScenarioTests.cs +++ b/src/libraries/System.ServiceModel.Syndication/tests/BasicScenarioTests.cs @@ -177,6 +177,7 @@ public static void SyndicationFeed_Load_Write_Atom_Feed_() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/73721", typeof(PlatformDetection), nameof(PlatformDetection.IsNodeJSOnWindows))] public static void SyndicationFeed_Write_RSS_Atom() { string RssPath = Path.GetTempFileName(); diff --git a/src/mono/wasm/test-main.js b/src/mono/wasm/test-main.js index ac8dd0016a119c..eba90e542a172f 100644 --- a/src/mono/wasm/test-main.js +++ b/src/mono/wasm/test-main.js @@ -398,6 +398,9 @@ Promise.all([argsPromise, loadDotnetPromise]).then(async ([runArgs, createDotnet // Must be after loading npm modules. config.environmentVariables["IsWebSocketSupported"] = ("WebSocket" in globalThis).toString().toLowerCase(); + if (is_node) { + config.environmentVariables["NodeJSPlatform"] = process.platform; + } }, preRun: () => { if (!runArgs.enableGC) { From 16f1d26228c6134dabce4372569f027c11b2a592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 12 Aug 2022 21:50:45 +0900 Subject: [PATCH 07/56] Prevent devirtualization into unallocated types (#73839) If we were in a situation like in the regression test, we would devirtualize the `GrabObject` call to `SomeUnallocatedClass.GrabObject`. But because `SomeUnallocatedClass` was never allocated, the scanner didn't scan it, and bad things would happen. Prevent devirtualizing into types that were not seen as allocated. This is not a real issue for non-generic (non-shareable) types because the tentative instance method optimization generates throwing bodies for these. But tentative method optimization doesn't run for shared generics: https://github.com/dotnet/runtime/blob/4cbe6f99d23e04c56a89251d49de1b0f14000427/src/coreclr/tools/Common/Compiler/MethodExtensions.cs#L115 This was rare enough that we haven't seen it until I did #73683 and there was one useless constructor that we stopped generating and triggered this. This also includes what is essentially a rollback of https://github.com/dotnet/runtimelab/pull/1700. This should have been rolled back with https://github.com/dotnet/runtime/pull/66145 but I forgot we had this. It was not needed. * Update tests.proj --- .../ILCompiler.Compiler/Compiler/ILScanner.cs | 25 +++++-------------- src/libraries/tests.proj | 1 + .../SmokeTests/UnitTests/Devirtualization.cs | 25 +++++++++++++++++++ 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs index c2caeb24b3341f..da72ea35cb05b0 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs @@ -365,8 +365,8 @@ public override DictionaryLayoutNode GetLayout(TypeSystemEntity methodOrType) private class ScannedDevirtualizationManager : DevirtualizationManager { private HashSet _constructedTypes = new HashSet(); + private HashSet _canonConstructedTypes = new HashSet(); private HashSet _unsealedTypes = new HashSet(); - private HashSet _abstractButNonabstractlyOverriddenTypes = new HashSet(); public ScannedDevirtualizationManager(ImmutableArray> markedNodes) { @@ -390,16 +390,13 @@ public ScannedDevirtualizationManager(ImmutableArray + diff --git a/src/tests/nativeaot/SmokeTests/UnitTests/Devirtualization.cs b/src/tests/nativeaot/SmokeTests/UnitTests/Devirtualization.cs index 73e3ab4d3064fb..5954c6118e6696 100644 --- a/src/tests/nativeaot/SmokeTests/UnitTests/Devirtualization.cs +++ b/src/tests/nativeaot/SmokeTests/UnitTests/Devirtualization.cs @@ -12,6 +12,7 @@ internal static int Run() { RegressionBug73076.Run(); DevirtualizationCornerCaseTests.Run(); + DevirtualizeIntoUnallocatedGenericType.Run(); return 100; } @@ -111,4 +112,28 @@ public static void Run() TestIntf2((IIntf2)Activator.CreateInstance(typeof(Intf2Impl2<>).MakeGenericType(typeof(object))), 456); } } + + class DevirtualizeIntoUnallocatedGenericType + { + class Never { } + + class SomeGeneric + { + public virtual object GrabObject() => null; + } + + sealed class SomeUnallocatedClass : SomeGeneric + { + public override object GrabObject() => new Never(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static SomeUnallocatedClass GrabInst() => null; + + public static void Run() + { + if (GrabInst() != null) + GrabInst().GrabObject(); + } + } } From ae24786fbffbbac5b0f370cf316f2f2633285ac2 Mon Sep 17 00:00:00 2001 From: Caleb Cornett Date: Fri, 12 Aug 2022 08:52:54 -0400 Subject: [PATCH 08/56] Add FEATURE_READONLY_GS_COOKIE for NativeAOT (#73744) --- src/coreclr/nativeaot/Runtime/CMakeLists.txt | 1 + src/coreclr/nativeaot/Runtime/startup.cpp | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/coreclr/nativeaot/Runtime/CMakeLists.txt b/src/coreclr/nativeaot/Runtime/CMakeLists.txt index 7feb64dc6b59f8..4ccefca81f5328 100644 --- a/src/coreclr/nativeaot/Runtime/CMakeLists.txt +++ b/src/coreclr/nativeaot/Runtime/CMakeLists.txt @@ -223,6 +223,7 @@ if(WIN32) add_definitions(-DFEATURE_EVENT_TRACE) add_definitions(-DFEATURE_SUSPEND_REDIRECTION) else() + add_definitions(-DFEATURE_READONLY_GS_COOKIE) add_definitions(-DNO_UI_ASSERT) include(unix/configure.cmake) include_directories(${CMAKE_CURRENT_BINARY_DIR}) diff --git a/src/coreclr/nativeaot/Runtime/startup.cpp b/src/coreclr/nativeaot/Runtime/startup.cpp index 2d8d3b2b2baa29..20caf9d03e5da2 100644 --- a/src/coreclr/nativeaot/Runtime/startup.cpp +++ b/src/coreclr/nativeaot/Runtime/startup.cpp @@ -66,6 +66,9 @@ static bool InitGSCookie(); //----------------------------------------------------------------------------- // GSCookies (guard-stack cookies) for detecting buffer overruns //----------------------------------------------------------------------------- +typedef size_t GSCookie; + +#ifdef FEATURE_READONLY_GS_COOKIE #ifdef __APPLE__ #define READONLY_ATTR_ARGS section("__DATA,__const") @@ -74,14 +77,15 @@ static bool InitGSCookie(); #endif #define READONLY_ATTR __attribute__((READONLY_ATTR_ARGS)) -// Guard-stack cookie for preventing against stack buffer overruns -typedef size_t GSCookie; - // const is so that it gets placed in the .text section (which is read-only) // volatile is so that accesses to it do not get optimized away because of the const // extern "C" volatile READONLY_ATTR const GSCookie __security_cookie = 0; +#else +extern "C" volatile GSCookie __security_cookie = 0; +#endif // FEATURE_READONLY_GS_COOKIE + #endif // TARGET_UNIX static bool InitDLL(HANDLE hPalInstance) @@ -312,11 +316,13 @@ bool InitGSCookie() { volatile GSCookie * pGSCookiePtr = GetProcessGSCookiePtr(); +#ifdef FEATURE_READONLY_GS_COOKIE // The GS cookie is stored in a read only data segment if (!PalVirtualProtect((void*)pGSCookiePtr, sizeof(GSCookie), PAGE_READWRITE)) { return false; } +#endif // REVIEW: Need something better for PAL... GSCookie val = (GSCookie)PalGetTickCount64(); @@ -328,7 +334,11 @@ bool InitGSCookie() *pGSCookiePtr = val; +#ifdef FEATURE_READONLY_GS_COOKIE return PalVirtualProtect((void*)pGSCookiePtr, sizeof(GSCookie), PAGE_READONLY); +#else + return true; +#endif } #endif // TARGET_UNIX From 1ae7c5f816b9910beaea67c45bb3681e8630662d Mon Sep 17 00:00:00 2001 From: Victor Irzak <6209775+virzak@users.noreply.github.com> Date: Fri, 12 Aug 2022 09:21:32 -0400 Subject: [PATCH 09/56] Handle FileStream.Length for devices (#73708) Co-authored-by: Theodore Tsirpanis Co-authored-by: Adam Sitnik Co-authored-by: Jan Kotas --- .../Kernel32/Interop.DeviceIoControl.cs | 9 +++-- .../Kernel32/Interop.STORAGE_READ_CAPACITY.cs | 22 ++++++++++++ .../tests/RandomAccess/GetLength.cs | 17 +++++++++ .../SafeHandles/SafeFileHandle.Windows.cs | 36 +++++++++++++++++-- .../System.Private.CoreLib.Shared.projitems | 3 ++ .../src/System/IO/FileSystem.Windows.cs | 23 +++++++----- 6 files changed, 96 insertions(+), 14 deletions(-) create mode 100644 src/libraries/Common/src/Interop/Windows/Kernel32/Interop.STORAGE_READ_CAPACITY.cs diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeviceIoControl.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeviceIoControl.cs index 1d202087e5de07..aae40b9bb7fddc 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeviceIoControl.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeviceIoControl.cs @@ -12,14 +12,17 @@ internal static partial class Kernel32 // https://docs.microsoft.com/windows/win32/api/winioctl/ni-winioctl-fsctl_get_reparse_point internal const int FSCTL_GET_REPARSE_POINT = 0x000900a8; + // https://docs.microsoft.com/windows-hardware/drivers/ddi/ntddstor/ni-ntddstor-ioctl_storage_read_capacity + internal const int IOCTL_STORAGE_READ_CAPACITY = 0x002D5140; + [LibraryImport(Libraries.Kernel32, EntryPoint = "DeviceIoControl", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool DeviceIoControl( + internal static unsafe partial bool DeviceIoControl( SafeHandle hDevice, uint dwIoControlCode, - IntPtr lpInBuffer, + void* lpInBuffer, uint nInBufferSize, - byte[] lpOutBuffer, + void* lpOutBuffer, uint nOutBufferSize, out uint lpBytesReturned, IntPtr lpOverlapped); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.STORAGE_READ_CAPACITY.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.STORAGE_READ_CAPACITY.cs new file mode 100644 index 00000000000000..49d3af39f9b48f --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.STORAGE_READ_CAPACITY.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; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + // https://docs.microsoft.com/en-us/windows/win32/devio/storage-read-capacity + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct STORAGE_READ_CAPACITY + { + internal uint Version; + internal uint Size; + internal uint BlockLength; + internal long NumberOfBlocks; + internal long DiskLength; + } + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/GetLength.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/GetLength.cs index 1c21748f0198e8..0bb405c487f02c 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/GetLength.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/GetLength.cs @@ -36,5 +36,22 @@ public void ReturnsExactSizeForNonEmptyFiles(FileOptions options) Assert.Equal(fileSize, RandomAccess.GetLength(handle)); } } + + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsWindowsAndElevated))] + [MemberData(nameof(GetSyncAsyncOptions))] + public void ReturnsActualLengthForDevices(FileOptions options) + { + // both File.Exists and Path.Exists return false when "\\?\PhysicalDrive0" exists + // that is why we just try and swallow the exception when it occurs + try + { + using (SafeFileHandle handle = File.OpenHandle(@"\\?\PhysicalDrive0", FileMode.Open, options: options)) + { + long length = RandomAccess.GetLength(handle); + Assert.True(length > 0); + } + } + catch (FileNotFoundException) { } + } } } 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 5223557f5143f9..bbf81283719a17 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 @@ -6,6 +6,7 @@ using System.IO; using System.Runtime.InteropServices; using System.Threading; +using System.Buffers; namespace Microsoft.Win32.SafeHandles { @@ -286,12 +287,43 @@ unsafe long GetFileLengthCore() { Interop.Kernel32.FILE_STANDARD_INFO info; - if (!Interop.Kernel32.GetFileInformationByHandleEx(this, Interop.Kernel32.FileStandardInfo, &info, (uint)sizeof(Interop.Kernel32.FILE_STANDARD_INFO))) + if (Interop.Kernel32.GetFileInformationByHandleEx(this, Interop.Kernel32.FileStandardInfo, &info, (uint)sizeof(Interop.Kernel32.FILE_STANDARD_INFO))) + { + return info.EndOfFile; + } + + // In theory when GetFileInformationByHandleEx fails, then + // a) IsDevice can modify last error (not true today, but can be in the future), + // b) DeviceIoControl can succeed (last error set to ERROR_SUCCESS) but return fewer bytes than requested. + // The error is stored and in such cases exception for the first failure is going to be thrown. + int lastError = Marshal.GetLastWin32Error(); + + if (Path is null || !PathInternal.IsDevice(Path)) + { + throw Win32Marshal.GetExceptionForWin32Error(lastError, Path); + } + + Interop.Kernel32.STORAGE_READ_CAPACITY storageReadCapacity; + bool success = Interop.Kernel32.DeviceIoControl( + this, + dwIoControlCode: Interop.Kernel32.IOCTL_STORAGE_READ_CAPACITY, + lpInBuffer: null, + nInBufferSize: 0, + lpOutBuffer: &storageReadCapacity, + nOutBufferSize: (uint)sizeof(Interop.Kernel32.STORAGE_READ_CAPACITY), + out uint bytesReturned, + IntPtr.Zero); + + if (!success) { throw Win32Marshal.GetExceptionForLastWin32Error(Path); } + else if (bytesReturned != sizeof(Interop.Kernel32.STORAGE_READ_CAPACITY)) + { + throw Win32Marshal.GetExceptionForWin32Error(lastError, Path); + } - return info.EndOfFile; + return storageReadCapacity.DiskLength; } } } 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 d025ab28e0e7c3..6f3a16e6136363 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 @@ -1720,6 +1720,9 @@ Common\Interop\Windows\Kernel32\Interop.SECURITY_ATTRIBUTES.cs + + Common\Interop\Windows\Kernel32\Interop.STORAGE_READ_CAPACITY.cs + Common\Interop\Windows\Interop.UNICODE_STRING.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs index fbea595b861f3d..22d723cf1bec3b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs @@ -574,15 +574,20 @@ internal static void CreateSymbolicLink(string path, string pathToTarget, bool i byte[] buffer = ArrayPool.Shared.Rent(Interop.Kernel32.MAXIMUM_REPARSE_DATA_BUFFER_SIZE); try { - bool success = Interop.Kernel32.DeviceIoControl( - handle, - dwIoControlCode: Interop.Kernel32.FSCTL_GET_REPARSE_POINT, - lpInBuffer: IntPtr.Zero, - nInBufferSize: 0, - lpOutBuffer: buffer, - nOutBufferSize: Interop.Kernel32.MAXIMUM_REPARSE_DATA_BUFFER_SIZE, - out _, - IntPtr.Zero); + bool success; + + fixed (byte* pBuffer = buffer) + { + success = Interop.Kernel32.DeviceIoControl( + handle, + dwIoControlCode: Interop.Kernel32.FSCTL_GET_REPARSE_POINT, + lpInBuffer: null, + nInBufferSize: 0, + lpOutBuffer: pBuffer, + nOutBufferSize: Interop.Kernel32.MAXIMUM_REPARSE_DATA_BUFFER_SIZE, + out _, + IntPtr.Zero); + } if (!success) { From 820ffd46ba476a023b45e040e0b909f50d99d613 Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Fri, 12 Aug 2022 16:54:02 +0200 Subject: [PATCH 10/56] Tar: restore directory permissions while extracting. (#72078) * Tar: restore directory permissions while extracting. * PR feedback. * On Windows, as on Unix: don't set group/other write permission by default. * Fix Windows compilation. * Fix Windows compilation II. * Update test for Windows. * Fix test WindowsFileMode value. * Remove branch. * Apply suggestions from code review Co-authored-by: Eric Erhardt * Add back DefaultWindowsMode. * Fix build failure in TarWriter due to DefaultWindowsMode usage. * Fix DefaultWindowsMode. * Apply suggestions from code review Co-authored-by: Carlos Sanchez <1175054+carlossanlop@users.noreply.github.com> Co-authored-by: Eric Erhardt Co-authored-by: Carlos Sanchez <1175054+carlossanlop@users.noreply.github.com> --- .../src/System.Formats.Tar.csproj | 2 + .../src/System/Formats/Tar/TarEntry.cs | 27 +++- .../src/System/Formats/Tar/TarFile.cs | 35 ++-- .../src/System/Formats/Tar/TarHelpers.Unix.cs | 149 ++++++++++++++++++ .../System/Formats/Tar/TarHelpers.Windows.cs | 22 +++ .../src/System/Formats/Tar/TarHelpers.cs | 4 +- .../System/Formats/Tar/TarWriter.Windows.cs | 4 +- .../TarEntry.ExtractToFile.Tests.Unix.cs | 3 + .../TarEntry/TarEntry.ExtractToFile.Tests.cs | 15 ++ .../TarEntry.ExtractToFileAsync.Tests.cs | 13 ++ .../TarFile.CreateFromDirectory.File.Tests.cs | 36 ++++- ...ile.CreateFromDirectoryAsync.File.Tests.cs | 121 ++++++++------ .../TarFile.ExtractToDirectory.File.Tests.cs | 114 ++++++++++++++ ...File.ExtractToDirectoryAsync.File.Tests.cs | 90 +++++++++++ .../System.Formats.Tar/tests/TarTestsBase.cs | 85 +++++++++- .../TarWriter/TarWriter.File.Base.Windows.cs | 5 +- 16 files changed, 646 insertions(+), 79 deletions(-) create mode 100644 src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHelpers.Unix.cs create mode 100644 src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHelpers.Windows.cs diff --git a/src/libraries/System.Formats.Tar/src/System.Formats.Tar.csproj b/src/libraries/System.Formats.Tar/src/System.Formats.Tar.csproj index 7d122684fcef9f..c0487caf28049e 100644 --- a/src/libraries/System.Formats.Tar/src/System.Formats.Tar.csproj +++ b/src/libraries/System.Formats.Tar/src/System.Formats.Tar.csproj @@ -38,6 +38,7 @@ + @@ -51,6 +52,7 @@ + diff --git a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.cs b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.cs index ea965322d78095..00c790762e4b65 100644 --- a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.cs +++ b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.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 System.Diagnostics; using System.IO; using System.Threading; @@ -280,24 +281,24 @@ public Stream? DataStream internal abstract bool IsDataStreamSetterSupported(); // Extracts the current entry to a location relative to the specified directory. - internal void ExtractRelativeToDirectory(string destinationDirectoryPath, bool overwrite) + internal void ExtractRelativeToDirectory(string destinationDirectoryPath, bool overwrite, SortedDictionary? pendingModes) { (string fileDestinationPath, string? linkTargetPath) = GetDestinationAndLinkPaths(destinationDirectoryPath); if (EntryType == TarEntryType.Directory) { - Directory.CreateDirectory(fileDestinationPath); + TarHelpers.CreateDirectory(fileDestinationPath, Mode, overwrite, pendingModes); } else { // If it is a file, create containing directory. - Directory.CreateDirectory(Path.GetDirectoryName(fileDestinationPath)!); + TarHelpers.CreateDirectory(Path.GetDirectoryName(fileDestinationPath)!, mode: null, overwrite, pendingModes); ExtractToFileInternal(fileDestinationPath, linkTargetPath, overwrite); } } // Asynchronously extracts the current entry to a location relative to the specified directory. - internal Task ExtractRelativeToDirectoryAsync(string destinationDirectoryPath, bool overwrite, CancellationToken cancellationToken) + internal Task ExtractRelativeToDirectoryAsync(string destinationDirectoryPath, bool overwrite, SortedDictionary? pendingModes, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { @@ -308,13 +309,13 @@ internal Task ExtractRelativeToDirectoryAsync(string destinationDirectoryPath, b if (EntryType == TarEntryType.Directory) { - Directory.CreateDirectory(fileDestinationPath); + TarHelpers.CreateDirectory(fileDestinationPath, Mode, overwrite, pendingModes); return Task.CompletedTask; } else { // If it is a file, create containing directory. - Directory.CreateDirectory(Path.GetDirectoryName(fileDestinationPath)!); + TarHelpers.CreateDirectory(Path.GetDirectoryName(fileDestinationPath)!, mode: null, overwrite, pendingModes); return ExtractToFileInternalAsync(fileDestinationPath, linkTargetPath, overwrite, cancellationToken); } } @@ -403,7 +404,19 @@ private void CreateNonRegularFile(string filePath, string? linkTargetPath) { case TarEntryType.Directory: case TarEntryType.DirectoryList: - Directory.CreateDirectory(filePath); + // Mode must only be used for the leaf directory. + // VerifyPathsForEntryType ensures we're only creating a leaf. + Debug.Assert(Directory.Exists(Path.GetDirectoryName(filePath))); + Debug.Assert(!Directory.Exists(filePath)); + + if (!OperatingSystem.IsWindows()) + { + Directory.CreateDirectory(filePath, Mode); + } + else + { + Directory.CreateDirectory(filePath); + } break; case TarEntryType.SymbolicLink: diff --git a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarFile.cs b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarFile.cs index 8ce12347d73a3c..a77cb2c36a65ea 100644 --- a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarFile.cs +++ b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarFile.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Threading; @@ -279,7 +280,6 @@ private static void CreateFromDirectoryInternal(string sourceDirectoryName, Stre using (TarWriter writer = new TarWriter(destination, TarEntryFormat.Pax, leaveOpen)) { - bool baseDirectoryIsEmpty = true; DirectoryInfo di = new(sourceDirectoryName); string basePath = GetBasePathForCreateFromDirectory(di, includeBaseDirectory); @@ -287,15 +287,14 @@ private static void CreateFromDirectoryInternal(string sourceDirectoryName, Stre try { - foreach (FileSystemInfo file in di.EnumerateFileSystemInfos("*", SearchOption.AllDirectories)) + if (includeBaseDirectory) { - baseDirectoryIsEmpty = false; - writer.WriteEntry(file.FullName, GetEntryNameForFileSystemInfo(file, basePath.Length, ref entryNameBuffer)); + writer.WriteEntry(di.FullName, GetEntryNameForBaseDirectory(di.Name, ref entryNameBuffer)); } - if (includeBaseDirectory && baseDirectoryIsEmpty) + foreach (FileSystemInfo file in di.EnumerateFileSystemInfos("*", SearchOption.AllDirectories)) { - writer.WriteEntry(GetEntryForBaseDirectory(di.Name, ref entryNameBuffer)); + writer.WriteEntry(file.FullName, GetEntryNameForFileSystemInfo(file, basePath.Length, ref entryNameBuffer)); } } finally @@ -337,7 +336,6 @@ private static async Task CreateFromDirectoryInternalAsync(string sourceDirector TarWriter writer = new TarWriter(destination, TarEntryFormat.Pax, leaveOpen); await using (writer.ConfigureAwait(false)) { - bool baseDirectoryIsEmpty = true; DirectoryInfo di = new(sourceDirectoryName); string basePath = GetBasePathForCreateFromDirectory(di, includeBaseDirectory); @@ -345,15 +343,14 @@ private static async Task CreateFromDirectoryInternalAsync(string sourceDirector try { - foreach (FileSystemInfo file in di.EnumerateFileSystemInfos("*", SearchOption.AllDirectories)) + if (includeBaseDirectory) { - baseDirectoryIsEmpty = false; - await writer.WriteEntryAsync(file.FullName, GetEntryNameForFileSystemInfo(file, basePath.Length, ref entryNameBuffer), cancellationToken).ConfigureAwait(false); + await writer.WriteEntryAsync(di.FullName, GetEntryNameForBaseDirectory(di.Name, ref entryNameBuffer), cancellationToken).ConfigureAwait(false); } - if (includeBaseDirectory && baseDirectoryIsEmpty) + foreach (FileSystemInfo file in di.EnumerateFileSystemInfos("*", SearchOption.AllDirectories)) { - await writer.WriteEntryAsync(GetEntryForBaseDirectory(di.Name, ref entryNameBuffer), cancellationToken).ConfigureAwait(false); + await writer.WriteEntryAsync(file.FullName, GetEntryNameForFileSystemInfo(file, basePath.Length, ref entryNameBuffer), cancellationToken).ConfigureAwait(false); } } finally @@ -377,11 +374,9 @@ private static string GetEntryNameForFileSystemInfo(FileSystemInfo file, int bas return ArchivingUtils.EntryFromPath(file.FullName, basePathLength, entryNameLength, ref entryNameBuffer, appendPathSeparator: isDirectory); } - // Constructs a PaxTarEntry for a base directory entry when creating an archive. - private static PaxTarEntry GetEntryForBaseDirectory(string name, ref char[] entryNameBuffer) + private static string GetEntryNameForBaseDirectory(string name, ref char[] entryNameBuffer) { - string entryName = ArchivingUtils.EntryFromPath(name, 0, name.Length, ref entryNameBuffer, appendPathSeparator: true); - return new PaxTarEntry(TarEntryType.Directory, entryName); + return ArchivingUtils.EntryFromPath(name, 0, name.Length, ref entryNameBuffer, appendPathSeparator: true); } // Extracts an archive into the specified directory. @@ -392,14 +387,16 @@ private static void ExtractToDirectoryInternal(Stream source, string destination using TarReader reader = new TarReader(source, leaveOpen); + SortedDictionary? pendingModes = TarHelpers.CreatePendingModesDictionary(); TarEntry? entry; while ((entry = reader.GetNextEntry()) != null) { if (entry.EntryType is not TarEntryType.GlobalExtendedAttributes) { - entry.ExtractRelativeToDirectory(destinationDirectoryPath, overwriteFiles); + entry.ExtractRelativeToDirectory(destinationDirectoryPath, overwriteFiles, pendingModes); } } + TarHelpers.SetPendingModes(pendingModes); } // Asynchronously extracts the contents of a tar file into the specified directory. @@ -430,6 +427,7 @@ private static async Task ExtractToDirectoryInternalAsync(Stream source, string VerifyExtractToDirectoryArguments(source, destinationDirectoryPath); cancellationToken.ThrowIfCancellationRequested(); + SortedDictionary? pendingModes = TarHelpers.CreatePendingModesDictionary(); TarReader reader = new TarReader(source, leaveOpen); await using (reader.ConfigureAwait(false)) { @@ -438,10 +436,11 @@ private static async Task ExtractToDirectoryInternalAsync(Stream source, string { if (entry.EntryType is not TarEntryType.GlobalExtendedAttributes) { - await entry.ExtractRelativeToDirectoryAsync(destinationDirectoryPath, overwriteFiles, cancellationToken).ConfigureAwait(false); + await entry.ExtractRelativeToDirectoryAsync(destinationDirectoryPath, overwriteFiles, pendingModes, cancellationToken).ConfigureAwait(false); } } } + TarHelpers.SetPendingModes(pendingModes); } [Conditional("DEBUG")] diff --git a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHelpers.Unix.cs b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHelpers.Unix.cs new file mode 100644 index 00000000000000..f684dd78fde1c6 --- /dev/null +++ b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHelpers.Unix.cs @@ -0,0 +1,149 @@ +// 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.IO; +using System.Diagnostics; + +namespace System.Formats.Tar +{ + internal static partial class TarHelpers + { + private static readonly Lazy s_umask = new Lazy(DetermineUMask); + + private static UnixFileMode DetermineUMask() + { + // To determine the umask, we'll create a file with full permissions and see + // what gets filtered out. + // note: only the owner of a file, and root can change file permissions. + + const UnixFileMode OwnershipPermissions = + UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute | + UnixFileMode.GroupRead | UnixFileMode.GroupWrite | UnixFileMode.GroupExecute | + UnixFileMode.OtherRead | UnixFileMode.OtherWrite | UnixFileMode.OtherExecute; + + string filename = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + FileStreamOptions options = new() + { + Mode = FileMode.CreateNew, + UnixCreateMode = OwnershipPermissions, + Options = FileOptions.DeleteOnClose, + Access = FileAccess.Write, + BufferSize = 0 + }; + using var fs = new FileStream(filename, options); + UnixFileMode actual = File.GetUnixFileMode(fs.SafeFileHandle); + + return OwnershipPermissions & ~actual; + } + + private sealed class ReverseStringComparer : IComparer + { + public int Compare (string? x, string? y) + => StringComparer.Ordinal.Compare(y, x); + } + + private static readonly ReverseStringComparer s_reverseStringComparer = new(); + + private static UnixFileMode UMask => s_umask.Value; + + /* + Tar files are usually ordered: parent directories come before their child entries. + + They may be unordered. In that case we need to create parent directories before + we know the proper permissions for these directories. + + We create these directories with restrictive permissions. If we encounter an entry for + the directory later, we store the mode to apply it later. + + If the archive doesn't have an entry for the parent directory, we use the default mask. + + The pending modes to be applied are tracked through a reverse-sorted dictionary. + The reverse order is needed to apply permissions to children before their parent. + Otherwise we may apply a restrictive mask to the parent, that prevents us from + changing a child. + */ + + internal static SortedDictionary? CreatePendingModesDictionary() + => new SortedDictionary(s_reverseStringComparer); + + internal static void CreateDirectory(string fullPath, UnixFileMode? mode, bool overwriteMetadata, SortedDictionary? pendingModes) + { + // Restrictive mask for creating the missing parent directories while extracting. + const UnixFileMode ExtractPermissions = UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute; + + Debug.Assert(pendingModes is not null); + + if (Directory.Exists(fullPath)) + { + // Apply permissions to an existing directory when we're overwriting metadata + // or the directory was created as a missing parent (stored in pendingModes). + if (mode.HasValue) + { + bool hasExtractPermissions = (mode.Value & ExtractPermissions) == ExtractPermissions; + if (hasExtractPermissions) + { + bool removed = pendingModes.Remove(fullPath); + if (overwriteMetadata || removed) + { + UnixFileMode umask = UMask; + File.SetUnixFileMode(fullPath, mode.Value & ~umask); + } + } + else if (overwriteMetadata || pendingModes.ContainsKey(fullPath)) + { + pendingModes[fullPath] = mode.Value; + } + } + return; + } + + if (mode.HasValue) + { + // Ensure we have sufficient permissions to extract in the directory. + if ((mode.Value & ExtractPermissions) != ExtractPermissions) + { + pendingModes[fullPath] = mode.Value; + mode = ExtractPermissions; + } + } + else + { + pendingModes.Add(fullPath, DefaultDirectoryMode); + mode = ExtractPermissions; + } + + string parentDir = Path.GetDirectoryName(fullPath)!; + string rootDir = Path.GetPathRoot(parentDir)!; + bool hasMissingParents = false; + for (string dir = parentDir; dir != rootDir && !Directory.Exists(dir); dir = Path.GetDirectoryName(dir)!) + { + pendingModes.Add(dir, DefaultDirectoryMode); + hasMissingParents = true; + } + + if (hasMissingParents) + { + Directory.CreateDirectory(parentDir, ExtractPermissions); + } + + Directory.CreateDirectory(fullPath, mode.Value); + } + + internal static void SetPendingModes(SortedDictionary? pendingModes) + { + Debug.Assert(pendingModes is not null); + + if (pendingModes.Count == 0) + { + return; + } + + UnixFileMode umask = UMask; + foreach (KeyValuePair dir in pendingModes) + { + File.SetUnixFileMode(dir.Key, dir.Value & ~umask); + } + } + } +} diff --git a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHelpers.Windows.cs b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHelpers.Windows.cs new file mode 100644 index 00000000000000..e00f6476764aba --- /dev/null +++ b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHelpers.Windows.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.Collections.Generic; +using System.IO; +using System.Text; +using System.Diagnostics; + +namespace System.Formats.Tar +{ + internal static partial class TarHelpers + { + internal static SortedDictionary? CreatePendingModesDictionary() + => null; + + internal static void CreateDirectory(string fullPath, UnixFileMode? mode, bool overwriteMetadata, SortedDictionary? pendingModes) + => Directory.CreateDirectory(fullPath); + + internal static void SetPendingModes(SortedDictionary? pendingModes) + => Debug.Assert(pendingModes is null); + } +} diff --git a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHelpers.cs b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHelpers.cs index a34bdd0506a20e..d4592eb85d4b5f 100644 --- a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHelpers.cs +++ b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHelpers.cs @@ -12,7 +12,7 @@ namespace System.Formats.Tar { // Static class containing a variety of helper methods. - internal static class TarHelpers + internal static partial class TarHelpers { internal const short RecordSize = 512; internal const int MaxBufferLength = 4096; @@ -22,11 +22,13 @@ internal static class TarHelpers internal const byte EqualsChar = 0x3d; internal const byte NewLineChar = 0xa; + // Default mode for TarEntry created for a file-type. private const UnixFileMode DefaultFileMode = UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.GroupRead | UnixFileMode.OtherRead; + // Default mode for TarEntry created for a directory-type. private const UnixFileMode DefaultDirectoryMode = DefaultFileMode | UnixFileMode.UserExecute | UnixFileMode.GroupExecute | UnixFileMode.OtherExecute; diff --git a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarWriter.Windows.cs b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarWriter.Windows.cs index a3fa4ee0f595f8..bfb6cf17f11076 100644 --- a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarWriter.Windows.cs +++ b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarWriter.Windows.cs @@ -11,8 +11,8 @@ namespace System.Formats.Tar // Windows specific methods for the TarWriter class. public sealed partial class TarWriter : IDisposable { - // Creating archives in Windows always sets the mode to 777 - private const UnixFileMode DefaultWindowsMode = UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute | UnixFileMode.GroupRead | UnixFileMode.GroupWrite | UnixFileMode.GroupExecute | UnixFileMode.OtherRead | UnixFileMode.OtherWrite | UnixFileMode.UserExecute; + // Windows files don't have a mode. Use a mode of 755 for directories and files. + private const UnixFileMode DefaultWindowsMode = UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute | UnixFileMode.GroupRead | UnixFileMode.GroupExecute | UnixFileMode.OtherRead | UnixFileMode.OtherExecute; // Windows specific implementation of the method that reads an entry from disk and writes it into the archive stream. private TarEntry ConstructEntryForWriting(string fullPath, string entryName, FileOptions fileOptions) diff --git a/src/libraries/System.Formats.Tar/tests/TarEntry/TarEntry.ExtractToFile.Tests.Unix.cs b/src/libraries/System.Formats.Tar/tests/TarEntry/TarEntry.ExtractToFile.Tests.Unix.cs index f86136bcc8021d..cee5f6bc7dfd85 100644 --- a/src/libraries/System.Formats.Tar/tests/TarEntry/TarEntry.ExtractToFile.Tests.Unix.cs +++ b/src/libraries/System.Formats.Tar/tests/TarEntry/TarEntry.ExtractToFile.Tests.Unix.cs @@ -67,6 +67,7 @@ public async Task Extract_SpecialFiles_Async(TarEntryFormat format, TarEntryType entry.DeviceMajor = TestCharacterDeviceMajor; entry.DeviceMinor = TestCharacterDeviceMinor; } + entry.Mode = TestPermission1; return (entryName, destination, entry); } @@ -106,6 +107,8 @@ private void Verify_Extract_SpecialFiles(string destination, PosixTarEntry entry Assert.Equal((int)major, entry.DeviceMajor); Assert.Equal((int)minor, entry.DeviceMinor); } + + AssertFileModeEquals(destination, TestPermission1); } } } \ No newline at end of file diff --git a/src/libraries/System.Formats.Tar/tests/TarEntry/TarEntry.ExtractToFile.Tests.cs b/src/libraries/System.Formats.Tar/tests/TarEntry/TarEntry.ExtractToFile.Tests.cs index 93ba1559d2a7ce..3cfcf4e6e36ca7 100644 --- a/src/libraries/System.Formats.Tar/tests/TarEntry/TarEntry.ExtractToFile.Tests.cs +++ b/src/libraries/System.Formats.Tar/tests/TarEntry/TarEntry.ExtractToFile.Tests.cs @@ -1,8 +1,10 @@ // 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.IO; using System.Linq; +using System.Threading.Tasks; using Xunit; namespace System.Formats.Tar.Tests @@ -92,5 +94,18 @@ public void ExtractToFile_Link_Throws(TarEntryFormat format, TarEntryType entryT Assert.Equal(0, Directory.GetFileSystemEntries(root.Path).Count()); } + + [Theory] + [MemberData(nameof(GetFormatsAndFiles))] + public void Extract(TarEntryFormat format, TarEntryType entryType) + { + using TempDirectory root = new TempDirectory(); + + (string entryName, string destination, TarEntry entry) = Prepare_Extract(root, format, entryType); + + entry.ExtractToFile(destination, overwrite: true); + + Verify_Extract(destination, entry, entryType); + } } } diff --git a/src/libraries/System.Formats.Tar/tests/TarEntry/TarEntry.ExtractToFileAsync.Tests.cs b/src/libraries/System.Formats.Tar/tests/TarEntry/TarEntry.ExtractToFileAsync.Tests.cs index 58d0143b1f6ce2..c5404e9fd4d848 100644 --- a/src/libraries/System.Formats.Tar/tests/TarEntry/TarEntry.ExtractToFileAsync.Tests.cs +++ b/src/libraries/System.Formats.Tar/tests/TarEntry/TarEntry.ExtractToFileAsync.Tests.cs @@ -113,5 +113,18 @@ public async Task ExtractToFile_Link_Throws_Async(TarEntryFormat format, TarEntr Assert.Equal(0, Directory.GetFileSystemEntries(root.Path).Count()); } } + + [Theory] + [MemberData(nameof(GetFormatsAndFiles))] + public async Task Extract_Async(TarEntryFormat format, TarEntryType entryType) + { + using TempDirectory root = new TempDirectory(); + + (string entryName, string destination, TarEntry entry) = Prepare_Extract(root, format, entryType); + + await entry.ExtractToFileAsync(destination, overwrite: true); + + Verify_Extract(destination, entry, entryType); + } } } diff --git a/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.CreateFromDirectory.File.Tests.cs b/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.CreateFromDirectory.File.Tests.cs index d2fe36deaaa3ca..75a3495d94e5e5 100644 --- a/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.CreateFromDirectory.File.Tests.cs +++ b/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.CreateFromDirectory.File.Tests.cs @@ -52,17 +52,26 @@ public void VerifyIncludeBaseDirectory(bool includeBaseDirectory) using TempDirectory source = new TempDirectory(); using TempDirectory destination = new TempDirectory(); + UnixFileMode baseDirectoryMode = TestPermission1; + SetUnixFileMode(source.Path, baseDirectoryMode); + string fileName1 = "file1.txt"; string filePath1 = Path.Join(source.Path, fileName1); File.Create(filePath1).Dispose(); + UnixFileMode filename1Mode = TestPermission2; + SetUnixFileMode(filePath1, filename1Mode); string subDirectoryName = "dir/"; // The trailing separator is preserved in the TarEntry.Name string subDirectoryPath = Path.Join(source.Path, subDirectoryName); Directory.CreateDirectory(subDirectoryPath); + UnixFileMode subDirectoryMode = TestPermission3; + SetUnixFileMode(subDirectoryPath, subDirectoryMode); string fileName2 = "file2.txt"; string filePath2 = Path.Join(subDirectoryPath, fileName2); File.Create(filePath2).Dispose(); + UnixFileMode filename2Mode = TestPermission4; + SetUnixFileMode(filePath2, filename2Mode); string destinationArchiveFileName = Path.Join(destination.Path, "output.tar"); TarFile.CreateFromDirectory(source.Path, destinationArchiveFileName, includeBaseDirectory); @@ -78,25 +87,38 @@ public void VerifyIncludeBaseDirectory(bool includeBaseDirectory) entries.Add(entry); } - Assert.Equal(3, entries.Count); + int expectedCount = 3 + (includeBaseDirectory ? 1 : 0); + Assert.Equal(expectedCount, entries.Count); string prefix = includeBaseDirectory ? Path.GetFileName(source.Path) + '/' : string.Empty; + if (includeBaseDirectory) + { + TarEntry baseEntry = entries.FirstOrDefault(x => + x.EntryType == TarEntryType.Directory && + x.Name == prefix); + Assert.NotNull(baseEntry); + AssertEntryModeFromFileSystemEquals(baseEntry, baseDirectoryMode); + } + TarEntry entry1 = entries.FirstOrDefault(x => x.EntryType == TarEntryType.RegularFile && x.Name == prefix + fileName1); Assert.NotNull(entry1); + AssertEntryModeFromFileSystemEquals(entry1, filename1Mode); TarEntry directory = entries.FirstOrDefault(x => x.EntryType == TarEntryType.Directory && x.Name == prefix + subDirectoryName); Assert.NotNull(directory); + AssertEntryModeFromFileSystemEquals(directory, subDirectoryMode); string actualFileName2 = subDirectoryName + fileName2; // Notice the trailing separator in subDirectoryName TarEntry entry2 = entries.FirstOrDefault(x => x.EntryType == TarEntryType.RegularFile && x.Name == prefix + actualFileName2); Assert.NotNull(entry2); + AssertEntryModeFromFileSystemEquals(entry2, filename2Mode); } [Fact] @@ -144,7 +166,17 @@ public void IncludeAllSegmentsOfPath(bool includeBaseDirectory) string prefix = includeBaseDirectory ? Path.GetFileName(source.Path) + '/' : string.Empty; - TarEntry entry = reader.GetNextEntry(); + TarEntry entry; + + if (includeBaseDirectory) + { + entry = reader.GetNextEntry(); + Assert.NotNull(entry); + Assert.Equal(TarEntryType.Directory, entry.EntryType); + Assert.Equal(prefix, entry.Name); + } + + entry = reader.GetNextEntry(); Assert.NotNull(entry); Assert.Equal(TarEntryType.Directory, entry.EntryType); Assert.Equal(prefix + "segment1/", entry.Name); diff --git a/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.CreateFromDirectoryAsync.File.Tests.cs b/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.CreateFromDirectoryAsync.File.Tests.cs index 8891ea856ebb6e..c9b4377cd03285 100644 --- a/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.CreateFromDirectoryAsync.File.Tests.cs +++ b/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.CreateFromDirectoryAsync.File.Tests.cs @@ -61,65 +61,86 @@ public async Task DestinationExists_Throws_Async() [InlineData(true)] public async Task VerifyIncludeBaseDirectory_Async(bool includeBaseDirectory) { - using (TempDirectory source = new TempDirectory()) - using (TempDirectory destination = new TempDirectory()) - { - string fileName1 = "file1.txt"; - string filePath1 = Path.Join(source.Path, fileName1); - File.Create(filePath1).Dispose(); + using TempDirectory source = new TempDirectory(); + using TempDirectory destination = new TempDirectory(); - string subDirectoryName = "dir/"; // The trailing separator is preserved in the TarEntry.Name - string subDirectoryPath = Path.Join(source.Path, subDirectoryName); - Directory.CreateDirectory(subDirectoryPath); + UnixFileMode baseDirectoryMode = TestPermission1; + SetUnixFileMode(source.Path, baseDirectoryMode); - string fileName2 = "file2.txt"; - string filePath2 = Path.Join(subDirectoryPath, fileName2); - File.Create(filePath2).Dispose(); + string fileName1 = "file1.txt"; + string filePath1 = Path.Join(source.Path, fileName1); + File.Create(filePath1).Dispose(); + UnixFileMode filename1Mode = TestPermission2; + SetUnixFileMode(filePath1, filename1Mode); - string destinationArchiveFileName = Path.Join(destination.Path, "output.tar"); - TarFile.CreateFromDirectory(source.Path, destinationArchiveFileName, includeBaseDirectory); + string subDirectoryName = "dir/"; // The trailing separator is preserved in the TarEntry.Name + string subDirectoryPath = Path.Join(source.Path, subDirectoryName); + Directory.CreateDirectory(subDirectoryPath); + UnixFileMode subDirectoryMode = TestPermission3; + SetUnixFileMode(subDirectoryPath, subDirectoryMode); - List entries = new List(); + string fileName2 = "file2.txt"; + string filePath2 = Path.Join(subDirectoryPath, fileName2); + File.Create(filePath2).Dispose(); + UnixFileMode filename2Mode = TestPermission4; + SetUnixFileMode(filePath2, filename2Mode); - FileStreamOptions readOptions = new() - { - Access = FileAccess.Read, - Mode = FileMode.Open, - Options = FileOptions.Asynchronous, - }; + string destinationArchiveFileName = Path.Join(destination.Path, "output.tar"); + TarFile.CreateFromDirectory(source.Path, destinationArchiveFileName, includeBaseDirectory); - await using (FileStream fileStream = File.Open(destinationArchiveFileName, readOptions)) + List entries = new List(); + + FileStreamOptions readOptions = new() + { + Access = FileAccess.Read, + Mode = FileMode.Open, + Options = FileOptions.Asynchronous, + }; + + await using (FileStream fileStream = File.Open(destinationArchiveFileName, readOptions)) + { + await using (TarReader reader = new TarReader(fileStream)) { - await using (TarReader reader = new TarReader(fileStream)) + TarEntry entry; + while ((entry = await reader.GetNextEntryAsync()) != null) { - TarEntry entry; - while ((entry = await reader.GetNextEntryAsync()) != null) - { - entries.Add(entry); - } + entries.Add(entry); } } + } - Assert.Equal(3, entries.Count); - - string prefix = includeBaseDirectory ? Path.GetFileName(source.Path) + '/' : string.Empty; + int expectedCount = 3 + (includeBaseDirectory ? 1 : 0); + Assert.Equal(expectedCount, entries.Count); - TarEntry entry1 = entries.FirstOrDefault(x => - x.EntryType == TarEntryType.RegularFile && - x.Name == prefix + fileName1); - Assert.NotNull(entry1); + string prefix = includeBaseDirectory ? Path.GetFileName(source.Path) + '/' : string.Empty; - TarEntry directory = entries.FirstOrDefault(x => + if (includeBaseDirectory) + { + TarEntry baseEntry = entries.FirstOrDefault(x => x.EntryType == TarEntryType.Directory && - x.Name == prefix + subDirectoryName); - Assert.NotNull(directory); - - string actualFileName2 = subDirectoryName + fileName2; // Notice the trailing separator in subDirectoryName - TarEntry entry2 = entries.FirstOrDefault(x => - x.EntryType == TarEntryType.RegularFile && - x.Name == prefix + actualFileName2); - Assert.NotNull(entry2); + x.Name == prefix); + Assert.NotNull(baseEntry); + AssertEntryModeFromFileSystemEquals(baseEntry, baseDirectoryMode); } + + TarEntry entry1 = entries.FirstOrDefault(x => + x.EntryType == TarEntryType.RegularFile && + x.Name == prefix + fileName1); + Assert.NotNull(entry1); + AssertEntryModeFromFileSystemEquals(entry1, filename1Mode); + + TarEntry directory = entries.FirstOrDefault(x => + x.EntryType == TarEntryType.Directory && + x.Name == prefix + subDirectoryName); + Assert.NotNull(directory); + AssertEntryModeFromFileSystemEquals(directory, subDirectoryMode); + + string actualFileName2 = subDirectoryName + fileName2; // Notice the trailing separator in subDirectoryName + TarEntry entry2 = entries.FirstOrDefault(x => + x.EntryType == TarEntryType.RegularFile && + x.Name == prefix + actualFileName2); + Assert.NotNull(entry2); + AssertEntryModeFromFileSystemEquals(entry2, filename2Mode); } [Fact] @@ -186,7 +207,17 @@ public async Task IncludeAllSegmentsOfPath_Async(bool includeBaseDirectory) { string prefix = includeBaseDirectory ? Path.GetFileName(source.Path) + '/' : string.Empty; - TarEntry entry = await reader.GetNextEntryAsync(); + TarEntry entry; + + if (includeBaseDirectory) + { + entry = await reader.GetNextEntryAsync(); + Assert.NotNull(entry); + Assert.Equal(TarEntryType.Directory, entry.EntryType); + Assert.Equal(prefix, entry.Name); + } + + entry = await reader.GetNextEntryAsync(); Assert.NotNull(entry); Assert.Equal(TarEntryType.Directory, entry.EntryType); Assert.Equal(prefix + "segment1/", entry.Name); diff --git a/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectory.File.Tests.cs b/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectory.File.Tests.cs index a753358cb8a394..f741926cdd8ef1 100644 --- a/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectory.File.Tests.cs +++ b/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectory.File.Tests.cs @@ -159,5 +159,119 @@ public void ExtractArchiveWithEntriesThatStartWithSlashDotPrefix() Assert.True(Path.Exists(entryPath), $"Entry was not extracted: {entryPath}"); } } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void UnixFileModes(bool overwrite) + { + using TempDirectory source = new TempDirectory(); + using TempDirectory destination = new TempDirectory(); + + string archivePath = Path.Join(source.Path, "archive.tar"); + using FileStream archiveStream = File.Create(archivePath); + using (TarWriter writer = new TarWriter(archiveStream)) + { + PaxTarEntry dir = new PaxTarEntry(TarEntryType.Directory, "dir"); + dir.Mode = TestPermission1; + writer.WriteEntry(dir); + + PaxTarEntry file = new PaxTarEntry(TarEntryType.RegularFile, "file"); + file.Mode = TestPermission2; + writer.WriteEntry(file); + + // Archive has no entry for missing_parent. + PaxTarEntry missingParentDir = new PaxTarEntry(TarEntryType.Directory, "missing_parent/dir"); + missingParentDir.Mode = TestPermission3; + writer.WriteEntry(missingParentDir); + + // out_of_order_parent/file entry comes before out_of_order_parent entry. + PaxTarEntry outOfOrderFile = new PaxTarEntry(TarEntryType.RegularFile, "out_of_order_parent/file"); + writer.WriteEntry(outOfOrderFile); + + PaxTarEntry outOfOrderDir = new PaxTarEntry(TarEntryType.Directory, "out_of_order_parent"); + outOfOrderDir.Mode = TestPermission4; + writer.WriteEntry(outOfOrderDir); + } + + string dirPath = Path.Join(destination.Path, "dir"); + string filePath = Path.Join(destination.Path, "file"); + string missingParentPath = Path.Join(destination.Path, "missing_parent"); + string missingParentDirPath = Path.Join(missingParentPath, "dir"); + string outOfOrderDirPath = Path.Join(destination.Path, "out_of_order_parent"); + + if (overwrite) + { + File.OpenWrite(filePath).Dispose(); + Directory.CreateDirectory(dirPath); + Directory.CreateDirectory(missingParentDirPath); + Directory.CreateDirectory(outOfOrderDirPath); + } + + TarFile.ExtractToDirectory(archivePath, destination.Path, overwriteFiles: overwrite); + + Assert.True(Directory.Exists(dirPath), $"{dirPath}' does not exist."); + AssertFileModeEquals(dirPath, TestPermission1); + + Assert.True(File.Exists(filePath), $"{filePath}' does not exist."); + AssertFileModeEquals(filePath, TestPermission2); + + // Missing parents are created with DefaultDirectoryMode. + // The mode is not set when overwrite == true if there is no entry and the directory exists before extracting. + Assert.True(Directory.Exists(missingParentPath), $"{missingParentPath}' does not exist."); + if (!overwrite) + { + AssertFileModeEquals(missingParentPath, DefaultDirectoryMode); + } + + Assert.True(Directory.Exists(missingParentDirPath), $"{missingParentDirPath}' does not exist."); + AssertFileModeEquals(missingParentDirPath, TestPermission3); + + // Directory modes that are out-of-order are still applied. + Assert.True(Directory.Exists(outOfOrderDirPath), $"{outOfOrderDirPath}' does not exist."); + AssertFileModeEquals(outOfOrderDirPath, TestPermission4); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void UnixFileModes_RestrictiveParentDir(bool overwrite) + { + using TempDirectory source = new TempDirectory(); + using TempDirectory destination = new TempDirectory(); + + string archivePath = Path.Join(source.Path, "archive.tar"); + using FileStream archiveStream = File.Create(archivePath); + using (TarWriter writer = new TarWriter(archiveStream)) + { + PaxTarEntry dir = new PaxTarEntry(TarEntryType.Directory, "dir"); + dir.Mode = UnixFileMode.None; // Restrict permissions. + writer.WriteEntry(dir); + + PaxTarEntry file = new PaxTarEntry(TarEntryType.RegularFile, "dir/file"); + file.Mode = TestPermission1; + writer.WriteEntry(file); + } + + string dirPath = Path.Join(destination.Path, "dir"); + string filePath = Path.Join(dirPath, "file"); + + if (overwrite) + { + Directory.CreateDirectory(dirPath); + File.OpenWrite(filePath).Dispose(); + } + + TarFile.ExtractToDirectory(archivePath, destination.Path, overwriteFiles: overwrite); + + Assert.True(Directory.Exists(dirPath), $"{dirPath}' does not exist."); + AssertFileModeEquals(dirPath, UnixFileMode.None); + + // Set dir permissions so we can access file. + SetUnixFileMode(dirPath, UserAll); + + Assert.True(File.Exists(filePath), $"{filePath}' does not exist."); + AssertFileModeEquals(filePath, TestPermission1); + } } } diff --git a/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectoryAsync.File.Tests.cs b/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectoryAsync.File.Tests.cs index dfebe737493acc..01d2457018ce24 100644 --- a/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectoryAsync.File.Tests.cs +++ b/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectoryAsync.File.Tests.cs @@ -190,5 +190,95 @@ public async Task ExtractArchiveWithEntriesThatStartWithSlashDotPrefix_Async() } } } + + [Fact] + public async Task UnixFileModes_Async() + { + using TempDirectory source = new TempDirectory(); + using TempDirectory destination = new TempDirectory(); + + string archivePath = Path.Join(source.Path, "archive.tar"); + using FileStream archiveStream = File.Create(archivePath); + using (TarWriter writer = new TarWriter(archiveStream)) + { + PaxTarEntry dir = new PaxTarEntry(TarEntryType.Directory, "dir"); + dir.Mode = TestPermission1; + writer.WriteEntry(dir); + + PaxTarEntry file = new PaxTarEntry(TarEntryType.RegularFile, "file"); + file.Mode = TestPermission2; + writer.WriteEntry(file); + + // Archive has no entry for missing_parent. + PaxTarEntry missingParentDir = new PaxTarEntry(TarEntryType.Directory, "missing_parent/dir"); + missingParentDir.Mode = TestPermission3; + writer.WriteEntry(missingParentDir); + + // out_of_order_parent/file entry comes before out_of_order_parent entry. + PaxTarEntry outOfOrderFile = new PaxTarEntry(TarEntryType.RegularFile, "out_of_order_parent/file"); + writer.WriteEntry(outOfOrderFile); + + PaxTarEntry outOfOrderDir = new PaxTarEntry(TarEntryType.Directory, "out_of_order_parent"); + outOfOrderDir.Mode = TestPermission4; + writer.WriteEntry(outOfOrderDir); + } + + await TarFile.ExtractToDirectoryAsync(archivePath, destination.Path, overwriteFiles: false); + + string dirPath = Path.Join(destination.Path, "dir"); + Assert.True(Directory.Exists(dirPath), $"{dirPath}' does not exist."); + AssertFileModeEquals(dirPath, TestPermission1); + + string filePath = Path.Join(destination.Path, "file"); + Assert.True(File.Exists(filePath), $"{filePath}' does not exist."); + AssertFileModeEquals(filePath, TestPermission2); + + // Missing parents are created with DefaultDirectoryMode. + string missingParentPath = Path.Join(destination.Path, "missing_parent"); + Assert.True(Directory.Exists(missingParentPath), $"{missingParentPath}' does not exist."); + AssertFileModeEquals(missingParentPath, DefaultDirectoryMode); + + string missingParentDirPath = Path.Join(missingParentPath, "dir"); + Assert.True(Directory.Exists(missingParentDirPath), $"{missingParentDirPath}' does not exist."); + AssertFileModeEquals(missingParentDirPath, TestPermission3); + + // Directory modes that are out-of-order are still applied. + string outOfOrderDirPath = Path.Join(destination.Path, "out_of_order_parent"); + Assert.True(Directory.Exists(outOfOrderDirPath), $"{outOfOrderDirPath}' does not exist."); + AssertFileModeEquals(outOfOrderDirPath, TestPermission4); + } + + [Fact] + public async Task UnixFileModes_RestrictiveParentDir_Async() + { + using TempDirectory source = new TempDirectory(); + using TempDirectory destination = new TempDirectory(); + + string archivePath = Path.Join(source.Path, "archive.tar"); + using FileStream archiveStream = File.Create(archivePath); + using (TarWriter writer = new TarWriter(archiveStream)) + { + PaxTarEntry dir = new PaxTarEntry(TarEntryType.Directory, "dir"); + dir.Mode = UnixFileMode.None; // Restrict permissions. + writer.WriteEntry(dir); + + PaxTarEntry file = new PaxTarEntry(TarEntryType.RegularFile, "dir/file"); + file.Mode = TestPermission1; + writer.WriteEntry(file); + } + + await TarFile.ExtractToDirectoryAsync(archivePath, destination.Path, overwriteFiles: false); + + string dirPath = Path.Join(destination.Path, "dir"); + Assert.True(Directory.Exists(dirPath), $"{dirPath}' does not exist."); + AssertFileModeEquals(dirPath, UnixFileMode.None); + + // Set dir permissions so we can access file. + SetUnixFileMode(dirPath, UserAll); + + string filePath = Path.Join(dirPath, "file"); + Assert.True(File.Exists(filePath), $"{filePath}' does not exist."); + AssertFileModeEquals(filePath, TestPermission1); + } } } diff --git a/src/libraries/System.Formats.Tar/tests/TarTestsBase.cs b/src/libraries/System.Formats.Tar/tests/TarTestsBase.cs index 1c61d2e3ae1a7f..b167a980acd2b0 100644 --- a/src/libraries/System.Formats.Tar/tests/TarTestsBase.cs +++ b/src/libraries/System.Formats.Tar/tests/TarTestsBase.cs @@ -15,8 +15,18 @@ public abstract partial class TarTestsBase : FileCleanupTestBase // Default values are what a TarEntry created with its constructor will set protected const UnixFileMode DefaultFileMode = UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.GroupRead | UnixFileMode.OtherRead; // 644 in octal, internally used as default - private const UnixFileMode DefaultDirectoryMode = DefaultFileMode | UnixFileMode.UserExecute | UnixFileMode.GroupExecute | UnixFileMode.OtherExecute; // 755 in octal, internally used as default - protected const UnixFileMode DefaultWindowsMode = UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute | UnixFileMode.GroupRead | UnixFileMode.GroupWrite | UnixFileMode.GroupExecute | UnixFileMode.OtherRead | UnixFileMode.OtherWrite | UnixFileMode.UserExecute; // Creating archives in Windows always sets the mode to 777 + protected const UnixFileMode DefaultDirectoryMode = DefaultFileMode | UnixFileMode.UserExecute | UnixFileMode.GroupExecute | UnixFileMode.OtherExecute; // 755 in octal, internally used as default + + // Mode assumed for files and directories on Windows. + protected const UnixFileMode DefaultWindowsMode = DefaultFileMode | UnixFileMode.UserExecute | UnixFileMode.GroupExecute | UnixFileMode.OtherExecute; // 755 in octal, internally used as default + + // Permissions used by tests. User has all permissions to avoid permission errors. + protected const UnixFileMode UserAll = UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute; + protected const UnixFileMode TestPermission1 = UserAll | UnixFileMode.GroupRead; + protected const UnixFileMode TestPermission2 = UserAll | UnixFileMode.GroupExecute; + protected const UnixFileMode TestPermission3 = UserAll | UnixFileMode.OtherRead; + protected const UnixFileMode TestPermission4 = UserAll | UnixFileMode.OtherExecute; + protected const int DefaultGid = 0; protected const int DefaultUid = 0; protected const int DefaultDeviceMajor = 0; @@ -363,5 +373,76 @@ public static IEnumerable GetFormatsAndLinks() yield return new object[] { format, TarEntryType.HardLink }; } } + + public static IEnumerable GetFormatsAndFiles() + { + foreach (TarEntryType entryType in new[] { TarEntryType.V7RegularFile, TarEntryType.Directory }) + { + yield return new object[] { TarEntryFormat.V7, entryType }; + } + foreach (TarEntryFormat format in new[] { TarEntryFormat.Ustar, TarEntryFormat.Pax, TarEntryFormat.Gnu }) + { + foreach (TarEntryType entryType in new[] { TarEntryType.RegularFile, TarEntryType.Directory }) + { + yield return new object[] { format, entryType }; + } + } + } + + protected static void SetUnixFileMode(string path, UnixFileMode mode) + { + if (!PlatformDetection.IsWindows) + { + File.SetUnixFileMode(path, mode); + } + } + + protected static void AssertEntryModeFromFileSystemEquals(TarEntry entry, UnixFileMode fileMode) + { + if (PlatformDetection.IsWindows) + { + // Windows files don't have a mode. Set the expected value. + fileMode = DefaultWindowsMode; + } + Assert.Equal(fileMode, entry.Mode); + } + + protected static void AssertFileModeEquals(string path, UnixFileMode mode) + { + if (!PlatformDetection.IsWindows) + { + Assert.Equal(mode, File.GetUnixFileMode(path)); + } + } + + protected (string, string, TarEntry) Prepare_Extract(TempDirectory root, TarEntryFormat format, TarEntryType entryType) + { + string entryName = entryType.ToString(); + string destination = Path.Join(root.Path, entryName); + + TarEntry entry = InvokeTarEntryCreationConstructor(format, entryType, entryName); + Assert.NotNull(entry); + entry.Mode = TestPermission1; + + return (entryName, destination, entry); + } + + protected void Verify_Extract(string destination, TarEntry entry, TarEntryType entryType) + { + if (entryType is TarEntryType.RegularFile or TarEntryType.V7RegularFile) + { + Assert.True(File.Exists(destination)); + } + else if (entryType is TarEntryType.Directory) + { + Assert.True(Directory.Exists(destination)); + } + else + { + Assert.True(false, "Unchecked entry type."); + } + + AssertFileModeEquals(destination, TestPermission1); + } } } diff --git a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.File.Base.Windows.cs b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.File.Base.Windows.cs index 16fb8205685e7b..f37354a9ffef7e 100644 --- a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.File.Base.Windows.cs +++ b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.File.Base.Windows.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.IO; using Xunit; namespace System.Formats.Tar.Tests @@ -11,8 +12,8 @@ protected void VerifyPlatformSpecificMetadata(string filePath, TarEntry entry) { Assert.True(entry.ModificationTime > DateTimeOffset.UnixEpoch); - // Archives created in Windows always set mode to 777 - Assert.Equal(DefaultWindowsMode, entry.Mode); + UnixFileMode expectedMode = DefaultWindowsMode; + Assert.Equal(expectedMode, entry.Mode); Assert.Equal(DefaultUid, entry.Uid); Assert.Equal(DefaultGid, entry.Gid); From 199436b51fba6f0bee469c63ee42fa266d70222d Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Fri, 12 Aug 2022 10:21:32 -0500 Subject: [PATCH 11/56] EnableAOTAnalyzer for Microsoft.Extensions.DependencyInjection (#73829) Using MEDI is annotated as RequiresDynamicCode because it supports enumerable and generic servcies with ValueTypes. When using DI with ValuesTypes, the array and generic code might not be available. Contributes to #71654 --- ...icrosoft.Extensions.DependencyInjection.Abstractions.cs | 1 + ...soft.Extensions.DependencyInjection.Abstractions.csproj | 4 ++++ ...soft.Extensions.DependencyInjection.Abstractions.csproj | 5 +++++ .../src/ServiceProviderServiceExtensions.cs | 1 + .../ref/Microsoft.Extensions.DependencyInjection.cs | 4 ++++ .../ref/Microsoft.Extensions.DependencyInjection.csproj | 4 ++++ .../src/DefaultServiceProviderFactory.cs | 2 ++ .../src/Microsoft.Extensions.DependencyInjection.csproj | 7 ++++++- .../src/ServiceCollectionContainerBuilderExtensions.cs | 4 ++++ .../src/ServiceLookup/CallSiteFactory.cs | 1 + .../src/ServiceLookup/CallSiteRuntimeResolver.cs | 3 ++- .../src/ServiceLookup/CompiledServiceProviderEngine.cs | 2 ++ .../src/ServiceLookup/DynamicServiceProviderEngine.cs | 2 ++ .../ServiceLookup/Expressions/ExpressionResolverBuilder.cs | 2 ++ .../Expressions/ExpressionsServiceProviderEngine.cs | 2 ++ .../src/ServiceLookup/IEnumerableCallSite.cs | 2 ++ .../src/ServiceLookup/ILEmit/ILEmitResolverBuilder.cs | 2 ++ .../ServiceLookup/ILEmit/ILEmitServiceProviderEngine.cs | 3 +++ .../src/ServiceLookup/RuntimeServiceProviderEngine.cs | 2 ++ .../src/ServiceLookup/ServiceLookupHelpers.cs | 1 + .../src/ServiceProvider.cs | 6 ++++++ .../ref/Microsoft.Extensions.Logging.cs | 1 + .../ref/Microsoft.Extensions.Logging.csproj | 4 ++++ .../Microsoft.Extensions.Logging/src/LoggerFactory.cs | 1 + .../src/Microsoft.Extensions.Logging.csproj | 4 ++++ 25 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/ref/Microsoft.Extensions.DependencyInjection.Abstractions.cs b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/ref/Microsoft.Extensions.DependencyInjection.Abstractions.cs index 1b5dfaf70fb9af..0d7359b076e6d8 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/ref/Microsoft.Extensions.DependencyInjection.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/ref/Microsoft.Extensions.DependencyInjection.Abstractions.cs @@ -142,6 +142,7 @@ public static partial class ServiceProviderServiceExtensions public static Microsoft.Extensions.DependencyInjection.IServiceScope CreateScope(this System.IServiceProvider provider) { throw null; } public static object GetRequiredService(this System.IServiceProvider provider, System.Type serviceType) { throw null; } public static T GetRequiredService(this System.IServiceProvider provider) where T : notnull { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("The native code for an IEnumerable might not be available at runtime.")] public static System.Collections.Generic.IEnumerable GetServices(this System.IServiceProvider provider, System.Type serviceType) { throw null; } public static System.Collections.Generic.IEnumerable GetServices(this System.IServiceProvider provider) { throw null; } public static T? GetService(this System.IServiceProvider provider) { throw null; } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/ref/Microsoft.Extensions.DependencyInjection.Abstractions.csproj b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/ref/Microsoft.Extensions.DependencyInjection.Abstractions.csproj index d2f35679e26cb7..9742788ba462ff 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/ref/Microsoft.Extensions.DependencyInjection.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/ref/Microsoft.Extensions.DependencyInjection.Abstractions.csproj @@ -12,6 +12,10 @@ + + + + diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/Microsoft.Extensions.DependencyInjection.Abstractions.csproj b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/Microsoft.Extensions.DependencyInjection.Abstractions.csproj index 9afe192027166e..ec15fbd2398dfb 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/Microsoft.Extensions.DependencyInjection.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/Microsoft.Extensions.DependencyInjection.Abstractions.csproj @@ -4,6 +4,7 @@ $(NetCoreAppCurrent);$(NetCoreAppMinimum);netstandard2.1;netstandard2.0;$(NetFrameworkMinimum) true true + true Abstractions for dependency injection. Commonly Used Types: @@ -30,6 +31,10 @@ Microsoft.Extensions.DependencyInjection.IServiceCollection + + + + diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ServiceProviderServiceExtensions.cs b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ServiceProviderServiceExtensions.cs index dbeacc1d50a135..f3a15fde1a6ef8 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ServiceProviderServiceExtensions.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ServiceProviderServiceExtensions.cs @@ -84,6 +84,7 @@ public static IEnumerable GetServices(this IServiceProvider provider) /// The to retrieve the services from. /// An object that specifies the type of service object to get. /// An enumeration of services of type . + [RequiresDynamicCode("The native code for an IEnumerable might not be available at runtime.")] public static IEnumerable GetServices(this IServiceProvider provider, Type serviceType) { ThrowHelper.ThrowIfNull(provider); diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/ref/Microsoft.Extensions.DependencyInjection.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/ref/Microsoft.Extensions.DependencyInjection.cs index 0b85156d608859..92a159382c79f3 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/ref/Microsoft.Extensions.DependencyInjection.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/ref/Microsoft.Extensions.DependencyInjection.cs @@ -6,6 +6,7 @@ namespace Microsoft.Extensions.DependencyInjection { + [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("Using Microsoft.Extensions.DependencyInjection requires generating code dynamically at runtime. For example, when using enumerable and generic ValueType services.")] public partial class DefaultServiceProviderFactory : Microsoft.Extensions.DependencyInjection.IServiceProviderFactory { public DefaultServiceProviderFactory() { } @@ -15,8 +16,11 @@ public DefaultServiceProviderFactory(Microsoft.Extensions.DependencyInjection.Se } public static partial class ServiceCollectionContainerBuilderExtensions { + [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("Using Microsoft.Extensions.DependencyInjection requires generating code dynamically at runtime. For example, when using enumerable and generic ValueType services.")] public static Microsoft.Extensions.DependencyInjection.ServiceProvider BuildServiceProvider(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("Using Microsoft.Extensions.DependencyInjection requires generating code dynamically at runtime. For example, when using enumerable and generic ValueType services.")] public static Microsoft.Extensions.DependencyInjection.ServiceProvider BuildServiceProvider(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, Microsoft.Extensions.DependencyInjection.ServiceProviderOptions options) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("Using Microsoft.Extensions.DependencyInjection requires generating code dynamically at runtime. For example, when using enumerable and generic ValueType services.")] public static Microsoft.Extensions.DependencyInjection.ServiceProvider BuildServiceProvider(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, bool validateScopes) { throw null; } } public sealed partial class ServiceProvider : System.IAsyncDisposable, System.IDisposable, System.IServiceProvider diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/ref/Microsoft.Extensions.DependencyInjection.csproj b/src/libraries/Microsoft.Extensions.DependencyInjection/ref/Microsoft.Extensions.DependencyInjection.csproj index 7e9a119ec87578..d049b8e4e4fbe3 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/ref/Microsoft.Extensions.DependencyInjection.csproj +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/ref/Microsoft.Extensions.DependencyInjection.csproj @@ -12,6 +12,10 @@ + + + + diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/DefaultServiceProviderFactory.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/DefaultServiceProviderFactory.cs index 7fe53e3aa8551d..13863a4618a7dc 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/DefaultServiceProviderFactory.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/DefaultServiceProviderFactory.cs @@ -2,12 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.Extensions.DependencyInjection { /// /// Default implementation of . /// + [RequiresDynamicCode(ServiceProvider.RequiresDynamicCodeMessage)] public class DefaultServiceProviderFactory : IServiceProviderFactory { private readonly ServiceProviderOptions _options; diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/Microsoft.Extensions.DependencyInjection.csproj b/src/libraries/Microsoft.Extensions.DependencyInjection/src/Microsoft.Extensions.DependencyInjection.csproj index 2cbd54cbe94cae..7c63e352515e6b 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/Microsoft.Extensions.DependencyInjection.csproj +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/Microsoft.Extensions.DependencyInjection.csproj @@ -1,4 +1,4 @@ - + $(NetCoreAppCurrent);$(NetCoreAppMinimum);netstandard2.1;netstandard2.0;$(NetFrameworkMinimum) @@ -7,6 +7,7 @@ $(NoWarn);CP0001 true + true Default implementation of dependency injection for Microsoft.Extensions.DependencyInjection. @@ -35,6 +36,10 @@ + + + + diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceCollectionContainerBuilderExtensions.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceCollectionContainerBuilderExtensions.cs index c18f164cbb77bf..1cde6c4c690296 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceCollectionContainerBuilderExtensions.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceCollectionContainerBuilderExtensions.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using Microsoft.Extensions.DependencyInjection.ServiceLookup; @@ -18,6 +19,7 @@ public static class ServiceCollectionContainerBuilderExtensions /// The containing service descriptors. /// The . + [RequiresDynamicCode(ServiceProvider.RequiresDynamicCodeMessage)] public static ServiceProvider BuildServiceProvider(this IServiceCollection services) { return BuildServiceProvider(services, ServiceProviderOptions.Default); @@ -32,6 +34,7 @@ public static ServiceProvider BuildServiceProvider(this IServiceCollection servi /// true to perform check verifying that scoped services never gets resolved from root provider; otherwise false. /// /// The . + [RequiresDynamicCode(ServiceProvider.RequiresDynamicCodeMessage)] public static ServiceProvider BuildServiceProvider(this IServiceCollection services, bool validateScopes) { return services.BuildServiceProvider(new ServiceProviderOptions { ValidateScopes = validateScopes }); @@ -46,6 +49,7 @@ public static ServiceProvider BuildServiceProvider(this IServiceCollection servi /// Configures various service provider behaviors. /// /// The . + [RequiresDynamicCode(ServiceProvider.RequiresDynamicCodeMessage)] public static ServiceProvider BuildServiceProvider(this IServiceCollection services, ServiceProviderOptions options) { if (services is null) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs index 07052e920c67ea..22b3d4fd153ad6 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs @@ -11,6 +11,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { + [RequiresDynamicCode(ServiceProvider.RequiresDynamicCodeMessage)] internal sealed class CallSiteFactory : IServiceProviderIsService { private const int DefaultSlot = 0; diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteRuntimeResolver.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteRuntimeResolver.cs index 96fcd070295cf5..f41e18238bcf69 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteRuntimeResolver.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteRuntimeResolver.cs @@ -3,13 +3,14 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.ExceptionServices; using System.Threading; -using System.Diagnostics; namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { + [RequiresDynamicCode(ServiceProvider.RequiresDynamicCodeMessage)] internal sealed class CallSiteRuntimeResolver : CallSiteVisitor { public static CallSiteRuntimeResolver Instance { get; } = new(); diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CompiledServiceProviderEngine.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CompiledServiceProviderEngine.cs index eef3e1fe702141..2045140755d425 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CompiledServiceProviderEngine.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CompiledServiceProviderEngine.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { @@ -13,6 +14,7 @@ internal abstract class CompiledServiceProviderEngine : ServiceProviderEngine public ExpressionResolverBuilder ResolverBuilder { get; } #endif + [RequiresDynamicCode("Creates DynamicMethods")] public CompiledServiceProviderEngine(ServiceProvider provider) { ResolverBuilder = new(provider); diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs index 5c52efcc4f1815..45fe2c39ddf24b 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs @@ -3,10 +3,12 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Threading; namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { + [RequiresDynamicCode(ServiceProvider.RequiresDynamicCodeMessage)] internal sealed class DynamicServiceProviderEngine : CompiledServiceProviderEngine { private readonly ServiceProvider _serviceProvider; diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs index 7e0b988fa2b9e6..c8d4b7a6c57d8e 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs @@ -4,12 +4,14 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; using System.Reflection; namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { + [RequiresDynamicCode(ServiceProvider.RequiresDynamicCodeMessage)] internal sealed class ExpressionResolverBuilder : CallSiteVisitor { private static readonly ParameterExpression ScopeParameter = Expression.Parameter(typeof(ServiceProviderEngineScope)); diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionsServiceProviderEngine.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionsServiceProviderEngine.cs index 9de2fa8acbb09a..d179cb6f36a4e0 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionsServiceProviderEngine.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionsServiceProviderEngine.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { @@ -9,6 +10,7 @@ internal sealed class ExpressionsServiceProviderEngine : ServiceProviderEngine { private readonly ExpressionResolverBuilder _expressionResolverBuilder; + [RequiresDynamicCode(ServiceProvider.RequiresDynamicCodeMessage)] public ExpressionsServiceProviderEngine(ServiceProvider serviceProvider) { _expressionResolverBuilder = new ExpressionResolverBuilder(serviceProvider); diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/IEnumerableCallSite.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/IEnumerableCallSite.cs index 7e9b0f7f899541..5774a2c3212fca 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/IEnumerableCallSite.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/IEnumerableCallSite.cs @@ -3,9 +3,11 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { + [RequiresDynamicCode(ServiceProvider.RequiresDynamicCodeMessage)] internal sealed class IEnumerableCallSite : ServiceCallSite { internal Type ItemType { get; } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilder.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilder.cs index 8067a6676e9662..345dce793d0349 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilder.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilder.cs @@ -5,11 +5,13 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Reflection.Emit; namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { + [RequiresDynamicCode("Creates DynamicMethods")] internal sealed class ILEmitResolverBuilder : CallSiteVisitor { private static readonly MethodInfo ResolvedServicesGetter = typeof(ServiceProviderEngineScope).GetProperty( diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitServiceProviderEngine.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitServiceProviderEngine.cs index 5facb1a8ab95df..cd6d6866b616d3 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitServiceProviderEngine.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitServiceProviderEngine.cs @@ -2,12 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { internal sealed class ILEmitServiceProviderEngine : ServiceProviderEngine { private readonly ILEmitResolverBuilder _expressionResolverBuilder; + + [RequiresDynamicCode("Creates DynamicMethods")] public ILEmitServiceProviderEngine(ServiceProvider serviceProvider) { _expressionResolverBuilder = new ILEmitResolverBuilder(serviceProvider); diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/RuntimeServiceProviderEngine.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/RuntimeServiceProviderEngine.cs index 52c1afbafcbd09..b25e7f8216d0b3 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/RuntimeServiceProviderEngine.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/RuntimeServiceProviderEngine.cs @@ -2,9 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { + [RequiresDynamicCode(ServiceProvider.RequiresDynamicCodeMessage)] internal sealed class RuntimeServiceProviderEngine : ServiceProviderEngine { public static RuntimeServiceProviderEngine Instance { get; } = new RuntimeServiceProviderEngine(); diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceLookupHelpers.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceLookupHelpers.cs index d41f9cb7cec4fd..e33aadb591888b 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceLookupHelpers.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceLookupHelpers.cs @@ -34,6 +34,7 @@ internal static class ServiceLookupHelpers internal static readonly MethodInfo MonitorExitMethodInfo = typeof(Monitor) .GetMethod(nameof(Monitor.Exit), BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(object) }, null)!; + [RequiresDynamicCode("The code for an array of the specified type might not be available.")] [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2060:MakeGenericMethod", Justification = "Calling Array.Empty() is safe since the T doesn't have trimming annotations.")] internal static MethodInfo GetArrayEmptyMethodInfo(Type itemType) => diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs index f058108744c602..f66f36b3cf6ed2 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection.ServiceLookup; @@ -15,6 +16,8 @@ namespace Microsoft.Extensions.DependencyInjection /// public sealed class ServiceProvider : IServiceProvider, IDisposable, IAsyncDisposable { + internal const string RequiresDynamicCodeMessage = "Using Microsoft.Extensions.DependencyInjection requires generating code dynamically at runtime. For example, when using enumerable and generic ValueType services."; + private readonly CallSiteValidator? _callSiteValidator; private readonly Func> _createServiceAccessor; @@ -33,6 +36,7 @@ public sealed class ServiceProvider : IServiceProvider, IDisposable, IAsyncDispo internal static bool VerifyOpenGenericServiceTrimmability { get; } = AppContext.TryGetSwitch("Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability", out bool verifyOpenGenerics) ? verifyOpenGenerics : false; + [RequiresDynamicCode(RequiresDynamicCodeMessage)] internal ServiceProvider(ICollection serviceDescriptors, ServiceProviderOptions options) { // note that Root needs to be set before calling GetEngine(), because the engine may need to access Root @@ -151,6 +155,7 @@ private void ValidateService(ServiceDescriptor descriptor) } } + [RequiresDynamicCode(RequiresDynamicCodeMessage)] private Func CreateServiceAccessor(Type serviceType) { ServiceCallSite? callSite = CallSiteFactory.GetCallSite(serviceType, new CallSiteChain()); @@ -187,6 +192,7 @@ internal IServiceScope CreateScope() return new ServiceProviderEngineScope(this, isRootScope: false); } + [RequiresDynamicCode(RequiresDynamicCodeMessage)] private ServiceProviderEngine GetEngine() { ServiceProviderEngine engine; diff --git a/src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.cs b/src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.cs index 5b7fade6eaa438..dd65f6f0e04a60 100644 --- a/src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.cs +++ b/src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.cs @@ -61,6 +61,7 @@ public LoggerFactory(System.Collections.Generic.IEnumerable providers, Microsoft.Extensions.Options.IOptionsMonitor filterOption, Microsoft.Extensions.Options.IOptions? options = null, Microsoft.Extensions.Logging.IExternalScopeProvider? scopeProvider = null) { } public void AddProvider(Microsoft.Extensions.Logging.ILoggerProvider provider) { } protected virtual bool CheckDisposed() { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("LoggerFactory.Create uses Microsoft.Extensions.DependencyInjection, which may require generating code dynamically at runtime.")] public static Microsoft.Extensions.Logging.ILoggerFactory Create(System.Action configure) { throw null; } public Microsoft.Extensions.Logging.ILogger CreateLogger(string categoryName) { throw null; } public void Dispose() { } diff --git a/src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.csproj b/src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.csproj index 5ad076b2963813..3d305cb991b0dd 100644 --- a/src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.csproj @@ -7,6 +7,10 @@ + + + + diff --git a/src/libraries/Microsoft.Extensions.Logging/src/LoggerFactory.cs b/src/libraries/Microsoft.Extensions.Logging/src/LoggerFactory.cs index 315ea9115857aa..39651be0330078 100644 --- a/src/libraries/Microsoft.Extensions.Logging/src/LoggerFactory.cs +++ b/src/libraries/Microsoft.Extensions.Logging/src/LoggerFactory.cs @@ -103,6 +103,7 @@ public LoggerFactory(IEnumerable providers, IOptionsMonitor /// A delegate to configure the . /// The that was created. + [RequiresDynamicCode("LoggerFactory.Create uses Microsoft.Extensions.DependencyInjection, which may require generating code dynamically at runtime.")] public static ILoggerFactory Create(Action configure) { var serviceCollection = new ServiceCollection(); diff --git a/src/libraries/Microsoft.Extensions.Logging/src/Microsoft.Extensions.Logging.csproj b/src/libraries/Microsoft.Extensions.Logging/src/Microsoft.Extensions.Logging.csproj index 2bd514f088cd3a..e7cbf1e48f7d8a 100644 --- a/src/libraries/Microsoft.Extensions.Logging/src/Microsoft.Extensions.Logging.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/src/Microsoft.Extensions.Logging.csproj @@ -30,6 +30,10 @@ + + + + From 4fac43b00e3d5ba8159b5f877041f8e01462b4ba Mon Sep 17 00:00:00 2001 From: Brennan Date: Fri, 12 Aug 2022 08:28:26 -0700 Subject: [PATCH 12/56] [RateLimiting] Update TranslateKey to allow disposal of wrapped limiter (#73160) --- .../ref/System.Threading.RateLimiting.cs | 2 +- .../RateLimiting/PartitionedRateLimiter.T.cs | 5 +- .../RateLimiting/TranslatingLimiter.cs | 33 +++--- .../tests/PartitionedRateLimiterTests.cs | 100 ++++++++++++++++-- 4 files changed, 115 insertions(+), 25 deletions(-) diff --git a/src/libraries/System.Threading.RateLimiting/ref/System.Threading.RateLimiting.cs b/src/libraries/System.Threading.RateLimiting/ref/System.Threading.RateLimiting.cs index c8e968fa401e9b..1d48e8c717d126 100644 --- a/src/libraries/System.Threading.RateLimiting/ref/System.Threading.RateLimiting.cs +++ b/src/libraries/System.Threading.RateLimiting/ref/System.Threading.RateLimiting.cs @@ -79,7 +79,7 @@ protected virtual void Dispose(bool disposing) { } public System.Threading.Tasks.ValueTask DisposeAsync() { throw null; } protected virtual System.Threading.Tasks.ValueTask DisposeAsyncCore() { throw null; } public abstract int GetAvailablePermits(TResource resource); - public System.Threading.RateLimiting.PartitionedRateLimiter TranslateKey(System.Func keyAdapter) { throw null; } + public System.Threading.RateLimiting.PartitionedRateLimiter WithTranslatedKey(System.Func keyAdapter, bool leaveOpen) { throw null; } } public enum QueueProcessingOrder { diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/PartitionedRateLimiter.T.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/PartitionedRateLimiter.T.cs index 03c709bd16151a..fdc21eebeb61fb 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/PartitionedRateLimiter.T.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/PartitionedRateLimiter.T.cs @@ -127,15 +127,16 @@ public async ValueTask DisposeAsync() /// The type to translate into . /// The function to be called every time a is passed to /// PartitionedRateLimiter<TOuter>.Acquire(TOuter, int) or PartitionedRateLimiter<TOuter>.WaitAsync(TOuter, int, CancellationToken). + /// Specifies whether the returned will dispose the wrapped . /// or does not dispose the wrapped . /// A new PartitionedRateLimiter<TOuter> that translates /// to and calls the inner . - public PartitionedRateLimiter TranslateKey(Func keyAdapter) + public PartitionedRateLimiter WithTranslatedKey(Func keyAdapter, bool leaveOpen) { // REVIEW: Do we want to have an option to dispose the inner limiter? // Should the default be to dispose the inner limiter and have an option to not dispose it? // See Stream wrappers like SslStream for prior-art - return new TranslatingLimiter(this, keyAdapter); + return new TranslatingLimiter(this, keyAdapter, leaveOpen); } } } diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/TranslatingLimiter.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/TranslatingLimiter.cs index f2b80147fc1262..b5a59fe079bab7 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/TranslatingLimiter.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/TranslatingLimiter.cs @@ -1,10 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; namespace System.Threading.RateLimiting @@ -13,13 +9,15 @@ internal sealed class TranslatingLimiter : PartitionedRateLim { private readonly PartitionedRateLimiter _innerRateLimiter; private readonly Func _keyAdapter; + private readonly bool _disposeInnerLimiter; - private bool _disposed; + private int _disposed; - public TranslatingLimiter(PartitionedRateLimiter inner, Func keyAdapter) + public TranslatingLimiter(PartitionedRateLimiter inner, Func keyAdapter, bool leaveOpen) { _innerRateLimiter = inner; _keyAdapter = keyAdapter; + _disposeInnerLimiter = !leaveOpen; } public override int GetAvailablePermits(TResource resource) @@ -45,21 +43,32 @@ protected override ValueTask AcquireAsyncCore(TResource resource protected override void Dispose(bool disposing) { - _disposed = true; - base.Dispose(disposing); + if (Interlocked.CompareExchange(ref _disposed, 1, 0) == 0) + { + if (_disposeInnerLimiter) + { + _innerRateLimiter.Dispose(); + } + } } protected override ValueTask DisposeAsyncCore() { - _disposed = true; - return base.DisposeAsyncCore(); + if (Interlocked.CompareExchange(ref _disposed, 1, 0) == 0) + { + if (_disposeInnerLimiter) + { + return _innerRateLimiter.DisposeAsync(); + } + } + return default(ValueTask); } private void ThrowIfDispose() { - if (_disposed) + if (_disposed == 1) { - throw new ObjectDisposedException(nameof(PartitionedRateLimiter)); + throw new ObjectDisposedException(nameof(PartitionedRateLimiter)); } } } diff --git a/src/libraries/System.Threading.RateLimiting/tests/PartitionedRateLimiterTests.cs b/src/libraries/System.Threading.RateLimiting/tests/PartitionedRateLimiterTests.cs index 88710d19ef75e8..4ba9b60ae75f2e 100644 --- a/src/libraries/System.Threading.RateLimiting/tests/PartitionedRateLimiterTests.cs +++ b/src/libraries/System.Threading.RateLimiting/tests/PartitionedRateLimiterTests.cs @@ -662,11 +662,11 @@ public void Translate_AcquirePassesThroughToInnerLimiter() }); var translateCallCount = 0; - var translateLimiter = limiter.TranslateKey(i => + var translateLimiter = limiter.WithTranslatedKey(i => { translateCallCount++; return i.ToString(); - }); + }, leaveOpen: true); var lease = translateLimiter.AttemptAcquire(1); Assert.True(lease.IsAcquired); @@ -711,11 +711,11 @@ public async Task Translate_WaitAsyncPassesThroughToInnerLimiter() }); var translateCallCount = 0; - var translateLimiter = limiter.TranslateKey(i => + var translateLimiter = limiter.WithTranslatedKey(i => { translateCallCount++; return i.ToString(); - }); + }, leaveOpen: true); var lease = await translateLimiter.AcquireAsync(1); Assert.True(lease.IsAcquired); @@ -760,11 +760,11 @@ public void Translate_GetAvailablePermitsPassesThroughToInnerLimiter() }); var translateCallCount = 0; - var translateLimiter = limiter.TranslateKey(i => + var translateLimiter = limiter.WithTranslatedKey(i => { translateCallCount++; return i.ToString(); - }); + }, leaveOpen: true); Assert.Equal(1, translateLimiter.GetAvailablePermits(1)); Assert.Equal(1, translateCallCount); @@ -818,11 +818,11 @@ public void Translate_DisposeDoesNotDisposeInnerLimiter() }); var translateCallCount = 0; - var translateLimiter = limiter.TranslateKey(i => + var translateLimiter = limiter.WithTranslatedKey(i => { translateCallCount++; return i.ToString(); - }); + }, leaveOpen: true); translateLimiter.Dispose(); @@ -832,6 +832,46 @@ public void Translate_DisposeDoesNotDisposeInnerLimiter() Assert.Throws(() => translateLimiter.AttemptAcquire(1)); } + [Fact] + public void Translate_DisposeDoesDisposeInnerLimiter() + { + using var limiter = PartitionedRateLimiter.Create(resource => + { + if (resource == "1") + { + return RateLimitPartition.GetConcurrencyLimiter(1, + _ => new ConcurrencyLimiterOptions + { + PermitLimit = 1, + QueueProcessingOrder = QueueProcessingOrder.NewestFirst, + QueueLimit = 1 + }); + } + else + { + return RateLimitPartition.GetConcurrencyLimiter(1, + _ => new ConcurrencyLimiterOptions + { + PermitLimit = 1, + QueueProcessingOrder = QueueProcessingOrder.NewestFirst, + QueueLimit = 1 + }); + } + }); + + var translateCallCount = 0; + var translateLimiter = limiter.WithTranslatedKey(i => + { + translateCallCount++; + return i.ToString(); + }, leaveOpen: false); + + translateLimiter.Dispose(); + + Assert.Throws(() => limiter.AttemptAcquire("1")); + Assert.Throws(() => translateLimiter.AttemptAcquire(1)); + } + [Fact] public async Task Translate_DisposeAsyncDoesNotDisposeInnerLimiter() { @@ -860,11 +900,11 @@ public async Task Translate_DisposeAsyncDoesNotDisposeInnerLimiter() }); var translateCallCount = 0; - var translateLimiter = limiter.TranslateKey(i => + var translateLimiter = limiter.WithTranslatedKey(i => { translateCallCount++; return i.ToString(); - }); + }, leaveOpen: true); await translateLimiter.DisposeAsync(); @@ -873,5 +913,45 @@ public async Task Translate_DisposeAsyncDoesNotDisposeInnerLimiter() Assert.Throws(() => translateLimiter.AttemptAcquire(1)); } + + [Fact] + public async Task Translate_DisposeAsyncDoesDisposeInnerLimiter() + { + using var limiter = PartitionedRateLimiter.Create(resource => + { + if (resource == "1") + { + return RateLimitPartition.GetConcurrencyLimiter(1, + _ => new ConcurrencyLimiterOptions + { + PermitLimit = 1, + QueueProcessingOrder = QueueProcessingOrder.NewestFirst, + QueueLimit = 1 + }); + } + else + { + return RateLimitPartition.GetConcurrencyLimiter(1, + _ => new ConcurrencyLimiterOptions + { + PermitLimit = 1, + QueueProcessingOrder = QueueProcessingOrder.NewestFirst, + QueueLimit = 1 + }); + } + }); + + var translateCallCount = 0; + var translateLimiter = limiter.WithTranslatedKey(i => + { + translateCallCount++; + return i.ToString(); + }, leaveOpen: false); + + await translateLimiter.DisposeAsync(); + + Assert.Throws(() => limiter.AttemptAcquire("1")); + Assert.Throws(() => translateLimiter.AttemptAcquire(1)); + } } } From f4e8005a1e52a85af146b34a922ffe2a7d52bfb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Fri, 12 Aug 2022 17:39:05 +0200 Subject: [PATCH 13/56] [wasm] Log as warning in interop generator when method signature is not supported (#71477) Introduce WASM0001 warning code for not supported pinvoke and icall signatures. --- .../WasmAppBuilder/IcallTableGenerator.cs | 14 +++- .../ManagedToNativeGenerator.cs | 4 +- .../WasmAppBuilder/PInvokeTableGenerator.cs | 15 +++-- .../PInvokeTableGeneratorTests.cs | 65 +++++++++++++++---- 4 files changed, 75 insertions(+), 23 deletions(-) diff --git a/src/tasks/WasmAppBuilder/IcallTableGenerator.cs b/src/tasks/WasmAppBuilder/IcallTableGenerator.cs index 1b689ad196a334..a1620338bcbe7a 100644 --- a/src/tasks/WasmAppBuilder/IcallTableGenerator.cs +++ b/src/tasks/WasmAppBuilder/IcallTableGenerator.cs @@ -32,7 +32,7 @@ internal sealed class IcallTableGenerator // The runtime icall table should be generated using // mono --print-icall-table // - public IEnumerable GenIcallTable(string? runtimeIcallTableFile, string[] assemblies, string? outputPath) + public IEnumerable Generate(string? runtimeIcallTableFile, string[] assemblies, string? outputPath) { _icalls.Clear(); _signatures.Clear(); @@ -146,7 +146,15 @@ private void ProcessType(Type type) if ((method.GetMethodImplementationFlags() & MethodImplAttributes.InternalCall) == 0) continue; - AddSignature(type, method); + try + { + AddSignature(type, method); + } + catch (Exception ex) when (ex is not LogAsErrorException) + { + Log.LogWarning(null, "WASM0001", "", "", 0, 0, 0, 0, $"Could not get icall, or callbacks for method '{method.Name}' because '{ex.Message}'"); + continue; + } var className = method.DeclaringType!.FullName!; if (!_runtimeIcalls.ContainsKey(className)) @@ -214,7 +222,7 @@ void AddSignature(Type type, MethodInfo method) throw new LogAsErrorException($"Unsupported parameter type in method '{type.FullName}.{method.Name}'"); } - Log.LogMessage(MessageImportance.Normal, $"[icall] Adding signature {signature} for method '{type.FullName}.{method.Name}'"); + Log.LogMessage(MessageImportance.Low, $"Adding icall signature {signature} for method '{type.FullName}.{method.Name}'"); _signatures.Add(signature); } } diff --git a/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs b/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs index 26c174e297f638..df48afaa52f84a 100644 --- a/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs +++ b/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs @@ -69,8 +69,8 @@ private void ExecuteInternal() var icall = new IcallTableGenerator(Log); IEnumerable cookies = Enumerable.Concat( - pinvoke.GenPInvokeTable(PInvokeModules, Assemblies!, PInvokeOutputPath!), - icall.GenIcallTable(RuntimeIcallTableFile, Assemblies!, IcallOutputPath) + pinvoke.Generate(PInvokeModules, Assemblies!, PInvokeOutputPath!), + icall.Generate(RuntimeIcallTableFile, Assemblies!, IcallOutputPath) ); var m2n = new InterpToNativeGenerator(Log); diff --git a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs index 4f2631a20cd721..46073228f6777d 100644 --- a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs +++ b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs @@ -19,7 +19,7 @@ internal sealed class PInvokeTableGenerator public PInvokeTableGenerator(TaskLoggingHelper log) => Log = log; - public IEnumerable GenPInvokeTable(string[] pinvokeModules, string[] assemblies, string outputPath) + public IEnumerable Generate(string[] pinvokeModules, string[] assemblies, string outputPath) { var modules = new Dictionary(); foreach (var module in pinvokeModules) @@ -69,10 +69,9 @@ private void CollectPInvokes(List pinvokes, List callb { CollectPInvokesForMethod(method); } - catch (Exception ex) + catch (Exception ex) when (ex is not LogAsErrorException) { - Log.LogMessage(MessageImportance.Low, $"Could not get pinvoke, or callbacks for method {method.Name}: {ex}"); - continue; + Log.LogWarning(null, "WASM0001", "", "", 0, 0, 0, 0, $"Could not get pinvoke, or callbacks for method '{method.Name}' because '{ex.Message}'"); } } @@ -88,10 +87,10 @@ void CollectPInvokesForMethod(MethodInfo method) string? signature = SignatureMapper.MethodToSignature(method); if (signature == null) { - throw new LogAsErrorException($"Unsupported parameter type in method '{type.FullName}.{method.Name}'"); + throw new NotSupportedException($"Unsupported parameter type in method '{type.FullName}.{method.Name}'"); } - Log.LogMessage(MessageImportance.Normal, $"[pinvoke] Adding signature {signature} for method '{type.FullName}.{method.Name}'"); + Log.LogMessage(MessageImportance.Low, $"Adding pinvoke signature {signature} for method '{type.FullName}.{method.Name}'"); signatures.Add(signature); } @@ -101,6 +100,7 @@ void CollectPInvokesForMethod(MethodInfo method) { if (cattr.AttributeType.FullName == "System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute" || cattr.AttributeType.Name == "MonoPInvokeCallbackAttribute") + callbacks.Add(new PInvokeCallback(method)); } catch @@ -301,7 +301,8 @@ private static bool TryIsMethodGetParametersUnsupported(MethodInfo method, [NotN if (TryIsMethodGetParametersUnsupported(pinvoke.Method, out string? reason)) { - Log.LogWarning($"Skipping the following DllImport because '{reason}'. {Environment.NewLine} {pinvoke.Method}"); + Log.LogWarning(null, "WASM0001", "", "", 0, 0, 0, 0, $"Skipping pinvoke '{pinvoke.Method}' because '{reason}'."); + pinvoke.Skip = true; return null; } diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs index 79c8d349802a53..791c211dfdf111 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; +using System.Reflection; using Xunit; using Xunit.Abstractions; @@ -58,29 +59,32 @@ public static int Main(string[] args) [BuildAndRun(host: RunHost.Chrome)] public void DllImportWithFunctionPointersCompilesWithWarning(BuildArgs buildArgs, RunHost host, string id) { - string code = @" + string code = + """ using System; using System.Runtime.InteropServices; public class Test { public static int Main() { - Console.WriteLine($""Main running""); + Console.WriteLine("Main running"); return 42; } - [DllImport(""variadic"", EntryPoint=""sum"")] + [DllImport("variadic", EntryPoint="sum")] public unsafe static extern int using_sum_one(delegate* unmanaged callback); - [DllImport(""variadic"", EntryPoint=""sum"")] + [DllImport("variadic", EntryPoint="sum")] public static extern int sum_one(int a, int b); - }"; + } + """; (buildArgs, string output) = BuildForVariadicFunctionTests(code, buildArgs with { ProjectName = $"fnptr_{buildArgs.Config}_{id}" }, id); - Assert.Matches("warning.*Skipping.*because.*function pointer", output); - Assert.Matches("warning.*using_sum_one", output); + + Assert.Matches("warning\\sWASM0001.*Could\\snot\\sget\\spinvoke.*Parsing\\sfunction\\spointer\\stypes", output); + Assert.Matches("warning\\sWASM0001.*Skipping.*using_sum_one.*because.*function\\spointer", output); output = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id); Assert.Contains("Main running", output); @@ -108,8 +112,44 @@ public static int Main() (buildArgs, string output) = BuildForVariadicFunctionTests(code, buildArgs with { ProjectName = $"fnptr_variadic_{buildArgs.Config}_{id}" }, id); - Assert.Matches("warning.*Skipping.*because.*function pointer", output); - Assert.Matches("warning.*using_sum_one", output); + + Assert.Matches("warning\\sWASM0001.*Could\\snot\\sget\\spinvoke.*Parsing\\sfunction\\spointer\\stypes", output); + Assert.Matches("warning\\sWASM0001.*Skipping.*using_sum_one.*because.*function\\spointer", output); + + output = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id); + Assert.Contains("Main running", output); + } + + [Theory] + [BuildAndRun(host: RunHost.Chrome)] + public void DllImportWithFunctionPointers_WarningsAsMessages(BuildArgs buildArgs, RunHost host, string id) + { + string code = + """ + using System; + using System.Runtime.InteropServices; + public class Test + { + public static int Main() + { + Console.WriteLine("Main running"); + return 42; + } + + [DllImport("someting")] + public unsafe static extern void SomeFunction1(delegate* unmanaged callback); + } + """; + + (buildArgs, string output) = BuildForVariadicFunctionTests( + code, + buildArgs with { ProjectName = $"fnptr_{buildArgs.Config}_{id}" }, + id, + verbosity: "normal", + extraProperties: "$(MSBuildWarningsAsMessage);WASM0001" + ); + + Assert.DoesNotContain("warning WASM0001", output); output = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id); Assert.Contains("Main running", output); @@ -166,12 +206,14 @@ public void BuildNativeInNonEnglishCulture(BuildArgs buildArgs, string culture, Assert.Contains("square: 25", output); } - private (BuildArgs, string) BuildForVariadicFunctionTests(string programText, BuildArgs buildArgs, string id) + private (BuildArgs, string) BuildForVariadicFunctionTests(string programText, BuildArgs buildArgs, string id, string? verbosity = null, string extraProperties = "") { + extraProperties += "true<_WasmDevel>true"; + string filename = "variadic.o"; buildArgs = ExpandBuildArgs(buildArgs, extraItems: $"", - extraProperties: "true<_WasmDevel>true"); + extraProperties: extraProperties); (_, string output) = BuildProject(buildArgs, id: id, @@ -183,6 +225,7 @@ public void BuildNativeInNonEnglishCulture(BuildArgs buildArgs, string culture, Path.Combine(_projectDir!, filename)); }, Publish: buildArgs.AOT, + Verbosity: verbosity, DotnetWasmFromRuntimePack: false)); return (buildArgs, output); From 77d95331e5c34a5a676f9e3396ce7351651cfc8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Fri, 12 Aug 2022 17:51:13 +0200 Subject: [PATCH 14/56] Fix build error in mini-ppc.h (#73845) Looks like this got inadvertently broken by https://github.com/dotnet/runtime/pull/65723 --- src/mono/mono/mini/mini-ppc.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mono/mono/mini/mini-ppc.h b/src/mono/mono/mini/mini-ppc.h index 281eee97b5bb1b..688ca1f7992624 100644 --- a/src/mono/mono/mini/mini-ppc.h +++ b/src/mono/mono/mini/mini-ppc.h @@ -118,8 +118,7 @@ typedef struct MonoCompileArch { #else #define MONO_ARCH_CALLEE_FREGS (0xff << ppc_f1) #endif -#define MONO_ARCH_CALLEE_SAVED_FREGS (~(MONO_ARCH_CALLEE_FRE -GS | 1)) +#define MONO_ARCH_CALLEE_SAVED_FREGS (~(MONO_ARCH_CALLEE_FREGS | 1)) #ifdef TARGET_POWERPC64 #define MONO_ARCH_INST_FIXED_REG(desc) (((desc) == 'a')? ppc_r3: \ From dd05959f93a9415643d51b99814945ced936b6ad Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Fri, 12 Aug 2022 08:52:12 -0700 Subject: [PATCH 15/56] Ensure partial Vector256 acceleration is still possible on AVX only hardware (#73720) * Ensure that Vector256 APIs exposed in .NET Core 3.1 remain accelerated in .NET 7 * Ensure new Vector256 APIs that are trivially limited to Avx only are accelerated --- src/coreclr/jit/hwintrinsic.cpp | 4 +- src/coreclr/jit/hwintrinsic.h | 14 +++- src/coreclr/jit/hwintrinsiclistxarch.h | 92 +++++++++++++------------- 3 files changed, 61 insertions(+), 49 deletions(-) diff --git a/src/coreclr/jit/hwintrinsic.cpp b/src/coreclr/jit/hwintrinsic.cpp index 58a06532ae0ea7..b8e74010ca8f92 100644 --- a/src/coreclr/jit/hwintrinsic.cpp +++ b/src/coreclr/jit/hwintrinsic.cpp @@ -383,8 +383,8 @@ NamedIntrinsic HWIntrinsicInfo::lookupId(Compiler* comp, NamedIntrinsic ni = intrinsicInfo.id; #if defined(TARGET_XARCH) - // on AVX1-only CPUs we only support NI_Vector256_Create intrinsic in Vector256 - if (isLimitedVector256Isa && (ni != NI_Vector256_Create)) + // on AVX1-only CPUs we only support a subset of intrinsics in Vector256 + if (isLimitedVector256Isa && !AvxOnlyCompatible(ni)) { return NI_Illegal; } diff --git a/src/coreclr/jit/hwintrinsic.h b/src/coreclr/jit/hwintrinsic.h index e15f3ae6029ab0..8a82605991e165 100644 --- a/src/coreclr/jit/hwintrinsic.h +++ b/src/coreclr/jit/hwintrinsic.h @@ -147,7 +147,11 @@ enum HWIntrinsicFlag : unsigned int // Returns Per-Element Mask // the intrinsic returns a vector containing elements that are either "all bits set" or "all bits clear" // this output can be used as a per-element mask - HW_Flag_ReturnsPerElementMask = 0x20000 + HW_Flag_ReturnsPerElementMask = 0x20000, + + // AvxOnlyCompatible + // the intrinsic can be used on hardware with AVX but not AVX2 support + HW_Flag_AvxOnlyCompatible = 0x40000, #elif defined(TARGET_ARM64) // The intrinsic has an immediate operand @@ -658,6 +662,14 @@ struct HWIntrinsicInfo #endif } +#if defined(TARGET_XARCH) + static bool AvxOnlyCompatible(NamedIntrinsic id) + { + HWIntrinsicFlag flags = lookupFlags(id); + return (flags & HW_Flag_AvxOnlyCompatible) != 0; + } +#endif + static bool BaseTypeFromFirstArg(NamedIntrinsic id) { HWIntrinsicFlag flags = lookupFlags(id); diff --git a/src/coreclr/jit/hwintrinsiclistxarch.h b/src/coreclr/jit/hwintrinsiclistxarch.h index 8b5f283f02042f..7068e39bd1b560 100644 --- a/src/coreclr/jit/hwintrinsiclistxarch.h +++ b/src/coreclr/jit/hwintrinsiclistxarch.h @@ -129,44 +129,44 @@ HARDWARE_INTRINSIC(Vector128, Xor, // Vector256 Intrinsics HARDWARE_INTRINSIC(Vector256, Abs, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector256, Add, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, AndNot, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, As, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, AsByte, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, AsDouble, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, AsInt16, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, AsInt32, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, AsInt64, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, AsSByte, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, AsSingle, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, AsUInt16, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, AsUInt32, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, AsUInt64, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, AsVector, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, AsVector256, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, BitwiseAnd, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, BitwiseOr, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, Ceiling, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, ConditionalSelect, 32, 3, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) +HARDWARE_INTRINSIC(Vector256, AndNot, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, As, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, AsByte, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, AsDouble, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, AsInt16, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, AsInt32, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, AsInt64, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, AsSByte, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, AsSingle, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, AsUInt16, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, AsUInt32, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, AsUInt64, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, AsVector, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, AsVector256, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, BitwiseAnd, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, BitwiseOr, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, Ceiling, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, ConditionalSelect, 32, 3, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, ConvertToDouble, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector256, ConvertToInt32, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(Vector256, ConvertToInt32, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, ConvertToInt64, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector256, ConvertToSingle, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(Vector256, ConvertToSingle, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, ConvertToUInt32, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector256, ConvertToUInt64, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector256, Create, 32, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, CreateScalarUnsafe, 32, 1, {INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movss, INS_movsdsse2}, HW_Category_SIMDScalar, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_NoRMWSemantics) +HARDWARE_INTRINSIC(Vector256, Create, 32, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, CreateScalarUnsafe, 32, 1, {INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movss, INS_movsdsse2}, HW_Category_SIMDScalar, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_NoRMWSemantics|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, Divide, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector256, Dot, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector256, Equals, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_ReturnsPerElementMask) HARDWARE_INTRINSIC(Vector256, EqualsAll, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector256, EqualsAny, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector256, ExtractMostSignificantBits, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, Floor, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, get_AllBitsSet, 32, 0, {INS_pcmpeqd, INS_pcmpeqd, INS_pcmpeqd, INS_pcmpeqd, INS_pcmpeqd, INS_pcmpeqd, INS_pcmpeqd, INS_pcmpeqd, INS_cmpps, INS_cmpps}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_ReturnsPerElementMask) -HARDWARE_INTRINSIC(Vector256, get_Count, 32, 0, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, get_Zero, 32, 0, {INS_xorps, INS_xorps, INS_xorps, INS_xorps, INS_xorps, INS_xorps, INS_xorps, INS_xorps, INS_xorps, INS_xorps}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_ReturnsPerElementMask) -HARDWARE_INTRINSIC(Vector256, GetElement, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector256, GetLower, 32, 1, {INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movups, INS_movupd}, HW_Category_SimpleSIMD, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoRMWSemantics) +HARDWARE_INTRINSIC(Vector256, Floor, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, get_AllBitsSet, 32, 0, {INS_pcmpeqd, INS_pcmpeqd, INS_pcmpeqd, INS_pcmpeqd, INS_pcmpeqd, INS_pcmpeqd, INS_pcmpeqd, INS_pcmpeqd, INS_cmpps, INS_cmpps}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_ReturnsPerElementMask|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, get_Count, 32, 0, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, get_Zero, 32, 0, {INS_xorps, INS_xorps, INS_xorps, INS_xorps, INS_xorps, INS_xorps, INS_xorps, INS_xorps, INS_xorps, INS_xorps}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_ReturnsPerElementMask|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, GetElement, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, GetLower, 32, 1, {INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movups, INS_movupd}, HW_Category_SimpleSIMD, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoRMWSemantics|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, GreaterThan, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_ReturnsPerElementMask) HARDWARE_INTRINSIC(Vector256, GreaterThanAll, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector256, GreaterThanAny, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) @@ -179,44 +179,44 @@ HARDWARE_INTRINSIC(Vector256, LessThanAny, HARDWARE_INTRINSIC(Vector256, LessThanOrEqual, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_ReturnsPerElementMask) HARDWARE_INTRINSIC(Vector256, LessThanOrEqualAll, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector256, LessThanOrEqualAny, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, Load, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, LoadAligned, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, LoadAlignedNonTemporal, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, LoadUnsafe, 32, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) +HARDWARE_INTRINSIC(Vector256, Load, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, LoadAligned, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, LoadAlignedNonTemporal, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, LoadUnsafe, 32, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, Max, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector256, Min, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector256, Multiply, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector256, Narrow, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector256, Negate, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, OnesComplement, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) +HARDWARE_INTRINSIC(Vector256, OnesComplement, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, op_Addition, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, op_BitwiseAnd, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_Commutative) -HARDWARE_INTRINSIC(Vector256, op_BitwiseOr, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_Commutative) +HARDWARE_INTRINSIC(Vector256, op_BitwiseAnd, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_Commutative|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, op_BitwiseOr, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_Commutative|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, op_Division, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector256, op_Equality, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_Commutative) -HARDWARE_INTRINSIC(Vector256, op_ExclusiveOr, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) +HARDWARE_INTRINSIC(Vector256, op_ExclusiveOr, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, op_Inequality, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_Commutative) HARDWARE_INTRINSIC(Vector256, op_Multiply, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, op_OnesComplement, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) +HARDWARE_INTRINSIC(Vector256, op_OnesComplement, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, op_Subtraction, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector256, op_UnaryNegation, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, op_UnaryPlus, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) +HARDWARE_INTRINSIC(Vector256, op_UnaryPlus, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, ShiftLeft, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector256, ShiftRightArithmetic, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector256, ShiftRightLogical, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector256, Shuffle, 32, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, Sqrt, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, Store, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, StoreAligned, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, StoreAlignedNonTemporal, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, StoreUnsafe, 32, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) +HARDWARE_INTRINSIC(Vector256, Sqrt, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, Store, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, StoreAligned, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, StoreAlignedNonTemporal, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, StoreUnsafe, 32, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, Subtract, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector256, Sum, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector256, ToScalar, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movss, INS_movsdsse2}, HW_Category_SimpleSIMD, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoRMWSemantics) +HARDWARE_INTRINSIC(Vector256, ToScalar, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movss, INS_movsdsse2}, HW_Category_SimpleSIMD, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoRMWSemantics|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, WidenLower, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector256, WidenUpper, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector256, WithElement, 32, 3, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoContainment|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector256, Xor, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) +HARDWARE_INTRINSIC(Vector256, WithElement, 32, 3, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoContainment|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, Xor, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_AvxOnlyCompatible) // *************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** // ISA Function name SIMD size NumArg Instructions Category Flags From 21dc0040b5d6cd52501d9c502fb3ddbc23db97d7 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Fri, 12 Aug 2022 18:54:33 +0300 Subject: [PATCH 16/56] [mono][jit] Don't inflate Mono.ValueTuple with byrefs (#73689) Mono.ValueTuple's must have the same layout as the original vtype. Use normal IntPtr for ref fields. This also allows sharing of more signatures. Before this change the behavior was dubious when sharing valuetypes with ref fields (like Span). The value tuple class was inflated with byref types and it ended up having byref fields, even though it is not byreflike, resulting in failure during class loading. --- src/mono/mono/mini/mini-generic-sharing.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/mono/mono/mini/mini-generic-sharing.c b/src/mono/mono/mini/mini-generic-sharing.c index 8a4ba64a79ea91..50fe18a1c034eb 100644 --- a/src/mono/mono/mini/mini-generic-sharing.c +++ b/src/mono/mono/mini/mini-generic-sharing.c @@ -1246,6 +1246,16 @@ get_wrapper_shared_vtype (MonoType *t) } g_assert (tuple_class); + for (int i = 0; i < findex; i++) { + if (m_type_is_byref (args [i])) { +#if TARGET_SIZEOF_VOID_P == 8 + args [i] = m_class_get_byval_arg (mono_defaults.int_class); +#else + args [i] = m_class_get_byval_arg (mono_defaults.int32_class); +#endif + } + } + memset (&ctx, 0, sizeof (ctx)); ctx.class_inst = mono_metadata_get_generic_inst (findex, args); From 3db2ebfcfa2b5c7074a7e513a56a39f07f6ce6c0 Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Fri, 12 Aug 2022 13:28:46 -0300 Subject: [PATCH 17/56] [debugger] First draft of managed debugger on wasi (#67272) * first version of debug wasi * remove comment * fixing readme * Fix compilation error * fix compilation error * fix compilation * addressing steve comment * Stepping working * fix debugging aspnetcore app * wrong merge * addressing @viniciusjarina comment * addressing steve comments * work for remote debugging * make it work on windows * Update mini-wasi-debugger.c fix indentation * Update mini-wasi-debugger.c * Update mini-wasi-debugger.c * Trying to fix compilation * Update src/mono/mono/component/mini-wasi-debugger.c Co-authored-by: Ankit Jain * Update src/mono/mono/component/mini-wasi-debugger.c Co-authored-by: Ankit Jain * Update src/mono/mono/component/mini-wasi-debugger.c Co-authored-by: Ankit Jain * Update src/mono/mono/component/debugger-agent.c Co-authored-by: Ankit Jain * Update src/mono/mono/component/debugger-agent.c Co-authored-by: Ankit Jain * Update src/mono/mono/mini/interp/interp.c Co-authored-by: Ankit Jain * Update src/mono/mono/mini/interp/interp.c Co-authored-by: Ankit Jain * Update src/mono/wasi/Makefile Co-authored-by: Ankit Jain * Update src/mono/wasi/mono-wasi-driver/driver.c Co-authored-by: Ankit Jain * Update src/mono/mono/component/debugger-agent.c Co-authored-by: Ankit Jain * Addressing @radical comments * fix compilation errors * fixing readme * debugger working again * addressing @BrzVlad comments * Addressing @radical comments * Fix merge * Fix merge * Update diagnostics_server.c * Treat root directory as always existing, even if WASI isn't granting filesystem access. Needed for ASP.NET Core. * Update sample build config * Stop trying to use JS (browser) APIs for crypto, as there is no JS in wasi * Revert unneeded change * Apply suggestions from code review Co-authored-by: Ankit Jain * Addressing @radical comments * Addressing @radical comments * fixing debugger behavior and addressing @radical comments * Addressing @ SteveSandersonMS comments * Apply suggestions from code review Co-authored-by: Ankit Jain * Addressing radical comments * [wasi] Provision wasmtime as needed * [wasi] build, and run wasi sample on CI * [wasi] don't install wasmtime by default, but do that for CI * [wasi] Show a useful error if wasmtime is not found * [wasi] provision ninja * Fix path * Fix warnings to make CI green * debug * Move building wasi into a separate yml step, so it gets built after the whole wasm build is complete * fix yml * Enable wasi build on libtests job * Fix yml again Co-authored-by: Ankit Jain Co-authored-by: Steve Sanderson --- .../templates/additional-steps-then-helix.yml | 40 ++++ .../common/templates/wasm-library-tests.yml | 11 +- eng/pipelines/runtime.yml | 1 + src/mono/CMakeLists.txt | 5 +- src/mono/mono/component/CMakeLists.txt | 1 + src/mono/mono/component/debugger-agent.c | 185 +++++++++++++----- src/mono/mono/component/debugger-agent.h | 14 ++ src/mono/mono/component/debugger-engine.h | 6 + src/mono/mono/component/debugger-stub.c | 21 ++ src/mono/mono/component/debugger.c | 5 +- src/mono/mono/component/debugger.h | 3 + src/mono/mono/component/diagnostics_server.c | 2 +- src/mono/mono/component/event_pipe-stub.c | 4 +- src/mono/mono/component/event_pipe-wasm.h | 2 +- src/mono/mono/component/event_pipe.c | 10 +- src/mono/mono/component/mini-wasi-debugger.c | 180 +++++++++++++++++ src/mono/mono/component/mini-wasm-debugger.c | 3 + src/mono/mono/mini/CMakeLists.txt | 2 +- src/mono/mono/mini/debugger-agent-external.h | 3 + src/mono/mono/mini/interp/interp.c | 12 ++ src/mono/mono/mini/mini-exceptions.c | 7 +- src/mono/mono/utils/mono-threads-wasm.c | 2 + src/mono/mono/utils/mono-threads.c | 3 +- src/mono/wasi/Makefile | 30 ++- src/mono/wasi/Makefile.variable | 12 +- src/mono/wasi/README.md | 40 +++- src/mono/wasi/mono-wasi-driver/driver.c | 58 ++++-- src/mono/wasi/mono-wasi-driver/driver.h | 1 + src/mono/wasi/mono-wasi-driver/stubs.c | 2 +- src/mono/wasi/sample/SampleMakefile.variable | 8 +- src/mono/wasi/sample/console/Makefile | 14 +- .../WasiConsoleApp/WasiConsoleApp.csproj | 1 + src/mono/wasi/sample/console/main.c | 2 +- src/mono/wasm/wasm.proj | 1 - 34 files changed, 594 insertions(+), 97 deletions(-) create mode 100644 eng/pipelines/common/templates/additional-steps-then-helix.yml create mode 100644 src/mono/mono/component/mini-wasi-debugger.c diff --git a/eng/pipelines/common/templates/additional-steps-then-helix.yml b/eng/pipelines/common/templates/additional-steps-then-helix.yml new file mode 100644 index 00000000000000..af5c335b6d8535 --- /dev/null +++ b/eng/pipelines/common/templates/additional-steps-then-helix.yml @@ -0,0 +1,40 @@ +parameters: + archType: '' + buildConfig: '' + condition: always() + creator: '' + extraHelixArguments: '' + helixQueues: '' + interpreter: '' + osGroup: '' + additionalSteps: [] + runTests: true + runtimeFlavor: '' + shouldContinueOnError: false + targetRid: '' + testRunNamePrefixSuffix: '' + testScope: 'innerloop' # innerloop | outerloop | all + scenarios: ['normal'] + +steps: + - ${{ if ne(parameters.additionalSteps, '') }}: + - ${{ each additionalStep in parameters.additionalSteps }}: + - ${{ additionalStep }} + + - ${{ if eq(parameters.runTests, true) }}: + - template: /eng/pipelines/libraries/helix.yml + parameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + archType: ${{ parameters.archType }} + buildConfig: ${{ parameters.buildConfig }} + helixQueues: ${{ parameters.helixQueues }} + osGroup: ${{ parameters.osGroup }} + targetRid: ${{ parameters.targetRid }} + testRunNamePrefixSuffix: ${{ parameters.testRunNamePrefixSuffix }} + testScope: ${{ parameters.testScope }} + interpreter: ${{ parameters.interpreter }} + condition: ${{ parameters.condition }} + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + extraHelixArguments: ${{ parameters.extraHelixArguments }} /p:BrowserHost=$(_hostedOs) + creator: dotnet-bot + scenarios: ${{ parameters.scenarios }} diff --git a/eng/pipelines/common/templates/wasm-library-tests.yml b/eng/pipelines/common/templates/wasm-library-tests.yml index ada2484fedc375..a1f28093bfc9f2 100644 --- a/eng/pipelines/common/templates/wasm-library-tests.yml +++ b/eng/pipelines/common/templates/wasm-library-tests.yml @@ -1,5 +1,6 @@ parameters: alwaysRun: false + buildAndRunWasi: false extraBuildArgs: '' extraHelixArgs: '' isExtraPlatformsBuild: false @@ -49,8 +50,16 @@ jobs: eq(dependencies.evaluate_paths.outputs['SetPathVars_allwasm.containsChange'], true), eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true)) # extra steps, run tests - extraStepsTemplate: /eng/pipelines/libraries/helix.yml + extraStepsTemplate: /eng/pipelines/common/templates/additional-steps-then-helix.yml extraStepsParameters: + additionalSteps: + - ${{ if eq(parameters.buildAndRunWasi, true) }}: + - script: >- + make -C src/mono/wasi provision-deps all && + make -C src/mono/wasi/sample/console run + name: build_wasi + displayName: Build Wasi, and run a sample + creator: dotnet-bot testRunNamePrefixSuffix: Mono_$(_BuildConfig) extraHelixArguments: /p:BrowserHost=$(_hostedOs) ${{ parameters.runSmokeOnlyArg }} ${{ parameters.extraHelixArgs }} diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml index 1fa8399501b037..dddba18e3d1d52 100644 --- a/eng/pipelines/runtime.yml +++ b/eng/pipelines/runtime.yml @@ -360,6 +360,7 @@ jobs: parameters: platforms: - Browser_wasm + buildAndRunWasi: true alwaysRun: ${{ variables.isRollingBuild }} scenarios: - normal diff --git a/src/mono/CMakeLists.txt b/src/mono/CMakeLists.txt index dc4ff5b1886481..1629fe4dbcec93 100644 --- a/src/mono/CMakeLists.txt +++ b/src/mono/CMakeLists.txt @@ -247,13 +247,14 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Emscripten") set(INTERNAL_ZLIB 1) elseif(CMAKE_SYSTEM_NAME STREQUAL "WASI") set(HOST_WASI 1) - add_definitions(-D_WASI_EMULATED_SIGNAL -D_WASI_EMULATED_MMAN) + add_definitions(-D_WASI_EMULATED_SIGNAL -D_WASI_EMULATED_MMAN -DHOST_WASI) add_definitions(-DNO_GLOBALIZATION_SHIM) add_definitions(-D_THREAD_SAFE) + add_definitions(-DGEN_PINVOKE) set(DISABLE_SHARED_LIBS 1) set(INTERNAL_ZLIB 1) set(DISABLE_EXECUTABLES 1) - set(DISABLE_COMPONENTS 1) + set(STATIC_COMPONENTS 1) set(WASI_DRIVER_SOURCES wasi/mono-wasi-driver/driver.c diff --git a/src/mono/mono/component/CMakeLists.txt b/src/mono/mono/component/CMakeLists.txt index b8c1d608dd6832..d83c9144af07f9 100644 --- a/src/mono/mono/component/CMakeLists.txt +++ b/src/mono/mono/component/CMakeLists.txt @@ -31,6 +31,7 @@ set(${MONO_DEBUGGER_COMPONENT_NAME}-sources ${MONO_COMPONENT_PATH}/debugger-state-machine.h ${MONO_COMPONENT_PATH}/debugger-state-machine.c ${MONO_COMPONENT_PATH}/mini-wasm-debugger.c + ${MONO_COMPONENT_PATH}/mini-wasi-debugger.c ${MONO_COMPONENT_PATH}/debugger-protocol.h ${MONO_COMPONENT_PATH}/debugger-protocol.c ) diff --git a/src/mono/mono/component/debugger-agent.c b/src/mono/mono/component/debugger-agent.c index c92fdad737e4cb..45404d5967b3f5 100644 --- a/src/mono/mono/component/debugger-agent.c +++ b/src/mono/mono/component/debugger-agent.c @@ -177,6 +177,7 @@ typedef struct { int keepalive; gboolean setpgid; gboolean using_icordbg; + char *debugger_fd; } AgentConfig; struct _DebuggerTlsData { @@ -336,6 +337,10 @@ static AgentConfig agent_config; */ static gint32 agent_inited; +#ifdef HOST_WASI +static gboolean resumed_from_wasi; +#endif + #ifndef DISABLE_SOCKET_TRANSPORT static SOCKET conn_fd; static SOCKET listen_fd; @@ -429,11 +434,16 @@ static gboolean buffer_replies; tls = &debugger_wasm_thread; #endif +static void (*start_debugger_thread_func) (MonoError *error); +static void (*suspend_vm_func) (void); +static void (*suspend_current_func) (void); + //mono_native_tls_get_value (debugger_tls_id); #define dbg_lock mono_de_lock #define dbg_unlock mono_de_unlock +static void suspend_vm (void); static void transport_init (void); static void transport_connect (const char *address); static gboolean transport_handshake (void); @@ -601,7 +611,7 @@ debugger_agent_parse_options (char *options) int port; char *extra; -#ifndef MONO_ARCH_SOFT_DEBUG_SUPPORTED +#if !defined(MONO_ARCH_SOFT_DEBUG_SUPPORTED) && !defined(HOST_WASI) PRINT_ERROR_MSG ("--debugger-agent is not supported on this platform.\n"); exit (1); #endif @@ -656,6 +666,8 @@ debugger_agent_parse_options (char *options) agent_config.keepalive = atoi (arg + 10); } else if (strncmp (arg, "setpgid=", 8) == 0) { agent_config.setpgid = parse_flag ("setpgid", arg + 8); + } else if (strncmp (arg, "debugger_fd=", 12) == 0) { + agent_config.debugger_fd = g_strdup (arg + 12); } else { print_usage (); exit (1); @@ -677,10 +689,12 @@ debugger_agent_parse_options (char *options) exit (1); } +#ifndef HOST_WASI if (agent_config.address == NULL && !agent_config.server) { PRINT_ERROR_MSG ("debugger-agent: The 'address' option is mandatory.\n"); exit (1); } +#endif // FIXME: if (!strcmp (agent_config.transport, "dt_socket")) { @@ -750,8 +764,8 @@ mono_debugger_is_disconnected (void) return disconnected; } -static void -debugger_agent_init (void) +void +mono_debugger_agent_init_internal (void) { if (!agent_config.enabled) return; @@ -769,8 +783,12 @@ debugger_agent_init (void) cbs.handle_multiple_ss_requests = handle_multiple_ss_requests; mono_de_init (&cbs); - transport_init (); + + start_debugger_thread_func = start_debugger_thread; + suspend_vm_func = suspend_vm; + suspend_current_func = suspend_current; + /* Need to know whenever a thread has acquired the loader mutex */ mono_loader_lock_track_ownership (TRUE); @@ -873,14 +891,17 @@ finish_agent_init (gboolean on_startup) } #endif } - +#ifndef HOST_WASI transport_connect (agent_config.address); +#else + transport_connect (agent_config.debugger_fd); +#endif if (!on_startup) { /* Do some which is usually done after sending the VMStart () event */ vm_start_event_sent = TRUE; ERROR_DECL (error); - start_debugger_thread (error); + start_debugger_thread_func (error); mono_error_assert_ok (error); } } @@ -2234,6 +2255,25 @@ mono_wasm_get_tls (void) } #endif +#ifdef HOST_WASI +void +mono_wasi_suspend_current (void) +{ + GET_DEBUGGER_TLS(); + g_assert (tls); + tls->really_suspended = TRUE; + return; +} + +void +mono_debugger_agent_initialize_function_pointers (void *start_debugger_thread, void *suspend_vm, void *suspend_current) +{ + start_debugger_thread_func = start_debugger_thread; + suspend_vm_func = suspend_vm; + suspend_current_func = suspend_current; +} +#endif + static MonoCoopMutex suspend_mutex; /* Cond variable used to wait for suspend_count becoming 0 */ @@ -2501,7 +2541,7 @@ process_suspend (DebuggerTlsData *tls, MonoContext *ctx) save_thread_context (ctx); - suspend_current (); + suspend_current_func (); } @@ -2566,6 +2606,7 @@ suspend_vm (void) static void resume_vm (void) { +#ifndef HOST_WASI g_assert (is_debugger_thread ()); mono_loader_lock (); @@ -2590,6 +2631,9 @@ resume_vm (void) //g_assert (err == 0); mono_loader_unlock (); +#else + resumed_from_wasi = TRUE; +#endif } /* @@ -2765,6 +2809,9 @@ count_threads_to_wait_for (void) static void wait_for_suspend (void) { +#ifdef HOST_WASI + return; +#endif int nthreads, nwait, err; gboolean waited = FALSE; @@ -3057,8 +3104,13 @@ compute_frame_info (MonoInternalThread *thread, DebuggerTlsData *tls, gboolean f mono_walk_stack_with_state (process_frame, &tls->context, opts, &user_data); } else { // FIXME: +#ifdef HOST_WASI + mono_walk_stack_with_state (process_frame, NULL, opts, &user_data); + tls->context.valid = TRUE; +#else tls->frame_count = 0; return; +#endif } new_frame_count = g_slist_length (user_data.frames); @@ -3668,7 +3720,7 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx if (event == EVENT_KIND_VM_START) { if (!agent_config.defer) { ERROR_DECL (error); - start_debugger_thread (error); + start_debugger_thread_func (error); mono_error_assert_ok (error); } } @@ -3690,7 +3742,7 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx save_thread_context (ctx); DebuggerTlsData *tls = (DebuggerTlsData *)mono_g_hash_table_lookup (thread_to_tls, mono_thread_internal_current ()); tls->suspend_count++; - suspend_vm (); + suspend_vm_func (); if (keepalive_obj) /* This will keep this object alive */ @@ -3728,7 +3780,7 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx case SUSPEND_POLICY_NONE: break; case SUSPEND_POLICY_ALL: - suspend_current (); + suspend_current_func (); break; case SUSPEND_POLICY_EVENT_THREAD: NOT_IMPLEMENTED; @@ -3736,6 +3788,15 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx default: g_assert_not_reached (); } +#ifdef HOST_WASI + resumed_from_wasi = FALSE; + while (suspend_policy != SUSPEND_POLICY_NONE && !resumed_from_wasi) + { + GET_DEBUGGER_TLS(); + tls->really_suspended = TRUE; + mono_debugger_agent_receive_and_process_command (); + } +#endif } static void @@ -3765,7 +3826,7 @@ runtime_initialized (MonoProfiler *prof) process_profiler_event (EVENT_KIND_ASSEMBLY_LOAD, (mono_get_corlib ()->assembly)); if (agent_config.defer) { ERROR_DECL (error); - start_debugger_thread (error); + start_debugger_thread_func (error); mono_error_assert_ok (error); } } @@ -3839,7 +3900,7 @@ thread_startup (MonoProfiler *prof, uintptr_t tid) * suspend_vm () could have missed this thread, so wait for a resume. */ - suspend_current (); + suspend_current_func (); } static void @@ -4546,6 +4607,9 @@ handle_multiple_ss_requests (void) static int ensure_runtime_is_suspended (void) { +#ifdef HOST_WASI + return ERR_NONE; +#endif if (suspend_count == 0) return ERR_NOT_SUSPENDED; @@ -4565,8 +4629,11 @@ mono_ss_create_init_args (SingleStepReq *ss_req, SingleStepArgs *args) gboolean set_ip = FALSE; StackFrame **frames = NULL; int nframes = 0; - +#ifndef HOST_WASI GET_TLS_DATA_FROM_THREAD (ss_req->thread); +#else + GET_DEBUGGER_TLS(); +#endif g_assert (tls); if (!tls->context.valid) { @@ -6279,7 +6346,7 @@ invoke_method (void) if (mindex == invoke->nmethods - 1) { if (!(invoke->flags & INVOKE_FLAG_SINGLE_THREADED)) { for (guint32 i = 0; i < invoke->suspend_count; ++i) - suspend_vm (); + suspend_vm_func (); } } @@ -6673,10 +6740,11 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) break; } case CMD_VM_SUSPEND: - suspend_vm (); + suspend_vm_func (); wait_for_suspend (); break; case CMD_VM_RESUME: +#ifndef HOST_WASI if (suspend_count == 0) { if (agent_config.defer && !agent_config.suspend) // Workaround for issue in debugger-libs when running in defer attach mode. @@ -6684,6 +6752,7 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) else return ERR_NOT_SUSPENDED; } +#endif resume_vm (); clear_suspended_objs (); break; @@ -6723,7 +6792,7 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) * better than doing the shutdown ourselves, since it avoids various races. */ - suspend_vm (); + suspend_vm_func (); wait_for_suspend (); #ifdef TRY_MANAGED_SYSTEM_ENVIRONMENT_EXIT @@ -7228,7 +7297,7 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf) g_free (req); return err; } -#ifdef TARGET_WASM +#if defined(TARGET_WASM) && !defined(HOST_WASI) int isBPOnManagedCode = 0; SingleStepReq *ss_req = req->info; if (ss_req && ss_req->bps) { @@ -9045,10 +9114,12 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf) // Wait for suspending if it already started // FIXME: Races with suspend_count +#ifndef HOST_WASI while (!is_suspended ()) { if (suspend_count) wait_for_suspend (); } +#endif /* if (suspend_count) wait_for_suspend (); @@ -10228,19 +10299,10 @@ mono_process_dbg_packet (int id, CommandSet command_set, int command, gboolean * static gsize WINAPI debugger_thread (void *arg) { - int res, len, id, flags, command = 0; - CommandSet command_set = (CommandSet)0; - guint8 header [HEADER_LENGTH]; - guint8 *data, *p, *end; - Buffer buf; - ErrorCode err; - gboolean no_reply; gboolean attach_failed = FALSE; PRINT_DEBUG_MSG (1, "[dbg] Agent thread started, pid=%p\n", (gpointer) (gsize) mono_native_thread_id_get ()); - gboolean log_each_step = g_hasenv ("MONO_DEBUGGER_LOG_AFTER_COMMAND"); - debugger_thread_id = mono_native_thread_id_get (); MonoInternalThread *internal = mono_thread_internal_current (); @@ -10267,22 +10329,58 @@ debugger_thread (void *arg) if (mono_metadata_has_updates_api ()) { PRINT_DEBUG_MSG (1, "[dbg] Cannot attach after System.Reflection.Metadata.MetadataUpdater.ApplyChanges has been called.\n"); attach_failed = TRUE; - command_set = (CommandSet)0; - command = 0; dispose_vm (); } } #endif + gboolean is_vm_dispose_command = FALSE; + if (!attach_failed) + { + is_vm_dispose_command = mono_debugger_agent_receive_and_process_command (); + } + + + mono_set_is_debugger_attached (FALSE); + + mono_coop_mutex_lock (&debugger_thread_exited_mutex); + debugger_thread_exited = TRUE; + mono_coop_cond_signal (&debugger_thread_exited_cond); + mono_coop_mutex_unlock (&debugger_thread_exited_mutex); + + PRINT_DEBUG_MSG (1, "[dbg] Debugger thread exited.\n"); + + if (!attach_failed && is_vm_dispose_command && !(vm_death_event_sent || mono_runtime_is_shutting_down ())) { + PRINT_DEBUG_MSG (2, "[dbg] Detached - restarting clean debugger thread.\n"); + ERROR_DECL (error); + start_debugger_thread_func (error); + mono_error_cleanup (error); + } + return 0; +} + +bool mono_debugger_agent_receive_and_process_command (void) +{ + int res, len, id, flags, command = 0; + CommandSet command_set = (CommandSet)0; + guint8 header [HEADER_LENGTH]; + guint8 *data, *p, *end; + Buffer buf; + ErrorCode err; + gboolean no_reply; + + gboolean log_each_step = g_hasenv ("MONO_DEBUGGER_LOG_AFTER_COMMAND"); - while (!attach_failed) { + while (TRUE) { res = transport_recv (header, HEADER_LENGTH); /* This will break if the socket is closed during shutdown too */ if (res != HEADER_LENGTH) { +#ifndef HOST_WASI //on wasi we can try to get message from debugger and don't have any message PRINT_DEBUG_MSG (1, "[dbg] transport_recv () returned %d, expected %d.\n", res, HEADER_LENGTH); command_set = (CommandSet)0; command = 0; dispose_vm (); +#endif break; } else { p = header; @@ -10362,32 +10460,20 @@ debugger_thread (void *arg) if (command_set == CMD_SET_VM && (command == CMD_VM_DISPOSE || command == CMD_VM_EXIT)) break; } - - mono_set_is_debugger_attached (FALSE); - - mono_coop_mutex_lock (&debugger_thread_exited_mutex); - debugger_thread_exited = TRUE; - mono_coop_cond_signal (&debugger_thread_exited_cond); - mono_coop_mutex_unlock (&debugger_thread_exited_mutex); - - PRINT_DEBUG_MSG (1, "[dbg] Debugger thread exited.\n"); - - if (!attach_failed && command_set == CMD_SET_VM && command == CMD_VM_DISPOSE && !(vm_death_event_sent || mono_runtime_is_shutting_down ())) { - PRINT_DEBUG_MSG (2, "[dbg] Detached - restarting clean debugger thread.\n"); - ERROR_DECL (error); - start_debugger_thread (error); - mono_error_cleanup (error); - } - - return 0; + return !(command_set == CMD_SET_VM && command == CMD_VM_DISPOSE); } +static gboolean +debugger_agent_enabled (void) +{ + return agent_config.enabled; +} void debugger_agent_add_function_pointers(MonoComponentDebugger* fn_table) { fn_table->parse_options = debugger_agent_parse_options; - fn_table->init = debugger_agent_init; + fn_table->init = mono_debugger_agent_init_internal; fn_table->breakpoint_hit = debugger_agent_breakpoint_hit; fn_table->single_step_event = debugger_agent_single_step_event; fn_table->single_step_from_context = debugger_agent_single_step_from_context; @@ -10402,8 +10488,7 @@ debugger_agent_add_function_pointers(MonoComponentDebugger* fn_table) fn_table->debug_log_is_enabled = debugger_agent_debug_log_is_enabled; fn_table->transport_handshake = debugger_agent_transport_handshake; fn_table->send_enc_delta = send_enc_delta; + fn_table->debugger_enabled = debugger_agent_enabled; } - - #endif /* DISABLE_SDB */ diff --git a/src/mono/mono/component/debugger-agent.h b/src/mono/mono/component/debugger-agent.h index ec77b0bfaba8a1..13a9f504f8afb6 100644 --- a/src/mono/mono/component/debugger-agent.h +++ b/src/mono/mono/component/debugger-agent.h @@ -36,6 +36,17 @@ mono_wasm_save_thread_context (void); void mini_wasm_debugger_add_function_pointers (MonoComponentDebugger* fn_table); +void +mini_wasi_debugger_add_function_pointers (MonoComponentDebugger* fn_table); + +#if defined(HOST_WASI) +void +mono_wasi_suspend_current (void); + +void +mono_debugger_agent_initialize_function_pointers (void *start_debugger_thread, void *suspend_vm, void *suspend_current); +#endif + MdbgProtErrorCode mono_do_invoke_method (DebuggerTlsData *tls, MdbgProtBuffer *buf, InvokeData *invoke, guint8 *p, guint8 **endp); @@ -56,4 +67,7 @@ mono_ss_args_destroy (SingleStepArgs *ss_args); int mono_de_frame_async_id (DbgEngineStackFrame *frame); + +bool +mono_debugger_agent_receive_and_process_command (void); #endif diff --git a/src/mono/mono/component/debugger-engine.h b/src/mono/mono/component/debugger-engine.h index e6e47280e50b08..d174f3e4034502 100644 --- a/src/mono/mono/component/debugger-engine.h +++ b/src/mono/mono/component/debugger-engine.h @@ -330,6 +330,9 @@ mono_debugger_get_thread_states (void); gboolean mono_debugger_is_disconnected (void); +void +mono_debugger_agent_init (void); + gsize mono_debugger_tls_thread_id (DebuggerTlsData *debuggerTlsData); @@ -380,6 +383,9 @@ void mono_debugger_free_objref(gpointer value); #ifdef HOST_ANDROID #define PRINT_DEBUG_MSG(level, ...) do { if (G_UNLIKELY ((level) <= log_level)) { g_print (__VA_ARGS__); } } while (0) #define DEBUG(level,s) do { if (G_UNLIKELY ((level) <= log_level)) { s; } } while (0) +#elif HOST_WASI +#define PRINT_DEBUG_MSG(level, ...) do { if (G_UNLIKELY ((level) <= log_level)) { g_print (__VA_ARGS__); } } while (0) +#define DEBUG(level,s) do { if (G_UNLIKELY ((level) <= log_level)) { s; } } while (0) #elif HOST_WASM void wasm_debugger_log(int level, const gchar *format, ...); #define PRINT_DEBUG_MSG(level, ...) do { if (G_UNLIKELY ((level) <= log_level)) { wasm_debugger_log (level, __VA_ARGS__); } } while (0) diff --git a/src/mono/mono/component/debugger-stub.c b/src/mono/mono/component/debugger-stub.c index 18b0f6a143487b..615383b2a00ea5 100644 --- a/src/mono/mono/component/debugger-stub.c +++ b/src/mono/mono/component/debugger-stub.c @@ -66,6 +66,12 @@ stub_mono_wasm_single_step_hit (void); static void stub_send_enc_delta (MonoImage *image, gconstpointer dmeta_bytes, int32_t dmeta_len, gconstpointer dpdb_bytes, int32_t dpdb_len); +static void +stub_receive_and_process_command_from_debugger_agent (void); + +static gboolean +stub_debugger_enabled (void); + static MonoComponentDebugger fn_table = { { MONO_COMPONENT_ITF_VERSION, &debugger_available }, &stub_debugger_init, @@ -90,6 +96,10 @@ static MonoComponentDebugger fn_table = { //HotReload &stub_send_enc_delta, + + //wasi + &stub_receive_and_process_command_from_debugger_agent, + &stub_debugger_enabled, }; static bool @@ -204,6 +214,17 @@ stub_send_enc_delta (MonoImage *image, gconstpointer dmeta_bytes, int32_t dmeta_ { } +static void +stub_receive_and_process_command_from_debugger_agent (void) +{ +} + +static gboolean +stub_debugger_enabled (void) +{ + return FALSE; +} + #ifdef HOST_BROWSER #include diff --git a/src/mono/mono/component/debugger.c b/src/mono/mono/component/debugger.c index d8de70ab74f588..f6255d4c85d9a3 100644 --- a/src/mono/mono/component/debugger.c +++ b/src/mono/mono/component/debugger.c @@ -28,8 +28,11 @@ MonoComponentDebugger * mono_component_debugger_init (void) { debugger_agent_add_function_pointers (&fn_table); -#ifdef TARGET_WASM +#if defined(TARGET_WASM) && !defined(HOST_WASI) mini_wasm_debugger_add_function_pointers (&fn_table); +#endif +#if defined(HOST_WASI) + mini_wasi_debugger_add_function_pointers (&fn_table); #endif return &fn_table; } diff --git a/src/mono/mono/component/debugger.h b/src/mono/mono/component/debugger.h index 633bfc492cc033..f871535baf61c2 100644 --- a/src/mono/mono/component/debugger.h +++ b/src/mono/mono/component/debugger.h @@ -196,6 +196,9 @@ typedef struct MonoComponentDebugger { //HotReload void (*send_enc_delta) (MonoImage *image, gconstpointer dmeta_bytes, int32_t dmeta_len, gconstpointer dpdb_bytes, int32_t dpdb_len); + //wasi + void (*receive_and_process_command_from_debugger_agent) (void); + gboolean (*debugger_enabled) (void); } MonoComponentDebugger; diff --git a/src/mono/mono/component/diagnostics_server.c b/src/mono/mono/component/diagnostics_server.c index 4c1124c478258b..f81de18046f23b 100644 --- a/src/mono/mono/component/diagnostics_server.c +++ b/src/mono/mono/component/diagnostics_server.c @@ -7,7 +7,7 @@ #include #include #include -#ifdef HOST_WASM +#if defined (HOST_WASM) && !defined(HOST_WASI) #include #include #include diff --git a/src/mono/mono/component/event_pipe-stub.c b/src/mono/mono/component/event_pipe-stub.c index 194b7c05823b89..3865b3148a6fa7 100644 --- a/src/mono/mono/component/event_pipe-stub.c +++ b/src/mono/mono/component/event_pipe-stub.c @@ -517,7 +517,7 @@ mono_component_event_pipe_init (void) return component_event_pipe_stub_init (); } -#ifdef HOST_WASM +#if defined(HOST_WASM) && !defined(HOST_WASI) EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_event_pipe_enable (const ep_char8_t *output_path, @@ -548,4 +548,4 @@ mono_wasm_event_pipe_session_disable (MonoWasmEventPipeSessionID session_id) { g_assert_not_reached (); } -#endif /* HOST_WASM */ +#endif /* HOST_WASM && !HOST_WASI */ \ No newline at end of file diff --git a/src/mono/mono/component/event_pipe-wasm.h b/src/mono/mono/component/event_pipe-wasm.h index 3ed49233b90834..b75ddce0b15a94 100644 --- a/src/mono/mono/component/event_pipe-wasm.h +++ b/src/mono/mono/component/event_pipe-wasm.h @@ -10,7 +10,7 @@ #include #include -#ifdef HOST_WASM +#if defined(HOST_WASM) && !defined(HOST_WASI) #include #include diff --git a/src/mono/mono/component/event_pipe.c b/src/mono/mono/component/event_pipe.c index 4d59f5af94b681..4ba3489101263b 100644 --- a/src/mono/mono/component/event_pipe.c +++ b/src/mono/mono/component/event_pipe.c @@ -13,7 +13,7 @@ #include #include -#ifdef HOST_WASM +#if defined(HOST_WASM) && !defined(HOST_WASI) #include #endif @@ -98,14 +98,14 @@ event_pipe_wait_for_session_signal ( EventPipeSessionID session_id, uint32_t timeout); -#ifdef HOST_WASM +#if defined(HOST_WASM) && !defined(HOST_WASI) static void mono_wasm_event_pipe_init (void); #endif static MonoComponentEventPipe fn_table = { { MONO_COMPONENT_ITF_VERSION, &event_pipe_available }, -#ifndef HOST_WASM +#if !defined(HOST_WASM) || defined(HOST_WASI) &ep_init, #else &mono_wasm_event_pipe_init, @@ -345,7 +345,7 @@ mono_component_event_pipe_init (void) } -#ifdef HOST_WASM +#if defined(HOST_WASM) && !defined(HOST_WASI) static MonoWasmEventPipeSessionID @@ -426,4 +426,4 @@ mono_wasm_event_pipe_init (void) mono_wasm_event_pipe_early_startup_callback (); } -#endif /* HOST_WASM */ +#endif /* HOST_WASM && !HOST_WASI */ diff --git a/src/mono/mono/component/mini-wasi-debugger.c b/src/mono/mono/component/mini-wasi-debugger.c new file mode 100644 index 00000000000000..8b1b87f0b3fb37 --- /dev/null +++ b/src/mono/mono/component/mini-wasi-debugger.c @@ -0,0 +1,180 @@ +#include +#include +#include +#include + +#ifdef HOST_WASI +static int conn_fd; +static int log_level = 1; +static int retry_receive_message = 100; +static int connection_wait_us = 1000; + +__attribute__((import_module("wasi_snapshot_preview1"))) +__attribute__((import_name("sock_accept"))) +int sock_accept(int fd, int fdflags, int* result_ptr); + +static long long +time_in_milliseconds () +{ + struct timeval tv; + gettimeofday(&tv,NULL); + return (((long long)tv.tv_sec)*1000)+(tv.tv_usec/1000); +} + +static int +wasi_transport_recv (void *buf, int len) +{ + int res; + int total = 0; + int fd = conn_fd; + int num_recv_calls = 0; + + do { + res = read (fd, (char *) buf + total, len - total); + if (res > 0) + total += res; + num_recv_calls++; + if ((res > 0 && total < len) || (res == -1 && num_recv_calls < retry_receive_message)) { + // Wasmtime on Windows doesn't seem to be able to sleep for short periods like 1ms so we'll have to spinlock + long long start = time_in_milliseconds (); + while ((time_in_milliseconds () - start) < (connection_wait_us/1000)); + } else { + break; + } + } while (true); + return total; +} + +static gboolean +wasi_transport_send (void *data, int len) +{ + int res; + do { + res = write (conn_fd, (const char*)data, len); + } while (res == -1); + + return res == len; +} + +/* + * wasi_transport_connect: + * + * listen on the socket that is already opened by wasm runtime + */ +static void +wasi_transport_connect (const char *socket_fd) +{ + PRINT_DEBUG_MSG (1, "wasi_transport_connect.\n"); + bool handshake_ok = FALSE; + conn_fd = -1; + char *ptr; + PRINT_DEBUG_MSG (1, "Waiting for connection from client, socket fd=%s.\n", socket_fd); + long socket_fd_long = strtol(socket_fd, &ptr, 10); + if (socket_fd_long == 0) { + PRINT_DEBUG_MSG (1, "Invalid socket fd = %s.\n", socket_fd); + return; + } + while (!handshake_ok) { + int ret_accept = sock_accept (socket_fd_long, __WASI_FDFLAGS_NONBLOCK, &conn_fd); + if (ret_accept < 0) { + PRINT_DEBUG_MSG (1, "Error while accepting connection from client = %d.\n", ret_accept); + return; + } + int res = -1; + if (conn_fd != -1) { + res = write (conn_fd, (const char*)"", 0); + if (res != -1) { + handshake_ok = mono_component_debugger ()->transport_handshake (); + } + } + if (conn_fd == -1 || res == -1) { + sleep(1); + continue; + } + } + PRINT_DEBUG_MSG (1, "Accepted connection from client, socket fd=%d.\n", conn_fd); +} + +static void +wasi_transport_close1 (void) +{ +/* shutdown (conn_fd, SHUT_RD); + shutdown (listen_fd, SHUT_RDWR); + close (listen_fd);*/ +} + +static void +wasi_transport_close2 (void) +{ +// shutdown (conn_fd, SHUT_RDWR); +} + +static void +mono_wasi_start_debugger_thread (MonoError *error) +{ + mono_debugger_agent_receive_and_process_command (); + connection_wait_us = 250; +} + +static void +mono_wasi_suspend_vm (void) +{ +} + +static void +mono_wasi_debugger_init (void) +{ + DebuggerTransport trans; + trans.name = "wasi_socket"; + trans.send = wasi_transport_send; + trans.connect = wasi_transport_connect; + trans.recv = wasi_transport_recv; + trans.send = wasi_transport_send; + trans.close1 = wasi_transport_close1; + + mono_debugger_agent_register_transport (&trans); + + mono_debugger_agent_init_internal(); + + mono_debugger_agent_initialize_function_pointers(mono_wasi_start_debugger_thread, mono_wasi_suspend_vm, mono_wasi_suspend_current); +} + +static void +mono_wasi_receive_and_process_command_from_debugger_agent (void) +{ + retry_receive_message = 2; //when it comes from interpreter we don't want to spend a long time waiting for messages + mono_debugger_agent_receive_and_process_command (); + retry_receive_message = 50; //when it comes from debugger we are waiting for a debugger command (resume/step), we want to retry more times +} + +static void +mono_wasi_single_step_hit (void) +{ + mono_wasm_save_thread_context (); + mono_de_process_single_step (mono_wasm_get_tls (), FALSE); +} + +static void +mono_wasi_breakpoint_hit (void) +{ + mono_wasm_save_thread_context (); + mono_de_process_breakpoint (mono_wasm_get_tls (), FALSE); +} + +void +mini_wasi_debugger_add_function_pointers (MonoComponentDebugger* fn_table) +{ + fn_table->init = mono_wasi_debugger_init; + fn_table->receive_and_process_command_from_debugger_agent = mono_wasi_receive_and_process_command_from_debugger_agent; + fn_table->mono_wasm_breakpoint_hit = mono_wasi_breakpoint_hit; + fn_table->mono_wasm_single_step_hit = mono_wasi_single_step_hit; +} + +#else // HOST_WASI + +void +mini_wasi_debugger_add_function_pointers (MonoComponentDebugger* fn_table) +{ +} + +#endif diff --git a/src/mono/mono/component/mini-wasm-debugger.c b/src/mono/mono/component/mini-wasm-debugger.c index 5de1f3422090ca..98da53aa693b40 100644 --- a/src/mono/mono/component/mini-wasm-debugger.c +++ b/src/mono/mono/component/mini-wasm-debugger.c @@ -1,3 +1,4 @@ +#ifndef HOST_WASI #include #include #include @@ -486,3 +487,5 @@ mini_wasm_debugger_add_function_pointers (MonoComponentDebugger* fn_table) fn_table->mono_wasm_breakpoint_hit = mono_wasm_breakpoint_hit; fn_table->mono_wasm_single_step_hit = mono_wasm_single_step_hit; } + +#endif \ No newline at end of file diff --git a/src/mono/mono/mini/CMakeLists.txt b/src/mono/mono/mini/CMakeLists.txt index 9a71594e43c589..6ef0ebf57b4ea2 100644 --- a/src/mono/mono/mini/CMakeLists.txt +++ b/src/mono/mono/mini/CMakeLists.txt @@ -448,7 +448,7 @@ if(NOT DISABLE_SHARED_LIBS) endif() endif() -if(HOST_WASM) +if(HOST_WASM AND NOT HOST_WASI) # Add two static libs containing llvm-runtime.cpp compiled for JS based/WASM EH # This is the only source file which contains a c++ throw or catch add_library(mono-wasm-eh-js STATIC llvm-runtime.cpp) diff --git a/src/mono/mono/mini/debugger-agent-external.h b/src/mono/mono/mini/debugger-agent-external.h index b37cd6b5e046ef..661537e0e69378 100644 --- a/src/mono/mono/mini/debugger-agent-external.h +++ b/src/mono/mono/mini/debugger-agent-external.h @@ -20,6 +20,9 @@ mono_debugger_agent_transport_handshake (void); MONO_API void mono_debugger_agent_register_transport (DebuggerTransport *trans); +MONO_API void +mono_debugger_agent_init_internal (void); + MONO_COMPONENT_API DebuggerTransport * mono_debugger_agent_get_transports (int *ntrans); diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 0c69d67bf6376b..b89f8a7a181c7f 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -243,6 +243,10 @@ static gboolean ss_enabled; static gboolean interp_init_done = FALSE; +#ifdef HOST_WASI +static gboolean debugger_enabled = FALSE; +#endif + static void interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs *clause_args); @@ -6792,6 +6796,10 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK; MINT_IN_BREAK; MINT_IN_CASE(MINT_SDB_SEQ_POINT) /* Just a placeholder for a breakpoint */ +#if HOST_WASI + if (debugger_enabled) + mono_component_debugger()->receive_and_process_command_from_debugger_agent (); +#endif ++ip; MINT_IN_BREAK; MINT_IN_CASE(MINT_SDB_BREAKPOINT) { @@ -8189,4 +8197,8 @@ mono_ee_interp_init (const char *opts) mini_install_interp_callbacks (&mono_interp_callbacks); register_interp_stats (); + +#ifdef HOST_WASI + debugger_enabled = mini_get_debug_options ()->mdb_optimizations; +#endif } diff --git a/src/mono/mono/mini/mini-exceptions.c b/src/mono/mono/mini/mini-exceptions.c index 76d6dc7baf327f..f948c57444c025 100644 --- a/src/mono/mono/mini/mini-exceptions.c +++ b/src/mono/mono/mini/mini-exceptions.c @@ -1345,7 +1345,12 @@ mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoJitTlsD unwinder_init (&unwinder); - while (MONO_CONTEXT_GET_SP (&ctx) < jit_tls->end_of_stack) { +#ifdef HOST_WASI + gboolean ignore_end_of_stack = true; +#else + gboolean ignore_end_of_stack = false; +#endif + while (ignore_end_of_stack || MONO_CONTEXT_GET_SP (&ctx) < jit_tls->end_of_stack) { frame.lmf = lmf; res = unwinder_unwind_frame (&unwinder, jit_tls, NULL, &ctx, &new_ctx, NULL, &lmf, get_reg_locations ? new_reg_locations : NULL, &frame); if (!res) diff --git a/src/mono/mono/utils/mono-threads-wasm.c b/src/mono/mono/utils/mono-threads-wasm.c index 0727fe393606bf..436974f947db38 100644 --- a/src/mono/mono/utils/mono-threads-wasm.c +++ b/src/mono/mono/utils/mono-threads-wasm.c @@ -180,7 +180,9 @@ mono_threads_platform_yield (void) void mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize) { +#ifndef HOST_WASI int tmp; +#endif #ifdef __EMSCRIPTEN_PTHREADS__ pthread_attr_t attr; gint res; diff --git a/src/mono/mono/utils/mono-threads.c b/src/mono/mono/utils/mono-threads.c index fb75708a43d945..ac6d2078bb63b8 100644 --- a/src/mono/mono/utils/mono-threads.c +++ b/src/mono/mono/utils/mono-threads.c @@ -1616,7 +1616,9 @@ mono_thread_info_is_async_context (void) void mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize) { +#ifndef HOST_WASI guint8 *current = (guint8 *)&stsize; +#endif mono_threads_platform_get_stack_bounds (staddr, stsize); if (!*staddr) return; @@ -2173,4 +2175,3 @@ mono_thread_info_get_tools_data (void) return info ? info->tools_data : NULL; } - diff --git a/src/mono/wasi/Makefile b/src/mono/wasi/Makefile index df109a147be17d..53ae87f4f76eaf 100644 --- a/src/mono/wasi/Makefile +++ b/src/mono/wasi/Makefile @@ -5,25 +5,47 @@ all: build-all build-all: $(WASI_SDK_CLANG) mkdir -p $(WASI_OBJ_DIR) cd $(WASI_OBJ_DIR) && \ - cmake -G Ninja \ + PATH=$(NINJA_DIR):${PATH} cmake -G Ninja \ -DWASI_SDK_PREFIX=$(WASI_SDK_ROOT) \ -DCMAKE_SYSROOT=$(WASI_SDK_ROOT)/share/wasi-sysroot \ -DCMAKE_TOOLCHAIN_FILE=$(WASI_SDK_ROOT)/share/cmake/wasi-sdk.cmake \ - -DCMAKE_C_FLAGS="--sysroot=$(WASI_SDK_ROOT)/share/wasi-sysroot -I$(CURDIR)/include -I$(TOP)/src/mono -I$(TOP)/src/native/public" \ + -DCMAKE_C_FLAGS="--sysroot=$(WASI_SDK_ROOT)/share/wasi-sysroot \ + -I$(CURDIR)/include -I$(TOP)/src/mono -I$(TOP)/src/native/public -I$(TOP)/src/mono/mono/eglib -I$(WASI_OBJ_DIR)/mono/eglib -I$(WASI_OBJ_DIR) -I$(TOP)/artifacts/obj/wasm -I$(TOP)/src/mono/wasm/runtime" \ -DCMAKE_CXX_FLAGS="--sysroot=$(WASI_SDK_ROOT)/share/wasi-sysroot" \ -DENABLE_MINIMAL=jit,sgen_major_marksweep_conc,sgen_split_nursery,sgen_gc_bridge,sgen_toggleref,sgen_debug_helpers,sgen_binary_protocol,logging,interpreter,threads,qcalls,debugger_agent,sockets,eventpipe \ -DDISABLE_SHARED_LIBS=1 \ -Wl,--allow-undefined \ $(TOP)/src/mono - cd $(WASI_OBJ_DIR) && ninja + cd $(WASI_OBJ_DIR) && PATH=$(NINJA_DIR):${PATH} ninja mkdir -p $(WASI_BIN_DIR) cp $(WASI_OBJ_DIR)/mono/mini/*.a $(WASI_OBJ_DIR)/libmono-wasi-driver.a $(WASI_BIN_DIR) + rm -rf $(WASI_BIN_DIR)/libmono-component-hot_reload-static.a + rm -rf $(WASI_BIN_DIR)/libmono-component-diagnostics_tracing-static.a mkdir -p $(WASI_BIN_DIR)/include/mono-wasi cp mono-wasi-driver/*.h $(WASI_BIN_DIR)/include/mono-wasi $(WASI_SDK_CLANG): mkdir -p $(WASI_OBJ_DIR) cd $(WASI_OBJ_DIR) && \ - wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-$(WASI_SDK_VERSION)/wasi-sdk-$(WASI_SDK_VERSION).0-linux.tar.gz && \ + wget -q https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-$(WASI_SDK_VERSION)/wasi-sdk-$(WASI_SDK_VERSION).0-linux.tar.gz && \ tar xf wasi-sdk-*.tar.gz + +.stamp-wasmtime-$(WASMTIME_VERSION): + @echo "** Provisioning wasmtime $(WASMTIME_VERSION) **" + rm -Rf $(WASMTIME_DIR) && \ + wget -q https://github.com/bytecodealliance/wasmtime/releases/download/$(WASMTIME_VERSION)/$(WASMTIME_DIR_NAME).tar.xz -O - | tar -C `dirname $(WASMTIME_DIR)` -Jxf - && \ + touch $@ + +.stamp-ninja-$(NINJA_VERSION): + @echo "** Provisioning ninja $(NINJA_VERSION) **" + rm -Rf $(NINJA_DIR); \ + mkdir $(NINJA_DIR) && \ + wget -q https://github.com/ninja-build/ninja/releases/download/v1.11.0/ninja-linux.zip -O $(NINJA_DIR)/ninja.zip && \ + (cd $(NINJA_DIR) && unzip -q ninja.zip && rm ninja.zip) && \ + touch $@ + +provision-deps: .stamp-wasmtime-$(WASMTIME_VERSION) .stamp-ninja-$(NINJA_VERSION) + @echo "-------------------------------------------" + @echo "** Installed wasmtime in $(WASMTIME_DIR)" + @echo "** Installed ninja in $(NINJA_DIR)" diff --git a/src/mono/wasi/Makefile.variable b/src/mono/wasi/Makefile.variable index fb1d6d5a6e5de0..ef8b91b245c246 100644 --- a/src/mono/wasi/Makefile.variable +++ b/src/mono/wasi/Makefile.variable @@ -1,8 +1,16 @@ TOP=$(realpath $(CURDIR)/../../..) CONFIG?=Release +RUNTIME_CONFIG?=Release WASI_SDK_VERSION=12 -WASI_OBJ_DIR=$(TOP)/artifacts/obj/mono/Wasi.$(CONFIG) -WASI_BIN_DIR=$(TOP)/artifacts/bin/mono/Wasi.$(CONFIG) +WASI_OBJ_DIR=$(TOP)/artifacts/obj/mono/Wasi.$(RUNTIME_CONFIG) +WASI_BIN_DIR=$(TOP)/artifacts/bin/mono/Wasi.$(RUNTIME_CONFIG) WASI_SDK_ROOT=$(WASI_OBJ_DIR)/wasi-sdk-$(WASI_SDK_VERSION).0 WASI_SDK_CLANG=$(WASI_SDK_ROOT)/bin/clang + +WASMTIME_VERSION=v0.39.1 +WASMTIME_DIR_NAME=wasmtime-$(WASMTIME_VERSION)-x86_64-linux +WASMTIME_DIR=$(TOP)/artifacts/bin/$(WASMTIME_DIR_NAME) + +NINJA_VERSION=v1.11.0 +NINJA_DIR=$(TOP)/artifacts/bin/ninja diff --git a/src/mono/wasi/README.md b/src/mono/wasi/README.md index b4037f7701593b..8b17456eb864c0 100644 --- a/src/mono/wasi/README.md +++ b/src/mono/wasi/README.md @@ -8,6 +8,18 @@ The mechanism for executing .NET code in a WASI runtime environment is equivalen ## How to build the runtime +### 1. Build the WASM runtime + +To build the wasi runtime we need the file `wasm_m2n_invoke.g.h` which is generated when compiling wasm runtime + +``` +make -C src/mono/wasm provision-wasm +export EMDK_PATH=[path_printed_by_provision_wasm] +./build.sh mono+libs -os browser +``` + +### 2. Build the WASI runtime + Currently this can only be built in Linux or WSL (tested on Windows 11). Simply run `make` in this directory. It will automatically download and use [WASI SDK](https://github.com/WebAssembly/wasi-sdk). The resulting libraries are placed in `(repo_root)/artifacts/bin/mono/Wasi.Release`. @@ -47,6 +59,32 @@ You'll need to update these paths to match the location where you extracted the Finally, you can build and run the sample: ``` -cd samples/console +cd sample/console make run ``` + +### 4. Debug it + +Also, you can build and debug the sample: + +``` +cd sample/console +make debug +``` + +Using Visual Studio code, add a breakpoint on Program.cs line 17. +Download the Mono Debug extension and configure a launch.json like this: +``` +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Attach", + "type": "mono", + "request": "attach", + "address": "localhost", + "port": 64000 + } + ] +} +``` \ No newline at end of file diff --git a/src/mono/wasi/mono-wasi-driver/driver.c b/src/mono/wasi/mono-wasi-driver/driver.c index d101ddf29866f2..78b1bfa94e95c0 100644 --- a/src/mono/wasi/mono-wasi-driver/driver.c +++ b/src/mono/wasi/mono-wasi-driver/driver.c @@ -19,6 +19,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include @@ -39,6 +45,7 @@ int mono_wasm_register_root (char *start, size_t size, const char *name); void mono_wasm_deregister_root (char *addr); void mono_ee_interp_init (const char *opts); +void mono_marshal_ilgen_init (void); void mono_marshal_lightweight_init (void); void mono_method_builder_ilgen_init (void); void mono_sgen_mono_ilgen_init (void); @@ -244,6 +251,14 @@ int32_t SystemNative_Stat2(const char* path, FileStatus* output) ? 0x4000 // Dir : 0x8000; // File + // Never fail when looking for the root directory. Even if the WASI host isn't giving us filesystem access + // (which is the default), we need the root directory to appear to exist, otherwise things like ASP.NET Core + // will fail by default, whether or not it needs to read anything from the filesystem. + if (ret != 0 && path[0] == '/' && path[1] == 0) { + output->Mode = 0x4000; // Dir + return 0; + } + //printf("SystemNative_Stat2 for %s has ISDIR=%i and will return mode %i; ret=%i\n", path, S_ISDIR (stat_result.st_mode), output->Mode, ret); return ret; @@ -309,6 +324,15 @@ static PinvokeImport SystemGlobalizationNativeImports [] = { {NULL, NULL} }; +int SystemCryptoNativeBrowser_CanUseSubtleCryptoImpl() { + return 0; +} + +static PinvokeImport SystemSecurityCryptographyNativeBrowserImports [] = { + {"SystemCryptoNativeBrowser_CanUseSubtleCryptoImpl", SystemCryptoNativeBrowser_CanUseSubtleCryptoImpl }, + {NULL, NULL} +}; + static void* wasm_dl_load (const char *name, int flags, char **err, void *user_data) { @@ -316,6 +340,9 @@ wasm_dl_load (const char *name, int flags, char **err, void *user_data) return SystemNativeImports; if (!strcmp (name, "libSystem.Globalization.Native")) return SystemGlobalizationNativeImports; + if (!strcmp (name, "libSystem.Security.Cryptography.Native.Browser")) + return SystemSecurityCryptographyNativeBrowserImports; + //printf("In wasm_dl_load for name %s but treating as NOT FOUND\n", name); return 0; } @@ -394,7 +421,7 @@ cleanup_runtime_config (MonovmRuntimeConfigArguments *args, void *user_data) } void -mono_wasm_load_runtime (const char *unused, int debug_level) +mono_wasm_load_runtime (const char *argv, int debug_level) { const char *interp_opts = ""; @@ -412,6 +439,18 @@ mono_wasm_load_runtime (const char *unused, int debug_level) // corlib assemblies. monoeg_g_setenv ("COMPlus_DebugWriteToStdErr", "1", 0); #endif + char* debugger_fd = monoeg_g_getenv ("DEBUGGER_FD"); + if (debugger_fd != 0) + { + const char *debugger_str = "--debugger-agent=transport=wasi_socket,debugger_fd=%-2s,loglevel=0"; + char *debugger_str_with_fd = (char *)malloc (sizeof (char) * (strlen(debugger_str) + strlen(debugger_fd) + 1)); + snprintf (debugger_str_with_fd, strlen(debugger_str) + strlen(debugger_fd) + 1, debugger_str, debugger_fd); + mono_jit_parse_options (1, &debugger_str_with_fd); + mono_debug_init (MONO_DEBUG_FORMAT_MONO); + // Disable optimizations which interfere with debugging + interp_opts = "-all"; + free (debugger_str_with_fd); + } // When the list of app context properties changes, please update RuntimeConfigReservedProperties for // target _WasmGenerateRuntimeConfig in WasmApp.targets file const char *appctx_keys[2]; @@ -452,20 +491,8 @@ mono_wasm_load_runtime (const char *unused, int debug_level) mono_jit_set_aot_mode (MONO_AOT_MODE_INTERP_ONLY); - /* - * debug_level > 0 enables debugging and sets the debug log level to debug_level - * debug_level == 0 disables debugging and enables interpreter optimizations - * debug_level < 0 enabled debugging and disables debug logging. - * - * Note: when debugging is enabled interpreter optimizations are disabled. - */ - if (debug_level) { - // Disable optimizations which interfere with debugging - interp_opts = "-all"; - mono_wasm_enable_debugging (debug_level); - } - mono_ee_interp_init (interp_opts); + mono_marshal_lightweight_init (); mono_marshal_ilgen_init (); mono_method_builder_ilgen_init (); mono_sgen_mono_ilgen_init (); @@ -643,6 +670,7 @@ mono_wasm_string_get_utf8 (MonoString *str) return mono_string_to_utf8 (str); //XXX JS is responsible for freeing this } +MonoString * mono_wasm_string_from_js (const char *str) { if (str) @@ -778,4 +806,4 @@ MonoMethod* lookup_dotnet_method(const char* assembly_name, const char* namespac MonoMethod* method = mono_wasm_assembly_find_method (class, method_name, num_params); assert (method); return method; -} +} \ No newline at end of file diff --git a/src/mono/wasi/mono-wasi-driver/driver.h b/src/mono/wasi/mono-wasi-driver/driver.h index af5043e0a27a63..a27f2b8480614d 100644 --- a/src/mono/wasi/mono-wasi-driver/driver.h +++ b/src/mono/wasi/mono-wasi-driver/driver.h @@ -3,6 +3,7 @@ #include #include +#include void mono_wasm_load_runtime (const char *unused, int debug_level); int mono_wasm_add_assembly (const char *name, const unsigned char *data, unsigned int size); diff --git a/src/mono/wasi/mono-wasi-driver/stubs.c b/src/mono/wasi/mono-wasi-driver/stubs.c index d6f3d0d0a75378..6c09722ee9971f 100644 --- a/src/mono/wasi/mono-wasi-driver/stubs.c +++ b/src/mono/wasi/mono-wasi-driver/stubs.c @@ -10,7 +10,7 @@ int sem_destroy(int x) { return 0; } int sem_post(int x) { return 0; } int sem_wait(int x) { return 0; } int sem_trywait(int x) { return 0; } -int sem_timedwait (int *sem, const struct timespec *abs_timeout) { assert(0); return 0; } +int sem_timedwait (int *sem, void *abs_timeout) { assert(0); return 0; } int __errno_location() { return 0; } diff --git a/src/mono/wasi/sample/SampleMakefile.variable b/src/mono/wasi/sample/SampleMakefile.variable index ba14317ec54cba..99f7e6916e9cdc 100644 --- a/src/mono/wasi/sample/SampleMakefile.variable +++ b/src/mono/wasi/sample/SampleMakefile.variable @@ -4,15 +4,17 @@ COMPILE_FLAGS=\ $(WASI_BIN_DIR)/*.a \ $(BROWSER_WASM_RUNTIME_PATH)/native/libSystem.Native.a \ --sysroot=$(WASI_SDK_ROOT)/share/wasi-sysroot \ - -I$(TOP)/src/mono + -I$(TOP)/src/mono -I$(TOP)/src/native/public LINK_FLAGS=\ -Wl,--export=malloc,--export=free,--export=__heap_base,--export=__data_end \ -Wl,-z,stack-size=1048576,--initial-memory=5242880,--max-memory=52428800,-lwasi-emulated-mman ifndef DOTNET_ROOT -$(error DOTNET_ROOT is undefined. Example: export DOTNET_ROOT=~/dotnet7) +DOTNET_ROOT=$(TOP)/.dotnet +echo "DOTNET_ROOT is undefined. Example: export DOTNET_ROOT=~/dotnet7. Defaulting to $(DOTNET_ROOT) endif ifndef BROWSER_WASM_RUNTIME_PATH -$(error BROWSER_WASM_RUNTIME_PATH is undefined. Example: export BROWSER_WASM_RUNTIME_PATH=$(DOTNET_ROOT)/packs/Microsoft.NETCore.App.Runtime.Mono.browser-wasm/7.0.0-alpha.1.22061.11/runtimes/browser-wasm) +BROWSER_WASM_RUNTIME_PATH=$(TOP)/artifacts/bin/microsoft.netcore.app.runtime.browser-wasm/$(CONFIG)/runtimes/browser-wasm +echo "BROWSER_WASM_RUNTIME_PATH is undefined. Example: export BROWSER_WASM_RUNTIME_PATH=$(DOTNET_ROOT)/packs/Microsoft.NETCore.App.Runtime.Mono.browser-wasm/7.0.0-alpha.1.22061.11/runtimes/browser-wasm). Defaulting to $(BROWSER_WASM_RUNTIME_PATH)" endif diff --git a/src/mono/wasi/sample/console/Makefile b/src/mono/wasi/sample/console/Makefile index bc9fd14dcb7510..b4d731f9d3f37b 100644 --- a/src/mono/wasi/sample/console/Makefile +++ b/src/mono/wasi/sample/console/Makefile @@ -1,12 +1,15 @@ include ../../Makefile.variable include ../SampleMakefile.variable -BIN_DIR=WasiConsoleApp/bin/$(CONFIG)/net7.0 +BIN_DIR=WasiConsoleApp/bin/Debug/net7.0 all: console.wasm $(BIN_DIR)/WasiConsoleApp.dll run: all - wasmtime --dir=. console.wasm + @(which wasmtime || PATH=$(WASMTIME_DIR):${PATH} which wasmtime); \ + test "$$?" -ne 0 \ + && echo "wasmtime not found. Either install that yourself, or use 'make provision-deps' to install one." \ + || PATH=$(WASMTIME_DIR):${PATH} wasmtime --dir=. console.wasm run-wasmer: all wasmer --dir=. console.wasm @@ -15,6 +18,11 @@ console.wasm: main.c $(WASI_SDK_CLANG) main.c -o console.wasm $(COMPILE_FLAGS) $(LINK_FLAGS) $(BIN_DIR)/WasiConsoleApp.dll: WasiConsoleApp/*.csproj WasiConsoleApp/*.cs - cd WasiConsoleApp && $(DOTNET_ROOT)/dotnet build -c $(CONFIG) + find $(BROWSER_WASM_RUNTIME_PATH) -type f + cd WasiConsoleApp && $(DOTNET_ROOT)/dotnet build -c Debug touch $(BIN_DIR)/*.dll cp -R $(BROWSER_WASM_RUNTIME_PATH) $(BIN_DIR)/runtime + +debug: all +#wasmtime has a bug when passing --tcplisten and --dir, to make it work we should use this PR https://github.com/bytecodealliance/wasmtime/pull/3995 and then the fd of the socket will be 4. + PATH=$(WASMTIME_DIR):${PATH} wasmtime --tcplisten localhost:64000 --dir=. console.wasm --env DEBUGGER_FD=4 diff --git a/src/mono/wasi/sample/console/WasiConsoleApp/WasiConsoleApp.csproj b/src/mono/wasi/sample/console/WasiConsoleApp/WasiConsoleApp.csproj index 49755d024c8a13..2e577472d68606 100644 --- a/src/mono/wasi/sample/console/WasiConsoleApp/WasiConsoleApp.csproj +++ b/src/mono/wasi/sample/console/WasiConsoleApp/WasiConsoleApp.csproj @@ -4,6 +4,7 @@ Exe net7.0 true + embedded diff --git a/src/mono/wasi/sample/console/main.c b/src/mono/wasi/sample/console/main.c index 4ee373135cd728..8647c4878f2aba 100644 --- a/src/mono/wasi/sample/console/main.c +++ b/src/mono/wasi/sample/console/main.c @@ -4,7 +4,7 @@ int main() { // Assume the runtime pack has been copied into the output directory as 'runtime' // Otherwise we have to mount an unrelated part of the filesystem within the WASM environment - const char* app_base_dir = "./WasiConsoleApp/bin/Release/net7.0"; + const char* app_base_dir = "./WasiConsoleApp/bin/Debug/net7.0"; char* assemblies_path; asprintf(&assemblies_path, "%s:%s/runtime/native:%s/runtime/lib/net7.0", app_base_dir, app_base_dir, app_base_dir); diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj index 5d7f1facca07a9..9c362aa5ed513a 100644 --- a/src/mono/wasm/wasm.proj +++ b/src/mono/wasm/wasm.proj @@ -388,5 +388,4 @@ - From ec0662c8b53058b9e80abab521abb3f50988d3cf Mon Sep 17 00:00:00 2001 From: madelson <1269046+madelson@users.noreply.github.com> Date: Fri, 12 Aug 2022 12:42:45 -0400 Subject: [PATCH 18/56] Improve NullabilityInfoContext behavior for ByRef types. (#73520) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Improve NullabilityInfoContext behavior for ByRef types. fix #72320 Co-authored-by: Buyaa Namnan Co-authored-by: Michal Strehovský --- .../Reflection/NullabilityInfoContext.cs | 44 ++-- .../Reflection/NullabilityInfoContextTests.cs | 214 ++++++++++++++++-- 2 files changed, 228 insertions(+), 30 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/NullabilityInfoContext.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/NullabilityInfoContext.cs index 57727d1b0dc3e3..67590653f595ac 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/NullabilityInfoContext.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/NullabilityInfoContext.cs @@ -141,7 +141,7 @@ private static void CheckNullabilityAttributes(NullabilityInfo nullability, ILis else if ((attribute.AttributeType.Name == "MaybeNullAttribute" || attribute.AttributeType.Name == "MaybeNullWhenAttribute") && codeAnalysisReadState == NullabilityState.Unknown && - !nullability.Type.IsValueType) + !IsValueTypeOrValueTypeByRef(nullability.Type)) { codeAnalysisReadState = NullabilityState.Nullable; } @@ -151,7 +151,7 @@ private static void CheckNullabilityAttributes(NullabilityInfo nullability, ILis } else if (attribute.AttributeType.Name == "AllowNullAttribute" && codeAnalysisWriteState == NullabilityState.Unknown && - !nullability.Type.IsValueType) + !IsValueTypeOrValueTypeByRef(nullability.Type)) { codeAnalysisWriteState = NullabilityState.Nullable; } @@ -327,7 +327,7 @@ private NullabilityInfo GetNullabilityInfo(MemberInfo memberInfo, Type type, Nul int index = 0; NullabilityInfo nullability = GetNullabilityInfo(memberInfo, type, parser, ref index); - if (!type.IsValueType && nullability.ReadState != NullabilityState.Unknown) + if (nullability.ReadState != NullabilityState.Unknown) { TryLoadGenericMetaTypeNullability(memberInfo, nullability); } @@ -340,19 +340,22 @@ private NullabilityInfo GetNullabilityInfo(MemberInfo memberInfo, Type type, Nul NullabilityState state = NullabilityState.Unknown; NullabilityInfo? elementState = null; NullabilityInfo[] genericArgumentsState = Array.Empty(); - Type? underlyingType = type; + Type underlyingType = type; - if (type.IsValueType) + if (underlyingType.IsByRef || underlyingType.IsPointer) { - underlyingType = Nullable.GetUnderlyingType(type); + underlyingType = underlyingType.GetElementType()!; + } - if (underlyingType != null) + if (underlyingType.IsValueType) + { + if (Nullable.GetUnderlyingType(underlyingType) is { } nullableUnderlyingType) { + underlyingType = nullableUnderlyingType; state = NullabilityState.Nullable; } else { - underlyingType = type; state = NullabilityState.NotNull; } @@ -369,9 +372,9 @@ private NullabilityInfo GetNullabilityInfo(MemberInfo memberInfo, Type type, Nul state = contextState; } - if (type.IsArray) + if (underlyingType.IsArray) { - elementState = GetNullabilityInfo(memberInfo, type.GetElementType()!, parser, ref index); + elementState = GetNullabilityInfo(memberInfo, underlyingType.GetElementType()!, parser, ref index); } } @@ -468,6 +471,12 @@ private void CheckGenericParameters(NullabilityInfo nullability, MemberInfo meta { CheckGenericParameters(elementNullability, metaMember, metaType.GetElementType()!, reflectedType); } + // We could also follow this branch for metaType.IsPointer, but since pointers must be unmanaged this + // will be a no-op regardless + else if (metaType.IsByRef) + { + CheckGenericParameters(nullability, metaMember, metaType.GetElementType()!, reflectedType); + } } } @@ -482,6 +491,11 @@ private bool TryUpdateGenericParameterNullability(NullabilityInfo nullability, T return true; } + if (IsValueTypeOrValueTypeByRef(nullability.Type)) + { + return true; + } + var state = NullabilityState.Unknown; if (CreateParser(genericParameter.GetCustomAttributesData()).ParseNullableState(0, ref state)) { @@ -549,9 +563,10 @@ static int CountNullabilityStates(Type type) } return count; } - if (underlyingType.IsArray) + + if (underlyingType.HasElementType) { - return 1 + CountNullabilityStates(underlyingType.GetElementType()!); + return (underlyingType.IsArray ? 1 : 0) + CountNullabilityStates(underlyingType.GetElementType()!); } return type.IsValueType ? 0 : 1; @@ -560,7 +575,7 @@ static int CountNullabilityStates(Type type) private bool TryPopulateNullabilityInfo(NullabilityInfo nullability, NullableAttributeStateParser parser, ref int index) { - bool isValueType = nullability.Type.IsValueType; + bool isValueType = IsValueTypeOrValueTypeByRef(nullability.Type); if (!isValueType) { var state = NullabilityState.Unknown; @@ -606,6 +621,9 @@ private static NullabilityState TranslateByte(byte b) => _ => NullabilityState.Unknown }; + private static bool IsValueTypeOrValueTypeByRef(Type type) => + type.IsValueType || ((type.IsByRef || type.IsPointer) && type.GetElementType()!.IsValueType); + private readonly struct NullableAttributeStateParser { private static readonly object UnknownByte = (byte)0; diff --git a/src/libraries/System.Runtime/tests/System/Reflection/NullabilityInfoContextTests.cs b/src/libraries/System.Runtime/tests/System/Reflection/NullabilityInfoContextTests.cs index 89f2fe49c9c499..aa0e9c0d464939 100644 --- a/src/libraries/System.Runtime/tests/System/Reflection/NullabilityInfoContextTests.cs +++ b/src/libraries/System.Runtime/tests/System/Reflection/NullabilityInfoContextTests.cs @@ -670,6 +670,139 @@ public void MethodParametersTest(string methodName, NullabilityState stringState Assert.Null(dictionaryNullability.ElementType); } + public static IEnumerable MethodByRefParametersTestData() => new[] + { + new object[] { -1, NullabilityState.Nullable }, // ref readonly int? + new object[] { 0, NullabilityState.Nullable }, // out int? a + new object[] { 1, NullabilityState.NotNull }, // out string b + new object[] { 2, NullabilityState.Nullable }, // ref object? c + new object[] { 3, NullabilityState.NotNull }, // ref Type d + new object[] { 4, NullabilityState.Nullable }, // in decimal? e + new object[] { 5, NullabilityState.NotNull }, // in ICloneable f + }; + + [Theory] + [SkipOnMono("Nullability attributes trimmed on Mono")] + [MemberData(nameof(MethodByRefParametersTestData))] + public void MethodByRefParametersTest(int parameterIndex, NullabilityState state) + { + MethodInfo method = typeof(TypeWithNotNullContext).GetMethod(nameof(TypeWithNotNullContext.MethodWithByRefs))!; + ParameterInfo parameter = parameterIndex == -1 ? method.ReturnParameter : method.GetParameters()[parameterIndex]; + NullabilityInfo info = nullabilityContext.Create(parameter); + + Assert.Equal(parameter.ParameterType, info.Type); + Assert.Equal(state, info.ReadState); + Assert.Equal(state, info.WriteState); + } + + public static IEnumerable MethodGenericByRefParametersTestData() => new[] +{ + // ref Tuple + new object?[] { -1, NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable }, + // out KeyValuePair a + new object?[] { 0, NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.NotNull, null }, + // ref Tuple? b + new object?[] { 1, NullabilityState.Nullable, NullabilityState.Nullable, NullabilityState.NotNull, null }, + // in KeyValuePair? c + new object?[] { 2, NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable, null }, + }; + + [Theory] + [SkipOnMono("Nullability attributes trimmed on Mono")] + [MemberData(nameof(MethodGenericByRefParametersTestData))] + public void MethodGenericByRefParametersTest( + int parameterIndex, + NullabilityState state, + NullabilityState genericArgument0State, + NullabilityState genericArgument1State, + NullabilityState? genericArgument2State) + { + MethodInfo method = typeof(TypeWithNotNullContext).GetMethod(nameof(TypeWithNotNullContext.MethodWithGenericByRefs))!; + ParameterInfo parameter = parameterIndex == -1 ? method.ReturnParameter : method.GetParameters()[parameterIndex]; + NullabilityInfo info = nullabilityContext.Create(parameter); + Assert.Equal(parameter.ParameterType, info.Type); + Assert.Equal(state, info.ReadState); + Assert.Equal(state, info.WriteState); + + NullabilityState?[] genericArgumentStates = new[] { genericArgument0State, genericArgument1State, genericArgument2State }; + for (var i = 0; i < genericArgumentStates.Length; ++i) + { + if (genericArgumentStates[i] is null) + { + Assert.Equal(i, info.GenericTypeArguments.Length); + break; + } + + Assert.Equal(genericArgumentStates[i], info.GenericTypeArguments[i].ReadState); + Assert.Equal(genericArgumentStates[i], info.GenericTypeArguments[i].WriteState); + } + } + + [Fact] + [SkipOnMono("Nullability attributes trimmed on Mono")] + public void ConstrainedGenericMethodWithByRefTest() + { + // ref T ConstrainedGenericMethodWithByRef(out T[] array) where T : class? + MethodInfo method = typeof(TypeWithNotNullContext).GetMethod(nameof(TypeWithNotNullContext.ConstrainedGenericMethodWithByRef))! + .MakeGenericMethod(typeof(string)); + + NullabilityInfo info = nullabilityContext.Create(method.ReturnParameter); + Assert.Equal(method.ReturnType, info.Type); + Assert.Equal(NullabilityState.Nullable, info.ReadState); + Assert.Equal(NullabilityState.Nullable, info.WriteState); + + info = nullabilityContext.Create(method.GetParameters()[0]); + Assert.Equal(typeof(string[]).MakeByRefType(), info.Type); + Assert.Equal(NullabilityState.NotNull, info.ReadState); + Assert.Equal(NullabilityState.NotNull, info.WriteState); + Assert.Equal(typeof(string), info.ElementType!.Type); + Assert.Equal(NullabilityState.Nullable, info.ElementType.ReadState); + Assert.Equal(NullabilityState.Nullable, info.ElementType.WriteState); + } + + [Fact] + [SkipOnMono("Nullability attributes trimmed on Mono")] + public void MethodWithPointersTest() + { + // MethodWithPointers(int* a, int?* b) + MethodInfo method = typeof(TypeWithNotNullContext).GetMethod(nameof(TypeWithNotNullContext.MethodWithPointers))!; + ParameterInfo[] parameters = method.GetParameters(); + + NullabilityInfo info = nullabilityContext.Create(parameters[0]); + Assert.Equal(parameters[0].ParameterType, info.Type); + Assert.Equal(NullabilityState.NotNull, info.ReadState); + Assert.Equal(NullabilityState.NotNull, info.WriteState); + + info = nullabilityContext.Create(parameters[1]); + Assert.Equal(parameters[1].ParameterType, info.Type); + Assert.Equal(NullabilityState.Nullable, info.ReadState); + Assert.Equal(NullabilityState.Nullable, info.WriteState); + } + + [Fact] + [SkipOnMono("Nullability attributes trimmed on Mono")] + public unsafe void GenericMethodWithPointersTest() + { + // Dummy call to MakeGenericMethod to make the code generated ahead of time + if (string.Empty.Length > 0) + new TypeWithNotNullContext().GenericMethodWithPointers(null, null); + + // GenericMethodWithPointers(T* a, T?* b) + MethodInfo method = typeof(TypeWithNotNullContext).GetMethod(nameof(TypeWithNotNullContext.GenericMethodWithPointers))! + .MakeGenericMethod(typeof(float)); + ParameterInfo[] parameters = method.GetParameters(); + + NullabilityInfo info = nullabilityContext.Create(parameters[0]); + Assert.Equal(parameters[0].ParameterType, info.Type); + Assert.Equal(NullabilityState.NotNull, info.ReadState); + Assert.Equal(NullabilityState.NotNull, info.WriteState); + + info = nullabilityContext.Create(parameters[1]); + Assert.Equal(parameters[1].ParameterType, info.Type); + Assert.Equal(NullabilityState.Nullable, info.ReadState); + Assert.Equal(NullabilityState.Nullable, info.WriteState); + } + public static IEnumerable MethodGenericParametersTestData() { yield return new object[] { "MethodParametersUnknown", NullabilityState.Unknown, NullabilityState.Unknown, NullabilityState.Unknown, NullabilityState.Unknown }; @@ -692,6 +825,34 @@ public void MethodGenericParametersTest(string methodName, NullabilityState para Assert.Equal(dictValue, dictionaryNullability.GenericTypeArguments[1].ReadState); } + [Fact] + [SkipOnMono("Nullability attributes trimmed on Mono")] + public void PropertyWithByRefGenericParametersTest() + { + // ref T? this[in T a, in List b] { get; } + PropertyInfo property = typeof(GenericTest).GetProperty("Item", typeof(string).MakeByRefType())!; + + NullabilityInfo info = nullabilityContext.Create(property); + Assert.Equal(typeof(string).MakeByRefType(), info.Type); + Assert.Equal(NullabilityState.Nullable, info.ReadState); + Assert.Equal(NullabilityState.Unknown, info.WriteState); + + info = nullabilityContext.Create(property.GetIndexParameters()[0]); + Assert.Equal(typeof(string).MakeByRefType(), info.Type); + // in T a is nullable because T is not constrained + Assert.Equal(NullabilityState.Nullable, info.ReadState); + Assert.Equal(NullabilityState.Nullable, info.WriteState); + + info = nullabilityContext.Create(property.GetIndexParameters()[1]); + Assert.Equal(typeof(List).MakeByRefType(), info.Type); + Assert.Equal(NullabilityState.NotNull, info.ReadState); + Assert.Equal(NullabilityState.NotNull, info.WriteState); + info = Assert.Single(info.GenericTypeArguments); + Assert.Equal(typeof(string), info.Type); + Assert.Equal(NullabilityState.Nullable, info.ReadState); + Assert.Equal(NullabilityState.Nullable, info.WriteState); + } + public static IEnumerable StringTypeTestData() { yield return new object[] { "Format", NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.Nullable, new Type[] { typeof(string), typeof(object), typeof(object) } }; @@ -946,7 +1107,7 @@ public static IEnumerable RefReturnData() yield return new object[] { "RefReturnNotNullable", NullabilityState.NotNull, NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.NotNull }; // [return: NotNull]public ref string? RefReturnNotNull([NotNull] ref string? id) yield return new object[] { "RefReturnNotNull", NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable }; - // publiic ref string? RefReturnNullable([AllowNull] ref string id) + // public ref string? RefReturnNullable([AllowNull] ref string id) yield return new object[] { "RefReturnNullable", NullabilityState.Nullable, NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable }; } @@ -1090,12 +1251,18 @@ public void TestNestedGenericInheritanceWithMultipleParameters() Assert.Equal(NullabilityState.NotNull, item3Info.ElementType.WriteState); } - [Fact] - [SkipOnMono("Nullability attributes trimmed on Mono")] - public void TestNullabilityInfoCreationOnPropertiesWithNestedGenericTypeArguments() + public static IEnumerable TestNullabilityInfoCreationOnPropertiesWithNestedGenericTypeArgumentsData() => new[] { - Type type = typeof(TypeWithPropertiesNestingItsGenericTypeArgument); + new object[] { typeof(TypeWithPropertiesNestingItsGenericTypeArgument), NullabilityState.NotNull }, + new object[] { typeof(TypeWithPropertiesNestingItsGenericTypeArgument), NullabilityState.Nullable }, + new object[] { typeof(TypeWithPropertiesNestingItsGenericTypeArgument), NullabilityState.Nullable }, + }; + [Theory] + [SkipOnMono("Nullability attributes trimmed on Mono")] + [MemberData(nameof(TestNullabilityInfoCreationOnPropertiesWithNestedGenericTypeArgumentsData))] + public void TestNullabilityInfoCreationOnPropertiesWithNestedGenericTypeArguments(Type type, NullabilityState expectedGenericArgumentNullability) + { NullabilityInfo shallow1Info = nullabilityContext.Create(type.GetProperty("Shallow1")!); NullabilityInfo deep1Info = nullabilityContext.Create(type.GetProperty("Deep1")!); NullabilityInfo deep2Info = nullabilityContext.Create(type.GetProperty("Deep2")!); @@ -1103,51 +1270,51 @@ public void TestNullabilityInfoCreationOnPropertiesWithNestedGenericTypeArgument NullabilityInfo deep4Info = nullabilityContext.Create(type.GetProperty("Deep4")!); NullabilityInfo deep5Info = nullabilityContext.Create(type.GetProperty("Deep5")!); - //public Tuple? Shallow1 { get; set; } + // public Tuple? Shallow1 { get; set; } NullabilityInfo info = shallow1Info; Assert.Equal(1, info.GenericTypeArguments.Length); - Assert.Equal(NullabilityState.Nullable, info.GenericTypeArguments[0].ReadState); + Assert.Equal(expectedGenericArgumentNullability, info.GenericTypeArguments[0].ReadState); - //public Tuple>? Deep1 { get; set; } + // public Tuple>? Deep1 { get; set; } info = deep1Info; Assert.Equal(1, info.GenericTypeArguments.Length); Assert.Equal(1, info.GenericTypeArguments[0].GenericTypeArguments.Length); Assert.Equal(NullabilityState.NotNull, info.GenericTypeArguments[0].ReadState); - Assert.Equal(NullabilityState.Nullable, info.GenericTypeArguments[0].GenericTypeArguments[0].ReadState); + Assert.Equal(expectedGenericArgumentNullability, info.GenericTypeArguments[0].GenericTypeArguments[0].ReadState); - //public Tuple, int>? Deep2 { get; set; } + // public Tuple, int>? Deep2 { get; set; } info = deep2Info; Assert.Equal(2, info.GenericTypeArguments.Length); Assert.Equal(1, info.GenericTypeArguments[0].GenericTypeArguments.Length); Assert.Equal(NullabilityState.NotNull, info.GenericTypeArguments[0].ReadState); Assert.Equal(NullabilityState.NotNull, info.GenericTypeArguments[1].ReadState); - Assert.Equal(NullabilityState.Nullable, info.GenericTypeArguments[0].GenericTypeArguments[0].ReadState); + Assert.Equal(expectedGenericArgumentNullability, info.GenericTypeArguments[0].GenericTypeArguments[0].ReadState); - //public Tuple>? Deep3 { get; set; } + // public Tuple>? Deep3 { get; set; } info = deep3Info; Assert.Equal(2, info.GenericTypeArguments.Length); Assert.Equal(1, info.GenericTypeArguments[1].GenericTypeArguments.Length); Assert.Equal(NullabilityState.Nullable, info.GenericTypeArguments[0].ReadState); Assert.Equal(NullabilityState.NotNull, info.GenericTypeArguments[1].ReadState); - Assert.Equal(NullabilityState.Nullable, info.GenericTypeArguments[1].GenericTypeArguments[0].ReadState); + Assert.Equal(expectedGenericArgumentNullability, info.GenericTypeArguments[1].GenericTypeArguments[0].ReadState); - //public Tuple>? Deep4 { get; set; } + // public Tuple>? Deep4 { get; set; } info = deep4Info; Assert.Equal(3, info.GenericTypeArguments.Length); Assert.Equal(1, info.GenericTypeArguments[2].GenericTypeArguments.Length); Assert.Equal(NullabilityState.NotNull, info.GenericTypeArguments[0].ReadState); Assert.Equal(NullabilityState.Nullable, info.GenericTypeArguments[1].ReadState); Assert.Equal(NullabilityState.NotNull, info.GenericTypeArguments[2].ReadState); - Assert.Equal(NullabilityState.Nullable, info.GenericTypeArguments[2].GenericTypeArguments[0].ReadState); + Assert.Equal(expectedGenericArgumentNullability, info.GenericTypeArguments[2].GenericTypeArguments[0].ReadState); - //public Tuple?>? Deep5 { get; set; } + // public Tuple?>? Deep5 { get; set; } info = deep5Info; Assert.Equal(3, info.GenericTypeArguments.Length); Assert.Equal(2, info.GenericTypeArguments[2].GenericTypeArguments.Length); Assert.Equal(NullabilityState.NotNull, info.GenericTypeArguments[0].ReadState); Assert.Equal(NullabilityState.NotNull, info.GenericTypeArguments[1].ReadState); Assert.Equal(NullabilityState.Nullable, info.GenericTypeArguments[2].ReadState); - Assert.Equal(NullabilityState.Nullable, info.GenericTypeArguments[2].GenericTypeArguments[0].ReadState); + Assert.Equal(expectedGenericArgumentNullability, info.GenericTypeArguments[2].GenericTypeArguments[0].ReadState); Assert.Equal(NullabilityState.NotNull, info.GenericTypeArguments[2].GenericTypeArguments[1].ReadState); } } @@ -1298,6 +1465,17 @@ public void MethodNullNonNullNonNon(string? s, IDictionary dict public void MethodNonNullNonNullNotNull(string s, [NotNull] IDictionary? dict) { dict = new Dictionary(); } public void MethodNullNonNullNullNon(string? s, IDictionary dict) { } public void MethodAllowNullNonNonNonNull([AllowNull] string s, IDictionary? dict) { } + + public ref readonly int? MethodWithByRefs(out int? a, out string b, ref object? c, ref Type d, in decimal? e, in ICloneable f) => + throw new NotImplementedException(); + public ref Tuple MethodWithGenericByRefs( + out KeyValuePair a, + ref Tuple? b, + in KeyValuePair? c) => + throw new NotImplementedException(); + public ref T ConstrainedGenericMethodWithByRef(out T[] array) where T : class? => throw new NotImplementedException(); + public unsafe void MethodWithPointers(int* a, int?* b) { } + public unsafe void GenericMethodWithPointers(T* a, T?* b) where T : unmanaged { } } public struct GenericStruct { } @@ -1353,6 +1531,8 @@ public void MethodParametersUnknown(T s, IDictionary dict) { } public List? MethodNullListNonNullGeneric() => null; public void MethodArgsNullGenericNullDictValueGeneric(T? s, IDictionary? dict) { } public void MethodArgsGenericDictValueNullGeneric(T s, IDictionary dict) { } + + public ref T? this[in T a, in List b] => throw new NotImplementedException(); } internal class GenericTestConstrainedNotNull where T : notnull From e8adb12e9c3c1c7f83e5988db756a02558620516 Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Fri, 12 Aug 2022 18:58:43 +0200 Subject: [PATCH 19/56] Access the User-Agent for CONNECT tunnels early (#73331) --- .../SocketsHttpHandler/HttpConnectionPool.cs | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs index e560915288e56a..4a2362329b9f79 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs @@ -43,6 +43,9 @@ internal sealed class HttpConnectionPool : IDisposable /// If true, the will persist across a network change. If false, it will be reset to . private bool _persistAuthority; + /// The User-Agent header to use when creating a CONNECT tunnel. + private string? _connectTunnelUserAgent; + /// /// When an Alt-Svc authority fails due to 421 Misdirected Request, it is placed in the blocklist to be ignored /// for milliseconds. Initialized on first use. @@ -1482,6 +1485,15 @@ public ValueTask SendWithProxyAuthAsync(HttpRequestMessage public ValueTask SendAsync(HttpRequestMessage request, bool async, bool doRequestAuth, CancellationToken cancellationToken) { + // We need the User-Agent header when we send a CONNECT request to the proxy. + // We must read the header early, before we return the ownership of the request back to the user. + if ((Kind is HttpConnectionKind.ProxyTunnel or HttpConnectionKind.SslProxyTunnel) && + request.HasHeaders && + request.Headers.NonValidated.TryGetValues(HttpKnownHeaderNames.UserAgent, out HeaderStringValues userAgent)) + { + _connectTunnelUserAgent = userAgent.ToString(); + } + if (doRequestAuth && Settings._credentials != null) { return AuthenticationHelper.SendWithRequestAuthAsync(request, async, Settings._credentials, Settings._preAuthenticate, this, cancellationToken); @@ -1510,7 +1522,7 @@ public ValueTask SendAsync(HttpRequestMessage request, bool case HttpConnectionKind.ProxyTunnel: case HttpConnectionKind.SslProxyTunnel: - stream = await EstablishProxyTunnelAsync(async, request.HasHeaders ? request.Headers : null, cancellationToken).ConfigureAwait(false); + stream = await EstablishProxyTunnelAsync(async, cancellationToken).ConfigureAwait(false); break; case HttpConnectionKind.SocksTunnel: @@ -1700,7 +1712,7 @@ private async ValueTask ConstructHttp2ConnectionAsync(Stream st return http2Connection; } - private async ValueTask EstablishProxyTunnelAsync(bool async, HttpRequestHeaders? headers, CancellationToken cancellationToken) + private async ValueTask EstablishProxyTunnelAsync(bool async, CancellationToken cancellationToken) { Debug.Assert(_originAuthority != null); @@ -1708,9 +1720,9 @@ private async ValueTask EstablishProxyTunnelAsync(bool async, HttpReques HttpRequestMessage tunnelRequest = new HttpRequestMessage(HttpMethod.Connect, _proxyUri); tunnelRequest.Headers.Host = $"{_originAuthority.IdnHost}:{_originAuthority.Port}"; // This specifies destination host/port to connect to - if (headers != null && headers.TryGetValues(HttpKnownHeaderNames.UserAgent, out IEnumerable? values)) + if (_connectTunnelUserAgent is not null) { - tunnelRequest.Headers.TryAddWithoutValidation(HttpKnownHeaderNames.UserAgent, values); + tunnelRequest.Headers.TryAddWithoutValidation(KnownHeaders.UserAgent.Descriptor, _connectTunnelUserAgent); } HttpResponseMessage tunnelResponse = await _poolManager.SendProxyConnectAsync(tunnelRequest, _proxyUri!, async, cancellationToken).ConfigureAwait(false); From 80aeaa094df95c408a1fd1b530d20b5da2f23575 Mon Sep 17 00:00:00 2001 From: Alhad Deshpande <97085048+alhad-deshpande@users.noreply.github.com> Date: Fri, 12 Aug 2022 22:32:23 +0530 Subject: [PATCH 20/56] [Ppc64le] Added memory patch thunking for function calls (#73616) * [ppc64le] Added memory patch thunking for function calls * Incorporated code review comments and changes in OP_TAILCALL for mempry patching * [ppc64le] Fixed test failures and code refactoring * [ppc64le] Enabled g_assert for code sequence check and removed white spaces * [ppc64le] Fixed build issue in mini-ppc.h * [ppc64le] Incorporated code review comments --- src/mono/mono/mini/cpu-ppc.mdesc | 2 +- src/mono/mono/mini/cpu-ppc64.mdesc | 2 +- src/mono/mono/mini/mini-ppc.c | 64 ++++++++++++++++++++++++++++-- src/mono/mono/mini/mini-ppc.h | 9 +++++ src/mono/mono/mini/tramp-ppc.c | 12 +++++- 5 files changed, 83 insertions(+), 6 deletions(-) diff --git a/src/mono/mono/mini/cpu-ppc.mdesc b/src/mono/mono/mini/cpu-ppc.mdesc index c92495c356fa51..301b96a6530bdf 100644 --- a/src/mono/mono/mini/cpu-ppc.mdesc +++ b/src/mono/mono/mini/cpu-ppc.mdesc @@ -348,4 +348,4 @@ atomic_cas_i4: src1:b src2:i src3:i dest:i len:38 liverange_start: len:0 liverange_end: len:0 -gc_safe_point: clob:c src1:i len:40 +gc_safe_point: clob:c src1:i len:44 diff --git a/src/mono/mono/mini/cpu-ppc64.mdesc b/src/mono/mono/mini/cpu-ppc64.mdesc index 4dca1bb3ab43cf..d2437a34fda45e 100644 --- a/src/mono/mono/mini/cpu-ppc64.mdesc +++ b/src/mono/mono/mini/cpu-ppc64.mdesc @@ -417,4 +417,4 @@ atomic_cas_i8: src1:b src2:i src3:i dest:i len:38 liverange_start: len:0 liverange_end: len:0 -gc_safe_point: clob:c src1:i len:40 +gc_safe_point: clob:c src1:i len:44 diff --git a/src/mono/mono/mini/mini-ppc.c b/src/mono/mono/mini/mini-ppc.c index 30dc899ab6969b..93abd63562ff53 100644 --- a/src/mono/mono/mini/mini-ppc.c +++ b/src/mono/mono/mini/mini-ppc.c @@ -311,7 +311,12 @@ mono_ppc_is_direct_call_sequence (guint32 *code) if (ppc_opcode (code [-2]) == 24 && ppc_opcode (code [-3]) == 31) /* mr/nop */ return is_load_sequence (&code [-8]); else +#if !defined(PPC_USES_FUNCTION_DESCRIPTOR) + /* the memory patch thunk sequence for ppc64le is: lis/ori/sldi/oris/ori/ld/mtlr/blrl */ + return is_load_sequence (&code [-7]); +#else return is_load_sequence (&code [-6]); +#endif } return FALSE; #else @@ -2692,6 +2697,10 @@ emit_float_to_int (MonoCompile *cfg, guchar *code, int dreg, int sreg, int size, static void emit_thunk (guint8 *code, gconstpointer target) { +#if defined(TARGET_POWERPC64) && !defined(PPC_USES_FUNCTION_DESCRIPTOR) + *(guint64*)code = (guint64)target; + code += sizeof (guint64); +#else guint8 *p = code; /* 2 bytes on 32bit, 5 bytes on 64bit */ @@ -2701,6 +2710,7 @@ emit_thunk (guint8 *code, gconstpointer target) ppc_bcctr (code, PPC_BR_ALWAYS, 0); mono_arch_flush_icache (p, code - p); +#endif } static void @@ -2731,9 +2741,14 @@ handle_thunk (MonoCompile *cfg, guchar *code, const guchar *target) } g_assert (*(guint32*)thunks == 0); + + emit_thunk (thunks, target); +#if defined(TARGET_POWERPC64) && !defined(PPC_USES_FUNCTION_DESCRIPTOR) + ppc_load_ptr_sequence (code, PPC_CALL_REG, thunks); +#else ppc_patch (code, thunks); - +#endif cfg->arch.thunks += THUNK_SIZE; cfg->arch.thunks_size -= THUNK_SIZE; } else { @@ -2753,7 +2768,9 @@ handle_thunk (MonoCompile *cfg, guchar *code, const guchar *target) if (orig_target >= thunks && orig_target < thunks + thunks_size) { /* The call already points to a thunk, because of trampolines etc. */ target_thunk = orig_target; - } else { + } +#if (defined(TARGET_POWERPC64) && defined(PPC_USES_FUNCTION_DESCRIPTOR)) || !defined(TARGET_POWERPC64) + else { for (p = thunks; p < thunks + thunks_size; p += THUNK_SIZE) { if (((guint32 *) p) [0] == 0) { /* Free entry */ @@ -2777,7 +2794,7 @@ handle_thunk (MonoCompile *cfg, guchar *code, const guchar *target) } } } - +#endif // g_print ("THUNK: %p %p %p\n", code, target, target_thunk); if (!target_thunk) { @@ -2787,7 +2804,9 @@ handle_thunk (MonoCompile *cfg, guchar *code, const guchar *target) } emit_thunk (target_thunk, target); +#if (defined(TARGET_POWERPC64) && defined(PPC_USES_FUNCTION_DESCRIPTOR)) || !defined(TARGET_POWERPC64) ppc_patch (code, target_thunk); +#endif mono_mini_arch_unlock (); } @@ -2875,6 +2894,9 @@ ppc_patch_full (MonoCompile *cfg, guchar *code, const guchar *target, gboolean i if (prim == 15 || ins == 0x4e800021 || ins == 0x4e800020 || ins == 0x4e800420) { #ifdef TARGET_POWERPC64 +#if !defined(PPC_USES_FUNCTION_DESCRIPTOR) + handle_thunk (cfg, code, target); +#else guint32 *seq = (guint32*)code; guint32 *branch_ins; @@ -2923,6 +2945,7 @@ ppc_patch_full (MonoCompile *cfg, guchar *code, const guchar *target, gboolean i ppc_load_ptr_sequence (code, PPC_CALL_REG, target); #endif mono_arch_flush_icache ((guint8*)seq, 28); +#endif #else guint32 *seq; /* the trampoline code will try to patch the blrl, blr, bcctr */ @@ -3349,6 +3372,10 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_JIT_ICALL_ID, GUINT_TO_POINTER (MONO_JIT_ICALL_mono_break)); if ((FORCE_INDIR_CALL || cfg->method->dynamic) && !cfg->compile_aot) { ppc_load_func (code, PPC_CALL_REG, 0); +#if defined(TARGET_POWERPC64) && !defined(PPC_USES_FUNCTION_DESCRIPTOR) + ppc_ldr (code, PPC_CALL_REG, 0, PPC_CALL_REG); + cfg->thunk_area += THUNK_SIZE; +#endif ppc_mtlr (code, PPC_CALL_REG); ppc_blrl (code); } else { @@ -3794,7 +3821,14 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) ppc_mtctr (code, ppc_r0); ppc_bcctr (code, PPC_BR_ALWAYS, 0); } else { +#if defined(TARGET_POWERPC64) && !defined(PPC_USES_FUNCTION_DESCRIPTOR) + ppc_load_func (code, PPC_CALL_REG, 0); + ppc_ldr (code, PPC_CALL_REG, 0, PPC_CALL_REG); + ppc_mtctr (code, PPC_CALL_REG); + ppc_bcctr (code, PPC_BR_ALWAYS, 0); +#else ppc_b (code, 0); +#endif } break; } @@ -3823,6 +3857,10 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) mono_call_add_patch_info (cfg, call, offset); if ((FORCE_INDIR_CALL || cfg->method->dynamic) && !cfg->compile_aot) { ppc_load_func (code, PPC_CALL_REG, 0); +#if defined(TARGET_POWERPC64) && !defined(PPC_USES_FUNCTION_DESCRIPTOR) + ppc_ldr (code, PPC_CALL_REG, 0, PPC_CALL_REG); + cfg->thunk_area += THUNK_SIZE; +#endif ppc_mtlr (code, PPC_CALL_REG); ppc_blrl (code); } else { @@ -3926,6 +3964,10 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_JIT_ICALL_ID, GUINT_TO_POINTER (MONO_JIT_ICALL_mono_arch_throw_exception)); if ((FORCE_INDIR_CALL || cfg->method->dynamic) && !cfg->compile_aot) { ppc_load_func (code, PPC_CALL_REG, 0); +#if defined(TARGET_POWERPC64) && !defined(PPC_USES_FUNCTION_DESCRIPTOR) + ppc_ldr (code, PPC_CALL_REG, 0, PPC_CALL_REG); + cfg->thunk_area += THUNK_SIZE; +#endif ppc_mtlr (code, PPC_CALL_REG); ppc_blrl (code); } else { @@ -3940,6 +3982,10 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) GUINT_TO_POINTER (MONO_JIT_ICALL_mono_arch_rethrow_exception)); if ((FORCE_INDIR_CALL || cfg->method->dynamic) && !cfg->compile_aot) { ppc_load_func (code, PPC_CALL_REG, 0); +#if defined(TARGET_POWERPC64) && !defined(PPC_USES_FUNCTION_DESCRIPTOR) + ppc_ldr (code, PPC_CALL_REG, 0, PPC_CALL_REG); + cfg->thunk_area += THUNK_SIZE; +#endif ppc_mtlr (code, PPC_CALL_REG); ppc_blrl (code); } else { @@ -4584,6 +4630,10 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) GUINT_TO_POINTER (MONO_JIT_ICALL_mono_threads_state_poll)); if ((FORCE_INDIR_CALL || cfg->method->dynamic) && !cfg->compile_aot) { ppc_load_func (code, PPC_CALL_REG, 0); +#if defined(TARGET_POWERPC64) && !defined(PPC_USES_FUNCTION_DESCRIPTOR) + ppc_ldr (code, PPC_CALL_REG, 0, PPC_CALL_REG); + cfg->thunk_area += THUNK_SIZE; +#endif ppc_mtlr (code, PPC_CALL_REG); ppc_blrl (code); } else { @@ -5185,6 +5235,10 @@ mono_arch_emit_prolog (MonoCompile *cfg) GUINT_TO_POINTER (MONO_JIT_ICALL_mono_tls_get_lmf_addr_extern)); if ((FORCE_INDIR_CALL || cfg->method->dynamic) && !cfg->compile_aot) { ppc_load_func (code, PPC_CALL_REG, 0); +#if defined(TARGET_POWERPC64) && !defined(PPC_USES_FUNCTION_DESCRIPTOR) + ppc_ldr (code, PPC_CALL_REG, 0, PPC_CALL_REG); + cfg->thunk_area += THUNK_SIZE; +#endif ppc_mtlr (code, PPC_CALL_REG); ppc_blrl (code); } else { @@ -5466,6 +5520,10 @@ mono_arch_emit_exceptions (MonoCompile *cfg) patch_info->ip.i = code - cfg->native_code; if (FORCE_INDIR_CALL || cfg->method->dynamic) { ppc_load_func (code, PPC_CALL_REG, 0); +#if defined(TARGET_POWERPC64) && !defined(PPC_USES_FUNCTION_DESCRIPTOR) + ppc_ldr (code, PPC_CALL_REG, 0, PPC_CALL_REG); + cfg->thunk_area += THUNK_SIZE; +#endif ppc_mtctr (code, PPC_CALL_REG); ppc_bcctr (code, PPC_BR_ALWAYS, 0); } else { diff --git a/src/mono/mono/mini/mini-ppc.h b/src/mono/mono/mini/mini-ppc.h index 688ca1f7992624..0b962aac233d24 100644 --- a/src/mono/mono/mini/mini-ppc.h +++ b/src/mono/mono/mini/mini-ppc.h @@ -33,7 +33,16 @@ #define MONO_ARCH_CODE_ALIGNMENT 32 #ifdef TARGET_POWERPC64 +#if !defined(PPC_USES_FUNCTION_DESCRIPTOR) +#define THUNK_SIZE 8 +#define GET_MEMORY_SLOT_THUNK_ADDRESS(c) \ + ((guint64)(((c)) [0] & 0x0000ffff) << 48) \ + + ((guint64)(((c)) [1] & 0x0000ffff) << 32) \ + + ((guint64)(((c)) [3] & 0x0000ffff) << 16) \ + + (guint64)(((c)) [4] & 0x0000ffff) +#else #define THUNK_SIZE ((2 + 5) * 4) +#endif #else #define THUNK_SIZE ((2 + 2) * 4) #endif diff --git a/src/mono/mono/mini/tramp-ppc.c b/src/mono/mono/mini/tramp-ppc.c index 3a78709c6f8cb9..59bcb275a48609 100644 --- a/src/mono/mono/mini/tramp-ppc.c +++ b/src/mono/mono/mini/tramp-ppc.c @@ -669,7 +669,17 @@ mono_arch_get_call_target (guint8 *code) guint8 *target = code - 4 + (disp * 4); return target; - } else { + } +#if defined(TARGET_POWERPC64) && !defined(PPC_USES_FUNCTION_DESCRIPTOR) + else if (((guint32*)(code - 32)) [0] >> 26 == 15) { + guint8 *thunk = GET_MEMORY_SLOT_THUNK_ADDRESS((guint32*)(code - 32)); + return thunk; + } else if (((guint32*)(code - 4)) [0] >> 26 == 15) { + guint8 *thunk = GET_MEMORY_SLOT_THUNK_ADDRESS((guint32*)(code - 4)); + return thunk; + } +#endif + else { return NULL; } } From da24b54466d693c7fe8175ece3b60119b0ed841f Mon Sep 17 00:00:00 2001 From: Jose Perez Rodriguez Date: Fri, 12 Aug 2022 10:21:25 -0700 Subject: [PATCH 21/56] Adding documentation explaining case-insensitive behavior across engines (#73814) Co-authored-by: Stephen Toub --- .../RegularExpressions/GeneratedRegexAttribute.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/GeneratedRegexAttribute.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/GeneratedRegexAttribute.cs index cab35c27c1039b..d78299f651da10 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/GeneratedRegexAttribute.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/GeneratedRegexAttribute.cs @@ -8,7 +8,20 @@ namespace System.Text.RegularExpressions; /// Instructs the System.Text.RegularExpressions source generator to generate an implementation of the specified regular expression. -/// The generator associated with this attribute only supports C#. It only supplies an implementation when applied to static, partial, parameterless, non-generic methods that are typed to return . +/// +/// +/// The generator associated with this attribute only supports C#. It only supplies an implementation when applied to static, partial, parameterless, non-generic methods that +/// are typed to return . +/// +/// +/// When the supports case-insensitive matches (either by passing or using the inline `(?i)` switch in the pattern) the regex +/// engines will use an internal casing table to transform the passed in pattern into an equivalent case-sensitive one. For example, given the pattern `abc`, the engines +/// will transform it to the equivalent pattern `[Aa][Bb][Cc]`. The equivalences found in this internal casing table can change over time, for example in the case new characters are added to +/// a new version of Unicode. When using the source generator, this transformation happens at compile time, which means the casing table used to find the +/// equivalences will depend on the target framework at compile time. This differs from the rest of the engines, which perform this transformation at run-time, meaning +/// they will always use casing table for the current runtime. +/// +/// [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] public sealed class GeneratedRegexAttribute : Attribute { From 31d5d23e9c6f3da877343ccb020e85ca9f136c05 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 12 Aug 2022 10:21:44 -0700 Subject: [PATCH 22/56] [main] Update dependencies from dotnet/linker (#73410) * Update dependencies from https://github.com/dotnet/linker build 20220804.4 Microsoft.NET.ILLink.Tasks From Version 7.0.100-1.22377.1 -> To Version 7.0.100-1.22404.4 * Update dependencies from https://github.com/dotnet/linker build 20220804.5 Microsoft.NET.ILLink.Tasks From Version 7.0.100-1.22377.1 -> To Version 7.0.100-1.22404.5 * Update dependencies from https://github.com/dotnet/linker build 20220805.1 Microsoft.NET.ILLink.Tasks From Version 7.0.100-1.22377.1 -> To Version 7.0.100-1.22405.1 * Update dependencies from https://github.com/dotnet/linker build 20220808.3 Microsoft.NET.ILLink.Tasks From Version 7.0.100-1.22377.1 -> To Version 7.0.100-1.22408.3 * Update dependencies from https://github.com/dotnet/linker build 20220809.9 Microsoft.NET.ILLink.Tasks From Version 7.0.100-1.22377.1 -> To Version 7.0.100-1.22409.9 * Added and modified suppressions * Disable IL2121 everywhere for now * Disable IL2121 in trimming tests * Disable IL2121 in wam samples * Disable IL2121 in libraries tests * Update dependencies from https://github.com/dotnet/linker build 20220811.2 Microsoft.NET.ILLink.Tasks From Version 7.0.100-1.22377.1 -> To Version 7.0.100-1.22411.2 Co-authored-by: dotnet-maestro[bot] Co-authored-by: Ankit Jain Co-authored-by: Jeremi Kurdek Co-authored-by: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Co-authored-by: Sven Boemer --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 2 +- eng/illink.targets | 2 ++ eng/testing/linker/SupportFiles/Directory.Build.props | 1 + src/libraries/Directory.Build.props | 2 ++ .../System.Data.Common/src/System/Data/DataRowExtensions.cs | 2 ++ .../Converters/FSharp/FSharpTypeConverterFactory.cs | 2 ++ .../Serialization/Converters/Value/EnumConverterFactory.cs | 2 +- .../Converters/Value/NullableConverterFactory.cs | 2 +- src/libraries/oob.proj | 2 ++ src/libraries/sfx.proj | 2 ++ src/mono/sample/wasm/Directory.Build.props | 2 ++ 12 files changed, 20 insertions(+), 5 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 1c412944b83883..43b226bb71a595 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -234,9 +234,9 @@ https://github.com/dotnet/runtime 0c5ca95fec9b6e2cc5e757ad36d0b094f81a59a6 - + https://github.com/dotnet/linker - f09bacf09ef10b61cf9f19825f8782171a816dab + 81ffbb5af38a45ff60648999df8f35a79061ae43 https://github.com/dotnet/xharness diff --git a/eng/Versions.props b/eng/Versions.props index 06c3924253c555..36423dc87d38f0 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -171,7 +171,7 @@ 7.0.0-preview-20220721.1 - 7.0.100-1.22377.1 + 7.0.100-1.22411.2 $(MicrosoftNETILLinkTasksVersion) 7.0.0-rc.1.22408.1 diff --git a/eng/illink.targets b/eng/illink.targets index 44fbe03e50f75b..91ff0da2c3abc6 100644 --- a/eng/illink.targets +++ b/eng/illink.targets @@ -256,6 +256,8 @@ $(LinkerNoWarn);IL2062;IL2063;IL2064;IL2065;IL2066 $(LinkerNoWarn);IL2067;IL2068;IL2069;IL2070;IL2071;IL2072;IL2073;IL2074;IL2075;IL2076;IL2077;IL2078;IL2079;IL2080;IL2081;IL2082;IL2083;IL2084;IL2085;IL2086;IL2087;IL2088;IL2089;IL2090;IL2091 + + $(LinkerNoWarn);IL2121 $(ILLinkArgs) --nowarn $(LinkerNoWarn) $(ILLinkArgs) --disable-opt ipconstprop diff --git a/eng/testing/linker/SupportFiles/Directory.Build.props b/eng/testing/linker/SupportFiles/Directory.Build.props index c3be2bf7d265ab..0a48403f391258 100644 --- a/eng/testing/linker/SupportFiles/Directory.Build.props +++ b/eng/testing/linker/SupportFiles/Directory.Build.props @@ -13,5 +13,6 @@ false true + $(NoWarn);IL2121 diff --git a/src/libraries/Directory.Build.props b/src/libraries/Directory.Build.props index 392e6ba80664c4..2e3613356868ee 100644 --- a/src/libraries/Directory.Build.props +++ b/src/libraries/Directory.Build.props @@ -46,6 +46,8 @@ $(NoWarn);SYSLIB0011 + + $(NoWarn);IL2121 annotations diff --git a/src/libraries/System.Data.Common/src/System/Data/DataRowExtensions.cs b/src/libraries/System.Data.Common/src/System/Data/DataRowExtensions.cs index a10d119256e28b..8e6ba12be825d1 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataRowExtensions.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataRowExtensions.cs @@ -144,6 +144,8 @@ private static class UnboxT { internal static readonly Func s_unbox = Create(); + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2090:MakeGenericMethod", + Justification = "'NullableField where TElem : struct' implies 'TElem : new()'. Nullable does not make use of new() so it is safe.")] private static Func Create() { if (typeof(T).IsValueType && default(T) == null) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpTypeConverterFactory.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpTypeConverterFactory.cs index f42adbc31fe409..a95c813bf68169 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpTypeConverterFactory.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpTypeConverterFactory.cs @@ -24,6 +24,8 @@ public override bool CanConvert(Type typeToConvert) => [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The ctor is marked RequiresUnreferencedCode.")] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2055:MakeGenericType", + Justification = "The ctor is marked RequiresUnreferencedCode.")] public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) { Debug.Assert(CanConvert(typeToConvert)); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverterFactory.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverterFactory.cs index 2fe950eac54b6e..b23623fcfb86a9 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverterFactory.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverterFactory.cs @@ -27,7 +27,7 @@ internal static JsonConverter Create(Type enumType, EnumConverterOptions convert new object?[] { converterOptions, namingPolicy, options })!; } - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2055:MakeGenericType", + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", Justification = "'EnumConverter where T : struct' implies 'T : new()', so the trimmer is warning calling MakeGenericType here because enumType's constructors are not annotated. " + "But EnumConverter doesn't call new T(), so this is safe.")] [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/NullableConverterFactory.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/NullableConverterFactory.cs index cbc89a4fb7bba6..a674f805731a0e 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/NullableConverterFactory.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/NullableConverterFactory.cs @@ -44,7 +44,7 @@ public static JsonConverter CreateValueConverter(Type valueTypeToConvert, JsonCo culture: null)!; } - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2055:MakeGenericType", + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", Justification = "'NullableConverter where T : struct' implies 'T : new()', so the trimmer is warning calling MakeGenericType here because valueTypeToConvert's constructors are not annotated. " + "But NullableConverter doesn't call new T(), so this is safe.")] [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] diff --git a/src/libraries/oob.proj b/src/libraries/oob.proj index cc910f2d6c8c41..6dbbd63794b270 100644 --- a/src/libraries/oob.proj +++ b/src/libraries/oob.proj @@ -61,6 +61,8 @@ $(ILLinkArgs) + + $(ILLinkArgs) --nowarn IL2121 $(OOBILLinkArgs) --link-attributes "@(OOBLibrarySuppressionsXml->'%(FullPath)', '" --link-attributes "')" diff --git a/src/libraries/sfx.proj b/src/libraries/sfx.proj index aa345f53952e2f..bfe5a1c4902232 100644 --- a/src/libraries/sfx.proj +++ b/src/libraries/sfx.proj @@ -48,6 +48,8 @@ $(ILLinkArgs) + + $(ILLinkArgs) --nowarn IL2121 $(SharedFrameworkILLinkArgs) -b true $(SharedFrameworkILLinkArgs) --link-attributes "@(SharedFrameworkSuppressionsXml->'%(FullPath)', '" --link-attributes "')" diff --git a/src/mono/sample/wasm/Directory.Build.props b/src/mono/sample/wasm/Directory.Build.props index 9e62d874f5ee94..6db0cf52ee3ca1 100644 --- a/src/mono/sample/wasm/Directory.Build.props +++ b/src/mono/sample/wasm/Directory.Build.props @@ -6,6 +6,8 @@ browser wasm browser-wasm + + $(NoWarn);IL2121 From 30bec968fa52d414210a3d5a19469b7637981ba5 Mon Sep 17 00:00:00 2001 From: Katya Sokolova Date: Fri, 12 Aug 2022 19:28:01 +0200 Subject: [PATCH 23/56] No end stream on ws connect and flush every message (#73762) * No end stream on ws connect and flush every message * Apply suggestions from code review Co-authored-by: Natalia Kondratyeva * Await flush in web socket after write * Flush for web socket tests should be supported * feedback * Refactoring write and flush tasks in WebSocket * feedback * Replace check for more generic extended connect * Apply suggestions from code review Co-authored-by: Stephen Toub * feedback Co-authored-by: Natalia Kondratyeva Co-authored-by: Stephen Toub --- .../src/System/Net/Http/HttpRequestMessage.cs | 2 +- .../SocketsHttpHandler/Http2Connection.cs | 6 +++--- .../Http/SocketsHttpHandler/Http2Stream.cs | 4 ++-- .../SocketsHttpHandler/HttpConnectionPool.cs | 6 +++--- .../System/Net/WebSockets/ManagedWebSocket.cs | 21 ++++++++++++++++--- .../tests/WebSocketTestStream.cs | 2 +- 6 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestMessage.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestMessage.cs index f258c5ca8ae800..40884f72085e9e 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestMessage.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestMessage.cs @@ -168,7 +168,7 @@ public override string ToString() internal bool WasRedirected() => (_sendStatus & MessageIsRedirect) != 0; - internal bool IsWebSocketH2Request() => _version.Major == 2 && Method == HttpMethod.Connect && HasHeaders && string.Equals(Headers.Protocol, "websocket", StringComparison.OrdinalIgnoreCase); + internal bool IsExtendedConnectRequest => Method == HttpMethod.Connect && _headers?.Protocol != null; #region IDisposable Members diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs index 25c27d7d0fdc52..3b2019f5af657e 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs @@ -1600,7 +1600,7 @@ private async ValueTask SendHeadersAsync(HttpRequestMessage request // Start the write. This serializes access to write to the connection, and ensures that HEADERS // and CONTINUATION frames stay together, as they must do. We use the lock as well to ensure new // streams are created and started in order. - await PerformWriteAsync(totalSize, (thisRef: this, http2Stream, headerBytes, endStream: (request.Content == null && !http2Stream.ConnectProtocolEstablished), mustFlush), static (s, writeBuffer) => + await PerformWriteAsync(totalSize, (thisRef: this, http2Stream, headerBytes, endStream: (request.Content == null && !request.IsExtendedConnectRequest), mustFlush), static (s, writeBuffer) => { if (NetEventSource.Log.IsEnabled()) s.thisRef.Trace(s.http2Stream.StreamId, $"Started writing. Total header bytes={s.headerBytes.Length}"); @@ -1962,8 +1962,8 @@ public async Task SendAsync(HttpRequestMessage request, boo try { // Send request headers - bool shouldExpectContinue = request.Content != null && request.HasHeaders && request.Headers.ExpectContinue == true; - Http2Stream http2Stream = await SendHeadersAsync(request, cancellationToken, mustFlush: shouldExpectContinue).ConfigureAwait(false); + bool shouldExpectContinue = (request.Content != null && request.HasHeaders && request.Headers.ExpectContinue == true); + Http2Stream http2Stream = await SendHeadersAsync(request, cancellationToken, mustFlush: shouldExpectContinue || request.IsExtendedConnectRequest).ConfigureAwait(false); bool duplex = request.Content != null && request.Content.AllowDuplex; diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs index 1ecb968fa322a4..927a8e74b18a13 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs @@ -108,7 +108,7 @@ public Http2Stream(HttpRequestMessage request, Http2Connection connection) if (_request.Content == null) { _requestCompletionState = StreamCompletionState.Completed; - if (_request.IsWebSocketH2Request()) + if (_request.IsExtendedConnectRequest) { _requestBodyCancellationSource = new CancellationTokenSource(); } @@ -637,7 +637,7 @@ private void OnStatus(int statusCode) } else { - if (statusCode == 200 && _response.RequestMessage!.IsWebSocketH2Request()) + if (statusCode == 200 && _response.RequestMessage!.IsExtendedConnectRequest) { ConnectProtocolEstablished = true; } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs index 4a2362329b9f79..354b50b59668d0 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs @@ -1021,7 +1021,7 @@ public async ValueTask SendWithVersionDetectionAndRetryAsyn // Use HTTP/3 if possible. if (IsHttp3Supported() && // guard to enable trimming HTTP/3 support _http3Enabled && - !request.IsWebSocketH2Request() && + !request.IsExtendedConnectRequest && (request.Version.Major >= 3 || (request.VersionPolicy == HttpVersionPolicy.RequestVersionOrHigher && IsSecure))) { Debug.Assert(async); @@ -1050,7 +1050,7 @@ public async ValueTask SendWithVersionDetectionAndRetryAsyn Debug.Assert(connection is not null || !_http2Enabled); if (connection is not null) { - if (request.IsWebSocketH2Request()) + if (request.IsExtendedConnectRequest) { await connection.InitialSettingsReceived.WaitWithCancellationAsync(cancellationToken).ConfigureAwait(false); if (!connection.IsConnectEnabled) @@ -1123,7 +1123,7 @@ public async ValueTask SendWithVersionDetectionAndRetryAsyn if (request.VersionPolicy != HttpVersionPolicy.RequestVersionOrLower) { HttpRequestException exception = new HttpRequestException(SR.Format(SR.net_http_requested_version_server_refused, request.Version, request.VersionPolicy), e); - if (request.IsWebSocketH2Request()) + if (request.IsExtendedConnectRequest) { exception.Data["HTTP2_ENABLED"] = false; } diff --git a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs index 4130cff1cc65df..4c0c4e8d693b3f 100644 --- a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs +++ b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs @@ -423,7 +423,17 @@ private ValueTask SendFrameLockAcquiredNonCancelableAsync(MessageOpcode opcode, // the task, and we're done. if (writeTask.IsCompleted) { - return writeTask; + writeTask.GetAwaiter().GetResult(); + ValueTask flushTask = new ValueTask(_stream.FlushAsync()); + if (flushTask.IsCompleted) + { + return flushTask; + } + else + { + releaseSendBufferAndSemaphore = false; + return WaitForWriteTaskAsync(flushTask, shouldFlush: false); + } } // Up until this point, if an exception occurred (such as when accessing _stream or when @@ -447,14 +457,18 @@ private ValueTask SendFrameLockAcquiredNonCancelableAsync(MessageOpcode opcode, } } - return WaitForWriteTaskAsync(writeTask); + return WaitForWriteTaskAsync(writeTask, shouldFlush: true); } - private async ValueTask WaitForWriteTaskAsync(ValueTask writeTask) + private async ValueTask WaitForWriteTaskAsync(ValueTask writeTask, bool shouldFlush) { try { await writeTask.ConfigureAwait(false); + if (shouldFlush) + { + await _stream.FlushAsync().ConfigureAwait(false); + } } catch (Exception exc) when (!(exc is OperationCanceledException)) { @@ -478,6 +492,7 @@ private async ValueTask SendFrameFallbackAsync(MessageOpcode opcode, bool endOfM using (cancellationToken.Register(static s => ((ManagedWebSocket)s!).Abort(), this)) { await _stream.WriteAsync(new ReadOnlyMemory(_sendBuffer, 0, sendBytes), cancellationToken).ConfigureAwait(false); + await _stream.FlushAsync(cancellationToken).ConfigureAwait(false); } } catch (Exception exc) when (!(exc is OperationCanceledException)) diff --git a/src/libraries/System.Net.WebSockets/tests/WebSocketTestStream.cs b/src/libraries/System.Net.WebSockets/tests/WebSocketTestStream.cs index b7dfb3ea7f26da..1e416f4dc49ccb 100644 --- a/src/libraries/System.Net.WebSockets/tests/WebSocketTestStream.cs +++ b/src/libraries/System.Net.WebSockets/tests/WebSocketTestStream.cs @@ -206,7 +206,7 @@ public override async ValueTask WriteAsync(ReadOnlyMemory buffer, Cancella Write(buffer.Span); } - public override void Flush() => throw new NotSupportedException(); + public override void Flush() { } public override int Read(byte[] buffer, int offset, int count) => throw new NotSupportedException(); From e055449d89aefae252f95eca85e2790d8f5b51bc Mon Sep 17 00:00:00 2001 From: Brennan Date: Fri, 12 Aug 2022 10:56:48 -0700 Subject: [PATCH 24/56] [RateLimiting] Add statistics API (#72306) --- ...ng.RateLimiting.Typeforwards.netcoreapp.cs | 6 + .../ref/System.Threading.RateLimiting.cs | 20 +- .../ref/System.Threading.RateLimiting.csproj | 6 +- .../src/System.Threading.RateLimiting.csproj | 7 + .../ChainedPartitionedRateLimiter.cs | 28 ++- .../RateLimiting/ConcurrencyLimiter.cs | 36 +++- .../DefaultPartitionedRateLimiter.cs | 4 +- .../RateLimiting/FixedWindowRateLimiter.cs | 30 ++- .../Threading/RateLimiting/NoopLimiter.cs | 26 ++- .../RateLimiting/PartitionedRateLimiter.T.cs | 14 +- .../RateLimiting/PartitionedRateLimiter.cs | 4 +- .../RateLimiting/RateLimitPartition.cs | 2 +- .../Threading/RateLimiting/RateLimiter.cs | 6 +- .../RateLimiting/RateLimiterStatistics.cs | 36 ++++ .../RateLimiting/SlidingWindowRateLimiter.cs | 30 ++- ...ng.RateLimiting.Typeforwards.netcoreapp.cs | 6 + .../RateLimiting/TokenBucketRateLimiter.cs | 30 ++- .../RateLimiting/TranslatingLimiter.cs | 4 +- .../tests/BaseRateLimiterTests.cs | 12 ++ .../tests/ChainedLimiterTests.cs | 199 ++++++++++++++---- .../tests/ConcurrencyLimiterTests.cs | 130 +++++++++++- .../tests/FixedWindowRateLimiterTests.cs | 150 ++++++++++++- .../tests/Infrastructure/Utils.cs | 22 +- .../tests/PartitionedRateLimiterTests.cs | 33 ++- .../tests/RateLimiterPartitionTests.cs | 60 +++++- .../tests/SlidingWindowRateLimiterTests.cs | 155 +++++++++++++- .../tests/TokenBucketRateLimiterTests.cs | 159 ++++++++++++-- 27 files changed, 1074 insertions(+), 141 deletions(-) create mode 100644 src/libraries/System.Threading.RateLimiting/ref/System.Threading.RateLimiting.Typeforwards.netcoreapp.cs create mode 100644 src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterStatistics.cs create mode 100644 src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/System.Threading.RateLimiting.Typeforwards.netcoreapp.cs diff --git a/src/libraries/System.Threading.RateLimiting/ref/System.Threading.RateLimiting.Typeforwards.netcoreapp.cs b/src/libraries/System.Threading.RateLimiting/ref/System.Threading.RateLimiting.Typeforwards.netcoreapp.cs new file mode 100644 index 00000000000000..6a3b4117fcf50b --- /dev/null +++ b/src/libraries/System.Threading.RateLimiting/ref/System.Threading.RateLimiting.Typeforwards.netcoreapp.cs @@ -0,0 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// The compiler emits a reference to the internal copy of this type in our non-NETCoreApp assembly +// so we must include a forward to be compatible with libraries compiled against non-NETCoreApp System.Threading.RateLimiting +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.CompilerServices.IsExternalInit))] diff --git a/src/libraries/System.Threading.RateLimiting/ref/System.Threading.RateLimiting.cs b/src/libraries/System.Threading.RateLimiting/ref/System.Threading.RateLimiting.cs index 1d48e8c717d126..b4905c286ecd9f 100644 --- a/src/libraries/System.Threading.RateLimiting/ref/System.Threading.RateLimiting.cs +++ b/src/libraries/System.Threading.RateLimiting/ref/System.Threading.RateLimiting.cs @@ -14,7 +14,7 @@ public ConcurrencyLimiter(System.Threading.RateLimiting.ConcurrencyLimiterOption protected override System.Threading.RateLimiting.RateLimitLease AttemptAcquireCore(int permitCount) { throw null; } protected override void Dispose(bool disposing) { } protected override System.Threading.Tasks.ValueTask DisposeAsyncCore() { throw null; } - public override int GetAvailablePermits() { throw null; } + public override System.Threading.RateLimiting.RateLimiterStatistics? GetStatistics() { throw null; } } public sealed partial class ConcurrencyLimiterOptions { @@ -33,7 +33,7 @@ public FixedWindowRateLimiter(System.Threading.RateLimiting.FixedWindowRateLimit protected override System.Threading.RateLimiting.RateLimitLease AttemptAcquireCore(int requestCount) { throw null; } protected override void Dispose(bool disposing) { } protected override System.Threading.Tasks.ValueTask DisposeAsyncCore() { throw null; } - public override int GetAvailablePermits() { throw null; } + public override System.Threading.RateLimiting.RateLimiterStatistics? GetStatistics() { throw null; } public override bool TryReplenish() { throw null; } } public sealed partial class FixedWindowRateLimiterOptions @@ -78,7 +78,7 @@ public void Dispose() { } protected virtual void Dispose(bool disposing) { } public System.Threading.Tasks.ValueTask DisposeAsync() { throw null; } protected virtual System.Threading.Tasks.ValueTask DisposeAsyncCore() { throw null; } - public abstract int GetAvailablePermits(TResource resource); + public abstract System.Threading.RateLimiting.RateLimiterStatistics? GetStatistics(TResource resource); public System.Threading.RateLimiting.PartitionedRateLimiter WithTranslatedKey(System.Func keyAdapter, bool leaveOpen) { throw null; } } public enum QueueProcessingOrder @@ -98,7 +98,15 @@ public void Dispose() { } protected virtual void Dispose(bool disposing) { } public System.Threading.Tasks.ValueTask DisposeAsync() { throw null; } protected virtual System.Threading.Tasks.ValueTask DisposeAsyncCore() { throw null; } - public abstract int GetAvailablePermits(); + public abstract System.Threading.RateLimiting.RateLimiterStatistics? GetStatistics(); + } + public partial class RateLimiterStatistics + { + public RateLimiterStatistics() { } + public long CurrentAvailablePermits { get { throw null; } set { } } + public long CurrentQueuedCount { get { throw null; } set { } } + public long TotalFailedLeases { get { throw null; } set { } } + public long TotalSuccessfulLeases { get { throw null; } set { } } } public abstract partial class RateLimitLease : System.IDisposable { @@ -146,7 +154,7 @@ public SlidingWindowRateLimiter(System.Threading.RateLimiting.SlidingWindowRateL protected override System.Threading.RateLimiting.RateLimitLease AttemptAcquireCore(int requestCount) { throw null; } protected override void Dispose(bool disposing) { } protected override System.Threading.Tasks.ValueTask DisposeAsyncCore() { throw null; } - public override int GetAvailablePermits() { throw null; } + public override System.Threading.RateLimiting.RateLimiterStatistics? GetStatistics() { throw null; } public override bool TryReplenish() { throw null; } } public sealed partial class SlidingWindowRateLimiterOptions @@ -169,7 +177,7 @@ public TokenBucketRateLimiter(System.Threading.RateLimiting.TokenBucketRateLimit protected override System.Threading.RateLimiting.RateLimitLease AttemptAcquireCore(int tokenCount) { throw null; } protected override void Dispose(bool disposing) { } protected override System.Threading.Tasks.ValueTask DisposeAsyncCore() { throw null; } - public override int GetAvailablePermits() { throw null; } + public override System.Threading.RateLimiting.RateLimiterStatistics? GetStatistics() { throw null; } public override bool TryReplenish() { throw null; } } public sealed partial class TokenBucketRateLimiterOptions diff --git a/src/libraries/System.Threading.RateLimiting/ref/System.Threading.RateLimiting.csproj b/src/libraries/System.Threading.RateLimiting/ref/System.Threading.RateLimiting.csproj index 8927acfb8ad416..12660a96ffbc60 100644 --- a/src/libraries/System.Threading.RateLimiting/ref/System.Threading.RateLimiting.csproj +++ b/src/libraries/System.Threading.RateLimiting/ref/System.Threading.RateLimiting.csproj @@ -1,4 +1,4 @@ - + $(NetCoreAppCurrent);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum) @@ -7,7 +7,11 @@ + + + \ No newline at end of file diff --git a/src/libraries/System.Threading.RateLimiting/src/System.Threading.RateLimiting.csproj b/src/libraries/System.Threading.RateLimiting/src/System.Threading.RateLimiting.csproj index 648d18fe85399e..0d9f2ddcf56832 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System.Threading.RateLimiting.csproj +++ b/src/libraries/System.Threading.RateLimiting/src/System.Threading.RateLimiting.csproj @@ -27,12 +27,14 @@ System.Threading.RateLimiting.RateLimitLease + + @@ -44,4 +46,9 @@ System.Threading.RateLimiting.RateLimitLease + + + + + diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/ChainedPartitionedRateLimiter.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/ChainedPartitionedRateLimiter.cs index 9a9a6114004e42..91ebe1126d9c91 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/ChainedPartitionedRateLimiter.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/ChainedPartitionedRateLimiter.cs @@ -23,21 +23,33 @@ public ChainedPartitionedRateLimiter(PartitionedRateLimiter[] limiter _limiters = limiters; } - public override int GetAvailablePermits(TResource resource) + public override RateLimiterStatistics? GetStatistics(TResource resource) { ThrowIfDisposed(); - int lowestPermitCount = int.MaxValue; + long lowestAvailablePermits = long.MaxValue; + long currentQueuedCount = 0; + long totalFailedLeases = 0; + long innerMostSuccessfulLeases = 0; foreach (PartitionedRateLimiter limiter in _limiters) { - int permitCount = limiter.GetAvailablePermits(resource); - - if (permitCount < lowestPermitCount) + if (limiter.GetStatistics(resource) is { } statistics) { - lowestPermitCount = permitCount; + if (statistics.CurrentAvailablePermits < lowestAvailablePermits) + { + lowestAvailablePermits = statistics.CurrentAvailablePermits; + } + currentQueuedCount += statistics.CurrentQueuedCount; + totalFailedLeases += statistics.TotalFailedLeases; + innerMostSuccessfulLeases = statistics.TotalSuccessfulLeases; } } - - return lowestPermitCount; + return new RateLimiterStatistics() + { + CurrentAvailablePermits = lowestAvailablePermits, + CurrentQueuedCount = currentQueuedCount, + TotalFailedLeases = totalFailedLeases, + TotalSuccessfulLeases = innerMostSuccessfulLeases, + }; } protected override RateLimitLease AttemptAcquireCore(TResource resource, int permitCount) diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/ConcurrencyLimiter.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/ConcurrencyLimiter.cs index b9dd45c20b87d9..508340a2e138be 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/ConcurrencyLimiter.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/ConcurrencyLimiter.cs @@ -18,6 +18,9 @@ public sealed class ConcurrencyLimiter : RateLimiter private long? _idleSince = Stopwatch.GetTimestamp(); private bool _disposed; + private long _failedLeasesCount; + private long _successfulLeasesCount; + private readonly ConcurrencyLimiterOptions _options; private readonly Deque _queue = new Deque(); @@ -62,7 +65,17 @@ public ConcurrencyLimiter(ConcurrencyLimiterOptions options) } /// - public override int GetAvailablePermits() => _permitCount; + public override RateLimiterStatistics? GetStatistics() + { + ThrowIfDisposed(); + return new RateLimiterStatistics() + { + CurrentAvailablePermits = _permitCount, + CurrentQueuedCount = _queueCount, + TotalFailedLeases = Interlocked.Read(ref _failedLeasesCount), + TotalSuccessfulLeases = Interlocked.Read(ref _successfulLeasesCount), + }; + } /// protected override RateLimitLease AttemptAcquireCore(int permitCount) @@ -78,7 +91,13 @@ protected override RateLimitLease AttemptAcquireCore(int permitCount) // Return SuccessfulLease or FailedLease to indicate limiter state if (permitCount == 0) { - return _permitCount > 0 ? SuccessfulLease : FailedLease; + if (_permitCount > 0) + { + Interlocked.Increment(ref _successfulLeasesCount); + return SuccessfulLease; + } + Interlocked.Increment(ref _failedLeasesCount); + return FailedLease; } // Perf: Check SemaphoreSlim implementation instead of locking @@ -93,6 +112,7 @@ protected override RateLimitLease AttemptAcquireCore(int permitCount) } } + Interlocked.Increment(ref _failedLeasesCount); return FailedLease; } @@ -108,6 +128,7 @@ protected override ValueTask AcquireAsyncCore(int permitCount, C // Return SuccessfulLease if requestedCount is 0 and resources are available if (permitCount == 0 && _permitCount > 0 && !_disposed) { + Interlocked.Increment(ref _successfulLeasesCount); return new ValueTask(SuccessfulLease); } @@ -136,11 +157,16 @@ protected override ValueTask AcquireAsyncCore(int permitCount, C // Updating queue count is handled by the cancellation code _queueCount += oldestRequest.Count; } + else + { + Interlocked.Increment(ref _failedLeasesCount); + } } while (_options.QueueLimit - _queueCount < permitCount); } else { + Interlocked.Increment(ref _failedLeasesCount); // Don't queue if queue limit reached and QueueProcessingOrder is OldestFirst return new ValueTask(QueueLimitLease); } @@ -174,6 +200,7 @@ private bool TryLeaseUnsynchronized(int permitCount, [NotNullWhen(true)] out Rat { if (permitCount == 0) { + Interlocked.Increment(ref _successfulLeasesCount); // Edge case where the check before the lock showed 0 available permits but when we got the lock some permits were now available lease = SuccessfulLease; return true; @@ -186,6 +213,7 @@ private bool TryLeaseUnsynchronized(int permitCount, [NotNullWhen(true)] out Rat _idleSince = null; _permitCount -= permitCount; Debug.Assert(_permitCount >= 0); + Interlocked.Increment(ref _successfulLeasesCount); lease = new ConcurrencyLease(true, this, permitCount); return true; } @@ -234,6 +262,10 @@ private void Release(int releaseCount) // Updating queue count is handled by the cancellation code _queueCount += nextPendingRequest.Count; } + else + { + Interlocked.Increment(ref _successfulLeasesCount); + } nextPendingRequest.CancellationTokenRegistration.Dispose(); Debug.Assert(_queueCount >= 0); } diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/DefaultPartitionedRateLimiter.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/DefaultPartitionedRateLimiter.cs index b33bcbbd7b732b..04e17168aef7ae 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/DefaultPartitionedRateLimiter.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/DefaultPartitionedRateLimiter.cs @@ -65,9 +65,9 @@ private async Task RunTimer() _timer.Dispose(); } - public override int GetAvailablePermits(TResource resource) + public override RateLimiterStatistics? GetStatistics(TResource resource) { - return GetRateLimiter(resource).GetAvailablePermits(); + return GetRateLimiter(resource).GetStatistics(); } protected override RateLimitLease AttemptAcquireCore(TResource resource, int permitCount) diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs index e72b86b9f0b25f..fe4b0c29c3a627 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs @@ -19,6 +19,9 @@ public sealed class FixedWindowRateLimiter : ReplenishingRateLimiter private long? _idleSince; private bool _disposed; + private long _failedLeasesCount; + private long _successfulLeasesCount; + private readonly Timer? _renewTimer; private readonly FixedWindowRateLimiterOptions _options; private readonly Deque _queue = new Deque(); @@ -81,7 +84,17 @@ public FixedWindowRateLimiter(FixedWindowRateLimiterOptions options) } /// - public override int GetAvailablePermits() => _requestCount; + public override RateLimiterStatistics? GetStatistics() + { + ThrowIfDisposed(); + return new RateLimiterStatistics() + { + CurrentAvailablePermits = _requestCount, + CurrentQueuedCount = _queueCount, + TotalFailedLeases = Interlocked.Read(ref _failedLeasesCount), + TotalSuccessfulLeases = Interlocked.Read(ref _successfulLeasesCount), + }; + } /// protected override RateLimitLease AttemptAcquireCore(int requestCount) @@ -100,9 +113,11 @@ protected override RateLimitLease AttemptAcquireCore(int requestCount) // Requests will be allowed if the total served request is less than the max allowed requests (permit limit). if (_requestCount > 0) { + Interlocked.Increment(ref _successfulLeasesCount); return SuccessfulLease; } + Interlocked.Increment(ref _failedLeasesCount); return CreateFailedWindowLease(requestCount); } @@ -113,6 +128,7 @@ protected override RateLimitLease AttemptAcquireCore(int requestCount) return lease; } + Interlocked.Increment(ref _failedLeasesCount); return CreateFailedWindowLease(requestCount); } } @@ -131,6 +147,7 @@ protected override ValueTask AcquireAsyncCore(int requestCount, // Return SuccessfulAcquisition if requestCount is 0 and resources are available if (requestCount == 0 && _requestCount > 0) { + Interlocked.Increment(ref _successfulLeasesCount); return new ValueTask(SuccessfulLease); } @@ -157,11 +174,16 @@ protected override ValueTask AcquireAsyncCore(int requestCount, { _queueCount += oldestRequest.Count; } + else + { + Interlocked.Increment(ref _failedLeasesCount); + } } while (_options.QueueLimit - _queueCount < requestCount); } else { + Interlocked.Increment(ref _failedLeasesCount); // Don't queue if queue limit reached and QueueProcessingOrder is OldestFirst return new ValueTask(CreateFailedWindowLease(requestCount)); } @@ -204,6 +226,7 @@ private bool TryLeaseUnsynchronized(int requestCount, [NotNullWhen(true)] out Ra { if (requestCount == 0) { + Interlocked.Increment(ref _successfulLeasesCount); // Edge case where the check before the lock showed 0 available permit counters but when we got the lock, some permits were now available lease = SuccessfulLease; return true; @@ -216,6 +239,7 @@ private bool TryLeaseUnsynchronized(int requestCount, [NotNullWhen(true)] out Ra _idleSince = null; _requestCount -= requestCount; Debug.Assert(_requestCount >= 0); + Interlocked.Increment(ref _successfulLeasesCount); lease = SuccessfulLease; return true; } @@ -314,6 +338,10 @@ private void ReplenishInternal(long nowTicks) // Updating queue count is handled by the cancellation code _queueCount += nextPendingRequest.Count; } + else + { + Interlocked.Increment(ref _successfulLeasesCount); + } nextPendingRequest.CancellationTokenRegistration.Dispose(); Debug.Assert(_queueCount >= 0); } diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/NoopLimiter.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/NoopLimiter.cs index 1744459ba2b752..0c6488145323e5 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/NoopLimiter.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/NoopLimiter.cs @@ -10,18 +10,34 @@ internal sealed class NoopLimiter : RateLimiter { private static readonly RateLimitLease _lease = new NoopLease(); - private NoopLimiter() { } + private long _totalSuccessfulLeases; - public static NoopLimiter Instance { get; } = new NoopLimiter(); + public NoopLimiter() { } public override TimeSpan? IdleDuration => null; - public override int GetAvailablePermits() => int.MaxValue; + public override RateLimiterStatistics? GetStatistics() + { + return new RateLimiterStatistics() + { + CurrentAvailablePermits = long.MaxValue, + CurrentQueuedCount = 0, + TotalFailedLeases = 0, + TotalSuccessfulLeases = Interlocked.Read(ref _totalSuccessfulLeases) + }; + } - protected override RateLimitLease AttemptAcquireCore(int permitCount) => _lease; + protected override RateLimitLease AttemptAcquireCore(int permitCount) + { + Interlocked.Increment(ref _totalSuccessfulLeases); + return _lease; + } protected override ValueTask AcquireAsyncCore(int permitCount, CancellationToken cancellationToken) - => new ValueTask(_lease); + { + Interlocked.Increment(ref _totalSuccessfulLeases); + return new ValueTask(_lease); + } private sealed class NoopLease : RateLimitLease { diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/PartitionedRateLimiter.T.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/PartitionedRateLimiter.T.cs index fdc21eebeb61fb..b97ac2de2c1cb5 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/PartitionedRateLimiter.T.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/PartitionedRateLimiter.T.cs @@ -12,10 +12,10 @@ namespace System.Threading.RateLimiting public abstract class PartitionedRateLimiter : IAsyncDisposable, IDisposable { /// - /// An estimated count of available permits. + /// Gets a snapshot of the statistics for the if available. /// - /// - public abstract int GetAvailablePermits(TResource resource); + /// An instance of containing a snapshot of the statistics for a . + public abstract RateLimiterStatistics? GetStatistics(TResource resource); /// /// Fast synchronous attempt to acquire permits. @@ -126,16 +126,14 @@ public async ValueTask DisposeAsync() /// /// The type to translate into . /// The function to be called every time a is passed to - /// PartitionedRateLimiter<TOuter>.Acquire(TOuter, int) or PartitionedRateLimiter<TOuter>.WaitAsync(TOuter, int, CancellationToken). + /// PartitionedRateLimiter<TOuter>.Acquire(TOuter, int) or PartitionedRateLimiter<TOuter>.WaitAsync(TOuter, int, CancellationToken). + /// + /// should be implemented in a thread-safe way. /// Specifies whether the returned will dispose the wrapped . - /// or does not dispose the wrapped . /// A new PartitionedRateLimiter<TOuter> that translates /// to and calls the inner . public PartitionedRateLimiter WithTranslatedKey(Func keyAdapter, bool leaveOpen) { - // REVIEW: Do we want to have an option to dispose the inner limiter? - // Should the default be to dispose the inner limiter and have an option to not dispose it? - // See Stream wrappers like SslStream for prior-art return new TranslatingLimiter(this, keyAdapter, leaveOpen); } } diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/PartitionedRateLimiter.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/PartitionedRateLimiter.cs index aa79d5da4f0c8c..a2f592c5ea3b5d 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/PartitionedRateLimiter.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/PartitionedRateLimiter.cs @@ -38,7 +38,9 @@ public static PartitionedRateLimiter Create /// Methods on the returned will iterate over the passed in in the order given. /// /// - /// will return the lowest value of all the . + /// will return the lowest value for , + /// the inner-most limiter's , + /// and the aggregate values for the rest of the properties from the . /// /// /// s returned will aggregate metadata and for duplicates use the value of the first lease with the same metadata name. diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimitPartition.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimitPartition.cs index 9a98a8c5aff8f5..23c33d2ad3bb6f 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimitPartition.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimitPartition.cs @@ -48,7 +48,7 @@ public static RateLimitPartition GetConcurrencyLimiter( /// public static RateLimitPartition GetNoLimiter(TKey partitionKey) { - return Get(partitionKey, _ => NoopLimiter.Instance); + return Get(partitionKey, _ => new NoopLimiter()); } /// diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiter.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiter.cs index f44f85c1c2315f..10dfca40dcb7a3 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiter.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiter.cs @@ -11,10 +11,10 @@ namespace System.Threading.RateLimiting public abstract class RateLimiter : IAsyncDisposable, IDisposable { /// - /// An estimated count of available permits. + /// Gets a snapshot of the statistics if available. /// - /// - public abstract int GetAvailablePermits(); + /// An instance of containing a snapshot of the statistics. + public abstract RateLimiterStatistics? GetStatistics(); /// /// Specifies how long the has had all permits available. Used by RateLimiter managers that may want to diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterStatistics.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterStatistics.cs new file mode 100644 index 00000000000000..3853382a5f202c --- /dev/null +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterStatistics.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Threading.RateLimiting +{ + /// + /// Snapshot of statistics for a . + /// + public class RateLimiterStatistics + { + /// + /// Initializes an instance of . + /// + public RateLimiterStatistics() { } + + /// + /// Gets the number of permits currently available for the . + /// + public long CurrentAvailablePermits { get; init; } + + /// + /// Gets the number of queued permits for the . + /// + public long CurrentQueuedCount { get; init; } + + /// + /// Gets the total number of failed s returned. + /// + public long TotalFailedLeases { get; init; } + + /// + /// Gets the total number of successful s returned. + /// + public long TotalSuccessfulLeases { get; init; } + } +} diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/SlidingWindowRateLimiter.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/SlidingWindowRateLimiter.cs index a8b9b1fae35339..1ccf40775e2d87 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/SlidingWindowRateLimiter.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/SlidingWindowRateLimiter.cs @@ -21,6 +21,9 @@ public sealed class SlidingWindowRateLimiter : ReplenishingRateLimiter private long? _idleSince; private bool _disposed; + private long _failedLeasesCount; + private long _successfulLeasesCount; + private readonly Timer? _renewTimer; private readonly SlidingWindowRateLimiterOptions _options; private readonly Deque _queue = new Deque(); @@ -89,7 +92,17 @@ public SlidingWindowRateLimiter(SlidingWindowRateLimiterOptions options) } /// - public override int GetAvailablePermits() => _requestCount; + public override RateLimiterStatistics? GetStatistics() + { + ThrowIfDisposed(); + return new RateLimiterStatistics() + { + CurrentAvailablePermits = _requestCount, + CurrentQueuedCount = _queueCount, + TotalFailedLeases = Interlocked.Read(ref _failedLeasesCount), + TotalSuccessfulLeases = Interlocked.Read(ref _successfulLeasesCount), + }; + } /// protected override RateLimitLease AttemptAcquireCore(int requestCount) @@ -105,9 +118,11 @@ protected override RateLimitLease AttemptAcquireCore(int requestCount) { if (_requestCount > 0) { + Interlocked.Increment(ref _successfulLeasesCount); return SuccessfulLease; } + Interlocked.Increment(ref _failedLeasesCount); return FailedLease; } @@ -119,6 +134,7 @@ protected override RateLimitLease AttemptAcquireCore(int requestCount) } // TODO: Acquire additional metadata during a failed lease decision + Interlocked.Increment(ref _failedLeasesCount); return FailedLease; } } @@ -137,6 +153,7 @@ protected override ValueTask AcquireAsyncCore(int requestCount, // Return SuccessfulAcquisition if resources are available if (requestCount == 0 && _requestCount > 0) { + Interlocked.Increment(ref _successfulLeasesCount); return new ValueTask(SuccessfulLease); } @@ -163,11 +180,16 @@ protected override ValueTask AcquireAsyncCore(int requestCount, { _queueCount += oldestRequest.Count; } + else + { + Interlocked.Increment(ref _failedLeasesCount); + } } while (_options.QueueLimit - _queueCount < requestCount); } else { + Interlocked.Increment(ref _failedLeasesCount); // Don't queue if queue limit reached and QueueProcessingOrder is OldestFirst return new ValueTask(FailedLease); } @@ -201,6 +223,7 @@ private bool TryLeaseUnsynchronized(int requestCount, [NotNullWhen(true)] out Ra { if (requestCount == 0) { + Interlocked.Increment(ref _successfulLeasesCount); // Edge case where the check before the lock showed 0 available permits but when we got the lock some permits were now available lease = SuccessfulLease; return true; @@ -214,6 +237,7 @@ private bool TryLeaseUnsynchronized(int requestCount, [NotNullWhen(true)] out Ra _requestsPerSegment[_currentSegmentIndex] += requestCount; _requestCount -= requestCount; Debug.Assert(_requestCount >= 0); + Interlocked.Increment(ref _successfulLeasesCount); lease = SuccessfulLease; return true; } @@ -314,6 +338,10 @@ private void ReplenishInternal(long nowTicks) // Updating queue count is handled by the cancellation code _queueCount += nextPendingRequest.Count; } + else + { + Interlocked.Increment(ref _successfulLeasesCount); + } nextPendingRequest.CancellationTokenRegistration.Dispose(); Debug.Assert(_queueCount >= 0); } diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/System.Threading.RateLimiting.Typeforwards.netcoreapp.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/System.Threading.RateLimiting.Typeforwards.netcoreapp.cs new file mode 100644 index 00000000000000..6a3b4117fcf50b --- /dev/null +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/System.Threading.RateLimiting.Typeforwards.netcoreapp.cs @@ -0,0 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// The compiler emits a reference to the internal copy of this type in our non-NETCoreApp assembly +// so we must include a forward to be compatible with libraries compiled against non-NETCoreApp System.Threading.RateLimiting +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.CompilerServices.IsExternalInit))] diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/TokenBucketRateLimiter.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/TokenBucketRateLimiter.cs index 90906e02355636..7baf91ea590804 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/TokenBucketRateLimiter.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/TokenBucketRateLimiter.cs @@ -19,6 +19,9 @@ public sealed class TokenBucketRateLimiter : ReplenishingRateLimiter private long? _idleSince; private bool _disposed; + private long _failedLeasesCount; + private long _successfulLeasesCount; + private readonly Timer? _renewTimer; private readonly TokenBucketRateLimiterOptions _options; private readonly Deque _queue = new Deque(); @@ -83,7 +86,17 @@ public TokenBucketRateLimiter(TokenBucketRateLimiterOptions options) } /// - public override int GetAvailablePermits() => _tokenCount; + public override RateLimiterStatistics? GetStatistics() + { + ThrowIfDisposed(); + return new RateLimiterStatistics() + { + CurrentAvailablePermits = _tokenCount, + CurrentQueuedCount = _queueCount, + TotalFailedLeases = Interlocked.Read(ref _failedLeasesCount), + TotalSuccessfulLeases = Interlocked.Read(ref _successfulLeasesCount), + }; + } /// protected override RateLimitLease AttemptAcquireCore(int tokenCount) @@ -99,9 +112,11 @@ protected override RateLimitLease AttemptAcquireCore(int tokenCount) { if (_tokenCount > 0) { + Interlocked.Increment(ref _successfulLeasesCount); return SuccessfulLease; } + Interlocked.Increment(ref _failedLeasesCount); return CreateFailedTokenLease(tokenCount); } @@ -112,6 +127,7 @@ protected override RateLimitLease AttemptAcquireCore(int tokenCount) return lease; } + Interlocked.Increment(ref _failedLeasesCount); return CreateFailedTokenLease(tokenCount); } } @@ -130,6 +146,7 @@ protected override ValueTask AcquireAsyncCore(int tokenCount, Ca // Return SuccessfulAcquisition if requestedCount is 0 and resources are available if (tokenCount == 0 && _tokenCount > 0) { + Interlocked.Increment(ref _successfulLeasesCount); return new ValueTask(SuccessfulLease); } @@ -157,11 +174,16 @@ protected override ValueTask AcquireAsyncCore(int tokenCount, Ca // Updating queue count is handled by the cancellation code _queueCount += oldestRequest.Count; } + else + { + Interlocked.Increment(ref _failedLeasesCount); + } } while (_options.QueueLimit - _queueCount < tokenCount); } else { + Interlocked.Increment(ref _failedLeasesCount); // Don't queue if queue limit reached and QueueProcessingOrder is OldestFirst return new ValueTask(CreateFailedTokenLease(tokenCount)); } @@ -206,6 +228,7 @@ private bool TryLeaseUnsynchronized(int tokenCount, [NotNullWhen(true)] out Rate { if (tokenCount == 0) { + Interlocked.Increment(ref _successfulLeasesCount); // Edge case where the check before the lock showed 0 available permits but when we got the lock some permits were now available lease = SuccessfulLease; return true; @@ -218,6 +241,7 @@ private bool TryLeaseUnsynchronized(int tokenCount, [NotNullWhen(true)] out Rate _idleSince = null; _tokenCount -= tokenCount; Debug.Assert(_tokenCount >= 0); + Interlocked.Increment(ref _successfulLeasesCount); lease = SuccessfulLease; return true; } @@ -318,6 +342,10 @@ private void ReplenishInternal(long nowTicks) // Updating queue count is handled by the cancellation code _queueCount += nextPendingRequest.Count; } + else + { + Interlocked.Increment(ref _successfulLeasesCount); + } nextPendingRequest.CancellationTokenRegistration.Dispose(); Debug.Assert(_queueCount >= 0); } diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/TranslatingLimiter.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/TranslatingLimiter.cs index b5a59fe079bab7..3c6748ab696035 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/TranslatingLimiter.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/TranslatingLimiter.cs @@ -20,11 +20,11 @@ public TranslatingLimiter(PartitionedRateLimiter inner, Func(() => chainedLimiter.GetAvailablePermits("")); + Assert.Throws(() => chainedLimiter.GetStatistics("")); Assert.Throws(() => chainedLimiter.AttemptAcquire("")); await Assert.ThrowsAsync(async () => await chainedLimiter.AcquireAsync("")); } @@ -84,13 +84,13 @@ public async Task DisposeAsyncMakesMethodsThrow() await chainedLimiter.DisposeAsync(); - Assert.Throws(() => chainedLimiter.GetAvailablePermits("")); + Assert.Throws(() => chainedLimiter.GetStatistics("")); Assert.Throws(() => chainedLimiter.AttemptAcquire("")); await Assert.ThrowsAsync(async () => await chainedLimiter.AcquireAsync("")); } [Fact] - public void AvailablePermitsReturnsLowestValue() + public void GetStatisticsReturnsLowestOrAggregateValues() { using var limiter1 = PartitionedRateLimiter.Create(resource => { @@ -99,7 +99,7 @@ public void AvailablePermitsReturnsLowestValue() { PermitLimit = 34, QueueProcessingOrder = QueueProcessingOrder.NewestFirst, - QueueLimit = 0 + QueueLimit = 4 }); }); using var limiter2 = PartitionedRateLimiter.Create(resource => @@ -109,7 +109,7 @@ public void AvailablePermitsReturnsLowestValue() { PermitLimit = 22, QueueProcessingOrder = QueueProcessingOrder.NewestFirst, - QueueLimit = 0 + QueueLimit = 2 }); }); using var limiter3 = PartitionedRateLimiter.Create(resource => @@ -119,16 +119,21 @@ public void AvailablePermitsReturnsLowestValue() { PermitLimit = 13, QueueProcessingOrder = QueueProcessingOrder.NewestFirst, - QueueLimit = 0 + QueueLimit = 10 }); }); using var chainedLimiter = PartitionedRateLimiter.CreateChained(limiter1, limiter2, limiter3); - Assert.Equal(13, chainedLimiter.GetAvailablePermits("")); + + var stats = chainedLimiter.GetStatistics(""); + Assert.Equal(13, stats.CurrentAvailablePermits); + Assert.Equal(0, stats.CurrentQueuedCount); + Assert.Equal(0, stats.TotalFailedLeases); + Assert.Equal(0, stats.TotalSuccessfulLeases); } [Fact] - public void AvailablePermitsWithSingleLimiterWorks() + public void GetStatisticsWithSingleLimiterWorks() { using var limiter = PartitionedRateLimiter.Create(resource => { @@ -142,7 +147,121 @@ public void AvailablePermitsWithSingleLimiterWorks() }); using var chainedLimiter = PartitionedRateLimiter.CreateChained(limiter); - Assert.Equal(34, chainedLimiter.GetAvailablePermits("")); + + var stats = chainedLimiter.GetStatistics(""); + Assert.Equal(34, stats.CurrentAvailablePermits); + Assert.Equal(0, stats.CurrentQueuedCount); + Assert.Equal(0, stats.TotalFailedLeases); + Assert.Equal(0, stats.TotalSuccessfulLeases); + } + + [Fact] + public void GetStatisticsReturnsNewInstances() + { + using var limiter1 = PartitionedRateLimiter.Create(resource => + { + return RateLimitPartition.GetConcurrencyLimiter(1, _ => new ConcurrencyLimiterOptions + { + PermitLimit = 34, + QueueProcessingOrder = QueueProcessingOrder.NewestFirst, + QueueLimit = 4 + }); + }); + using var limiter2 = PartitionedRateLimiter.Create(resource => + { + return RateLimitPartition.GetConcurrencyLimiter(1, _ => new ConcurrencyLimiterOptions + { + PermitLimit = 22, + QueueProcessingOrder = QueueProcessingOrder.NewestFirst, + QueueLimit = 2 + }); + }); + using var limiter3 = PartitionedRateLimiter.Create(resource => + { + return RateLimitPartition.GetConcurrencyLimiter(1, _ => new ConcurrencyLimiterOptions + { + PermitLimit = 13, + QueueProcessingOrder = QueueProcessingOrder.NewestFirst, + QueueLimit = 10 + }); + }); + + using var chainedLimiter = PartitionedRateLimiter.CreateChained(limiter1, limiter2, limiter3); + + var stats = chainedLimiter.GetStatistics(""); + var stats2 = chainedLimiter.GetStatistics(""); + Assert.NotSame(stats, stats2); + } + + [Fact] + public async Task GetStatisticsHasCorrectValues() + { + using var limiter1 = PartitionedRateLimiter.Create(resource => + { + return RateLimitPartition.GetConcurrencyLimiter(1, _ => new ConcurrencyLimiterOptions + { + PermitLimit = 34, + QueueProcessingOrder = QueueProcessingOrder.NewestFirst, + QueueLimit = 4 + }); + }); + using var limiter2 = PartitionedRateLimiter.Create(resource => + { + return RateLimitPartition.GetConcurrencyLimiter(1, _ => new ConcurrencyLimiterOptions + { + PermitLimit = 22, + QueueProcessingOrder = QueueProcessingOrder.NewestFirst, + QueueLimit = 2 + }); + }); + using var limiter3 = PartitionedRateLimiter.Create(resource => + { + return RateLimitPartition.GetConcurrencyLimiter(1, _ => new ConcurrencyLimiterOptions + { + PermitLimit = 13, + QueueProcessingOrder = QueueProcessingOrder.NewestFirst, + QueueLimit = 10 + }); + }); + + using var chainedLimiter = PartitionedRateLimiter.CreateChained(limiter1, limiter2, limiter3); + + var lease = chainedLimiter.AttemptAcquire("", 10); + var stats = chainedLimiter.GetStatistics(""); + + Assert.Equal(3, stats.CurrentAvailablePermits); + Assert.Equal(0, stats.CurrentQueuedCount); + Assert.Equal(1, stats.TotalSuccessfulLeases); + Assert.Equal(0, stats.TotalFailedLeases); + + var lease2 = chainedLimiter.AttemptAcquire("", 10); + Assert.False(lease2.IsAcquired); + stats = chainedLimiter.GetStatistics(""); + + Assert.Equal(3, stats.CurrentAvailablePermits); + Assert.Equal(0, stats.CurrentQueuedCount); + Assert.Equal(1, stats.TotalSuccessfulLeases); + Assert.Equal(1, stats.TotalFailedLeases); + + var task = chainedLimiter.AcquireAsync("", 10); + Assert.False(task.IsCompleted); + stats = chainedLimiter.GetStatistics(""); + + Assert.Equal(2, stats.CurrentAvailablePermits); + Assert.Equal(10, stats.CurrentQueuedCount); + Assert.Equal(1, stats.TotalSuccessfulLeases); + Assert.Equal(1, stats.TotalFailedLeases); + + lease.Dispose(); + + lease = await task; + Assert.True(lease.IsAcquired); + stats = chainedLimiter.GetStatistics(""); + + Assert.Equal(3, stats.CurrentAvailablePermits); + Assert.Equal(0, stats.CurrentQueuedCount); + Assert.Equal(2, stats.TotalSuccessfulLeases); + Assert.Equal(1, stats.TotalFailedLeases); } [Fact] @@ -257,12 +376,12 @@ public void AcquireLeaseCorrectlyDisposesWithMultipleLimiters() var lease = chainedLimiter.AttemptAcquire(""); Assert.True(lease.IsAcquired); - Assert.Equal(0, concurrencyLimiter1.GetAvailablePermits()); - Assert.Equal(0, concurrencyLimiter2.GetAvailablePermits()); + Assert.Equal(0, concurrencyLimiter1.GetStatistics().CurrentAvailablePermits); + Assert.Equal(0, concurrencyLimiter2.GetStatistics().CurrentAvailablePermits); lease.Dispose(); - Assert.Equal(1, concurrencyLimiter1.GetAvailablePermits()); - Assert.Equal(1, concurrencyLimiter2.GetAvailablePermits()); + Assert.Equal(1, concurrencyLimiter1.GetStatistics().CurrentAvailablePermits); + Assert.Equal(1, concurrencyLimiter2.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -293,12 +412,12 @@ public async Task AcquireAsyncLeaseCorrectlyDisposesWithMultipleLimiters() var lease = await chainedLimiter.AcquireAsync(""); Assert.True(lease.IsAcquired); - Assert.Equal(0, concurrencyLimiter1.GetAvailablePermits()); - Assert.Equal(0, concurrencyLimiter2.GetAvailablePermits()); + Assert.Equal(0, concurrencyLimiter1.GetStatistics().CurrentAvailablePermits); + Assert.Equal(0, concurrencyLimiter2.GetStatistics().CurrentAvailablePermits); lease.Dispose(); - Assert.Equal(1, concurrencyLimiter1.GetAvailablePermits()); - Assert.Equal(1, concurrencyLimiter2.GetAvailablePermits()); + Assert.Equal(1, concurrencyLimiter1.GetStatistics().CurrentAvailablePermits); + Assert.Equal(1, concurrencyLimiter2.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -319,10 +438,10 @@ public void AcquireLeaseCorrectlyDisposesWithSingleLimiter() var lease = chainedLimiter.AttemptAcquire(""); Assert.True(lease.IsAcquired); - Assert.Equal(0, concurrencyLimiter.GetAvailablePermits()); + Assert.Equal(0, concurrencyLimiter.GetStatistics().CurrentAvailablePermits); lease.Dispose(); - Assert.Equal(1, concurrencyLimiter.GetAvailablePermits()); + Assert.Equal(1, concurrencyLimiter.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -343,10 +462,10 @@ public async Task AcquireAsyncLeaseCorrectlyDisposesWithSingleLimiter() var lease = await chainedLimiter.AcquireAsync(""); Assert.True(lease.IsAcquired); - Assert.Equal(0, concurrencyLimiter.GetAvailablePermits()); + Assert.Equal(0, concurrencyLimiter.GetStatistics().CurrentAvailablePermits); lease.Dispose(); - Assert.Equal(1, concurrencyLimiter.GetAvailablePermits()); + Assert.Equal(1, concurrencyLimiter.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -442,7 +561,7 @@ public void AcquireFailsAndReleasesAcquiredResources() using var lease = chainedLimiter.AttemptAcquire(""); Assert.False(lease.IsAcquired); - Assert.Equal(1, concurrencyLimiter1.GetAvailablePermits()); + Assert.Equal(1, concurrencyLimiter1.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -476,7 +595,7 @@ public async Task AcquireAsyncFailsAndReleasesAcquiredResources() using var lease = chainedLimiter.AttemptAcquire(""); Assert.False(lease.IsAcquired); - Assert.Equal(1, concurrencyLimiter1.GetAvailablePermits()); + Assert.Equal(1, concurrencyLimiter1.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -499,7 +618,7 @@ public void AcquireThrowsAndReleasesAcquiredResources() using var chainedLimiter = PartitionedRateLimiter.CreateChained(limiter1, limiter2); Assert.Throws(() => chainedLimiter.AttemptAcquire("")); - Assert.Equal(1, concurrencyLimiter.GetAvailablePermits()); + Assert.Equal(1, concurrencyLimiter.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -522,7 +641,7 @@ public async Task AcquireAsyncThrowsAndReleasesAcquiredResources() using var chainedLimiter = PartitionedRateLimiter.CreateChained(limiter1, limiter2); await Assert.ThrowsAsync(async () => await chainedLimiter.AcquireAsync("")); - Assert.Equal(1, concurrencyLimiter.GetAvailablePermits()); + Assert.Equal(1, concurrencyLimiter.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -759,12 +878,12 @@ public void AcquireSucceedsDisposeThrowsAndReleasesResources() using var chainedLimiter = PartitionedRateLimiter.CreateChained(limiter1, limiter2); var lease = chainedLimiter.AttemptAcquire(""); Assert.True(lease.IsAcquired); - Assert.Equal(0, concurrencyLimiter.GetAvailablePermits()); + Assert.Equal(0, concurrencyLimiter.GetStatistics().CurrentAvailablePermits); var ex = Assert.Throws(() => lease.Dispose()); Assert.Single(ex.InnerExceptions); Assert.IsType(ex.InnerException); - Assert.Equal(1, concurrencyLimiter.GetAvailablePermits()); + Assert.Equal(1, concurrencyLimiter.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -788,12 +907,12 @@ public async Task AcquireAsyncSucceedsDisposeThrowsAndReleasesResources() using var chainedLimiter = PartitionedRateLimiter.CreateChained(limiter1, limiter2); var lease = await chainedLimiter.AcquireAsync(""); Assert.True(lease.IsAcquired); - Assert.Equal(0, concurrencyLimiter.GetAvailablePermits()); + Assert.Equal(0, concurrencyLimiter.GetStatistics().CurrentAvailablePermits); var ex = Assert.Throws(() => lease.Dispose()); Assert.Single(ex.InnerExceptions); Assert.IsType(ex.InnerException); - Assert.Equal(1, concurrencyLimiter.GetAvailablePermits()); + Assert.Equal(1, concurrencyLimiter.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -823,12 +942,12 @@ public void AcquireForwardsCorrectPermitCount() var lease = chainedLimiter.AttemptAcquire("", 3); Assert.True(lease.IsAcquired); - Assert.Equal(2, concurrencyLimiter1.GetAvailablePermits()); - Assert.Equal(0, concurrencyLimiter2.GetAvailablePermits()); + Assert.Equal(2, concurrencyLimiter1.GetStatistics().CurrentAvailablePermits); + Assert.Equal(0, concurrencyLimiter2.GetStatistics().CurrentAvailablePermits); lease.Dispose(); - Assert.Equal(5, concurrencyLimiter1.GetAvailablePermits()); - Assert.Equal(3, concurrencyLimiter2.GetAvailablePermits()); + Assert.Equal(5, concurrencyLimiter1.GetStatistics().CurrentAvailablePermits); + Assert.Equal(3, concurrencyLimiter2.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -858,12 +977,12 @@ public async Task AcquireAsyncForwardsCorrectPermitCount() var lease = await chainedLimiter.AcquireAsync("", 3); Assert.True(lease.IsAcquired); - Assert.Equal(2, concurrencyLimiter1.GetAvailablePermits()); - Assert.Equal(0, concurrencyLimiter2.GetAvailablePermits()); + Assert.Equal(2, concurrencyLimiter1.GetStatistics().CurrentAvailablePermits); + Assert.Equal(0, concurrencyLimiter2.GetStatistics().CurrentAvailablePermits); lease.Dispose(); - Assert.Equal(5, concurrencyLimiter1.GetAvailablePermits()); - Assert.Equal(3, concurrencyLimiter2.GetAvailablePermits()); + Assert.Equal(5, concurrencyLimiter1.GetStatistics().CurrentAvailablePermits); + Assert.Equal(3, concurrencyLimiter2.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -974,15 +1093,15 @@ public async Task AcquireAsyncCanceledReleasesAcquiredResources() var lease = chainedLimiter.AttemptAcquire(""); Assert.True(lease.IsAcquired); - Assert.Equal(1, concurrencyLimiter.GetAvailablePermits()); + Assert.Equal(1, concurrencyLimiter.GetStatistics().CurrentAvailablePermits); var cts = new CancellationTokenSource(); var task = chainedLimiter.AcquireAsync("", 1, cts.Token); - Assert.Equal(0, concurrencyLimiter.GetAvailablePermits()); + Assert.Equal(0, concurrencyLimiter.GetStatistics().CurrentAvailablePermits); cts.Cancel(); await Assert.ThrowsAsync(async () => await task); - Assert.Equal(1, concurrencyLimiter.GetAvailablePermits()); + Assert.Equal(1, concurrencyLimiter.GetStatistics().CurrentAvailablePermits); } [Fact] diff --git a/src/libraries/System.Threading.RateLimiting/tests/ConcurrencyLimiterTests.cs b/src/libraries/System.Threading.RateLimiting/tests/ConcurrencyLimiterTests.cs index 45b22a28ae47e9..54e2bbecc19752 100644 --- a/src/libraries/System.Threading.RateLimiting/tests/ConcurrencyLimiterTests.cs +++ b/src/libraries/System.Threading.RateLimiting/tests/ConcurrencyLimiterTests.cs @@ -536,7 +536,7 @@ public override async Task CanCancelAcquireAsyncAfterQueuing() lease.Dispose(); - Assert.Equal(1, limiter.GetAvailablePermits()); + Assert.Equal(1, limiter.GetStatistics()?.CurrentAvailablePermits); } [Fact] @@ -559,7 +559,7 @@ public override async Task CanCancelAcquireAsyncBeforeQueuing() lease.Dispose(); - Assert.Equal(1, limiter.GetAvailablePermits()); + Assert.Equal(1, limiter.GetStatistics()?.CurrentAvailablePermits); } [Fact] @@ -804,5 +804,131 @@ public override void IdleDurationUpdatesWhenChangingFromActive() lease.Dispose(); Assert.NotNull(limiter.IdleDuration); } + + [Fact] + public override void GetStatisticsReturnsNewInstances() + { + var limiter = new ConcurrencyLimiter(new ConcurrencyLimiterOptions + { + PermitLimit = 1, + QueueProcessingOrder = QueueProcessingOrder.OldestFirst, + QueueLimit = 1 + }); + + var stats = limiter.GetStatistics(); + Assert.Equal(1, stats.CurrentAvailablePermits); + + var lease = limiter.AttemptAcquire(1); + + var stats2 = limiter.GetStatistics(); + Assert.NotSame(stats, stats2); + Assert.Equal(1, stats.CurrentAvailablePermits); + Assert.Equal(0, stats2.CurrentAvailablePermits); + } + + [Fact] + public override async Task GetStatisticsHasCorrectValues() + { + var limiter = new ConcurrencyLimiter(new ConcurrencyLimiterOptions + { + PermitLimit = 100, + QueueProcessingOrder = QueueProcessingOrder.OldestFirst, + QueueLimit = 50 + }); + + var stats = limiter.GetStatistics(); + Assert.Equal(100, stats.CurrentAvailablePermits); + Assert.Equal(0, stats.CurrentQueuedCount); + Assert.Equal(0, stats.TotalFailedLeases); + Assert.Equal(0, stats.TotalSuccessfulLeases); + + // success from acquire + available + var lease1 = limiter.AttemptAcquire(60); + stats = limiter.GetStatistics(); + Assert.Equal(40, stats.CurrentAvailablePermits); + Assert.Equal(0, stats.CurrentQueuedCount); + Assert.Equal(0, stats.TotalFailedLeases); + Assert.Equal(1, stats.TotalSuccessfulLeases); + + // queue + var lease2Task = limiter.AcquireAsync(50); + stats = limiter.GetStatistics(); + Assert.Equal(40, stats.CurrentAvailablePermits); + Assert.Equal(50, stats.CurrentQueuedCount); + Assert.Equal(0, stats.TotalFailedLeases); + Assert.Equal(1, stats.TotalSuccessfulLeases); + + // failure from wait + var lease3 = await limiter.AcquireAsync(1); + Assert.False(lease3.IsAcquired); + stats = limiter.GetStatistics(); + Assert.Equal(40, stats.CurrentAvailablePermits); + Assert.Equal(50, stats.CurrentQueuedCount); + Assert.Equal(1, stats.TotalFailedLeases); + Assert.Equal(1, stats.TotalSuccessfulLeases); + + // failure from acquire + var lease4 = limiter.AttemptAcquire(100); + Assert.False(lease4.IsAcquired); + stats = limiter.GetStatistics(); + Assert.Equal(40, stats.CurrentAvailablePermits); + Assert.Equal(50, stats.CurrentQueuedCount); + Assert.Equal(2, stats.TotalFailedLeases); + Assert.Equal(1, stats.TotalSuccessfulLeases); + + lease1.Dispose(); + await lease2Task; + + // success from wait + available + queue + stats = limiter.GetStatistics(); + Assert.Equal(50, stats.CurrentAvailablePermits); + Assert.Equal(0, stats.CurrentQueuedCount); + Assert.Equal(2, stats.TotalFailedLeases); + Assert.Equal(2, stats.TotalSuccessfulLeases); + } + + [Fact] + public override async Task GetStatisticsWithZeroPermitCount() + { + var limiter = new ConcurrencyLimiter(new ConcurrencyLimiterOptions + { + PermitLimit = 100, + QueueProcessingOrder = QueueProcessingOrder.OldestFirst, + QueueLimit = 50 + }); + var lease = limiter.AttemptAcquire(0); + Assert.True(lease.IsAcquired); + Assert.Equal(1, limiter.GetStatistics().TotalSuccessfulLeases); + Assert.Equal(100, limiter.GetStatistics().CurrentAvailablePermits); + + lease = await limiter.AcquireAsync(0); + Assert.True(lease.IsAcquired); + Assert.Equal(2, limiter.GetStatistics().TotalSuccessfulLeases); + Assert.Equal(100, limiter.GetStatistics().CurrentAvailablePermits); + + lease = limiter.AttemptAcquire(100); + Assert.True(lease.IsAcquired); + Assert.Equal(3, limiter.GetStatistics().TotalSuccessfulLeases); + Assert.Equal(0, limiter.GetStatistics().CurrentAvailablePermits); + + var lease2 = limiter.AttemptAcquire(0); + Assert.False(lease2.IsAcquired); + Assert.Equal(3, limiter.GetStatistics().TotalSuccessfulLeases); + Assert.Equal(1, limiter.GetStatistics().TotalFailedLeases); + Assert.Equal(0, limiter.GetStatistics().CurrentAvailablePermits); + } + + [Fact] + public override void GetStatisticsThrowsAfterDispose() + { + var limiter = new ConcurrencyLimiter(new ConcurrencyLimiterOptions + { + PermitLimit = 100, + QueueProcessingOrder = QueueProcessingOrder.OldestFirst, + QueueLimit = 50 + }); + limiter.Dispose(); + Assert.Throws(limiter.GetStatistics); + } } } diff --git a/src/libraries/System.Threading.RateLimiting/tests/FixedWindowRateLimiterTests.cs b/src/libraries/System.Threading.RateLimiting/tests/FixedWindowRateLimiterTests.cs index 7b521e6ba04424..6830a1ce742816 100644 --- a/src/libraries/System.Threading.RateLimiting/tests/FixedWindowRateLimiterTests.cs +++ b/src/libraries/System.Threading.RateLimiting/tests/FixedWindowRateLimiterTests.cs @@ -114,7 +114,7 @@ public override async Task CanAcquireResourceAsync_QueuesAndGrabsOldest() Assert.False(wait2.IsCompleted); lease.Dispose(); - Assert.Equal(0, limiter.GetAvailablePermits()); + Assert.Equal(0, limiter.GetStatistics().CurrentAvailablePermits); Assert.True(limiter.TryReplenish()); lease = await wait2; @@ -150,7 +150,7 @@ public override async Task CanAcquireResourceAsync_QueuesAndGrabsNewest() Assert.False(wait1.IsCompleted); lease.Dispose(); - Assert.Equal(1, limiter.GetAvailablePermits()); + Assert.Equal(1, limiter.GetStatistics().CurrentAvailablePermits); Assert.True(limiter.TryReplenish()); lease = await wait1; @@ -502,7 +502,7 @@ public override async Task CanCancelAcquireAsyncAfterQueuing() lease.Dispose(); Assert.True(limiter.TryReplenish()); - Assert.Equal(1, limiter.GetAvailablePermits()); + Assert.Equal(1, limiter.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -528,7 +528,7 @@ public override async Task CanCancelAcquireAsyncBeforeQueuing() lease.Dispose(); Assert.True(limiter.TryReplenish()); - Assert.Equal(1, limiter.GetAvailablePermits()); + Assert.Equal(1, limiter.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -739,9 +739,9 @@ public void TryReplenishWithAutoReplenish_ReturnsFalse() Window = TimeSpan.FromSeconds(1), AutoReplenishment = true }); - Assert.Equal(2, limiter.GetAvailablePermits()); + Assert.Equal(2, limiter.GetStatistics().CurrentAvailablePermits); Assert.False(limiter.TryReplenish()); - Assert.Equal(2, limiter.GetAvailablePermits()); + Assert.Equal(2, limiter.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -755,7 +755,7 @@ public async Task AutoReplenish_ReplenishesCounters() Window = TimeSpan.FromMilliseconds(1000), AutoReplenishment = true }); - Assert.Equal(2, limiter.GetAvailablePermits()); + Assert.Equal(2, limiter.GetStatistics().CurrentAvailablePermits); limiter.AttemptAcquire(2); var lease = await limiter.AcquireAsync(1); @@ -780,7 +780,7 @@ public override async Task CanAcquireResourcesWithAcquireAsyncWithQueuedItemsIfN var wait = limiter.AcquireAsync(2); Assert.False(wait.IsCompleted); - Assert.Equal(1, limiter.GetAvailablePermits()); + Assert.Equal(1, limiter.GetStatistics().CurrentAvailablePermits); lease = await limiter.AcquireAsync(1); Assert.True(lease.IsAcquired); Assert.False(wait.IsCompleted); @@ -1017,5 +1017,139 @@ public override async Task CanDisposeAfterCancelingQueuedRequest() // Make sure dispose doesn't have any side-effects when dealing with a canceled queued item limiter.Dispose(); } + + [Fact] + public override void GetStatisticsReturnsNewInstances() + { + var limiter = new FixedWindowRateLimiter(new FixedWindowRateLimiterOptions + { + PermitLimit = 1, + QueueProcessingOrder = QueueProcessingOrder.OldestFirst, + QueueLimit = 1, + Window = TimeSpan.Zero, + AutoReplenishment = false + }); + + var stats = limiter.GetStatistics(); + Assert.Equal(1, stats.CurrentAvailablePermits); + + var lease = limiter.AttemptAcquire(1); + + var stats2 = limiter.GetStatistics(); + Assert.NotSame(stats, stats2); + Assert.Equal(1, stats.CurrentAvailablePermits); + Assert.Equal(0, stats2.CurrentAvailablePermits); + } + + [Fact] + public override async Task GetStatisticsHasCorrectValues() + { + var limiter = new FixedWindowRateLimiter(new FixedWindowRateLimiterOptions + { + PermitLimit = 100, + QueueProcessingOrder = QueueProcessingOrder.OldestFirst, + QueueLimit = 50, + Window = TimeSpan.Zero, + AutoReplenishment = false + }); + + var stats = limiter.GetStatistics(); + Assert.Equal(100, stats.CurrentAvailablePermits); + Assert.Equal(0, stats.CurrentQueuedCount); + Assert.Equal(0, stats.TotalFailedLeases); + Assert.Equal(0, stats.TotalSuccessfulLeases); + + // success from acquire + available + var lease1 = limiter.AttemptAcquire(60); + stats = limiter.GetStatistics(); + Assert.Equal(40, stats.CurrentAvailablePermits); + Assert.Equal(0, stats.CurrentQueuedCount); + Assert.Equal(0, stats.TotalFailedLeases); + Assert.Equal(1, stats.TotalSuccessfulLeases); + + // queue + var lease2Task = limiter.AcquireAsync(50); + stats = limiter.GetStatistics(); + Assert.Equal(40, stats.CurrentAvailablePermits); + Assert.Equal(50, stats.CurrentQueuedCount); + Assert.Equal(0, stats.TotalFailedLeases); + Assert.Equal(1, stats.TotalSuccessfulLeases); + + // failure from wait + var lease3 = await limiter.AcquireAsync(1); + Assert.False(lease3.IsAcquired); + stats = limiter.GetStatistics(); + Assert.Equal(40, stats.CurrentAvailablePermits); + Assert.Equal(50, stats.CurrentQueuedCount); + Assert.Equal(1, stats.TotalFailedLeases); + Assert.Equal(1, stats.TotalSuccessfulLeases); + + // failure from acquire + var lease4 = limiter.AttemptAcquire(100); + Assert.False(lease4.IsAcquired); + stats = limiter.GetStatistics(); + Assert.Equal(40, stats.CurrentAvailablePermits); + Assert.Equal(50, stats.CurrentQueuedCount); + Assert.Equal(2, stats.TotalFailedLeases); + Assert.Equal(1, stats.TotalSuccessfulLeases); + + limiter.TryReplenish(); + await lease2Task; + + // success from wait + available + queue + stats = limiter.GetStatistics(); + Assert.Equal(50, stats.CurrentAvailablePermits); + Assert.Equal(0, stats.CurrentQueuedCount); + Assert.Equal(2, stats.TotalFailedLeases); + Assert.Equal(2, stats.TotalSuccessfulLeases); + } + + [Fact] + public override async Task GetStatisticsWithZeroPermitCount() + { + var limiter = new FixedWindowRateLimiter(new FixedWindowRateLimiterOptions + { + PermitLimit = 100, + QueueProcessingOrder = QueueProcessingOrder.OldestFirst, + QueueLimit = 50, + Window = TimeSpan.Zero, + AutoReplenishment = false + }); + var lease = limiter.AttemptAcquire(0); + Assert.True(lease.IsAcquired); + Assert.Equal(1, limiter.GetStatistics().TotalSuccessfulLeases); + Assert.Equal(100, limiter.GetStatistics().CurrentAvailablePermits); + + lease = await limiter.AcquireAsync(0); + Assert.True(lease.IsAcquired); + Assert.Equal(2, limiter.GetStatistics().TotalSuccessfulLeases); + Assert.Equal(100, limiter.GetStatistics().CurrentAvailablePermits); + + lease = limiter.AttemptAcquire(100); + Assert.True(lease.IsAcquired); + Assert.Equal(3, limiter.GetStatistics().TotalSuccessfulLeases); + Assert.Equal(0, limiter.GetStatistics().CurrentAvailablePermits); + + var lease2 = limiter.AttemptAcquire(0); + Assert.False(lease2.IsAcquired); + Assert.Equal(3, limiter.GetStatistics().TotalSuccessfulLeases); + Assert.Equal(1, limiter.GetStatistics().TotalFailedLeases); + Assert.Equal(0, limiter.GetStatistics().CurrentAvailablePermits); + } + + [Fact] + public override void GetStatisticsThrowsAfterDispose() + { + var limiter = new FixedWindowRateLimiter(new FixedWindowRateLimiterOptions + { + PermitLimit = 100, + QueueProcessingOrder = QueueProcessingOrder.OldestFirst, + QueueLimit = 50, + Window = TimeSpan.Zero, + AutoReplenishment = false + }); + limiter.Dispose(); + Assert.Throws(limiter.GetStatistics); + } } } diff --git a/src/libraries/System.Threading.RateLimiting/tests/Infrastructure/Utils.cs b/src/libraries/System.Threading.RateLimiting/tests/Infrastructure/Utils.cs index de62a2d6c065a9..b506b7ee3e1dd4 100644 --- a/src/libraries/System.Threading.RateLimiting/tests/Infrastructure/Utils.cs +++ b/src/libraries/System.Threading.RateLimiting/tests/Infrastructure/Utils.cs @@ -49,20 +49,20 @@ internal static Task RunTimerFunc(PartitionedRateLimiter limiter) internal sealed class NotImplementedPartitionedRateLimiter : PartitionedRateLimiter { - public override int GetAvailablePermits(T resource) => throw new NotImplementedException(); + public override RateLimiterStatistics? GetStatistics(T resource) => throw new NotImplementedException(); protected override RateLimitLease AttemptAcquireCore(T resource, int permitCount) => throw new NotImplementedException(); protected override ValueTask AcquireAsyncCore(T resource, int permitCount, CancellationToken cancellationToken) => throw new NotImplementedException(); } internal sealed class TrackingRateLimiter : RateLimiter { - private int _getAvailablePermitsCallCount; + private int _getStatisticsCallCount; private int _acquireCallCount; private int _waitAsyncCallCount; private int _disposeCallCount; private int _disposeAsyncCallCount; - public int GetAvailablePermitsCallCount => _getAvailablePermitsCallCount; + public int GetStatisticsCallCount => _getStatisticsCallCount; public int AcquireCallCount => _acquireCallCount; public int AcquireAsyncCallCount => _waitAsyncCallCount; public int DisposeCallCount => _disposeCallCount; @@ -70,10 +70,10 @@ internal sealed class TrackingRateLimiter : RateLimiter public override TimeSpan? IdleDuration => null; - public override int GetAvailablePermits() + public override RateLimiterStatistics? GetStatistics() { - Interlocked.Increment(ref _getAvailablePermitsCallCount); - return 1; + Interlocked.Increment(ref _getStatisticsCallCount); + return null; } protected override RateLimitLease AttemptAcquireCore(int permitCount) @@ -149,7 +149,7 @@ internal sealed class NotImplementedLimiter : RateLimiter { public override TimeSpan? IdleDuration => throw new NotImplementedException(); - public override int GetAvailablePermits() => throw new NotImplementedException(); + public override RateLimiterStatistics? GetStatistics() => throw new NotImplementedException(); protected override RateLimitLease AttemptAcquireCore(int permitCount) => throw new NotImplementedException(); protected override ValueTask AcquireAsyncCore(int permitCount, CancellationToken cancellationToken) => throw new NotImplementedException(); } @@ -159,8 +159,8 @@ internal sealed class CustomizableLimiter : RateLimiter public Func IdleDurationImpl { get; set; } = () => null; public override TimeSpan? IdleDuration => IdleDurationImpl(); - public Func GetAvailablePermitsImpl { get; set; } = () => throw new NotImplementedException(); - public override int GetAvailablePermits() => GetAvailablePermitsImpl(); + public Func GetStatisticsImpl{ get; set; } = () => throw new NotImplementedException(); + public override RateLimiterStatistics? GetStatistics() => GetStatisticsImpl(); public Func AttemptAcquireCoreImpl { get; set; } = _ => new Lease(); protected override RateLimitLease AttemptAcquireCore(int permitCount) => AttemptAcquireCoreImpl(permitCount); @@ -189,8 +189,8 @@ internal sealed class CustomizableReplenishingLimiter : ReplenishingRateLimiter public Func IdleDurationImpl { get; set; } = () => null; public override TimeSpan? IdleDuration => IdleDurationImpl(); - public Func GetAvailablePermitsImpl { get; set; } = () => throw new NotImplementedException(); - public override int GetAvailablePermits() => GetAvailablePermitsImpl(); + public Func GetStatisticsImpl { get; set; } = () => throw new NotImplementedException(); + public override RateLimiterStatistics? GetStatistics() => GetStatisticsImpl(); public Func AttemptAcquireCoreImpl { get; set; } = _ => new Lease(); protected override RateLimitLease AttemptAcquireCore(int permitCount) => AttemptAcquireCoreImpl(permitCount); diff --git a/src/libraries/System.Threading.RateLimiting/tests/PartitionedRateLimiterTests.cs b/src/libraries/System.Threading.RateLimiting/tests/PartitionedRateLimiterTests.cs index 4ba9b60ae75f2e..b0380a0dcd6a4f 100644 --- a/src/libraries/System.Threading.RateLimiting/tests/PartitionedRateLimiterTests.cs +++ b/src/libraries/System.Threading.RateLimiting/tests/PartitionedRateLimiterTests.cs @@ -61,7 +61,7 @@ public async Task Create_WaitAsyncCallsUnderlyingPartitionsLimiter() } [Fact] - public void Create_GetAvailablePermitsCallsUnderlyingPartitionsLimiter() + public void Create_GetStatisticsCallsUnderlyingPartitionsLimiter() { var limiterFactory = new TrackingRateLimiterFactory(); using var limiter = PartitionedRateLimiter.Create(resource => @@ -69,9 +69,9 @@ public void Create_GetAvailablePermitsCallsUnderlyingPartitionsLimiter() return RateLimitPartition.Get(1, key => limiterFactory.GetLimiter(key)); }); - limiter.GetAvailablePermits(""); + limiter.GetStatistics(""); Assert.Equal(1, limiterFactory.Limiters.Count); - Assert.Equal(1, limiterFactory.Limiters[0].Limiter.GetAvailablePermitsCallCount); + Assert.Equal(1, limiterFactory.Limiters[0].Limiter.GetStatisticsCallCount); } [Fact] @@ -766,13 +766,13 @@ public void Translate_GetAvailablePermitsPassesThroughToInnerLimiter() return i.ToString(); }, leaveOpen: true); - Assert.Equal(1, translateLimiter.GetAvailablePermits(1)); + Assert.Equal(1, translateLimiter.GetStatistics(1).CurrentAvailablePermits); Assert.Equal(1, translateCallCount); var lease = translateLimiter.AttemptAcquire(1); Assert.True(lease.IsAcquired); Assert.Equal(2, translateCallCount); - Assert.Equal(0, translateLimiter.GetAvailablePermits(1)); + Assert.Equal(0, translateLimiter.GetStatistics(1).CurrentAvailablePermits); Assert.Equal(3, translateCallCount); var lease2 = limiter.AttemptAcquire("1"); @@ -780,13 +780,13 @@ public void Translate_GetAvailablePermitsPassesThroughToInnerLimiter() lease.Dispose(); - Assert.Equal(1, translateLimiter.GetAvailablePermits(1)); + Assert.Equal(1, translateLimiter.GetStatistics(1).CurrentAvailablePermits); Assert.Equal(4, translateCallCount); lease = limiter.AttemptAcquire("1"); Assert.True(lease.IsAcquired); - Assert.Equal(0, translateLimiter.GetAvailablePermits(1)); + Assert.Equal(0, translateLimiter.GetStatistics(1).CurrentAvailablePermits); Assert.Equal(5, translateCallCount); } @@ -953,5 +953,24 @@ public async Task Translate_DisposeAsyncDoesDisposeInnerLimiter() Assert.Throws(() => limiter.AttemptAcquire("1")); Assert.Throws(() => translateLimiter.AttemptAcquire(1)); } + + [Fact] + public void Translate_GetStatisticsCallsUnderlyingLimiter() + { + var limiterFactory = new TrackingRateLimiterFactory(); + using var limiter = PartitionedRateLimiter.Create(resource => + { + return RateLimitPartition.Get(1, key => limiterFactory.GetLimiter(key)); + }); + + var translateLimiter = limiter.WithTranslatedKey(i => + { + return i.ToString(); + }, leaveOpen: false); + + translateLimiter.GetStatistics(1); + Assert.Equal(1, limiterFactory.Limiters.Count); + Assert.Equal(1, limiterFactory.Limiters[0].Limiter.GetStatisticsCallCount); + } } } diff --git a/src/libraries/System.Threading.RateLimiting/tests/RateLimiterPartitionTests.cs b/src/libraries/System.Threading.RateLimiting/tests/RateLimiterPartitionTests.cs index 5676d1c16b96af..94fe202307ee92 100644 --- a/src/libraries/System.Threading.RateLimiting/tests/RateLimiterPartitionTests.cs +++ b/src/libraries/System.Threading.RateLimiting/tests/RateLimiterPartitionTests.cs @@ -21,7 +21,7 @@ public void Create_Concurrency() var limiter = partition.Factory(1); var concurrencyLimiter = Assert.IsType(limiter); - Assert.Equal(options.PermitLimit, concurrencyLimiter.GetAvailablePermits()); + Assert.Equal(options.PermitLimit, concurrencyLimiter.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -40,7 +40,7 @@ public void Create_TokenBucket() var limiter = partition.Factory(1); var tokenBucketLimiter = Assert.IsType(limiter); - Assert.Equal(options.TokenLimit, tokenBucketLimiter.GetAvailablePermits()); + Assert.Equal(options.TokenLimit, tokenBucketLimiter.GetStatistics().CurrentAvailablePermits); Assert.Equal(options.ReplenishmentPeriod, tokenBucketLimiter.ReplenishmentPeriod); Assert.False(tokenBucketLimiter.IsAutoReplenishing); } @@ -53,10 +53,10 @@ public async Task Create_NoLimiter() var limiter = partition.Factory(1); // How do we test an internal implementation of a limiter that doesn't limit? Just try some stuff that normal limiters would probably block on and see if it works. - var available = limiter.GetAvailablePermits(); + var available = limiter.GetStatistics().CurrentAvailablePermits; var lease = limiter.AttemptAcquire(int.MaxValue); Assert.True(lease.IsAcquired); - Assert.Equal(available, limiter.GetAvailablePermits()); + Assert.Equal(available, limiter.GetStatistics().CurrentAvailablePermits); lease = limiter.AttemptAcquire(int.MaxValue); Assert.True(lease.IsAcquired); @@ -69,6 +69,50 @@ public async Task Create_NoLimiter() lease.Dispose(); } + [Fact] + public async Task NoLimiter_GetStatistics() + { + var partition = RateLimitPartition.GetNoLimiter(1); + + var limiter = partition.Factory(1); + + var stats = limiter.GetStatistics(); + Assert.NotSame(stats, limiter.GetStatistics()); + Assert.Equal(long.MaxValue, stats.CurrentAvailablePermits); + Assert.Equal(0, stats.CurrentQueuedCount); + Assert.Equal(0, stats.TotalFailedLeases); + Assert.Equal(0, stats.TotalSuccessfulLeases); + + var leaseCount = 0; + for (var i = 0; i < 134; i++) + { + var lease = limiter.AttemptAcquire(i); + Assert.True(lease.IsAcquired); + ++leaseCount; + } + + stats = limiter.GetStatistics(); + Assert.Equal(long.MaxValue, stats.CurrentAvailablePermits); + Assert.Equal(0, stats.CurrentQueuedCount); + Assert.Equal(0, stats.TotalFailedLeases); + Assert.Equal(leaseCount, stats.TotalSuccessfulLeases); + + for (var i = 0; i < 165; i++) + { + var wait = limiter.AcquireAsync(int.MaxValue); + Assert.True(wait.IsCompletedSuccessfully); + var lease = await wait; + Assert.True(lease.IsAcquired); + ++leaseCount; + } + + stats = limiter.GetStatistics(); + Assert.Equal(long.MaxValue, stats.CurrentAvailablePermits); + Assert.Equal(0, stats.CurrentQueuedCount); + Assert.Equal(0, stats.TotalFailedLeases); + Assert.Equal(leaseCount, stats.TotalSuccessfulLeases); + } + [Fact] public void Create_AnyLimiter() { @@ -81,7 +125,7 @@ public void Create_AnyLimiter() var limiter = partition.Factory(1); var concurrencyLimiter = Assert.IsType(limiter); - Assert.Equal(1, concurrencyLimiter.GetAvailablePermits()); + Assert.Equal(1, concurrencyLimiter.GetStatistics().CurrentAvailablePermits); var partition2 = RateLimitPartition.Get(1, key => new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions { @@ -94,7 +138,7 @@ public void Create_AnyLimiter() })); limiter = partition2.Factory(1); var tokenBucketLimiter = Assert.IsType(limiter); - Assert.Equal(1, tokenBucketLimiter.GetAvailablePermits()); + Assert.Equal(1, tokenBucketLimiter.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -112,7 +156,7 @@ public void Create_FixedWindow() var limiter = partition.Factory(1); var fixedWindowLimiter = Assert.IsType(limiter); - Assert.Equal(options.PermitLimit, fixedWindowLimiter.GetAvailablePermits()); + Assert.Equal(options.PermitLimit, fixedWindowLimiter.GetStatistics().CurrentAvailablePermits); Assert.Equal(options.Window, fixedWindowLimiter.ReplenishmentPeriod); Assert.False(fixedWindowLimiter.IsAutoReplenishing); } @@ -133,7 +177,7 @@ public void Create_SlidingWindow() var limiter = partition.Factory(1); var slidingWindowLimiter = Assert.IsType(limiter); - Assert.Equal(options.PermitLimit, slidingWindowLimiter.GetAvailablePermits()); + Assert.Equal(options.PermitLimit, slidingWindowLimiter.GetStatistics().CurrentAvailablePermits); Assert.Equal(TimeSpan.FromSeconds(11), slidingWindowLimiter.ReplenishmentPeriod); Assert.False(slidingWindowLimiter.IsAutoReplenishing); } diff --git a/src/libraries/System.Threading.RateLimiting/tests/SlidingWindowRateLimiterTests.cs b/src/libraries/System.Threading.RateLimiting/tests/SlidingWindowRateLimiterTests.cs index fc373ff2ec7863..7241a39e0f1f4d 100644 --- a/src/libraries/System.Threading.RateLimiting/tests/SlidingWindowRateLimiterTests.cs +++ b/src/libraries/System.Threading.RateLimiting/tests/SlidingWindowRateLimiterTests.cs @@ -148,7 +148,7 @@ public async Task CanAcquireMultipleRequestsAsync() Assert.True((await wait3).IsAcquired); Assert.False((await wait).IsAcquired); - Assert.Equal(0, limiter.GetAvailablePermits()); + Assert.Equal(0, limiter.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -182,7 +182,7 @@ public override async Task CanAcquireResourceAsync_QueuesAndGrabsOldest() Assert.False(wait2.IsCompleted); lease.Dispose(); - Assert.Equal(1, limiter.GetAvailablePermits()); + Assert.Equal(1, limiter.GetStatistics().CurrentAvailablePermits); Assert.True(limiter.TryReplenish()); Assert.True(limiter.TryReplenish()); @@ -222,7 +222,7 @@ public override async Task CanAcquireResourceAsync_QueuesAndGrabsNewest() Assert.False(wait1.IsCompleted); lease.Dispose(); - Assert.Equal(1, limiter.GetAvailablePermits()); + Assert.Equal(1, limiter.GetStatistics().CurrentAvailablePermits); Assert.True(limiter.TryReplenish()); Assert.True(limiter.TryReplenish()); @@ -602,7 +602,7 @@ public override async Task CanCancelAcquireAsyncAfterQueuing() lease.Dispose(); Assert.True(limiter.TryReplenish()); - Assert.Equal(0, limiter.GetAvailablePermits()); + Assert.Equal(0, limiter.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -629,7 +629,7 @@ public override async Task CanCancelAcquireAsyncBeforeQueuing() lease.Dispose(); Assert.True(limiter.TryReplenish()); - Assert.Equal(0, limiter.GetAvailablePermits()); + Assert.Equal(0, limiter.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -662,7 +662,7 @@ public override async Task CancelUpdatesQueueLimit() lease = await wait; Assert.True(lease.IsAcquired); - Assert.Equal(1, limiter.GetAvailablePermits()); + Assert.Equal(1, limiter.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -777,9 +777,9 @@ public void TryReplenishWithAutoReplenish_ReturnsFalse() SegmentsPerWindow = 1, AutoReplenishment = true }); - Assert.Equal(2, limiter.GetAvailablePermits()); + Assert.Equal(2, limiter.GetStatistics().CurrentAvailablePermits); Assert.False(limiter.TryReplenish()); - Assert.Equal(2, limiter.GetAvailablePermits()); + Assert.Equal(2, limiter.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -794,7 +794,7 @@ public async Task AutoReplenish_ReplenishesCounters() SegmentsPerWindow = 2, AutoReplenishment = true }); - Assert.Equal(2, limiter.GetAvailablePermits()); + Assert.Equal(2, limiter.GetStatistics().CurrentAvailablePermits); limiter.AttemptAcquire(2); var lease = await limiter.AcquireAsync(1); @@ -820,7 +820,7 @@ public override async Task CanAcquireResourcesWithAcquireAsyncWithQueuedItemsIfN var wait = limiter.AcquireAsync(2); Assert.False(wait.IsCompleted); - Assert.Equal(1, limiter.GetAvailablePermits()); + Assert.Equal(1, limiter.GetStatistics().CurrentAvailablePermits); lease = await limiter.AcquireAsync(1); Assert.True(lease.IsAcquired); Assert.False(wait.IsCompleted); @@ -1080,5 +1080,140 @@ public override async Task CanDisposeAfterCancelingQueuedRequest() // Make sure dispose doesn't have any side-effects when dealing with a canceled queued item limiter.Dispose(); } + + [Fact] + public override void GetStatisticsReturnsNewInstances() + { + var limiter = new SlidingWindowRateLimiter(new SlidingWindowRateLimiterOptions + { + PermitLimit = 1, + QueueProcessingOrder = QueueProcessingOrder.OldestFirst, + QueueLimit = 1, + Window = TimeSpan.Zero, + SegmentsPerWindow = 2, + AutoReplenishment = false + }); + + var stats = limiter.GetStatistics(); + Assert.Equal(1, stats.CurrentAvailablePermits); + + var lease = limiter.AttemptAcquire(1); + + var stats2 = limiter.GetStatistics(); + Assert.NotSame(stats, stats2); + Assert.Equal(1, stats.CurrentAvailablePermits); + Assert.Equal(0, stats2.CurrentAvailablePermits); + } + + [Fact] + public override async Task GetStatisticsHasCorrectValues() + { + var limiter = new SlidingWindowRateLimiter(new SlidingWindowRateLimiterOptions + { + PermitLimit = 100, + QueueProcessingOrder = QueueProcessingOrder.OldestFirst, + QueueLimit = 50, + Window = TimeSpan.Zero, + SegmentsPerWindow = 2, + AutoReplenishment = false + }); + + var stats = limiter.GetStatistics(); + Assert.Equal(100, stats.CurrentAvailablePermits); + Assert.Equal(0, stats.CurrentQueuedCount); + Assert.Equal(0, stats.TotalFailedLeases); + Assert.Equal(0, stats.TotalSuccessfulLeases); + + var lease1 = limiter.AttemptAcquire(60); + stats = limiter.GetStatistics(); + Assert.Equal(40, stats.CurrentAvailablePermits); + Assert.Equal(0, stats.CurrentQueuedCount); + Assert.Equal(0, stats.TotalFailedLeases); + Assert.Equal(1, stats.TotalSuccessfulLeases); + + var lease2Task = limiter.AcquireAsync(50); + stats = limiter.GetStatistics(); + Assert.Equal(40, stats.CurrentAvailablePermits); + Assert.Equal(50, stats.CurrentQueuedCount); + Assert.Equal(0, stats.TotalFailedLeases); + Assert.Equal(1, stats.TotalSuccessfulLeases); + + limiter.TryReplenish(); + + var lease3 = await limiter.AcquireAsync(1); + Assert.False(lease3.IsAcquired); + stats = limiter.GetStatistics(); + Assert.Equal(40, stats.CurrentAvailablePermits); + Assert.Equal(50, stats.CurrentQueuedCount); + Assert.Equal(1, stats.TotalFailedLeases); + Assert.Equal(1, stats.TotalSuccessfulLeases); + + var lease4 = limiter.AttemptAcquire(100); + Assert.False(lease4.IsAcquired); + stats = limiter.GetStatistics(); + Assert.Equal(40, stats.CurrentAvailablePermits); + Assert.Equal(50, stats.CurrentQueuedCount); + Assert.Equal(2, stats.TotalFailedLeases); + Assert.Equal(1, stats.TotalSuccessfulLeases); + + limiter.TryReplenish(); + await lease2Task; + + stats = limiter.GetStatistics(); + Assert.Equal(50, stats.CurrentAvailablePermits); + Assert.Equal(0, stats.CurrentQueuedCount); + Assert.Equal(2, stats.TotalFailedLeases); + Assert.Equal(2, stats.TotalSuccessfulLeases); + } + + [Fact] + public override async Task GetStatisticsWithZeroPermitCount() + { + var limiter = new SlidingWindowRateLimiter(new SlidingWindowRateLimiterOptions + { + PermitLimit = 100, + QueueProcessingOrder = QueueProcessingOrder.OldestFirst, + QueueLimit = 50, + Window = TimeSpan.Zero, + SegmentsPerWindow = 3, + AutoReplenishment = false + }); + var lease = limiter.AttemptAcquire(0); + Assert.True(lease.IsAcquired); + Assert.Equal(1, limiter.GetStatistics().TotalSuccessfulLeases); + Assert.Equal(100, limiter.GetStatistics().CurrentAvailablePermits); + + lease = await limiter.AcquireAsync(0); + Assert.True(lease.IsAcquired); + Assert.Equal(2, limiter.GetStatistics().TotalSuccessfulLeases); + Assert.Equal(100, limiter.GetStatistics().CurrentAvailablePermits); + + lease = limiter.AttemptAcquire(100); + Assert.True(lease.IsAcquired); + Assert.Equal(3, limiter.GetStatistics().TotalSuccessfulLeases); + Assert.Equal(0, limiter.GetStatistics().CurrentAvailablePermits); + + var lease2 = limiter.AttemptAcquire(0); + Assert.False(lease2.IsAcquired); + Assert.Equal(3, limiter.GetStatistics().TotalSuccessfulLeases); + Assert.Equal(1, limiter.GetStatistics().TotalFailedLeases); + Assert.Equal(0, limiter.GetStatistics().CurrentAvailablePermits); + } + + [Fact] + public override void GetStatisticsThrowsAfterDispose() + { + var limiter = new SlidingWindowRateLimiter(new SlidingWindowRateLimiterOptions + { + PermitLimit = 100, + QueueProcessingOrder = QueueProcessingOrder.OldestFirst, + QueueLimit = 50, + Window = TimeSpan.Zero, + SegmentsPerWindow = 3, + AutoReplenishment = false + }); + limiter.Dispose(); + Assert.Throws(limiter.GetStatistics); + } } } diff --git a/src/libraries/System.Threading.RateLimiting/tests/TokenBucketRateLimiterTests.cs b/src/libraries/System.Threading.RateLimiting/tests/TokenBucketRateLimiterTests.cs index 67e6624267fadb..272c294a09b345 100644 --- a/src/libraries/System.Threading.RateLimiting/tests/TokenBucketRateLimiterTests.cs +++ b/src/libraries/System.Threading.RateLimiting/tests/TokenBucketRateLimiterTests.cs @@ -130,7 +130,7 @@ public override async Task CanAcquireResourceAsync_QueuesAndGrabsOldest() Assert.False(wait2.IsCompleted); lease.Dispose(); - Assert.Equal(0, limiter.GetAvailablePermits()); + Assert.Equal(0, limiter.GetStatistics().CurrentAvailablePermits); Assert.True(limiter.TryReplenish()); lease = await wait2; @@ -167,7 +167,7 @@ public override async Task CanAcquireResourceAsync_QueuesAndGrabsNewest() Assert.False(wait1.IsCompleted); lease.Dispose(); - Assert.Equal(0, limiter.GetAvailablePermits()); + Assert.Equal(0, limiter.GetStatistics().CurrentAvailablePermits); Assert.True(limiter.TryReplenish()); Assert.True(limiter.TryReplenish()); @@ -537,7 +537,7 @@ public override async Task CanCancelAcquireAsyncAfterQueuing() lease.Dispose(); Assert.True(limiter.TryReplenish()); - Assert.Equal(1, limiter.GetAvailablePermits()); + Assert.Equal(1, limiter.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -630,7 +630,7 @@ public override async Task CanCancelAcquireAsyncBeforeQueuing() lease.Dispose(); Assert.True(limiter.TryReplenish()); - Assert.Equal(1, limiter.GetAvailablePermits()); + Assert.Equal(1, limiter.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -902,12 +902,12 @@ public void TryReplenishHonorsTokensPerPeriod() Assert.True(limiter.AttemptAcquire(5).IsAcquired); Assert.False(limiter.AttemptAcquire(3).IsAcquired); - Assert.Equal(2, limiter.GetAvailablePermits()); + Assert.Equal(2, limiter.GetStatistics().CurrentAvailablePermits); Assert.True(limiter.TryReplenish()); - Assert.Equal(5, limiter.GetAvailablePermits()); + Assert.Equal(5, limiter.GetStatistics().CurrentAvailablePermits); Assert.True(limiter.TryReplenish()); - Assert.Equal(7, limiter.GetAvailablePermits()); + Assert.Equal(7, limiter.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -922,9 +922,9 @@ public void TryReplenishWithAllTokensAvailable_Noops() TokensPerPeriod = 1, AutoReplenishment = false }); - Assert.Equal(2, limiter.GetAvailablePermits()); + Assert.Equal(2, limiter.GetStatistics().CurrentAvailablePermits); Assert.True(limiter.TryReplenish()); - Assert.Equal(2, limiter.GetAvailablePermits()); + Assert.Equal(2, limiter.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -939,9 +939,9 @@ public void TryReplenishWithAutoReplenish_ReturnsFalse() TokensPerPeriod = 1, AutoReplenishment = true }); - Assert.Equal(2, limiter.GetAvailablePermits()); + Assert.Equal(2, limiter.GetStatistics().CurrentAvailablePermits); Assert.False(limiter.TryReplenish()); - Assert.Equal(2, limiter.GetAvailablePermits()); + Assert.Equal(2, limiter.GetStatistics().CurrentAvailablePermits); } [Fact] @@ -956,7 +956,7 @@ public async Task AutoReplenish_ReplenishesTokens() TokensPerPeriod = 1, AutoReplenishment = true }); - Assert.Equal(2, limiter.GetAvailablePermits()); + Assert.Equal(2, limiter.GetStatistics().CurrentAvailablePermits); limiter.AttemptAcquire(2); var lease = await limiter.AcquireAsync(1); @@ -982,7 +982,7 @@ public override async Task CanAcquireResourcesWithAcquireAsyncWithQueuedItemsIfN var wait = limiter.AcquireAsync(2); Assert.False(wait.IsCompleted); - Assert.Equal(1, limiter.GetAvailablePermits()); + Assert.Equal(1, limiter.GetStatistics().CurrentAvailablePermits); lease = await limiter.AcquireAsync(1); Assert.True(lease.IsAcquired); Assert.False(wait.IsCompleted); @@ -1205,5 +1205,138 @@ public void ReplenishingRateLimiterPropertiesHaveCorrectValues() Assert.False(limiter2.IsAutoReplenishing); Assert.Equal(replenishPeriod, limiter2.ReplenishmentPeriod); } + + [Fact] + public override void GetStatisticsReturnsNewInstances() + { + var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions + { + TokenLimit = 1, + QueueProcessingOrder = QueueProcessingOrder.OldestFirst, + QueueLimit = 1, + ReplenishmentPeriod = TimeSpan.Zero, + TokensPerPeriod = 2, + AutoReplenishment = false + }); + + var stats = limiter.GetStatistics(); + Assert.Equal(1, stats.CurrentAvailablePermits); + + var lease = limiter.AttemptAcquire(1); + + var stats2 = limiter.GetStatistics(); + Assert.NotSame(stats, stats2); + Assert.Equal(1, stats.CurrentAvailablePermits); + Assert.Equal(0, stats2.CurrentAvailablePermits); + } + + [Fact] + public override async Task GetStatisticsHasCorrectValues() + { + var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions + { + TokenLimit = 100, + QueueProcessingOrder = QueueProcessingOrder.OldestFirst, + QueueLimit = 50, + ReplenishmentPeriod = TimeSpan.Zero, + TokensPerPeriod = 30, + AutoReplenishment = false + }); + + var stats = limiter.GetStatistics(); + Assert.Equal(100, stats.CurrentAvailablePermits); + Assert.Equal(0, stats.CurrentQueuedCount); + Assert.Equal(0, stats.TotalFailedLeases); + Assert.Equal(0, stats.TotalSuccessfulLeases); + + var lease1 = limiter.AttemptAcquire(60); + stats = limiter.GetStatistics(); + Assert.Equal(40, stats.CurrentAvailablePermits); + Assert.Equal(0, stats.CurrentQueuedCount); + Assert.Equal(0, stats.TotalFailedLeases); + Assert.Equal(1, stats.TotalSuccessfulLeases); + + var lease2Task = limiter.AcquireAsync(50); + stats = limiter.GetStatistics(); + Assert.Equal(40, stats.CurrentAvailablePermits); + Assert.Equal(50, stats.CurrentQueuedCount); + Assert.Equal(0, stats.TotalFailedLeases); + Assert.Equal(1, stats.TotalSuccessfulLeases); + + var lease3 = await limiter.AcquireAsync(1); + Assert.False(lease3.IsAcquired); + stats = limiter.GetStatistics(); + Assert.Equal(40, stats.CurrentAvailablePermits); + Assert.Equal(50, stats.CurrentQueuedCount); + Assert.Equal(1, stats.TotalFailedLeases); + Assert.Equal(1, stats.TotalSuccessfulLeases); + + var lease4 = limiter.AttemptAcquire(100); + Assert.False(lease4.IsAcquired); + stats = limiter.GetStatistics(); + Assert.Equal(40, stats.CurrentAvailablePermits); + Assert.Equal(50, stats.CurrentQueuedCount); + Assert.Equal(2, stats.TotalFailedLeases); + Assert.Equal(1, stats.TotalSuccessfulLeases); + + limiter.TryReplenish(); + await lease2Task; + + stats = limiter.GetStatistics(); + Assert.Equal(20, stats.CurrentAvailablePermits); + Assert.Equal(0, stats.CurrentQueuedCount); + Assert.Equal(2, stats.TotalFailedLeases); + Assert.Equal(2, stats.TotalSuccessfulLeases); + } + + [Fact] + public override async Task GetStatisticsWithZeroPermitCount() + { + var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions + { + TokenLimit = 100, + QueueProcessingOrder = QueueProcessingOrder.OldestFirst, + QueueLimit = 50, + ReplenishmentPeriod = TimeSpan.Zero, + TokensPerPeriod = 30, + AutoReplenishment = false + }); + var lease = limiter.AttemptAcquire(0); + Assert.True(lease.IsAcquired); + Assert.Equal(1, limiter.GetStatistics().TotalSuccessfulLeases); + Assert.Equal(100, limiter.GetStatistics().CurrentAvailablePermits); + + lease = await limiter.AcquireAsync(0); + Assert.True(lease.IsAcquired); + Assert.Equal(2, limiter.GetStatistics().TotalSuccessfulLeases); + Assert.Equal(100, limiter.GetStatistics().CurrentAvailablePermits); + + lease = limiter.AttemptAcquire(100); + Assert.True(lease.IsAcquired); + Assert.Equal(3, limiter.GetStatistics().TotalSuccessfulLeases); + Assert.Equal(0, limiter.GetStatistics().CurrentAvailablePermits); + + var lease2 = limiter.AttemptAcquire(0); + Assert.False(lease2.IsAcquired); + Assert.Equal(3, limiter.GetStatistics().TotalSuccessfulLeases); + Assert.Equal(1, limiter.GetStatistics().TotalFailedLeases); + Assert.Equal(0, limiter.GetStatistics().CurrentAvailablePermits); + } + + [Fact] + public override void GetStatisticsThrowsAfterDispose() + { + var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions + { + TokenLimit = 100, + QueueProcessingOrder = QueueProcessingOrder.OldestFirst, + QueueLimit = 50, + ReplenishmentPeriod = TimeSpan.Zero, + TokensPerPeriod = 30, + AutoReplenishment = false + }); + limiter.Dispose(); + Assert.Throws(limiter.GetStatistics); + } } } From 207e31c89831f6917fc7a238e8eeaf53e0d1d3a4 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Fri, 12 Aug 2022 11:31:20 -0700 Subject: [PATCH 25/56] Mark RunGetBitLengthTestsLarge as outerloop and skipped on iOS/tvOS/Android/Browser (#73773) --- .../tests/BigInteger/GetBitLengthTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/GetBitLengthTests.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/GetBitLengthTests.cs index 8eee40fd0a8d16..ab02a31ad5b17f 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/GetBitLengthTests.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/GetBitLengthTests.cs @@ -39,8 +39,8 @@ public static void RunGetBitLengthTests() } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))] // OOM on 32 bit - [SkipOnPlatform(TestPlatforms.Browser, "OOM on browser due to large array allocations")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/37093", TestPlatforms.Android)] + [OuterLoop("Allocates large arrays")] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.Android | TestPlatforms.Browser, "OOM on browser and mobile due to large array allocations")] public static void RunGetBitLengthTestsLarge() { // Very large cases From 4f1c56fd89f72495899fc2c076f37ea75f158ce7 Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Fri, 12 Aug 2022 20:35:06 +0200 Subject: [PATCH 26/56] Respect the Keep-Alive response header on HTTP/1.1 as well (#73585) * Respect the Keep-Alive response header on HTTP/1.1 as well * Add some more comments --- .../Http/SocketsHttpHandler/HttpConnection.cs | 67 ++++++++++--------- .../SocketsHttpHandlerTest.Http1KeepAlive.cs | 36 ++-------- 2 files changed, 41 insertions(+), 62 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs index 8d55e073efebea..550d3e3ba81a94 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs @@ -236,7 +236,8 @@ async ValueTask ReadAheadWithZeroByteReadAsync() private bool CheckKeepAliveTimeoutExceeded() { - // We only honor a Keep-Alive timeout on HTTP/1.0 responses. + // We intentionally honor the Keep-Alive timeout on all HTTP/1.X versions, not just 1.0. This is to maximize compat with + // servers that use a lower idle timeout than the client, but give us a hint in the form of a Keep-Alive timeout parameter. // If _keepAliveTimeoutSeconds is 0, no timeout has been set. return _keepAliveTimeoutSeconds != 0 && GetIdleTicks(Environment.TickCount64) >= _keepAliveTimeoutSeconds * 1000; @@ -665,11 +666,6 @@ public async Task SendAsyncCore(HttpRequestMessage request, ParseHeaderNameValue(this, line.Span, response, isFromTrailer: false); } - if (response.Version.Minor == 0) - { - ProcessHttp10KeepAliveHeader(response); - } - if (HttpTelemetry.Log.IsEnabled()) HttpTelemetry.Log.ResponseHeadersStop(); if (allowExpect100ToContinue != null) @@ -1124,49 +1120,58 @@ private static void ParseHeaderNameValue(HttpConnection connection, ReadOnlySpan } else { - // Request headers returned on the response must be treated as custom headers. string headerValue = connection.GetResponseHeaderValueWithCaching(descriptor, value, valueEncoding); + + if (descriptor.Equals(KnownHeaders.KeepAlive)) + { + // We are intentionally going against RFC to honor the Keep-Alive header even if + // we haven't received a Keep-Alive connection token to maximize compat with servers. + connection.ProcessKeepAliveHeader(headerValue); + } + + // Request headers returned on the response must be treated as custom headers. response.Headers.TryAddWithoutValidation( (descriptor.HeaderType & HttpHeaderType.Request) == HttpHeaderType.Request ? descriptor.AsCustomHeader() : descriptor, headerValue); } } - private void ProcessHttp10KeepAliveHeader(HttpResponseMessage response) + private void ProcessKeepAliveHeader(string keepAlive) { - if (response.Headers.NonValidated.TryGetValues(KnownHeaders.KeepAlive.Name, out HeaderStringValues keepAliveValues)) - { - string keepAlive = keepAliveValues.ToString(); - var parsedValues = new UnvalidatedObjectCollection(); + var parsedValues = new UnvalidatedObjectCollection(); - if (NameValueHeaderValue.GetNameValueListLength(keepAlive, 0, ',', parsedValues) == keepAlive.Length) + if (NameValueHeaderValue.GetNameValueListLength(keepAlive, 0, ',', parsedValues) == keepAlive.Length) + { + foreach (NameValueHeaderValue nameValue in parsedValues) { - foreach (NameValueHeaderValue nameValue in parsedValues) + // The HTTP/1.1 spec does not define any parameters for the Keep-Alive header, so we are using the de facto standard ones - timeout and max. + if (string.Equals(nameValue.Name, "timeout", StringComparison.OrdinalIgnoreCase)) { - if (string.Equals(nameValue.Name, "timeout", StringComparison.OrdinalIgnoreCase)) + if (!string.IsNullOrEmpty(nameValue.Value) && + HeaderUtilities.TryParseInt32(nameValue.Value, out int timeout) && + timeout >= 0) { - if (!string.IsNullOrEmpty(nameValue.Value) && - HeaderUtilities.TryParseInt32(nameValue.Value, out int timeout) && - timeout >= 0) + // Some servers are very strict with closing the connection exactly at the timeout. + // Avoid using the connection if it is about to exceed the timeout to avoid resulting request failures. + const int OffsetSeconds = 1; + + if (timeout <= OffsetSeconds) { - if (timeout == 0) - { - _connectionClose = true; - } - else - { - _keepAliveTimeoutSeconds = timeout; - } + _connectionClose = true; } - } - else if (string.Equals(nameValue.Name, "max", StringComparison.OrdinalIgnoreCase)) - { - if (nameValue.Value == "0") + else { - _connectionClose = true; + _keepAliveTimeoutSeconds = timeout - OffsetSeconds; } } } + else if (string.Equals(nameValue.Name, "max", StringComparison.OrdinalIgnoreCase)) + { + if (nameValue.Value == "0") + { + _connectionClose = true; + } + } } } } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http1KeepAlive.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http1KeepAlive.cs index 75ed4219e17837..db03b063d17f33 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http1KeepAlive.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http1KeepAlive.cs @@ -44,7 +44,7 @@ await LoopbackServer.CreateClientAndServerAsync(async uri => [OuterLoop("Uses Task.Delay")] [Fact] - public async Task Http10ResponseWithKeepAliveTimeout_ConnectionRecycledAfterTimeout() + public async Task Http1ResponseWithKeepAliveTimeout_ConnectionRecycledAfterTimeout() { await LoopbackServer.CreateClientAndServerAsync(async uri => { @@ -60,7 +60,7 @@ await LoopbackServer.CreateClientAndServerAsync(async uri => await server.AcceptConnectionAsync(async connection => { await connection.ReadRequestDataAsync(); - await connection.WriteStringAsync("HTTP/1.0 200 OK\r\nKeep-Alive: timeout=1\r\nContent-Length: 1\r\n\r\n1"); + await connection.WriteStringAsync("HTTP/1.1 200 OK\r\nKeep-Alive: timeout=2\r\nContent-Length: 1\r\n\r\n1"); connection.CompleteRequestProcessing(); await Assert.ThrowsAnyAsync(() => connection.ReadRequestDataAsync()); @@ -74,6 +74,7 @@ await server.AcceptConnectionAsync(async connection => [InlineData("timeout=1000", true)] [InlineData("timeout=30", true)] [InlineData("timeout=0", false)] + [InlineData("timeout=1", false)] [InlineData("foo, bar=baz, timeout=30", true)] [InlineData("foo, bar=baz, timeout=0", false)] [InlineData("timeout=-1", true)] @@ -86,7 +87,7 @@ await server.AcceptConnectionAsync(async connection => [InlineData("timeout=30, max=0", false)] [InlineData("timeout=0, max=1", false)] [InlineData("timeout=0, max=0", false)] - public async Task Http10ResponseWithKeepAlive_ConnectionNotReusedForShortTimeoutOrMax0(string keepAlive, bool shouldReuseConnection) + public async Task Http1ResponseWithKeepAlive_ConnectionNotReusedForShortTimeoutOrMax0(string keepAlive, bool shouldReuseConnection) { await LoopbackServer.CreateClientAndServerAsync(async uri => { @@ -100,7 +101,7 @@ await LoopbackServer.CreateClientAndServerAsync(async uri => await server.AcceptConnectionAsync(async connection => { await connection.ReadRequestDataAsync(); - await connection.WriteStringAsync($"HTTP/1.0 200 OK\r\nKeep-Alive: {keepAlive}\r\nContent-Length: 1\r\n\r\n1"); + await connection.WriteStringAsync($"HTTP/1.{Random.Shared.Next(10)} 200 OK\r\nKeep-Alive: {keepAlive}\r\nContent-Length: 1\r\n\r\n1"); connection.CompleteRequestProcessing(); if (shouldReuseConnection) @@ -119,32 +120,5 @@ await server.AcceptConnectionAsync(async connection => } }); } - - [Theory] - [InlineData("timeout=1")] - [InlineData("timeout=0")] - [InlineData("max=1")] - [InlineData("max=0")] - public async Task Http11ResponseWithKeepAlive_KeepAliveIsIgnored(string keepAlive) - { - await LoopbackServer.CreateClientAndServerAsync(async uri => - { - using HttpClient client = CreateHttpClient(); - - await client.GetAsync(uri); - await client.GetAsync(uri); - }, - async server => - { - await server.AcceptConnectionAsync(async connection => - { - await connection.ReadRequestDataAsync(); - await connection.WriteStringAsync($"HTTP/1.1 200 OK\r\nKeep-Alive: {keepAlive}\r\nContent-Length: 1\r\n\r\n1"); - connection.CompleteRequestProcessing(); - - await connection.HandleRequestAsync(); - }); - }); - } } } From afc9675d25cbd039634f40b3f836271673db6dec Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Fri, 12 Aug 2022 11:42:33 -0700 Subject: [PATCH 27/56] support InfiniteTimeSpan in Select (#73204) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * support InfiniteTimeSpan in Select * fix test * feedback from review * Apply suggestions from code review Co-authored-by: Marie Píchová <11718369+ManickaP@users.noreply.github.com> Co-authored-by: Marie Píchová <11718369+ManickaP@users.noreply.github.com> --- .../src/System/Net/Sockets/Socket.cs | 20 +++++-- .../ArgumentValidationTests.cs | 52 +++++++++++++++++++ 2 files changed, 67 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs index 8225f802f39878..94d1cea46d1b0e 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs @@ -2155,7 +2155,7 @@ public bool Poll(int microSeconds, SelectMode mode) } /// Determines the status of the . - /// The time to wait for a response. + /// The time to wait for a response. indicates an infinite timeout. /// One of the values. /// /// The status of the based on the polling mode value passed in the parameter. @@ -2167,7 +2167,7 @@ public bool Poll(int microSeconds, SelectMode mode) /// does not block and the connection has failed, or if OutOfBandInline is not set and out-of-band data is available. /// Otherwise, it returns . /// - /// is less than -1 milliseconds or greater than milliseconds. + /// was negative or greater than TimeSpan.FromMicroseconds(int.MaxValue). /// An error occurred when attempting to access the socket. /// The has been closed. public bool Poll(TimeSpan timeout, SelectMode mode) => @@ -2217,10 +2217,10 @@ public static void Select(IList? checkRead, IList? checkWrite, IList? checkError /// An of instances to check for readability. /// An of instances to check for writability. /// An of instances to check for errors. - /// The timeout value. A value equal to -1 microseconds indicates an infinite timeout. + /// The timeout value. A value equal to indicates an infinite timeout. /// The , , or parameter is or empty. /// The , , or parameter contains too many sockets. - /// The was less than -1 microseconds or greater than microseconds + /// The was negative or greater than TimeSpan.FromMicroseconds(int.MaxValue). /// An error occurred when attempting to access the socket. /// One or more sockets was disposed. public static void Select(IList? checkRead, IList? checkWrite, IList? checkError, TimeSpan timeout) => @@ -2228,8 +2228,18 @@ public static void Select(IList? checkRead, IList? checkWrite, IList? checkError private static int ToTimeoutMicroseconds(TimeSpan timeout) { + if (timeout == Timeout.InfiniteTimeSpan) + { + return -1; + } + + if (timeout < TimeSpan.Zero) + { + throw new ArgumentOutOfRangeException(nameof(timeout)); + } long totalMicroseconds = (long)timeout.TotalMicroseconds; - if (totalMicroseconds < -1 || totalMicroseconds > int.MaxValue) + + if (totalMicroseconds > int.MaxValue) { throw new ArgumentOutOfRangeException(nameof(timeout)); } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs index 31da90800351ab..f0f7ed672ac044 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs @@ -371,6 +371,58 @@ public void Select_NullOrEmptyLists_Throws_ArgumentNull_TimeSpan() Assert.Throws(() => Socket.Select(emptyList, emptyList, emptyList, nonInfinity)); } + [Fact] + public void SelectPoll_NegativeTimeSpan_Throws() + { + using (Socket host = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + host.Bind(new IPEndPoint(IPAddress.Loopback, 0)); + host.Listen(1); + Task accept = host.AcceptAsync(); + + using (Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + s.Connect(new IPEndPoint(IPAddress.Loopback, ((IPEndPoint)host.LocalEndPoint).Port)); + + var list = new List(); + list.Add(s); + + Assert.Throws(() => Socket.Select(null, list, null, TimeSpan.FromMicroseconds(-1))); + Assert.Throws(() => Socket.Select(null, list, null, TimeSpan.FromMicroseconds((double)int.MaxValue + 1))); + Assert.Throws(() => Socket.Select(null, list, null, TimeSpan.FromMilliseconds(-1.1))); + + Assert.Throws(() => s.Poll(TimeSpan.FromMicroseconds(-1), SelectMode.SelectWrite)); + Assert.Throws(() => s.Poll(TimeSpan.FromMicroseconds((double)int.MaxValue + 1), SelectMode.SelectWrite)); + Assert.Throws(() => s.Poll(TimeSpan.FromMilliseconds(-1.1), SelectMode.SelectWrite)); + } + } + } + + [Fact] + public void SelectPoll_InfiniteTimeSpan_Ok() + { + using (Socket host = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + host.Bind(new IPEndPoint(IPAddress.Loopback, 0)); + host.Listen(1); + Task accept = host.AcceptAsync(); + + using (Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + s.Connect(new IPEndPoint(IPAddress.Loopback, ((IPEndPoint)host.LocalEndPoint).Port)); + + var list = new List(); + list.Add(s); + + // should be writable + Socket.Select(null, list, null, Timeout.InfiniteTimeSpan); + Socket.Select(null, list, null, -1); + s.Poll(Timeout.InfiniteTimeSpan, SelectMode.SelectWrite); + s.Poll(-1, SelectMode.SelectWrite); + } + } + } + [Fact] public void Select_LargeList_Throws_ArgumentOutOfRange() { From b14a21384fe308ce036553552676465c0d98f02c Mon Sep 17 00:00:00 2001 From: Lakshan Fernando Date: Fri, 12 Aug 2022 11:44:14 -0700 Subject: [PATCH 28/56] Fix component model aot test (#73734) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Partial fix for ComponentModel.TypeConverter tests * Fix ComponentModel.TypeConverter tests * enable the test proj * FB * working around a test build fail * Update src/libraries/System.ComponentModel.TypeConverter/tests/BindableAttributeTests.cs Co-authored-by: Michal Strehovský * Update src/libraries/tests.proj Co-authored-by: Michal Strehovský Co-authored-by: Michal Strehovský --- .../Core/Execution/ExecutionDomain.cs | 2 +- .../tests/TypeDescriptorTests.cs | 62 +++++++++---------- .../tests/XTypeDescriptionProviderTests.cs | 14 ++--- src/libraries/tests.proj | 1 - 4 files changed, 39 insertions(+), 40 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/ExecutionDomain.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/ExecutionDomain.cs index 181b2ad0513fbd..84617796861634 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/ExecutionDomain.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/ExecutionDomain.cs @@ -42,7 +42,7 @@ internal ExecutionDomain(ReflectionDomainSetup executionDomainSetup, ExecutionEn public Type GetType(string typeName, Func assemblyResolver, Func typeResolver, bool throwOnError, bool ignoreCase, IList defaultAssemblyNames) { if (typeName == null) - throw new ArgumentNullException(); + throw new ArgumentNullException(nameof(typeName)); if (typeName.Length == 0) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs b/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs index 37c6f5b03b9f9b..299d73cfe31502 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs @@ -13,7 +13,7 @@ namespace System.ComponentModel.Tests [Collection(nameof(DisableParallelization))] // manipulates cache public class TypeDescriptorTests { - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void AddProvider_InvokeObject_GetProviderReturnsExpected() { var instance = new object(); @@ -47,7 +47,7 @@ public void AddProvider_InvokeObject_GetProviderReturnsExpected() mockProvider2.Verify(p => p.IsSupportedType(typeof(int)), Times.Once()); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void AddProvider_InvokeObjectMultipleTimes_Refreshes() { var instance = new object(); @@ -96,7 +96,7 @@ public void AddProvider_InvokeObjectMultipleTimes_Refreshes() } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void AddProvider_InvokeType_GetProviderReturnsExpected() { Type type = typeof(AddProvider_InvokeType_GetProviderReturnsExpectedType); @@ -133,7 +133,7 @@ public void AddProvider_InvokeType_GetProviderReturnsExpected() private class AddProvider_InvokeType_GetProviderReturnsExpectedType { } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void AddProvider_InvokeTypeMultipleTimes_Refreshes() { var type = typeof(AddProvider_InvokeTypeMultipleTimes_RefreshesType); @@ -184,28 +184,28 @@ public void AddProvider_InvokeTypeMultipleTimes_Refreshes() private class AddProvider_InvokeTypeMultipleTimes_RefreshesType { } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void AddProvider_NullProvider_ThrowsArgumentNullException() { Assert.Throws("provider", () => TypeDescriptor.AddProvider(null, new object())); Assert.Throws("provider", () => TypeDescriptor.AddProvider(null, typeof(int))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void AddProvider_NullInstance_ThrowsArgumentNullException() { var mockProvider = new Mock(MockBehavior.Strict); Assert.Throws("instance", () => TypeDescriptor.AddProvider(mockProvider.Object, (object)null)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void AddProvider_NullType_ThrowsArgumentNullException() { var mockProvider = new Mock(MockBehavior.Strict); Assert.Throws("type", () => TypeDescriptor.AddProvider(mockProvider.Object, null)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void AddProviderTransparent_InvokeObject_GetProviderReturnsExpected() { var instance = new object(); @@ -239,7 +239,7 @@ public void AddProviderTransparent_InvokeObject_GetProviderReturnsExpected() mockProvider2.Verify(p => p.IsSupportedType(typeof(int)), Times.Once()); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void AddProviderTransparent_InvokeObjectMultipleTimes_Refreshes() { var instance = new object(); @@ -288,7 +288,7 @@ public void AddProviderTransparent_InvokeObjectMultipleTimes_Refreshes() } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void AddProviderTransparent_InvokeType_GetProviderReturnsExpected() { Type type = typeof(AddProviderTransparent_InvokeType_GetProviderReturnsExpectedType); @@ -324,7 +324,7 @@ public void AddProviderTransparent_InvokeType_GetProviderReturnsExpected() private class AddProviderTransparent_InvokeType_GetProviderReturnsExpectedType { } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void AddProviderTransparent_InvokeTypeMultipleTimes_Refreshes() { var type = typeof(AddProviderTransparent_InvokeTypeMultipleTimes_RefreshesType); @@ -375,21 +375,21 @@ public void AddProviderTransparent_InvokeTypeMultipleTimes_Refreshes() private class AddProviderTransparent_InvokeTypeMultipleTimes_RefreshesType { } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void AddProviderTransparent_NullProvider_ThrowsArgumentNullException() { Assert.Throws("provider", () => TypeDescriptor.AddProviderTransparent(null, new object())); Assert.Throws("provider", () => TypeDescriptor.AddProviderTransparent(null, typeof(int))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void AddProviderTransparent_NullInstance_ThrowsArgumentNullException() { var mockProvider = new Mock(MockBehavior.Strict); Assert.Throws("instance", () => TypeDescriptor.AddProviderTransparent(mockProvider.Object, (object)null)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void AddProviderTransparent_NullType_ThrowsArgumentNullException() { var mockProvider = new Mock(MockBehavior.Strict); @@ -548,7 +548,7 @@ public void GetPropertiesFiltersByAttribute() Assert.Equal(1, properties.Count); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void RemoveProvider_InvokeObject_RemovesProvider() { var instance = new object(); @@ -603,7 +603,7 @@ public void RemoveProvider_InvokeObject_RemovesProvider() mockProvider3.Verify(p => p.IsSupportedType(typeof(int)), Times.Once()); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void RemoveProvider_InvokeObjectWithProviders_Refreshes() { var instance = new object(); @@ -640,7 +640,7 @@ public void RemoveProvider_InvokeObjectWithProviders_Refreshes() } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void RemoveProvider_InvokeObjectWithoutProviders_Refreshes() { var instance = new object(); @@ -665,7 +665,7 @@ public void RemoveProvider_InvokeObjectWithoutProviders_Refreshes() } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void RemoveProvider_InvokeType_RemovesProvider() { Type type = typeof(RemoveProvider_InvokeType_RemovesProviderType); @@ -722,7 +722,7 @@ public void RemoveProvider_InvokeType_RemovesProvider() private class RemoveProvider_InvokeType_RemovesProviderType { } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void RemoveProvider_InvokeTypeWithProviders_Refreshes() { Type type = typeof(RemoveProvider_InvokeObjectWithProviders_RefreshesType); @@ -761,7 +761,7 @@ public void RemoveProvider_InvokeTypeWithProviders_Refreshes() private class RemoveProvider_InvokeObjectWithProviders_RefreshesType { } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void RemoveProvider_InvokeTypeWithoutProviders_Refreshes() { Type type = typeof(RemoveProvider_InvokeTypeWithoutProviders_RefreshesType); @@ -795,14 +795,14 @@ public void RemoveProvider_NullProvider_ThrowsArgumentNullException() Assert.Throws("provider", () => TypeDescriptor.RemoveProvider(null, typeof(int))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void RemoveProvider_NullInstance_ThrowsArgumentNullException() { var mockProvider = new Mock(MockBehavior.Strict); Assert.Throws("instance", () => TypeDescriptor.RemoveProvider(mockProvider.Object, (object)null)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void RemoveProvider_NullType_ThrowsArgumentNullException() { var mockProvider = new Mock(MockBehavior.Strict); @@ -810,7 +810,7 @@ public void RemoveProvider_NullType_ThrowsArgumentNullException() } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void RemoveProviderTransparent_InvokeObject_RemovesProvider() { var instance = new object(); @@ -865,7 +865,7 @@ public void RemoveProviderTransparent_InvokeObject_RemovesProvider() mockProvider3.Verify(p => p.IsSupportedType(typeof(int)), Times.Once()); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void RemoveProviderTransparent_InvokeObjectWithProviders_Refreshes() { var instance = new object(); @@ -902,7 +902,7 @@ public void RemoveProviderTransparent_InvokeObjectWithProviders_Refreshes() } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void RemoveProviderTransparent_InvokeObjectWithoutProviders_Refreshes() { var instance = new object(); @@ -927,7 +927,7 @@ public void RemoveProviderTransparent_InvokeObjectWithoutProviders_Refreshes() } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void RemoveProviderTransparent_InvokeType_RemovesProvider() { Type type = typeof(RemoveProviderTransparent_InvokeType_RemovesProviderType); @@ -984,7 +984,7 @@ public void RemoveProviderTransparent_InvokeType_RemovesProvider() private class RemoveProviderTransparent_InvokeType_RemovesProviderType { } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void RemoveProviderTransparent_InvokeTypeWithProviders_Refreshes() { Type type = typeof(RemoveProviderTransparent_InvokeObjectWithProviders_RefreshesType); @@ -1023,7 +1023,7 @@ public void RemoveProviderTransparent_InvokeTypeWithProviders_Refreshes() private class RemoveProviderTransparent_InvokeObjectWithProviders_RefreshesType { } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void RemoveProviderTransparent_InvokeTypeWithoutProviders_Refreshes() { Type type = typeof(RemoveProviderTransparent_InvokeTypeWithoutProviders_RefreshesType); @@ -1057,14 +1057,14 @@ public void RemoveProviderTransparent_NullProvider_ThrowsArgumentNullException() Assert.Throws("provider", () => TypeDescriptor.RemoveProviderTransparent(null, typeof(int))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void RemoveProviderTransparent_NullInstance_ThrowsArgumentNullException() { var mockProvider = new Mock(MockBehavior.Strict); Assert.Throws("instance", () => TypeDescriptor.RemoveProviderTransparent(mockProvider.Object, (object)null)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void RemoveProviderTransparent_NullType_ThrowsArgumentNullException() { var mockProvider = new Mock(MockBehavior.Strict); @@ -1111,7 +1111,7 @@ public void RemoveSingleAssociation() Assert.NotEqual(firstAssociatedObject, firstAssociation); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // Mock will try to JIT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Mock will try to JIT public void SortDescriptorArray_Invoke_ReturnsExpected() { var notADescriptor1 = new object(); diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/XTypeDescriptionProviderTests.cs b/src/libraries/System.ComponentModel.TypeConverter/tests/XTypeDescriptionProviderTests.cs index 7dde31b29544f5..7bdba591a133e5 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/tests/XTypeDescriptionProviderTests.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/tests/XTypeDescriptionProviderTests.cs @@ -12,7 +12,7 @@ namespace System.Xml.Linq.Tests { public class XTypeDescriptionProviderTests { - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNativeAot))] public void XAttributeValuePropertyDescriptor() { var xatt = new XAttribute("someAttribute", "someValue"); @@ -41,7 +41,7 @@ public void XAttributeValuePropertyDescriptor() Assert.Equal(newValue, xatt.Value); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNativeAot))] public void XElementAttributePropertyDescriptor() { var xel = new XElement("someElement"); @@ -117,7 +117,7 @@ public void XElementAttributePropertyDescriptor() Assert.True(valueChanged); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNativeAot))] public void XElementDescendantsPropertyDescriptor() { var xel = new XElement("someElement"); @@ -150,7 +150,7 @@ public void XElementDescendantsPropertyDescriptor() Assert.True(valueChanged); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNativeAot))] public void XElementElementPropertyDescriptor() { var xel = new XElement("someElement"); @@ -225,7 +225,7 @@ public void XElementElementPropertyDescriptor() Assert.True(valueChanged); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNativeAot))] public void XElementElementsPropertyDescriptor() { var xel = new XElement("someElement"); @@ -258,7 +258,7 @@ public void XElementElementsPropertyDescriptor() Assert.True(valueChanged); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNativeAot))] public void XElementValuePropertyDescriptor() { var xel = new XElement("someElement", "someValue"); @@ -292,7 +292,7 @@ public void XElementValuePropertyDescriptor() Assert.Equal(xel.Value, xelValPD.GetValue(xel)); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNativeAot))] public void XElementXmlPropertyDescriptor() { var xel = new XElement("someElement"); diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index da787c4710b61c..afba0d8a60ee23 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -503,7 +503,6 @@ - From 9ee667fc2dfe06e8e2434c5788117b54e3227d82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Cant=C3=BA?= Date: Fri, 12 Aug 2022 13:45:23 -0500 Subject: [PATCH 29/56] COSE: Add APIs needed for countersign (#73796) * Add APIs needed for countersign * Use associated with instead of associated to --- .../ref/System.Security.Cryptography.Cose.cs | 4 + .../Security/Cryptography/Cose/CoseMessage.cs | 20 ++- .../Cryptography/Cose/CoseMultiSignMessage.cs | 4 +- .../Cryptography/Cose/CoseSign1Message.cs | 6 + .../Cryptography/Cose/CoseSignature.cs | 19 ++- .../CoseMessageTests.Sign.CustomHeaderMaps.cs | 44 ++++++ .../tests/CoseTestHelpers.cs | 130 +++++++++++++++++- 7 files changed, 210 insertions(+), 17 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.Cose/ref/System.Security.Cryptography.Cose.cs b/src/libraries/System.Security.Cryptography.Cose/ref/System.Security.Cryptography.Cose.cs index 0f922c13cd6abe..8b736e2fab5f6c 100644 --- a/src/libraries/System.Security.Cryptography.Cose/ref/System.Security.Cryptography.Cose.cs +++ b/src/libraries/System.Security.Cryptography.Cose/ref/System.Security.Cryptography.Cose.cs @@ -78,6 +78,7 @@ public abstract partial class CoseMessage internal CoseMessage() { } public System.ReadOnlyMemory? Content { get { throw null; } } public System.Security.Cryptography.Cose.CoseHeaderMap ProtectedHeaders { get { throw null; } } + public System.ReadOnlyMemory RawProtectedHeaders { get { throw null; } } public System.Security.Cryptography.Cose.CoseHeaderMap UnprotectedHeaders { get { throw null; } } public static System.Security.Cryptography.Cose.CoseMultiSignMessage DecodeMultiSign(byte[] cborPayload) { throw null; } public static System.Security.Cryptography.Cose.CoseMultiSignMessage DecodeMultiSign(System.ReadOnlySpan cborPayload) { throw null; } @@ -114,6 +115,7 @@ public void RemoveSignature(System.Security.Cryptography.Cose.CoseSignature sign public sealed partial class CoseSign1Message : System.Security.Cryptography.Cose.CoseMessage { internal CoseSign1Message() { } + public System.ReadOnlyMemory Signature { get { throw null; } } public override int GetEncodedLength() { throw null; } public static byte[] SignDetached(byte[] detachedContent, System.Security.Cryptography.Cose.CoseSigner signer, byte[]? associatedData = null) { throw null; } public static byte[] SignDetached(System.IO.Stream detachedContent, System.Security.Cryptography.Cose.CoseSigner signer, System.ReadOnlySpan associatedData = default(System.ReadOnlySpan)) { throw null; } @@ -135,6 +137,8 @@ public sealed partial class CoseSignature { internal CoseSignature() { } public System.Security.Cryptography.Cose.CoseHeaderMap ProtectedHeaders { get { throw null; } } + public System.ReadOnlyMemory RawProtectedHeaders { get { throw null; } } + public System.ReadOnlyMemory Signature { get { throw null; } } public System.Security.Cryptography.Cose.CoseHeaderMap UnprotectedHeaders { get { throw null; } } public bool VerifyDetached(System.Security.Cryptography.AsymmetricAlgorithm key, byte[] detachedContent, byte[]? associatedData = null) { throw null; } public bool VerifyDetached(System.Security.Cryptography.AsymmetricAlgorithm key, System.IO.Stream detachedContent, System.ReadOnlySpan associatedData = default(System.ReadOnlySpan)) { throw null; } diff --git a/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseMessage.cs b/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseMessage.cs index c04a35c47f485f..1f5abdc4dd8b8f 100644 --- a/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseMessage.cs +++ b/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseMessage.cs @@ -27,25 +27,31 @@ public abstract class CoseMessage internal const CborTag Sign1Tag = (CborTag)18; internal const CborTag MultiSignTag = (CborTag)98; - internal byte[]? _content; - internal byte[] _protectedHeaderAsBstr; - internal bool _isTagged; + internal readonly byte[]? _content; + internal readonly byte[] _protectedHeaderAsBstr; + internal readonly bool _isTagged; private CoseHeaderMap _protectedHeaders; private CoseHeaderMap _unprotectedHeaders; /// - /// Gets the protected header parameters associated to this message. + /// Gets the protected header parameters associated with this message. /// - /// A collection of protected header parameters associated to this message. + /// A collection of protected header parameters associated with this message. public CoseHeaderMap ProtectedHeaders => _protectedHeaders; /// - /// Gets the unprotected header parameters associated to this message. + /// Gets the unprotected header parameters associated with this message. /// - /// A collection of unprotected header parameters associated to this message. + /// A collection of unprotected header parameters associated with this message. public CoseHeaderMap UnprotectedHeaders => _unprotectedHeaders; + /// + /// Gets the raw bytes of the protected header parameters associated with this message. + /// + /// A region of memory that contains the raw bytes of the protected header parameters associated with this message. + public ReadOnlyMemory RawProtectedHeaders => _protectedHeaderAsBstr; + internal CoseMessage(CoseHeaderMap protectedHeader, CoseHeaderMap unprotectedHeader, byte[]? content, byte[] encodedProtectedHeader, bool isTagged) { _content = content; diff --git a/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseMultiSignMessage.cs b/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseMultiSignMessage.cs index 3e5b9367551298..3b0ecebc015f6e 100644 --- a/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseMultiSignMessage.cs +++ b/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseMultiSignMessage.cs @@ -24,9 +24,9 @@ public sealed class CoseMultiSignMessage : CoseMessage private readonly List _signatures; /// - /// Gets a read-only collection of signatures associated to this message. + /// Gets a read-only collection of signatures associated with this message. /// - /// A read-only collection of signatures associated to this message. + /// A read-only collection of signatures associated with this message. public ReadOnlyCollection Signatures { get; } internal CoseMultiSignMessage(CoseHeaderMap protectedHeader, CoseHeaderMap unprotectedHeader, byte[]? content, List signatures, byte[] encodedProtectedHeader, bool isTagged) diff --git a/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseSign1Message.cs b/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseSign1Message.cs index c2ffd0cecbb0a0..a38a5847507dcc 100644 --- a/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseSign1Message.cs +++ b/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseSign1Message.cs @@ -19,6 +19,12 @@ public sealed class CoseSign1Message : CoseMessage private const int Sign1SizeOfCborTag = 1; private readonly byte[] _signature; + /// + /// Gets the digital signature. + /// + /// A region of memory that contains the digital signature. + public ReadOnlyMemory Signature => _signature; + internal CoseSign1Message(CoseHeaderMap protectedHeader, CoseHeaderMap unprotectedHeader, byte[]? content, byte[] signature, byte[] protectedHeaderAsBstr, bool isTagged) : base(protectedHeader, unprotectedHeader, content, protectedHeaderAsBstr, isTagged) { diff --git a/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseSignature.cs b/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseSignature.cs index b723988f51f5f7..c716e077124b2c 100644 --- a/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseSignature.cs +++ b/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseSignature.cs @@ -20,17 +20,28 @@ public sealed class CoseSignature private CoseMultiSignMessage? _message; /// - /// Gets the protected header parameters of this instance. + /// Gets the protected header parameters associated with this instance. /// - /// A collection of protected header parameters associated to this instance. + /// A collection of protected header parameters associated with this instance. public CoseHeaderMap ProtectedHeaders { get; } /// - /// Gets the unprotected header parameters of this instance. + /// Gets the unprotected header parameters associated with this instance. /// - /// A collection of unprotected header parameters associated to this instance. + /// A collection of unprotected header parameters associated with this instance. public CoseHeaderMap UnprotectedHeaders { get; } + /// + /// Gets the raw bytes of the protected header parameters associated with this instance. + /// + /// A region of memory that contains the raw bytes of the protected header parameters associated with this instance. + public ReadOnlyMemory RawProtectedHeaders => _encodedSignProtectedHeaders; + + /// + /// Gets the digital signature. + /// + /// A region of memory that contains the digital signature. + public ReadOnlyMemory Signature => _signature; internal CoseSignature(CoseMultiSignMessage message, CoseHeaderMap protectedHeaders, CoseHeaderMap unprotectedHeaders, byte[] encodedBodyProtectedHeaders, byte[] encodedSignProtectedHeaders, byte[] signature) : this(protectedHeaders, unprotectedHeaders, encodedBodyProtectedHeaders, encodedSignProtectedHeaders, signature) diff --git a/src/libraries/System.Security.Cryptography.Cose/tests/CoseMessageTests.Sign.CustomHeaderMaps.cs b/src/libraries/System.Security.Cryptography.Cose/tests/CoseMessageTests.Sign.CustomHeaderMaps.cs index a7b1348a3f7322..e7e3fe1ca782b3 100644 --- a/src/libraries/System.Security.Cryptography.Cose/tests/CoseMessageTests.Sign.CustomHeaderMaps.cs +++ b/src/libraries/System.Security.Cryptography.Cose/tests/CoseMessageTests.Sign.CustomHeaderMaps.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Formats.Cbor; using System.Linq; using Xunit; @@ -632,5 +633,48 @@ private static void AddCriticalHeaders( expectedHeaders?.Add((myCritHeaderLabel, myCritHeaderValue.EncodedValue)); } } + + [Fact] + public void MultiSign_SignWithCounterSignature() + { + if (MessageKind != CoseMessageKind.MultiSign) + { + return; + } + + CoseMessage msg = Decode(Sign(s_sampleContent, GetCoseSigner(DefaultKey, DefaultHash))); + CoseMultiSignMessage multiSignMsg = Assert.IsType(msg); + + // counter sign + CoseHeaderLabel counterSignLabel = new(7); + CoseSignature signatureToCounterSign = multiSignMsg.Signatures[0]; + byte[] encodedCounterSignature = GetCounterSign(multiSignMsg, signatureToCounterSign, DefaultAlgorithm); + signatureToCounterSign.UnprotectedHeaders[counterSignLabel] = CoseHeaderValue.FromEncodedValue(encodedCounterSignature); + + // re-encode + byte[] encodedMsg = msg.Encode(); + + List<(CoseHeaderLabel, ReadOnlyMemory)> expectedProtected = GetExpectedProtectedHeaders(DefaultAlgorithm); + List<(CoseHeaderLabel, ReadOnlyMemory)> expectedUnprotected = GetEmptyExpectedHeaders(); + expectedUnprotected.Add((counterSignLabel, encodedCounterSignature)); + + AssertCoseSignMessage(encodedMsg, s_sampleContent, DefaultKey, DefaultAlgorithm, expectedProtected, expectedUnprotected); + + // decode + msg = Decode(encodedMsg); + multiSignMsg = Assert.IsType(msg); + + ReadOnlyCollection signatures = multiSignMsg.Signatures; + Assert.Equal(1, signatures.Count); + + CoseSignature counterSignedSignature = signatures[0]; + Assert.True(counterSignedSignature.UnprotectedHeaders.TryGetValue(counterSignLabel, out CoseHeaderValue value)); + + // verify counter signature + (byte[] EncodedProtectedHeaders, byte[] Signature) counterSignInfo = ReadCounterSign(value, DefaultKey); + byte[] toBeSigned = GetToBeSignedForCounterSign(multiSignMsg, counterSignedSignature, counterSignInfo.EncodedProtectedHeaders); + + Assert.True(VerifyCounterSign(DefaultKey, DefaultHash, toBeSigned, counterSignInfo.Signature)); + } } } diff --git a/src/libraries/System.Security.Cryptography.Cose/tests/CoseTestHelpers.cs b/src/libraries/System.Security.Cryptography.Cose/tests/CoseTestHelpers.cs index 23f6428f8ab386..8716f3c34071b8 100644 --- a/src/libraries/System.Security.Cryptography.Cose/tests/CoseTestHelpers.cs +++ b/src/libraries/System.Security.Cryptography.Cose/tests/CoseTestHelpers.cs @@ -130,7 +130,8 @@ internal static void AssertSign1MessageCore( Assert.Equal(4, reader.ReadStartArray()); // Protected headers - AssertProtectedHeaders(reader.ReadByteString(), expectedProtectedHeaders ?? GetExpectedProtectedHeaders(algorithm)); + byte[] rawProtectedHeaders = reader.ReadByteString(); + AssertProtectedHeaders(rawProtectedHeaders, expectedProtectedHeaders ?? GetExpectedProtectedHeaders(algorithm)); // Unprotected headers AssertHeaders(reader, expectedUnprotectedHeaders ?? GetEmptyExpectedHeaders()); @@ -164,6 +165,12 @@ internal static void AssertSign1MessageCore( Assert.True(msg.VerifyEmbedded(signingKey), "msg.Verify(key)"); } + // Raw Protected Headers + AssertExtensions.SequenceEqual(rawProtectedHeaders, msg.RawProtectedHeaders.Span); + + // Signature + AssertExtensions.SequenceEqual(signatureBytes, msg.Signature.Span); + // GetEncodedLength Assert.Equal(encodedMsg.Length, msg.GetEncodedLength()); @@ -190,7 +197,8 @@ internal static void AssertMultiSignMessageCore( Assert.Equal(4, reader.ReadStartArray()); // Body's Protected headers - AssertProtectedHeaders(reader.ReadByteString(), expectedBodyProtectedHeaders); + byte[] encodedBodyProtectedHeaders = reader.ReadByteString(); + AssertProtectedHeaders(encodedBodyProtectedHeaders, expectedBodyProtectedHeaders); // Body's Unprotected headers AssertHeaders(reader, expectedBodyUnprotectedHeaders ?? GetEmptyExpectedHeaders()); @@ -206,6 +214,8 @@ internal static void AssertMultiSignMessageCore( } Assert.Equal(expectedSignatures, reader.ReadStartArray()); + List listOfRawSignProtectedHeaders = new(); + List listOfSignatureBytes = new(); for (int i = 0; i < expectedSignatures; i++) { @@ -213,7 +223,9 @@ internal static void AssertMultiSignMessageCore( Assert.Equal(3, reader.ReadStartArray()); // Sign's Protected headers - AssertProtectedHeaders(reader.ReadByteString(), expectedSignProtectedHeaders ?? GetExpectedProtectedHeaders(algorithm)); + byte[] rawSignProtectedHeaders = reader.ReadByteString(); + listOfRawSignProtectedHeaders.Add(rawSignProtectedHeaders); + AssertProtectedHeaders(rawSignProtectedHeaders, expectedSignProtectedHeaders ?? GetExpectedProtectedHeaders(algorithm)); // Sign's Unprotected headers AssertHeaders(reader, expectedSignUnprotectedHeaders ?? GetEmptyExpectedHeaders()); @@ -221,6 +233,7 @@ internal static void AssertMultiSignMessageCore( // Signature byte[] signatureBytes = reader.ReadByteString(); Assert.Equal(GetSignatureSize(signingKey), signatureBytes.Length); + listOfSignatureBytes.Add(signatureBytes); reader.ReadEndArray(); // End of Cose_Signature. } @@ -233,7 +246,8 @@ internal static void AssertMultiSignMessageCore( CoseMultiSignMessage msg = CoseMessage.DecodeMultiSign(encodedMsg); Assert.Equal(expectedSignatures, msg.Signatures.Count); - CoseSignature signature = msg.Signatures[0]; + ReadOnlyCollection signatures = msg.Signatures; + CoseSignature signature = signatures[0]; if (expectedDetachedContent) { @@ -244,6 +258,18 @@ internal static void AssertMultiSignMessageCore( Assert.True(signature.VerifyEmbedded(signingKey), "msg.Verify(ecdsa)"); } + // Raw Body Protected Headers + AssertExtensions.SequenceEqual(encodedBodyProtectedHeaders, msg.RawProtectedHeaders.Span); + + for (int i = 0; i < signatures.Count; i++) + { + // Raw Sign Protected Headers + AssertExtensions.SequenceEqual(listOfRawSignProtectedHeaders[i], signatures[i].RawProtectedHeaders.Span); + + // Signature + AssertExtensions.SequenceEqual(listOfSignatureBytes[i], signatures[i].Signature.Span); + } + // GetEncodedLength Assert.Equal(encodedMsg.Length, msg.GetEncodedLength()); @@ -652,5 +678,101 @@ static byte[] ReturnDataAndReset(CborWriter w) return encodedValue; } } + + internal static byte[] GetCounterSign(CoseMultiSignMessage msg, CoseSignature signature, CoseAlgorithm algorithm) + { + Assert.True(msg.Signatures.Contains(signature)); + var writer = new CborWriter(); + writer.WriteStartArray(3); + + // encoded protected + byte[] encodedProtectedHeaders = GetCounterSignProtectedHeaders((int)algorithm); + writer.WriteByteString(encodedProtectedHeaders); + + // empty unprotected headers + writer.WriteStartMap(0); + writer.WriteEndMap(); + + // signature + (AsymmetricAlgorithm key, HashAlgorithmName hash, _) = GetKeyHashPaddingTriplet(algorithm); + byte[] signatureBytes = GetSignature(key, hash, GetToBeSignedForCounterSign(msg, signature, encodedProtectedHeaders)); + writer.WriteByteString(signatureBytes); + writer.WriteEndArray(); + + return writer.Encode(); + } + + private static byte[] GetCounterSignProtectedHeaders(int algorithm) + { + var writer = new CborWriter(); + writer.WriteStartMap(1); + writer.WriteInt32(KnownHeaderAlg); + writer.WriteInt32(algorithm); + writer.WriteEndMap(); + + return writer.Encode(); + } + + private static byte[] GetSignature(AsymmetricAlgorithm key, HashAlgorithmName hash, byte[] toBeSigned) + { + if (key is ECDsa ecdsa) + { + return ecdsa.SignData(toBeSigned, hash); + } + else if (key is RSA rsa) + { + return rsa.SignData(toBeSigned, hash, RSASignaturePadding.Pss); + } + + throw new ArgumentException("Key must be ECDsa or RSA", nameof(key)); + } + + internal static bool VerifyCounterSign(AsymmetricAlgorithm key, HashAlgorithmName hash, byte[] toBeSigned, byte[] signature) + { + if (key is ECDsa ecdsa) + { + return ecdsa.VerifyData(toBeSigned, signature, hash); + } + else if (key is RSA rsa) + { + return rsa.VerifyData(toBeSigned, signature, hash, RSASignaturePadding.Pss); + } + + throw new ArgumentException("Key must be ECDsa or RSA", nameof(key)); + } + + internal static byte[] GetToBeSignedForCounterSign(CoseMultiSignMessage msg, CoseSignature signature, byte[] signProtected) + { + var writer = new CborWriter(); + writer.WriteStartArray(5); + writer.WriteTextString("CounterSignature"); + writer.WriteByteString(msg.RawProtectedHeaders.Span); // body_protected + writer.WriteByteString(signProtected); // sign_protected + writer.WriteByteString(default(Span)); // external_aad + writer.WriteByteString(signature.Signature.Span); + writer.WriteEndArray(); + + return writer.Encode(); + } + + internal static (byte[], byte[]) ReadCounterSign(CoseHeaderValue value, AsymmetricAlgorithm key) + { + var reader = new CborReader(value.EncodedValue); + Assert.Equal(3, reader.ReadStartArray()); + + // encoded protected + byte[] encodedProtectedHeaders = reader.ReadByteString(); + + // empty unprotected headers + Assert.Equal(0, reader.ReadStartMap()); + reader.ReadEndMap(); + + // signature + byte[] signature = reader.ReadByteString(); + Assert.Equal(GetSignatureSize(key), signature.Length); + + reader.ReadEndArray(); + return (encodedProtectedHeaders, signature); + } } } From ad7feeb55b44ef5c39ce5cdd213be7be25f37cff Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Fri, 12 Aug 2022 12:20:59 -0700 Subject: [PATCH 30/56] Rename 'up-for-grabs' label to 'help wanted' (#73810) --- CONTRIBUTING.md | 4 ++-- README.md | 2 +- docs/project/issue-guide.md | 8 ++++---- docs/workflow/building/libraries/code-coverage.md | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7574b9ffee9547..1b9e0d28cf20e9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -121,9 +121,9 @@ We use and recommend the following workflow: - The next official build will automatically include your change. - You can delete the branch you used for making the change. -### Up for Grabs +### Help Wanted (Up for Grabs) -The team marks the most straightforward issues as [up for grabs](https://github.com/dotnet/runtime/labels/up-for-grabs). This set of issues is the place to start if you are interested in contributing but new to the codebase. +The team marks the most straightforward issues as [help wanted](https://github.com/dotnet/runtime/labels/help%20wanted). This set of issues is the place to start if you are interested in contributing but new to the codebase. ### Commit Messages diff --git a/README.md b/README.md index 9e10ff0350fdf5..4910f5a612b4e6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # .NET Runtime [![Build Status](https://dnceng.visualstudio.com/public/_apis/build/status/dotnet/runtime/runtime?branchName=main)](https://dnceng.visualstudio.com/public/_build/latest?definitionId=686&branchName=main) -[![Help Wanted](https://img.shields.io/github/issues/dotnet/runtime/up-for-grabs?style=flat-square&color=%232EA043&label=help%20wanted)](https://github.com/dotnet/runtime/issues?q=is%3Aissue+is%3Aopen+label%3A%22up-for-grabs%22) +[![Help Wanted](https://img.shields.io/github/issues/dotnet/runtime/help%20wanted?style=flat-square&color=%232EA043&label=help%20wanted)](https://github.com/dotnet/runtime/labels/help%20wanted) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/dotnet/runtime) [![Discord](https://img.shields.io/discord/732297728826277939?style=flat-square&label=Discord&logo=discord&logoColor=white&color=7289DA)](https://aka.ms/dotnet-discord) diff --git a/docs/project/issue-guide.md b/docs/project/issue-guide.md index 3d140429b5b3a3..2af1afee69d186 100644 --- a/docs/project/issue-guide.md +++ b/docs/project/issue-guide.md @@ -10,7 +10,7 @@ As noted above, we don't close issues just because we don't plan to address them 2. Cross cutting work better suited for another team. Sometimes the line between the framework, languages and runtime blurs. For some issues, we may feel that the work is better suited for the runtime team, language team or other partner. In these cases, we'll close the issue and open it with the partner team. If they end up not deciding to take on the issue, we can reconsider it here. 3. Nebulous and Large open issues. Large open issues are sometimes better suited for [User Voice](http://visualstudio.uservoice.com/forums/121579-visual-studio/category/31481--net), especially when the work will cross the boundaries of the framework, language and runtime. A good example of this is the SIMD support we recently added to CoreFx. This started as a [User Voice request](https://visualstudio.uservoice.com/forums/121579-visual-studio-2015/suggestions/2212443-c-and-simd), and eventually turned into work for both the core libraries and runtime. -Sometimes after debate, we'll decide an issue isn't a good fit for CoreFx. In that case, we'll also close it. Because of this, we ask that you don't start working on an issue until it's tagged with [up-for-grabs](https://github.com/dotnet/runtime/labels/up-for-grabs) or [api-approved](https://github.com/dotnet/runtime/labels/api-approved). Both you and the team will be unhappy if you spend time and effort working on a change we'll ultimately be unable to take. We try to avoid that. +Sometimes after debate, we'll decide an issue isn't a good fit for the .NET runtime codebase. In that case, we'll also close it. Because of this, we ask that you don't start working on an issue until it's tagged with [help wanted](https://github.com/dotnet/runtime/labels/help%20wanted) or [api-approved](https://github.com/dotnet/runtime/labels/api-approved). Both you and the team will be unhappy if you spend time and effort working on a change we'll ultimately be unable to take. We try to avoid that. ### Labels We use GitHub [labels](https://github.com/dotnet/runtime/labels) on our issues in order to classify them. We have the following categories per issue: @@ -25,7 +25,7 @@ We use GitHub [labels](https://github.com/dotnet/runtime/labels) on our issues i * [question](https://github.com/dotnet/runtime/labels/question): Questions about the product, source code, etc. * **Other**: * [easy](https://github.com/dotnet/runtime/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aopen%20label%3Aeasy%20no%3Aassignee) - Good for starting contributors. - * [up-for-grabs](https://github.com/dotnet/runtime/labels/up-for-grabs): Small sections of work which we believe are well scoped. These sorts of issues are a good place to start if you are new. Anyone is free to work on these issues. + * [help wanted](https://github.com/dotnet/runtime/labels/help%20wanted): Small sections of work which we believe are well scoped. These sorts of issues are a good place to start if you are new. Anyone is free to work on these issues. * [needs more info](https://github.com/dotnet/runtime/labels/needs%20more%20info): Issues which need more information to be actionable. Usually this will be because we can't reproduce a reported bug. We'll close these issues after a little bit if we haven't gotten actionable information, but we welcome folks who have acquired more information to reopen the issue. * [wishlist](https://github.com/dotnet/runtime/issues?q=is%3Aissue+is%3Aopen+label%3Awishlist) - Issues on top of our backlog we won't likely get to. Warning: Might not be easy. @@ -50,7 +50,7 @@ Areas are tracked by labels area-* (e.g. area-System.Collections). Each area 1. Each issue has exactly one **area-*** label 1. Issue has no **Assignee**, unless someone is working on the issue at the moment -1. Use **up-for-grabs** as much as possible, ideally with a quick note about next steps / complexity of the issue +1. Use **help wanted** as much as possible, ideally notes about next steps / complexity of the issue / acceptance criteria 1. Set milestone to **Future**, unless you can 95%-commit you can fund the issue in specific milestone 1. Each issue has exactly one "*issue type*" label (**bug**, **enhancement**, **api-needs-work**, **test bug**, **test enhancement**, **question**, **documentation**, etc.) 1. Don't be afraid to say no, or close issues - just explain why and be polite @@ -64,7 +64,7 @@ Feel free to use other labels if it helps your triage efforts (e.g. **needs more * Motivation: Issues with multiple areas have loose responsibility (everyone blames the other side) and issues are double counted in reports. 1. Issue has no **Assignee**, unless someone is working on the issue at the moment * Motivation: Observation is that contributors are less likely to grab assigned issues, no matter what the repo rules say. -1. Use **up-for-grabs** as much as possible, ideally with a quick note about next steps / complexity of the issue +1. Use **help wanted** as much as possible, ideally with a quick note about next steps / complexity of the issue * Note: Per http://up-for-grabs.net, such issues should be no longer than few nights' worth of work. They should be actionable (i.e. no mysterious CI failures or UWP issues that can't be tested in the open). 1. Set milestone to **Future**, unless you can 95%-commit you can fund the issue in specific milestone * Motivation: Helps communicate desire/timeline to community. Can spark further priority/impact discussion. diff --git a/docs/workflow/building/libraries/code-coverage.md b/docs/workflow/building/libraries/code-coverage.md index d5fc0ed99d4f39..66d77441ecb24a 100644 --- a/docs/workflow/building/libraries/code-coverage.md +++ b/docs/workflow/building/libraries/code-coverage.md @@ -20,7 +20,7 @@ Our default, somewhat-arbitrary initial goal for a library is 90% code coverage. ## Issues -Issues are opened for a library when a cursory examination of its code coverage reveal that there are likely still some meaningful gaps that need to be addressed. We welcome contributions to our test suites to help address these gaps and close these issues. Many of these issues are marked as [up-for-grabs](https://github.com/dotnet/runtime/labels/up-for-grabs). +Issues are opened for a library when a cursory examination of its code coverage reveal that there are likely still some meaningful gaps that need to be addressed. We welcome contributions to our test suites to help address these gaps and close these issues. Many of these issues are marked as [help wanted](https://github.com/dotnet/runtime/labels/help%20wanted). An issue need not be addressed in its entirety. We happily accept contributions that improve our tests and work towards improving code coverage numbers even if they only incrementally improve the situation. From 0e4e3520a87209e6ace8a41197543385b0314053 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 12 Aug 2022 21:24:42 +0200 Subject: [PATCH 31/56] Update dependencies from https://github.com/dotnet/xharness build 20220811.1 (#73846) Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Common , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 1.0.0-prerelease.22408.1 -> To Version 1.0.0-prerelease.22411.1 Co-authored-by: dotnet-maestro[bot] --- .config/dotnet-tools.json | 2 +- eng/Version.Details.xml | 12 ++++++------ eng/Versions.props | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 856cc4c5edb1ff..5a423ee75b3788 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "1.0.0-prerelease.22408.1", + "version": "1.0.0-prerelease.22411.1", "commands": [ "xharness" ] diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 43b226bb71a595..51bb36c04ead5e 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -238,17 +238,17 @@ https://github.com/dotnet/linker 81ffbb5af38a45ff60648999df8f35a79061ae43 - + https://github.com/dotnet/xharness - c98b5a327992608c54e50d48e24bb4dde0fa853e + 5ebf69650b9f7b4ecab485be840b3022420f7812 - + https://github.com/dotnet/xharness - c98b5a327992608c54e50d48e24bb4dde0fa853e + 5ebf69650b9f7b4ecab485be840b3022420f7812 - + https://github.com/dotnet/xharness - c98b5a327992608c54e50d48e24bb4dde0fa853e + 5ebf69650b9f7b4ecab485be840b3022420f7812 https://github.com/dotnet/arcade diff --git a/eng/Versions.props b/eng/Versions.props index 36423dc87d38f0..ffc83bf599f612 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -152,9 +152,9 @@ 1.1.0 17.4.0-preview-20220707-01 - 1.0.0-prerelease.22408.1 - 1.0.0-prerelease.22408.1 - 1.0.0-prerelease.22408.1 + 1.0.0-prerelease.22411.1 + 1.0.0-prerelease.22411.1 + 1.0.0-prerelease.22411.1 1.1.0-alpha.0.22362.1 2.4.2-pre.22 0.12.0-pre.20 From 30a41a20bb1c61092a4e14eb200b878b44d16005 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 12 Aug 2022 21:26:17 +0200 Subject: [PATCH 32/56] [main] Update dependencies from dotnet/arcade (#73767) * Update dependencies from https://github.com/dotnet/arcade build 20220810.3 Microsoft.DotNet.ApiCompat , 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.VersionTools.Tasks , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.XUnitExtensions From Version 7.0.0-beta.22408.3 -> To Version 7.0.0-beta.22410.3 * Update dependencies from https://github.com/dotnet/arcade build 20220811.2 Microsoft.DotNet.ApiCompat , 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.VersionTools.Tasks , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.XUnitExtensions From Version 7.0.0-beta.22408.3 -> To Version 7.0.0-beta.22411.2 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 76 +++++++-------- eng/Versions.props | 32 +++---- eng/common/cross/build-rootfs.sh | 92 ++++++++++++++++++- eng/common/cross/toolchain.cmake | 41 ++++++++- eng/common/templates/job/execute-sdl.yml | 2 +- eng/common/templates/job/onelocbuild.yml | 2 +- .../templates/job/source-index-stage1.yml | 4 +- eng/common/templates/jobs/jobs.yml | 2 +- .../templates/post-build/post-build.yml | 8 +- eng/common/tools.ps1 | 9 +- global.json | 6 +- 11 files changed, 203 insertions(+), 71 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 51bb36c04ead5e..da25ebce1d5a98 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -54,77 +54,77 @@ - + https://github.com/dotnet/arcade - 13b342360ba81ca3fdf911fda985dc8420d51627 + 6a638cd0c13962ab2a1943cb1c878be5a41dd82e - + https://github.com/dotnet/arcade - 13b342360ba81ca3fdf911fda985dc8420d51627 + 6a638cd0c13962ab2a1943cb1c878be5a41dd82e - + https://github.com/dotnet/arcade - 13b342360ba81ca3fdf911fda985dc8420d51627 + 6a638cd0c13962ab2a1943cb1c878be5a41dd82e - + https://github.com/dotnet/arcade - 13b342360ba81ca3fdf911fda985dc8420d51627 + 6a638cd0c13962ab2a1943cb1c878be5a41dd82e - + https://github.com/dotnet/arcade - 13b342360ba81ca3fdf911fda985dc8420d51627 + 6a638cd0c13962ab2a1943cb1c878be5a41dd82e - + https://github.com/dotnet/arcade - 13b342360ba81ca3fdf911fda985dc8420d51627 + 6a638cd0c13962ab2a1943cb1c878be5a41dd82e - + https://github.com/dotnet/arcade - 13b342360ba81ca3fdf911fda985dc8420d51627 + 6a638cd0c13962ab2a1943cb1c878be5a41dd82e - + https://github.com/dotnet/arcade - 13b342360ba81ca3fdf911fda985dc8420d51627 + 6a638cd0c13962ab2a1943cb1c878be5a41dd82e - + https://github.com/dotnet/arcade - 13b342360ba81ca3fdf911fda985dc8420d51627 + 6a638cd0c13962ab2a1943cb1c878be5a41dd82e - + https://github.com/dotnet/arcade - 13b342360ba81ca3fdf911fda985dc8420d51627 + 6a638cd0c13962ab2a1943cb1c878be5a41dd82e - + https://github.com/dotnet/arcade - 13b342360ba81ca3fdf911fda985dc8420d51627 + 6a638cd0c13962ab2a1943cb1c878be5a41dd82e - + https://github.com/dotnet/arcade - 13b342360ba81ca3fdf911fda985dc8420d51627 + 6a638cd0c13962ab2a1943cb1c878be5a41dd82e - + https://github.com/dotnet/arcade - 13b342360ba81ca3fdf911fda985dc8420d51627 + 6a638cd0c13962ab2a1943cb1c878be5a41dd82e - + https://github.com/dotnet/arcade - 13b342360ba81ca3fdf911fda985dc8420d51627 + 6a638cd0c13962ab2a1943cb1c878be5a41dd82e - + https://github.com/dotnet/arcade - 13b342360ba81ca3fdf911fda985dc8420d51627 + 6a638cd0c13962ab2a1943cb1c878be5a41dd82e - + https://github.com/dotnet/arcade - 13b342360ba81ca3fdf911fda985dc8420d51627 + 6a638cd0c13962ab2a1943cb1c878be5a41dd82e - + https://github.com/dotnet/arcade - 13b342360ba81ca3fdf911fda985dc8420d51627 + 6a638cd0c13962ab2a1943cb1c878be5a41dd82e - + https://github.com/dotnet/arcade - 13b342360ba81ca3fdf911fda985dc8420d51627 + 6a638cd0c13962ab2a1943cb1c878be5a41dd82e https://github.com/dotnet/runtime-assets @@ -250,9 +250,9 @@ https://github.com/dotnet/xharness 5ebf69650b9f7b4ecab485be840b3022420f7812 - + https://github.com/dotnet/arcade - 13b342360ba81ca3fdf911fda985dc8420d51627 + 6a638cd0c13962ab2a1943cb1c878be5a41dd82e https://dev.azure.com/dnceng/internal/_git/dotnet-optimization diff --git a/eng/Versions.props b/eng/Versions.props index ffc83bf599f612..3659964a3d011f 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -54,22 +54,22 @@ 7.0.100-rc.1.22402.1 - 7.0.0-beta.22408.3 - 7.0.0-beta.22408.3 - 7.0.0-beta.22408.3 - 7.0.0-beta.22408.3 - 7.0.0-beta.22408.3 - 7.0.0-beta.22408.3 - 2.5.1-beta.22408.3 - 7.0.0-beta.22408.3 - 7.0.0-beta.22408.3 - 7.0.0-beta.22408.3 - 7.0.0-beta.22408.3 - 7.0.0-beta.22408.3 - 7.0.0-beta.22408.3 - 7.0.0-beta.22408.3 - 7.0.0-beta.22408.3 - 7.0.0-beta.22408.3 + 7.0.0-beta.22411.2 + 7.0.0-beta.22411.2 + 7.0.0-beta.22411.2 + 7.0.0-beta.22411.2 + 7.0.0-beta.22411.2 + 7.0.0-beta.22411.2 + 2.5.1-beta.22411.2 + 7.0.0-beta.22411.2 + 7.0.0-beta.22411.2 + 7.0.0-beta.22411.2 + 7.0.0-beta.22411.2 + 7.0.0-beta.22411.2 + 7.0.0-beta.22411.2 + 7.0.0-beta.22411.2 + 7.0.0-beta.22411.2 + 7.0.0-beta.22411.2 6.0.0-preview.1.102 diff --git a/eng/common/cross/build-rootfs.sh b/eng/common/cross/build-rootfs.sh index f058c98763aa5c..d3b0ac3ba7b600 100755 --- a/eng/common/cross/build-rootfs.sh +++ b/eng/common/cross/build-rootfs.sh @@ -8,11 +8,13 @@ usage() echo "BuildArch can be: arm(default), arm64, armel, armv6, ppc64le, riscv64, s390x, x64, x86" echo "CodeName - optional, Code name for Linux, can be: xenial(default), zesty, bionic, alpine, alpine3.13 or alpine3.14. If BuildArch is armel, LinuxCodeName is jessie(default) or tizen." echo " for FreeBSD can be: freebsd12, freebsd13" - echo " for illumos can be: illumos." + echo " for illumos can be: illumos" + echo " for Haiku can be: haiku." echo "lldbx.y - optional, LLDB version, can be: lldb3.9(default), lldb4.0, lldb5.0, lldb6.0 no-lldb. Ignored for alpine and FreeBSD" echo "llvmx[.y] - optional, LLVM version for LLVM related packages." echo "--skipunmount - optional, will skip the unmount of rootfs folder." echo "--use-mirror - optional, use mirror URL to fetch resources, when available." + echo "--jobs N - optional, restrict to N jobs." exit 1 } @@ -79,6 +81,17 @@ __IllumosPackages+=" mit-krb5-1.16.2nb4" __IllumosPackages+=" openssl-1.1.1e" __IllumosPackages+=" zlib-1.2.11" +__HaikuPackages="gmp" +__HaikuPackages+=" gmp_devel" +__HaikuPackages+=" krb5" +__HaikuPackages+=" krb5_devel" +__HaikuPackages+=" libiconv" +__HaikuPackages+=" libiconv_devel" +__HaikuPackages+=" llvm12_libunwind" +__HaikuPackages+=" llvm12_libunwind_devel" +__HaikuPackages+=" mpfr" +__HaikuPackages+=" mpfr_devel" + # ML.NET dependencies __UbuntuPackages+=" libomp5" __UbuntuPackages+=" libomp-dev" @@ -263,6 +276,11 @@ while :; do __CodeName=illumos __SkipUnmount=1 ;; + haiku) + __CodeName=haiku + __BuildArch=x64 + __SkipUnmount=1 + ;; --skipunmount) __SkipUnmount=1 ;; @@ -273,6 +291,10 @@ while :; do --use-mirror) __UseMirror=1 ;; + --use-jobs) + shift + MAXJOBS=$1 + ;; *) __UnprocessedBuildArgs="$__UnprocessedBuildArgs $1" ;; @@ -326,7 +348,7 @@ if [[ "$__CodeName" == "alpine" ]]; then rm -r "$__ApkToolsDir" elif [[ "$__CodeName" == "freebsd" ]]; then mkdir -p "$__RootfsDir"/usr/local/etc - JOBS="$(getconf _NPROCESSORS_ONLN)" + JOBS=${MAXJOBS:="$(getconf _NPROCESSORS_ONLN)"} wget -O - "https://download.freebsd.org/ftp/releases/${__FreeBSDArch}/${__FreeBSDMachineArch}/${__FreeBSDBase}/base.txz" | tar -C "$__RootfsDir" -Jxf - ./lib ./usr/lib ./usr/libdata ./usr/include ./usr/share/keys ./etc ./bin/freebsd-version echo "ABI = \"FreeBSD:${__FreeBSDABI}:${__FreeBSDMachineArch}\"; FINGERPRINTS = \"${__RootfsDir}/usr/share/keys\"; REPOS_DIR = [\"${__RootfsDir}/etc/pkg\"]; REPO_AUTOUPDATE = NO; RUN_SCRIPTS = NO;" > "${__RootfsDir}"/usr/local/etc/pkg.conf echo "FreeBSD: { url: \"pkg+http://pkg.FreeBSD.org/\${ABI}/quarterly\", mirror_type: \"srv\", signature_type: \"fingerprints\", fingerprints: \"${__RootfsDir}/usr/share/keys/pkg\", enabled: yes }" > "${__RootfsDir}"/etc/pkg/FreeBSD.conf @@ -344,7 +366,7 @@ elif [[ "$__CodeName" == "freebsd" ]]; then elif [[ "$__CodeName" == "illumos" ]]; then mkdir "$__RootfsDir/tmp" pushd "$__RootfsDir/tmp" - JOBS="$(getconf _NPROCESSORS_ONLN)" + JOBS=${MAXJOBS:="$(getconf _NPROCESSORS_ONLN)"} echo "Downloading sysroot." wget -O - https://github.com/illumos/sysroot/releases/download/20181213-de6af22ae73b-v1/illumos-sysroot-i386-20181213-de6af22ae73b-v1.tar.gz | tar -C "$__RootfsDir" -xzf - echo "Building binutils. Please wait.." @@ -386,6 +408,70 @@ elif [[ "$__CodeName" == "illumos" ]]; then wget -P "$__RootfsDir"/usr/include/net https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/io/bpf/net/dlt.h wget -P "$__RootfsDir"/usr/include/netpacket https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/inet/sockmods/netpacket/packet.h wget -P "$__RootfsDir"/usr/include/sys https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/sys/sdt.h +elif [[ "$__CodeName" == "haiku" ]]; then + JOBS=${MAXJOBS:="$(getconf _NPROCESSORS_ONLN)"} + + echo "Building Haiku sysroot for x86_64" + mkdir -p "$__RootfsDir/tmp" + cd "$__RootfsDir/tmp" + git clone -b hrev56235 https://review.haiku-os.org/haiku + git clone -b btrev43195 https://review.haiku-os.org/buildtools + cd "$__RootfsDir/tmp/buildtools" && git checkout 7487388f5110021d400b9f3b88e1a7f310dc066d + + # Fetch some unmerged patches + cd "$__RootfsDir/tmp/haiku" + ## Add development build profile (slimmer than nightly) + git fetch origin refs/changes/64/4164/1 && git -c commit.gpgsign=false cherry-pick FETCH_HEAD + + # Build jam + cd "$__RootfsDir/tmp/buildtools/jam" + make + + # Configure cross tools + echo "Building cross-compiler" + mkdir -p "$__RootfsDir/generated" + cd "$__RootfsDir/generated" + "$__RootfsDir/tmp/haiku/configure" -j"$JOBS" --sysroot "$__RootfsDir" --cross-tools-source "$__RootfsDir/tmp/buildtools" --build-cross-tools x86_64 + + # Build Haiku packages + echo "Building Haiku" + echo 'HAIKU_BUILD_PROFILE = "development-raw" ;' > UserProfileConfig + "$__RootfsDir/tmp/buildtools/jam/jam0" -j"$JOBS" -q 'package' 'Haiku' + + BaseUrl="https://depot.haiku-os.org/__api/v2/pkg/get-pkg" + + # Download additional packages + echo "Downloading additional required packages" + read -ra array <<<"$__HaikuPackages" + for package in "${array[@]}"; do + echo "Downloading $package..." + # API documented here: https://github.com/haiku/haikudepotserver/blob/master/haikudepotserver-api2/src/main/resources/api2/pkg.yaml#L60 + # The schema here: https://github.com/haiku/haikudepotserver/blob/master/haikudepotserver-api2/src/main/resources/api2/pkg.yaml#L598 + hpkgDownloadUrl="$(wget -qO- --post-data='{"name":"'"$package"'","repositorySourceCode":"haikuports_x86_64","versionType":"LATEST","naturalLanguageCode":"en"}' \ + --header='Content-Type:application/json' "$BaseUrl" | jq -r '.result.versions[].hpkgDownloadURL')" + wget -P "$__RootfsDir/generated/download" "$hpkgDownloadUrl" + done + + # Setup the sysroot + echo "Setting up sysroot and extracting needed packages" + mkdir -p "$__RootfsDir/boot/system" + for file in "$__RootfsDir/generated/objects/haiku/x86_64/packaging/packages/"*.hpkg; do + "$__RootfsDir/generated/objects/linux/x86_64/release/tools/package/package" extract -C "$__RootfsDir/boot/system" "$file" + done + for file in "$__RootfsDir/generated/download/"*.hpkg; do + "$__RootfsDir/generated/objects/linux/x86_64/release/tools/package/package" extract -C "$__RootfsDir/boot/system" "$file" + done + + # Cleaning up temporary files + echo "Cleaning up temporary files" + rm -rf "$__RootfsDir/tmp" + for name in "$__RootfsDir/generated/"*; do + if [[ "$name" =~ "cross-tools-" ]]; then + : # Keep the cross-compiler + else + rm -rf "$name" + fi + done elif [[ -n "$__CodeName" ]]; then qemu-debootstrap $__Keyring --arch "$__UbuntuArch" "$__CodeName" "$__RootfsDir" "$__UbuntuRepo" cp "$__CrossDir/$__BuildArch/sources.list.$__CodeName" "$__RootfsDir/etc/apt/sources.list" diff --git a/eng/common/cross/toolchain.cmake b/eng/common/cross/toolchain.cmake index 909117759e61b5..561576be97c262 100644 --- a/eng/common/cross/toolchain.cmake +++ b/eng/common/cross/toolchain.cmake @@ -7,6 +7,8 @@ if(EXISTS ${CROSS_ROOTFS}/bin/freebsd-version) elseif(EXISTS ${CROSS_ROOTFS}/usr/platform/i86pc) set(CMAKE_SYSTEM_NAME SunOS) set(ILLUMOS 1) +elseif(EXISTS ${CROSS_ROOTFS}/boot/system/develop/headers/config/HaikuConfig.h) + set(CMAKE_SYSTEM_NAME Haiku) else() set(CMAKE_SYSTEM_NAME Linux) set(LINUX 1) @@ -76,6 +78,8 @@ elseif(TARGET_ARCH_NAME STREQUAL "x64") set(triple "x86_64-unknown-freebsd12") elseif(ILLUMOS) set(TOOLCHAIN "x86_64-illumos") + elseif(HAIKU) + set(TOOLCHAIN "x64_64-unknown-haiku") endif() elseif(TARGET_ARCH_NAME STREQUAL "x86") set(CMAKE_SYSTEM_PROCESSOR i686) @@ -170,6 +174,41 @@ elseif(ILLUMOS) set(CMAKE_C_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES} -lssp") set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} -lssp") +elseif(HAIKU) + set(CMAKE_SYSROOT "${CROSS_ROOTFS}") + + set(TOOLSET_PREFIX ${TOOLCHAIN}-) + function(locate_toolchain_exec exec var) + string(TOUPPER ${exec} EXEC_UPPERCASE) + if(NOT "$ENV{CLR_${EXEC_UPPERCASE}}" STREQUAL "") + set(${var} "$ENV{CLR_${EXEC_UPPERCASE}}" PARENT_SCOPE) + return() + endif() + + set(SEARCH_PATH "${CROSS_ROOTFS}/generated/cross-tools-x86_64/bin") + + find_program(EXEC_LOCATION_${exec} + PATHS ${SEARCH_PATH} + NAMES + "${TOOLSET_PREFIX}${exec}${CLR_CMAKE_COMPILER_FILE_NAME_VERSION}" + "${TOOLSET_PREFIX}${exec}") + + if (EXEC_LOCATION_${exec} STREQUAL "EXEC_LOCATION_${exec}-NOTFOUND") + message(FATAL_ERROR "Unable to find toolchain executable. Name: ${exec}, Prefix: ${TOOLSET_PREFIX}.") + endif() + set(${var} ${EXEC_LOCATION_${exec}} PARENT_SCOPE) + endfunction() + + set(CMAKE_SYSTEM_PREFIX_PATH "${CROSS_ROOTFS}") + + locate_toolchain_exec(gcc CMAKE_C_COMPILER) + locate_toolchain_exec(g++ CMAKE_CXX_COMPILER) + + set(CMAKE_C_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES} -lssp") + set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} -lssp") + + # let CMake set up the correct search paths + include(Platform/Haiku) else() set(CMAKE_SYSROOT "${CROSS_ROOTFS}") @@ -229,7 +268,7 @@ endif() # Specify compile options -if((TARGET_ARCH_NAME MATCHES "^(arm|arm64|armel|armv6|ppc64le|riscv64|s390x)$" AND NOT ANDROID AND NOT FREEBSD) OR ILLUMOS) +if((TARGET_ARCH_NAME MATCHES "^(arm|arm64|armel|armv6|ppc64le|riscv64|s390x)$" AND NOT ANDROID AND NOT FREEBSD) OR ILLUMOS OR HAIKU) set(CMAKE_C_COMPILER_TARGET ${TOOLCHAIN}) set(CMAKE_CXX_COMPILER_TARGET ${TOOLCHAIN}) set(CMAKE_ASM_COMPILER_TARGET ${TOOLCHAIN}) diff --git a/eng/common/templates/job/execute-sdl.yml b/eng/common/templates/job/execute-sdl.yml index 24cec0424e5d64..9ff6a10a682c27 100644 --- a/eng/common/templates/job/execute-sdl.yml +++ b/eng/common/templates/job/execute-sdl.yml @@ -54,7 +54,7 @@ jobs: # If it's not devdiv, it's dnceng ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}: name: NetCore1ESPool-Internal - demands: ImageOverride -equals Build.Server.Amd64.VS2019 + demands: ImageOverride -equals windows.vs2019.amd64 steps: - checkout: self clean: true diff --git a/eng/common/templates/job/onelocbuild.yml b/eng/common/templates/job/onelocbuild.yml index 3bcd243c46b678..6c523b714f407a 100644 --- a/eng/common/templates/job/onelocbuild.yml +++ b/eng/common/templates/job/onelocbuild.yml @@ -41,7 +41,7 @@ jobs: # If it's not devdiv, it's dnceng ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}: name: NetCore1ESPool-Internal - demands: ImageOverride -equals Build.Server.Amd64.VS2019 + demands: ImageOverride -equals windows.vs2019.amd64 variables: - group: OneLocBuildVariables # Contains the CeapexPat and GithubPat diff --git a/eng/common/templates/job/source-index-stage1.yml b/eng/common/templates/job/source-index-stage1.yml index 4e37210857d154..c85044a6849054 100644 --- a/eng/common/templates/job/source-index-stage1.yml +++ b/eng/common/templates/job/source-index-stage1.yml @@ -29,10 +29,10 @@ jobs: pool: ${{ if eq(variables['System.TeamProject'], 'public') }}: name: NetCore1ESPool-Public - demands: ImageOverride -equals Build.Server.Amd64.VS2019.Open + demands: ImageOverride -equals windows.vs2019.amd64.open ${{ if eq(variables['System.TeamProject'], 'internal') }}: name: NetCore1ESPool-Internal - demands: ImageOverride -equals Build.Server.Amd64.VS2019 + demands: ImageOverride -equals windows.vs2019.amd64 steps: - ${{ each preStep in parameters.preSteps }}: diff --git a/eng/common/templates/jobs/jobs.yml b/eng/common/templates/jobs/jobs.yml index 2cca53c2d1d531..64e5929f22161b 100644 --- a/eng/common/templates/jobs/jobs.yml +++ b/eng/common/templates/jobs/jobs.yml @@ -96,7 +96,7 @@ jobs: # If it's not devdiv, it's dnceng ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}: name: NetCore1ESPool-Internal - demands: ImageOverride -equals Build.Server.Amd64.VS2019 + demands: ImageOverride -equals windows.vs2019.amd64 runAsPublic: ${{ parameters.runAsPublic }} publishUsingPipelines: ${{ parameters.enablePublishUsingPipelines }} diff --git a/eng/common/templates/post-build/post-build.yml b/eng/common/templates/post-build/post-build.yml index e0beb25d4e7610..87fcae940cff07 100644 --- a/eng/common/templates/post-build/post-build.yml +++ b/eng/common/templates/post-build/post-build.yml @@ -107,7 +107,7 @@ stages: # If it's not devdiv, it's dnceng ${{ else }}: name: NetCore1ESPool-Internal - demands: ImageOverride -equals Build.Server.Amd64.VS2019 + demands: ImageOverride -equals windows.vs2019.amd64 steps: - template: setup-maestro-vars.yml @@ -144,7 +144,7 @@ stages: # If it's not devdiv, it's dnceng ${{ else }}: name: NetCore1ESPool-Internal - demands: ImageOverride -equals Build.Server.Amd64.VS2019 + demands: ImageOverride -equals windows.vs2019.amd64 steps: - template: setup-maestro-vars.yml parameters: @@ -204,7 +204,7 @@ stages: # If it's not devdiv, it's dnceng ${{ else }}: name: NetCore1ESPool-Internal - demands: ImageOverride -equals Build.Server.Amd64.VS2019 + demands: ImageOverride -equals windows.vs2019.amd64 steps: - template: setup-maestro-vars.yml parameters: @@ -263,7 +263,7 @@ stages: # If it's not devdiv, it's dnceng ${{ else }}: name: NetCore1ESPool-Internal - demands: ImageOverride -equals Build.Server.Amd64.VS2019 + demands: ImageOverride -equals windows.vs2019.amd64 steps: - template: setup-maestro-vars.yml parameters: diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index 9638c63c7258b5..f83a748c37e9cf 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -368,7 +368,14 @@ function InitializeVisualStudioMSBuild([bool]$install, [object]$vsRequirements = # https://dev.azure.com/dnceng/public/_packaging?_a=package&feed=dotnet-eng&package=RoslynTools.MSBuild&protocolType=NuGet&version=17.1.0&view=overview $defaultXCopyMSBuildVersion = '17.1.0' - if (!$vsRequirements) { $vsRequirements = $GlobalJson.tools.vs } + if (!$vsRequirements) { + if (Get-Member -InputObject $GlobalJson.tools -Name 'vs') { + $vsRequirements = $GlobalJson.tools.vs + } + else { + $vsRequirements = New-Object PSObject -Property @{ version = $vsMinVersionReqdStr } + } + } $vsMinVersionStr = if ($vsRequirements.version) { $vsRequirements.version } else { $vsMinVersionReqdStr } $vsMinVersion = [Version]::new($vsMinVersionStr) diff --git a/global.json b/global.json index 31b8c266d8aabc..f84b02d1ed39e3 100644 --- a/global.json +++ b/global.json @@ -8,9 +8,9 @@ "dotnet": "7.0.100-preview.5.22307.18" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.22408.3", - "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.22408.3", - "Microsoft.DotNet.SharedFramework.Sdk": "7.0.0-beta.22408.3", + "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.22411.2", + "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.22411.2", + "Microsoft.DotNet.SharedFramework.Sdk": "7.0.0-beta.22411.2", "Microsoft.Build.NoTargets": "3.5.0", "Microsoft.Build.Traversal": "3.1.6", "Microsoft.NET.Sdk.IL": "7.0.0-rc.1.22407.4" From f44da5225f894eced11b23d820f5e28b079fa76b Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Fri, 12 Aug 2022 12:39:31 -0700 Subject: [PATCH 33/56] Only include host.native instead of all host in default clr/mono subsets (#73800) --- eng/Subsets.props | 4 ++-- eng/pipelines/libraries/run-test-job.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Subsets.props b/eng/Subsets.props index 45b2a49471e5c6..08a9d5dfaefc61 100644 --- a/eng/Subsets.props +++ b/eng/Subsets.props @@ -51,7 +51,7 @@ - clr.native+linuxdac+clr.corelib+clr.tools+clr.nativecorelib+clr.packages+clr.nativeaotlibs+clr.crossarchtools+host + clr.native+linuxdac+clr.corelib+clr.tools+clr.nativecorelib+clr.packages+clr.nativeaotlibs+clr.crossarchtools+host.native clr.iltools+clr.packages @@ -62,7 +62,7 @@ $(DefaultMonoSubsets)mono.wasmruntime+ $(DefaultMonoSubsets)mono.aotcross+ $(DefaultMonoSubsets)mono.runtime+mono.corelib+mono.packages+mono.tools+ - $(DefaultMonoSubsets)host+ + $(DefaultMonoSubsets)host.native+ Object type is not supported. - Resolving of external URIs was prohibited. + Resolving of external URIs was prohibited. Attempted access to: {0} Relative URIs are not supported. diff --git a/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj b/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj index e9b3d71a468438..405e6375a9f6bf 100644 --- a/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj +++ b/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj @@ -127,13 +127,11 @@ - - + - @@ -748,6 +746,7 @@ + diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XmlNullResolver.cs b/src/libraries/System.Private.Xml/src/System/Xml/XmlNullResolver.cs deleted file mode 100644 index cae8ad85496957..00000000000000 --- a/src/libraries/System.Private.Xml/src/System/Xml/XmlNullResolver.cs +++ /dev/null @@ -1,25 +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.Net; - -namespace System.Xml -{ - internal sealed class XmlNullResolver : XmlResolver - { - public static readonly XmlNullResolver Singleton = new XmlNullResolver(); - - // Private constructor ensures existing only one instance of XmlNullResolver - private XmlNullResolver() { } - - public override object GetEntity(Uri absoluteUri, string? role, Type? ofObjectToReturn) - { - throw new XmlException(SR.Xml_NullResolver, string.Empty); - } - - public override ICredentials Credentials - { - set { /* Do nothing */ } - } - } -} diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XmlResolver.ThrowingResolver.cs b/src/libraries/System.Private.Xml/src/System/Xml/XmlResolver.ThrowingResolver.cs new file mode 100644 index 00000000000000..674ddb630e0896 --- /dev/null +++ b/src/libraries/System.Private.Xml/src/System/Xml/XmlResolver.ThrowingResolver.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Net; +using System.Threading.Tasks; + +namespace System.Xml +{ + public abstract partial class XmlResolver + { + /// + /// Gets an XML resolver which forbids entity resolution. + /// + /// An XML resolver which forbids entity resolution. + /// + /// Calling or on the + /// instance returned by this property is forbidden + /// and will result in being thrown. + /// + /// Use when external entity resolution must be + /// prohibited, even when DTD processing is otherwise enabled. + /// + public static XmlResolver ThrowingResolver => XmlThrowingResolver.s_singleton; + + // An XmlResolver that forbids all external entity resolution. + private sealed class XmlThrowingResolver : XmlResolver + { + internal static readonly XmlThrowingResolver s_singleton = new(); + + // Private constructor ensures existing only one instance of XmlThrowingResolver + private XmlThrowingResolver() { } + + public override ICredentials Credentials + { + set { /* Do nothing */ } + } + + public override object GetEntity(Uri absoluteUri, string? role, Type? ofObjectToReturn) + { + throw new XmlException(SR.Format(SR.Xml_NullResolver, absoluteUri)); + } + + public override Task GetEntityAsync(Uri absoluteUri, string? role, Type? ofObjectToReturn) + { + throw new XmlException(SR.Format(SR.Xml_NullResolver, absoluteUri)); + } + } + } +} diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XmlResolver.cs b/src/libraries/System.Private.Xml/src/System/Xml/XmlResolver.cs index 87d23a5341ce45..b5df8713be87cd 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XmlResolver.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XmlResolver.cs @@ -28,7 +28,12 @@ public abstract partial class XmlResolver string? role, Type? ofObjectToReturn); - + public virtual Task GetEntityAsync(Uri absoluteUri, + string? role, + Type? ofObjectToReturn) + { + throw new NotImplementedException(); + } /// /// [To be supplied.] diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XmlResolverAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/XmlResolverAsync.cs deleted file mode 100644 index 21504ff5e06ddb..00000000000000 --- a/src/libraries/System.Private.Xml/src/System/Xml/XmlResolverAsync.cs +++ /dev/null @@ -1,17 +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.Threading.Tasks; - -namespace System.Xml -{ - public abstract partial class XmlResolver - { - public virtual Task GetEntityAsync(Uri absoluteUri, - string? role, - Type? ofObjectToReturn) - { - throw new NotImplementedException(); - } - } -} diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XmlSecureResolver.cs b/src/libraries/System.Private.Xml/src/System/Xml/XmlSecureResolver.cs index aae411766ba0dc..d5a8ca7761b739 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XmlSecureResolver.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XmlSecureResolver.cs @@ -3,32 +3,31 @@ using System.Net; using System.Security; -using System.Runtime.Versioning; +using System.Threading.Tasks; namespace System.Xml { - public partial class XmlSecureResolver : XmlResolver + [Obsolete(Obsoletions.XmlSecureResolverMessage, DiagnosticId = Obsoletions.XmlSecureResolverDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] + public class XmlSecureResolver : XmlResolver { - private readonly XmlResolver _resolver; - public XmlSecureResolver(XmlResolver resolver, string? securityUrl) { - _resolver = resolver; + // no-op } public override ICredentials Credentials { - set { _resolver.Credentials = value; } + set { /* no-op */ } } - public override object? GetEntity(Uri absoluteUri, string? role, Type? ofObjectToReturn) - { - return _resolver.GetEntity(absoluteUri, role, ofObjectToReturn); - } + // Forward to ThrowingResolver to get its exception message + public override object? GetEntity(Uri absoluteUri, string? role, Type? ofObjectToReturn) => XmlResolver.ThrowingResolver.GetEntity(absoluteUri, role, ofObjectToReturn); - public override Uri ResolveUri(Uri? baseUri, string? relativeUri) - { - return _resolver.ResolveUri(baseUri, relativeUri); - } + // Forward to ThrowingResolver to get its exception message + public override Task GetEntityAsync(Uri absoluteUri, string? role, Type? ofObjectToReturn) => XmlResolver.ThrowingResolver.GetEntityAsync(absoluteUri, role, ofObjectToReturn); + + // An earlier implementation of this type overrode this method, so we'll continue to do so + // to preserve binary compatibility. + public override Uri ResolveUri(Uri? baseUri, string? relativeUri) => base.ResolveUri(baseUri, relativeUri); } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XmlSecureResolverAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/XmlSecureResolverAsync.cs deleted file mode 100644 index 59ffe4bcb36254..00000000000000 --- a/src/libraries/System.Private.Xml/src/System/Xml/XmlSecureResolverAsync.cs +++ /dev/null @@ -1,15 +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.Threading.Tasks; - -namespace System.Xml -{ - public partial class XmlSecureResolver : XmlResolver - { - public override Task GetEntityAsync(Uri absoluteUri, string? role, Type? ofObjectToReturn) - { - return _resolver.GetEntityAsync(absoluteUri, role, ofObjectToReturn); - } - } -} diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XmlILCommand.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XmlILCommand.cs index 53ffbdd9fb82da..5d646eb6c6f522 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XmlILCommand.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XmlILCommand.cs @@ -83,7 +83,7 @@ private void Execute(object defaultDocument, XmlResolver? dataSources, XsltArgum Debug.Assert(results != null); // Ensure that dataSources is always non-null - dataSources ??= XmlNullResolver.Singleton; + dataSources ??= XmlResolver.ThrowingResolver; _delExec(new XmlQueryRuntime(_staticData, defaultDocument, dataSources, argumentList, results)); } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Compiler.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Compiler.cs index 8b637b57b773b5..d5f44fe82342d4 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Compiler.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Compiler.cs @@ -70,12 +70,12 @@ public Compiler(XsltSettings settings, bool debug, string? scriptAssemblyPath) Scripts = new Scripts(this); } - public CompilerErrorCollection Compile(object stylesheet, XmlResolver? xmlResolver, out QilExpression qil) + public CompilerErrorCollection Compile(object stylesheet, XmlResolver? xmlResolver, XmlResolver? origResolver, out QilExpression qil) { Debug.Assert(stylesheet != null); Debug.Assert(Root == null, "Compiler cannot be reused"); - new XsltLoader().Load(this, stylesheet, xmlResolver); + new XsltLoader().Load(this, stylesheet, xmlResolver, origResolver); qil = QilGenerator.CompileStylesheet(this); SortErrors(); return CompilerErrorColl; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XsltLoader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XsltLoader.cs index a77d24a3c1d475..a0b2297a9bcd9e 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XsltLoader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XsltLoader.cs @@ -38,11 +38,11 @@ internal sealed class XsltLoader : IErrorHelper public static int V2Opt = 4; public static int V2Req = 8; - public void Load(Compiler compiler, object stylesheet, XmlResolver? xmlResolver) + public void Load(Compiler compiler, object stylesheet, XmlResolver? xmlResolver, XmlResolver? origResolver) { Debug.Assert(compiler != null); _compiler = compiler; - _xmlResolver = xmlResolver ?? XmlNullResolver.Singleton; + _xmlResolver = xmlResolver ?? XmlResolver.ThrowingResolver; XmlReader? reader = stylesheet as XmlReader; if (reader != null) @@ -57,10 +57,21 @@ public void Load(Compiler compiler, object stylesheet, XmlResolver? xmlResolver) string? uri = stylesheet as string; if (uri != null) { - // If xmlResolver == null, then the original uri will be resolved using XmlUrlResolver - XmlResolver origResolver = xmlResolver!; - if (xmlResolver == null || xmlResolver == XmlNullResolver.Singleton) - origResolver = new XmlUrlResolver(); + // If the stylesheet has been provided as a string (URI, really), then we'll bounce + // through an XmlResolver to look up its contents. There's a complication here since + // the default resolver provided by our caller is likely a throwing resolver, and + // a throwing resolver would fail even when attempting to read this URL. We need + // to at minimum allow this URL to be read, because the user after all did explicitly + // ask us to do so. + // + // In this case, we'll rely on the 'origResolver' argument, which is the XmlResolver + // which was provided *to our caller* before any default substitution took place. + // If an explicit resolver was specified, we'll honor it. Otherwise we'll substitute + // an XmlUrlResolver for this one read operation. The stored resolver (which is used + // for reads beyond the initial stylesheet read) will use a throwing resolver as its + // default, as shown at the very top of this method. + + origResolver ??= new XmlUrlResolver(); Uri resolvedUri = origResolver.ResolveUri(null, uri); if (resolvedUri == null) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/Processor.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/Processor.cs index b467573b7e17cb..32af222ca8b7c9 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/Processor.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/Processor.cs @@ -345,7 +345,7 @@ public Processor( _builder = null; _actionStack = new HWStack(StackIncrement); _output = _rootAction.Output; - _resolver = resolver ?? XmlNullResolver.Singleton; + _resolver = resolver ?? XmlResolver.ThrowingResolver; _args = args ?? new XsltArgumentList(); _debugger = debugger; if (_debugger != null) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xslt/XslCompiledTransform.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xslt/XslCompiledTransform.cs index 7254705d6eed63..4a734c289cb94b 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xslt/XslCompiledTransform.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xslt/XslCompiledTransform.cs @@ -82,7 +82,7 @@ private void Reset() public void Load(XmlReader stylesheet) { Reset(); - LoadInternal(stylesheet, XsltSettings.Default, CreateDefaultResolver()); + LoadInternal(stylesheet, XsltSettings.Default, CreateDefaultResolver(), originalStylesheetResolver: null); } // SxS: This method does not take any resource name and does not expose any resources to the caller. @@ -90,7 +90,7 @@ public void Load(XmlReader stylesheet) public void Load(XmlReader stylesheet, XsltSettings? settings, XmlResolver? stylesheetResolver) { Reset(); - LoadInternal(stylesheet, settings, stylesheetResolver); + LoadInternal(stylesheet, settings, stylesheetResolver, stylesheetResolver); } // SxS: This method does not take any resource name and does not expose any resources to the caller. @@ -98,7 +98,7 @@ public void Load(XmlReader stylesheet, XsltSettings? settings, XmlResolver? styl public void Load(IXPathNavigable stylesheet) { Reset(); - LoadInternal(stylesheet, XsltSettings.Default, CreateDefaultResolver()); + LoadInternal(stylesheet, XsltSettings.Default, CreateDefaultResolver(), originalStylesheetResolver: null); } // SxS: This method does not take any resource name and does not expose any resources to the caller. @@ -106,29 +106,32 @@ public void Load(IXPathNavigable stylesheet) public void Load(IXPathNavigable stylesheet, XsltSettings? settings, XmlResolver? stylesheetResolver) { Reset(); - LoadInternal(stylesheet, settings, stylesheetResolver); + LoadInternal(stylesheet, settings, stylesheetResolver, stylesheetResolver); } public void Load(string stylesheetUri) { Reset(); ArgumentNullException.ThrowIfNull(stylesheetUri); - LoadInternal(stylesheetUri, XsltSettings.Default, CreateDefaultResolver()); + LoadInternal(stylesheetUri, XsltSettings.Default, CreateDefaultResolver(), originalStylesheetResolver: null); } public void Load(string stylesheetUri, XsltSettings? settings, XmlResolver? stylesheetResolver) { Reset(); ArgumentNullException.ThrowIfNull(stylesheetUri); - LoadInternal(stylesheetUri, settings, stylesheetResolver); + LoadInternal(stylesheetUri, settings, stylesheetResolver, stylesheetResolver); } - private void LoadInternal(object stylesheet, XsltSettings? settings, XmlResolver? stylesheetResolver) + // The 'originalStylesheetResolver' argument should be the original XmlResolver + // that was passed to the caller (or null), *before* any default substitutions + // were made by the caller. + private void LoadInternal(object stylesheet, XsltSettings? settings, XmlResolver? stylesheetResolver, XmlResolver? originalStylesheetResolver) { ArgumentNullException.ThrowIfNull(stylesheet); settings ??= XsltSettings.Default; - CompileXsltToQil(stylesheet, settings, stylesheetResolver); + CompileXsltToQil(stylesheet, settings, stylesheetResolver, originalStylesheetResolver); CompilerError? error = GetFirstError(); if (error != null) { @@ -142,9 +145,9 @@ private void LoadInternal(object stylesheet, XsltSettings? settings, XmlResolver [MemberNotNull(nameof(_compilerErrorColl))] [MemberNotNull(nameof(_qil))] - private void CompileXsltToQil(object stylesheet, XsltSettings settings, XmlResolver? stylesheetResolver) + private void CompileXsltToQil(object stylesheet, XsltSettings settings, XmlResolver? stylesheetResolver, XmlResolver? originalStylesheetResolver) { - _compilerErrorColl = new Compiler(settings, _enableDebug, null).Compile(stylesheet, stylesheetResolver, out _qil); + _compilerErrorColl = new Compiler(settings, _enableDebug, null).Compile(stylesheet, stylesheetResolver, originalStylesheetResolver, out _qil); } /// @@ -405,7 +408,7 @@ private static XmlResolver CreateDefaultResolver() return new XmlUrlResolver(); } - return XmlNullResolver.Singleton; + return XmlResolver.ThrowingResolver; } } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xslt/XslTransform.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xslt/XslTransform.cs index fbc9448619fdb1..15826ec1d9c4fb 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xslt/XslTransform.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xslt/XslTransform.cs @@ -246,7 +246,7 @@ private void Compile(XPathNavigator stylesheet, XmlResolver? resolver) Compiler compiler = new Compiler(); NavigatorInput input = new NavigatorInput(stylesheet); - compiler.Compile(input, resolver ?? XmlNullResolver.Singleton); + compiler.Compile(input, resolver ?? XmlResolver.ThrowingResolver); Debug.Assert(compiler.CompiledStylesheet != null); Debug.Assert(compiler.QueryStore != null); @@ -264,7 +264,7 @@ private static XmlResolver CreateDefaultResolver() } else { - return XmlNullResolver.Singleton; + return XmlResolver.ThrowingResolver; } } } diff --git a/src/libraries/System.Private.Xml/tests/Misc/System.Xml.Misc.Tests.csproj b/src/libraries/System.Private.Xml/tests/Misc/System.Xml.Misc.Tests.csproj index 7c7c909e2fe01e..7e365510221057 100644 --- a/src/libraries/System.Private.Xml/tests/Misc/System.Xml.Misc.Tests.csproj +++ b/src/libraries/System.Private.Xml/tests/Misc/System.Xml.Misc.Tests.csproj @@ -4,6 +4,8 @@ $(NetCoreAppCurrent) + + diff --git a/src/libraries/System.Private.Xml/tests/Misc/XmlSecureResolverTests.cs b/src/libraries/System.Private.Xml/tests/Misc/XmlSecureResolverTests.cs new file mode 100644 index 00000000000000..64dbd4b8decca5 --- /dev/null +++ b/src/libraries/System.Private.Xml/tests/Misc/XmlSecureResolverTests.cs @@ -0,0 +1,88 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using System.Net; +using System.Reflection; +using System.Threading.Tasks; +using Xunit; + +#pragma warning disable SYSLIB0047 // XmlSecureResolver is obsolete + +namespace System.Xml.Tests +{ + public class XmlSecureResolverTests + { + [Fact] + public void GetEntity_ThrowsXmlException() + { + PoisonedXmlResolver innerResolver = new PoisonedXmlResolver(); + XmlSecureResolver outerResolver = new XmlSecureResolver(innerResolver, "some-url"); + Uri absoluteUri = new Uri("https://dot.net/"); + Type typeToReturn = typeof(Stream); + + Assert.Throws(() => outerResolver.GetEntity(absoluteUri, "role", typeToReturn)); + Assert.False(innerResolver.WasAnyApiInvoked); + } + + [Fact] + public void GetEntityAsync_ThrowsXmlException() + { + PoisonedXmlResolver innerResolver = new PoisonedXmlResolver(); + XmlSecureResolver outerResolver = new XmlSecureResolver(innerResolver, "some-url"); + Uri absoluteUri = new Uri("https://dot.net/"); + Type typeToReturn = typeof(Stream); + + Assert.Throws(() => (object)outerResolver.GetEntityAsync(absoluteUri, "role", typeToReturn)); + Assert.False(innerResolver.WasAnyApiInvoked); + } + + [Fact] + public void Instance_HasNoState() + { + // This is a safety check to ensure we're not keeping the inner resolver in an instance field, + // since we don't want to risk invoking it. + + FieldInfo[] allDeclaredInstanceFields = typeof(XmlSecureResolver).GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + Assert.Empty(allDeclaredInstanceFields); + } + + private sealed class PoisonedXmlResolver : XmlResolver + { + public bool WasAnyApiInvoked { get; private set; } + + public override ICredentials Credentials + { + set + { + WasAnyApiInvoked = true; + throw new NotImplementedException(); + } + } + + public override object? GetEntity(Uri absoluteUri, string? role, Type? ofObjectToReturn) + { + WasAnyApiInvoked = true; + throw new NotImplementedException(); + } + + public override Task GetEntityAsync(Uri absoluteUri, string? role, Type? ofObjectToReturn) + { + WasAnyApiInvoked = true; + throw new NotImplementedException(); + } + + public override Uri ResolveUri(Uri? baseUri, string? relativeUri) + { + WasAnyApiInvoked = true; + throw new NotImplementedException(); + } + + public override bool SupportsType(Uri absoluteUri, Type? type) + { + WasAnyApiInvoked = true; + throw new NotImplementedException(); + } + } + } +} diff --git a/src/libraries/System.Private.Xml/tests/Misc/XmlThrowingResolverTests.cs b/src/libraries/System.Private.Xml/tests/Misc/XmlThrowingResolverTests.cs new file mode 100644 index 00000000000000..9943057677aa3c --- /dev/null +++ b/src/libraries/System.Private.Xml/tests/Misc/XmlThrowingResolverTests.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using Xunit; + +namespace System.Xml.Tests +{ + public class XmlThrowingResolverTests + { + [Fact] + public void PropertyAccessor_ReturnsSingleton() + { + XmlResolver resolver1 = XmlResolver.ThrowingResolver; + Assert.NotNull(resolver1); + + XmlResolver resolver2 = XmlResolver.ThrowingResolver; + Assert.Same(resolver1, resolver2); + Assert.Equal(resolver1, resolver2); // default comparer should also say they're equal + } + + [Fact] + public void GetEntity_ThrowsXmlException() + { + XmlResolver resolver = XmlResolver.ThrowingResolver; + Uri absoluteUri = new Uri("https://dot.net/"); + Type typeToReturn = typeof(Stream); + + Assert.Throws(() => resolver.GetEntity(absoluteUri, "role", typeToReturn)); + } + + [Fact] + public void GetEntityAsync_ThrowsXmlException() + { + XmlResolver resolver = XmlResolver.ThrowingResolver; + Uri absoluteUri = new Uri("https://dot.net/"); + Type typeToReturn = typeof(Stream); + + Assert.Throws(() => (object)resolver.GetEntityAsync(absoluteUri, "role", typeToReturn)); + } + } +} diff --git a/src/libraries/System.Private.Xml/tests/XmlSchema/XmlSchemaValidatorApi/CXmlTestResolver.cs b/src/libraries/System.Private.Xml/tests/XmlSchema/XmlSchemaValidatorApi/CXmlTestResolver.cs index 1515ad5c5fc99b..55d32e737b533a 100644 --- a/src/libraries/System.Private.Xml/tests/XmlSchema/XmlSchemaValidatorApi/CXmlTestResolver.cs +++ b/src/libraries/System.Private.Xml/tests/XmlSchema/XmlSchemaValidatorApi/CXmlTestResolver.cs @@ -86,12 +86,6 @@ public CXmlTestResolver() _resolver = new XmlUrlResolver(); } - public CXmlTestResolver(string securityUri) - : base() - { - _resolver = new XmlSecureResolver(new XmlUrlResolver(), securityUri); - } - // ----------------- // Events // ----------------- diff --git a/src/libraries/System.Private.Xml/tests/Xslt/TestFiles/TestData/XsltApi/Exceptions.xml b/src/libraries/System.Private.Xml/tests/Xslt/TestFiles/TestData/XsltApi/Exceptions.xml index 442eb4ef71281a..64539830495278 100644 --- a/src/libraries/System.Private.Xml/tests/Xslt/TestFiles/TestData/XsltApi/Exceptions.xml +++ b/src/libraries/System.Private.Xml/tests/Xslt/TestFiles/TestData/XsltApi/Exceptions.xml @@ -98,6 +98,6 @@ - + \ No newline at end of file diff --git a/src/libraries/System.Private.Xml/tests/Xslt/TestFiles/TestData/XsltApiV2/Exceptions.xml b/src/libraries/System.Private.Xml/tests/Xslt/TestFiles/TestData/XsltApiV2/Exceptions.xml index ffc3a7b8a7c2ed..7978a96ab9a666 100644 --- a/src/libraries/System.Private.Xml/tests/Xslt/TestFiles/TestData/XsltApiV2/Exceptions.xml +++ b/src/libraries/System.Private.Xml/tests/Xslt/TestFiles/TestData/XsltApiV2/Exceptions.xml @@ -101,6 +101,6 @@ - + \ No newline at end of file diff --git a/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompiledTransform.cs b/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompiledTransform.cs index c62332a5cc7668..89b19b00a0fb55 100644 --- a/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompiledTransform.cs +++ b/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompiledTransform.cs @@ -5,6 +5,7 @@ using Xunit.Abstractions; using System; using System.Collections; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; @@ -1486,6 +1487,35 @@ public void LoadUrlResolver3(XslInputType xslInputType, ReaderType readerType) _output.WriteLine("Passing null stylesheet parameter should have thrown ArgumentNullException"); Assert.True(false); } + + //[Variation("Call Load with custom resolver; custom resolver should be honored.")] + [InlineData(XslInputType.URI, ReaderType.XmlValidatingReader)] + [Theory] + public void LoadUrlResolver4(XslInputType xslInputType, ReaderType readerType) + { + var auditingResolver = new XmlAuditingUrlResolver(); + LoadXSL_Resolver(Path.Combine("XmlResolver", "XmlResolverTestMain.xsl"), xslInputType, readerType, auditingResolver); + + HashSet expected = new() + { + new Uri(Path.Combine(Environment.CurrentDirectory, FullFilePath(Path.Combine("XmlResolver", "XmlResolverTestMain.xsl")))), + new Uri(Path.Combine(Environment.CurrentDirectory, FullFilePath(Path.Combine("XmlResolver", "XmlResolverInclude.xsl")))), + new Uri(Path.Combine(Environment.CurrentDirectory, FullFilePath(Path.Combine("XmlResolver", "XmlResolverImport.xsl")))), + }; + + Assert.Equal(expected, auditingResolver.FetchedUris); + } + + private sealed class XmlAuditingUrlResolver : XmlUrlResolver + { + internal readonly HashSet FetchedUris = new(); + + public override object? GetEntity(Uri absoluteUri, string? role, Type? ofObjectToReturn) + { + FetchedUris.Add(absoluteUri); + return base.GetEntity(absoluteUri, role, ofObjectToReturn); + } + } } /***********************************************************/ diff --git a/src/libraries/System.Private.Xml/tests/Xslt/XslTransformApi/CXslTransform.cs b/src/libraries/System.Private.Xml/tests/Xslt/XslTransformApi/CXslTransform.cs index 1ec189b53e9504..5220c7d36932f3 100644 --- a/src/libraries/System.Private.Xml/tests/Xslt/XslTransformApi/CXslTransform.cs +++ b/src/libraries/System.Private.Xml/tests/Xslt/XslTransformApi/CXslTransform.cs @@ -1019,7 +1019,8 @@ public void LoadGeneric4(InputType inputType, ReaderType readerType) } catch (System.Xml.Xsl.XsltCompileException e) { - CheckExpectedError(e.InnerException, "System.Xml", "Xml_NullResolver", new string[] { "" }); + var absoluteUri = new Uri(Path.Combine(Environment.CurrentDirectory, FullFilePath("XmlResolver_Include.xsl"))).AbsoluteUri; + CheckExpectedError(e.InnerException, "System.Xml", "Xml_NullResolver", new string[] { absoluteUri }); return; } _output.WriteLine("Exception not thrown for null resolver"); @@ -1104,7 +1105,9 @@ public void TC_No_Explicit_Resolver_Prohibits_External_Url(InputType inputType, Assert.False(isEnabled); var e = Assert.Throws(() => LoadXSL("XmlResolver_Main.xsl", inputType, readerType)); var xmlException = Assert.IsType(e.InnerException); - CheckExpectedError(xmlException, "System.Xml", "Xml_NullResolver", Array.Empty()); + + var absoluteUri = new Uri(Path.Combine(Environment.CurrentDirectory, FullFilePath("XmlResolver_Include.xsl"))).AbsoluteUri; + CheckExpectedError(xmlException, "System.Xml", "Xml_NullResolver", new string[] { absoluteUri }); } //[Variation("Load with resolver with credentials, then load XSL that does not need cred.")] diff --git a/src/libraries/System.Security.Cryptography.Xml/src/Resources/Strings.resx b/src/libraries/System.Security.Cryptography.Xml/src/Resources/Strings.resx index 78697d97f96f91..88441504b6f1ef 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/Resources/Strings.resx +++ b/src/libraries/System.Security.Cryptography.Xml/src/Resources/Strings.resx @@ -393,4 +393,7 @@ Root element must be {0} element in namespace {1} - + + External entity resolution is not supported. + + \ No newline at end of file diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System.Security.Cryptography.Xml.csproj b/src/libraries/System.Security.Cryptography.Xml/src/System.Security.Cryptography.Xml.csproj index e0b9361891af88..20a3d77a4641f7 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System.Security.Cryptography.Xml.csproj +++ b/src/libraries/System.Security.Cryptography.Xml/src/System.Security.Cryptography.Xml.csproj @@ -1,4 +1,4 @@ - + $(NetCoreAppCurrent);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum) true @@ -126,6 +126,7 @@ System.Security.Cryptography.Xml.XmlLicenseTransform + diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/Reference.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/Reference.cs index 13295058f5ec41..05c7625170e5b5 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/Reference.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/Reference.cs @@ -359,7 +359,7 @@ internal byte[] CalculateHashValue(XmlDocument document, CanonicalXmlNodeList re { case ReferenceTargetType.Stream: // This is the easiest case. We already have a stream, so just pump it through the TransformChain - resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), baseUri)); + resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : XmlResolverHelper.GetThrowingResolver()); hashInputStream = TransformChain.TransformToOctetStream((Stream)_refTarget, resolver, baseUri); break; case ReferenceTargetType.UriReference: @@ -369,7 +369,7 @@ internal byte[] CalculateHashValue(XmlDocument document, CanonicalXmlNodeList re if (_uri == null) { // We need to create a DocumentNavigator out of the XmlElement - resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), baseUri)); + resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : XmlResolverHelper.GetThrowingResolver()); // In the case of a Uri-less reference, we will simply pass null to the transform chain. // The first transform in the chain is expected to know how to retrieve the data to hash. hashInputStream = TransformChain.TransformToOctetStream((Stream)null, resolver, baseUri); @@ -382,7 +382,7 @@ internal byte[] CalculateHashValue(XmlDocument document, CanonicalXmlNodeList re throw new CryptographicException(SR.Format(SR.Cryptography_Xml_SelfReferenceRequiresContext, _uri)); // Normalize the containing document - resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), baseUri)); + resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : XmlResolverHelper.GetThrowingResolver()); XmlDocument docWithNoComments = Utils.DiscardComments(Utils.PreProcessDocumentInput(document, resolver, baseUri)); hashInputStream = TransformChain.TransformToOctetStream(docWithNoComments, resolver, baseUri); } @@ -398,7 +398,7 @@ internal byte[] CalculateHashValue(XmlDocument document, CanonicalXmlNodeList re throw new CryptographicException(SR.Format(SR.Cryptography_Xml_SelfReferenceRequiresContext, _uri)); // We should not discard comments here!!! - resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), baseUri)); + resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : XmlResolverHelper.GetThrowingResolver()); hashInputStream = TransformChain.TransformToOctetStream(Utils.PreProcessDocumentInput(document, resolver, baseUri), resolver, baseUri); break; } @@ -434,7 +434,7 @@ internal byte[] CalculateHashValue(XmlDocument document, CanonicalXmlNodeList re // Add the propagated attributes Utils.AddNamespaces(normDocument.DocumentElement, _namespaces); - resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), baseUri)); + resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : XmlResolverHelper.GetThrowingResolver()); if (discardComments) { // We should discard comments before going into the transform chain @@ -454,7 +454,7 @@ internal byte[] CalculateHashValue(XmlDocument document, CanonicalXmlNodeList re break; case ReferenceTargetType.XmlElement: // We need to create a DocumentNavigator out of the XmlElement - resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), baseUri)); + resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : XmlResolverHelper.GetThrowingResolver()); hashInputStream = TransformChain.TransformToOctetStream(Utils.PreProcessElementInput((XmlElement)_refTarget, resolver, baseUri), resolver, baseUri); break; default: diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/SignedXml.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/SignedXml.cs index a717789c74222a..5d722d0f180f50 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/SignedXml.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/SignedXml.cs @@ -775,7 +775,7 @@ private byte[] GetC14NDigest(HashAlgorithm hash) if (isKeyedHashAlgorithm || !_bCacheValid || !SignedInfo.CacheValid) { string baseUri = _containingDocument?.BaseURI; - XmlResolver resolver = (_bResolverSet ? _xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), baseUri)); + XmlResolver resolver = (_bResolverSet ? _xmlResolver : XmlResolverHelper.GetThrowingResolver()); XmlDocument doc = Utils.PreProcessElementInput(SignedInfo.GetXml(), resolver, baseUri); // Add non default namespaces in scope diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlDecryptionTransform.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlDecryptionTransform.cs index fe3fceb39bada9..b31bf6a700365d 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlDecryptionTransform.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlDecryptionTransform.cs @@ -142,7 +142,7 @@ private void LoadStreamInput(Stream stream) { XmlDocument document = new XmlDocument(); document.PreserveWhitespace = true; - XmlResolver resolver = (ResolverSet ? _xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), BaseURI)); + XmlResolver resolver = (ResolverSet ? _xmlResolver : XmlResolverHelper.GetThrowingResolver()); XmlReader xmlReader = Utils.PreProcessStreamInput(stream, resolver, BaseURI); document.Load(xmlReader); _containingDocument = document; diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlDsigC14NTransform.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlDsigC14NTransform.cs index b27276b370cf97..d70778e8fcf370 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlDsigC14NTransform.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlDsigC14NTransform.cs @@ -47,7 +47,7 @@ protected override XmlNodeList GetInnerXml() public override void LoadInput(object obj) { - XmlResolver resolver = (ResolverSet ? _xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), BaseURI)); + XmlResolver resolver = (ResolverSet ? _xmlResolver : XmlResolverHelper.GetThrowingResolver()); if (obj is Stream) { _cXml = new CanonicalXml((Stream)obj, _includeComments, resolver, BaseURI); diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlDsigEnvelopedSignatureTransform.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlDsigEnvelopedSignatureTransform.cs index 996a9fb599791e..3aa5b2b5cbb067 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlDsigEnvelopedSignatureTransform.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlDsigEnvelopedSignatureTransform.cs @@ -79,7 +79,7 @@ private void LoadStreamInput(Stream stream) { XmlDocument doc = new XmlDocument(); doc.PreserveWhitespace = true; - XmlResolver resolver = (ResolverSet ? _xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), BaseURI)); + XmlResolver resolver = (ResolverSet ? _xmlResolver : XmlResolverHelper.GetThrowingResolver()); XmlReader xmlReader = Utils.PreProcessStreamInput(stream, resolver, BaseURI); doc.Load(xmlReader); _containingDocument = doc; diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlDsigExcC14NTransform.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlDsigExcC14NTransform.cs index dba65b391cecb8..e0007c207fc25b 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlDsigExcC14NTransform.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlDsigExcC14NTransform.cs @@ -74,7 +74,7 @@ public override void LoadInnerXml(XmlNodeList nodeList) public override void LoadInput(object obj) { - XmlResolver resolver = (ResolverSet ? _xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), BaseURI)); + XmlResolver resolver = (ResolverSet ? _xmlResolver : XmlResolverHelper.GetThrowingResolver()); if (obj is Stream) { _excCanonicalXml = new ExcCanonicalXml((Stream)obj, _includeComments, _inclusiveNamespacesPrefixList, resolver, BaseURI); diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlDsigXPathTransform.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlDsigXPathTransform.cs index 1e8d6e384dd2b3..bd5675c3640fae 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlDsigXPathTransform.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlDsigXPathTransform.cs @@ -133,7 +133,7 @@ public override void LoadInput(object obj) private void LoadStreamInput(Stream stream) { - XmlResolver resolver = (ResolverSet ? _xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), BaseURI)); + XmlResolver resolver = (ResolverSet ? _xmlResolver : XmlResolverHelper.GetThrowingResolver()); XmlReader valReader = Utils.PreProcessStreamInput(stream, resolver, BaseURI); _document = new XmlDocument(); _document.PreserveWhitespace = true; @@ -143,7 +143,7 @@ private void LoadStreamInput(Stream stream) private void LoadXmlNodeListInput(XmlNodeList nodeList) { // Use C14N to get a document - XmlResolver resolver = (ResolverSet ? _xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), BaseURI)); + XmlResolver resolver = (ResolverSet ? _xmlResolver : XmlResolverHelper.GetThrowingResolver()); CanonicalXml c14n = new CanonicalXml((XmlNodeList)nodeList, resolver, true); using (MemoryStream ms = new MemoryStream(c14n.GetBytes())) { diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlResolverHelper.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlResolverHelper.cs new file mode 100644 index 00000000000000..3070ffa8e1d233 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlResolverHelper.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Net; +using System.Threading.Tasks; +using System.Xml; + +namespace System.Security.Cryptography.Xml +{ + internal static class XmlResolverHelper + { + internal static XmlResolver GetThrowingResolver() + { +#if NET7_0_OR_GREATER + return XmlResolver.ThrowingResolver; +#else + return XmlThrowingResolver.s_singleton; +#endif + } + +#if !NET7_0_OR_GREATER + // An XmlResolver that forbids all external entity resolution. + // (Copied from XmlResolver.ThrowingResolver.cs.) + private sealed class XmlThrowingResolver : XmlResolver + { + internal static readonly XmlThrowingResolver s_singleton = new(); + + // Private constructor ensures existing only one instance of XmlThrowingResolver + private XmlThrowingResolver() { } + + public override ICredentials Credentials + { + set { /* Do nothing */ } + } + + public override object GetEntity(Uri absoluteUri, string? role, Type? ofObjectToReturn) + { + throw new XmlException(SR.Cryptography_Xml_EntityResolutionNotSupported); + } + + public override Task GetEntityAsync(Uri absoluteUri, string? role, Type? ofObjectToReturn) + { + throw new XmlException(SR.Cryptography_Xml_EntityResolutionNotSupported); + } + } +#endif + } +} diff --git a/src/libraries/System.Security.Cryptography.Xml/tests/SignedXmlTest.cs b/src/libraries/System.Security.Cryptography.Xml/tests/SignedXmlTest.cs index ac5d3560a32da9..54f00d1154c754 100644 --- a/src/libraries/System.Security.Cryptography.Xml/tests/SignedXmlTest.cs +++ b/src/libraries/System.Security.Cryptography.Xml/tests/SignedXmlTest.cs @@ -11,8 +11,11 @@ using System.Globalization; using System.IO; +using System.Net; using System.Security.Cryptography.X509Certificates; using System.Text; +using System.Threading; +using System.Threading.Tasks; using System.Xml; using System.Xml.XPath; using Test.Cryptography; @@ -1587,6 +1590,127 @@ public void VerifyHMAC_HMACOutputLength_Invalid() Assert.Throws(() => sign.CheckSignature(new HMACSHA1("no clue"u8.ToArray()))); } + [Theory] + [InlineData(false)] + [InlineData(true)] + public void VerifyXmlResolver(bool provideResolver) + { + HttpListener listener; + int port = 9000; + + while (true) + { + listener = new HttpListener(); + listener.Prefixes.Add($"http://127.0.0.1:{port}/"); + listener.IgnoreWriteExceptions = true; + + try + { + listener.Start(); + break; + } + catch + { + } + + port++; + + if (port > 10000) + { + throw new InvalidOperationException("Could not find an open port"); + } + } + + string xml = $@"]> +Example doc to be signed.&xxe; + + + + + + + + + CLUSJx4H4EwydAT/CtNWYu/l6R8uZe0tO2rlM/o0iM4= + + + o0IAVyovNUYKs5CCIRpZVy6noLpdJBp8LwWrqzzhKPg= + +"; + + bool listenerContacted = false; + CancellationTokenSource tokenSource = new CancellationTokenSource(); + Task listenerTask = ProcessRequests(listener, req => listenerContacted = true, tokenSource.Token); + + XmlDocument doc = new XmlDocument(); + doc.LoadXml(xml); + + SignedXml signedXml = new SignedXml(doc); + signedXml.LoadXml((XmlElement)doc.GetElementsByTagName("Signature")[0]); + + try + { + using (HMAC key = new HMACSHA256(Encoding.UTF8.GetBytes("sample"))) + { + if (provideResolver) + { + signedXml.Resolver = new XmlUrlResolver(); + Assert.True(signedXml.CheckSignature(key), "signedXml.CheckSignature(key)"); + Assert.True(listenerContacted, "listenerContacted"); + } + else + { + XmlException ex = Assert.Throws(() => signedXml.CheckSignature(key)); + Assert.False(listenerContacted, "listenerContacted"); + } + } + } + finally + { + tokenSource.Cancel(); + + try + { + listener.Stop(); + } + catch + { + } + + ((IDisposable)listener).Dispose(); + } + + static async Task ProcessRequests( + HttpListener listener, + Action requestReceived, + CancellationToken cancellationToken) + { + while (!cancellationToken.IsCancellationRequested) + { + HttpListenerContext ctx; + + try + { + ctx = await listener.GetContextAsync(); + } + catch + { + break; + } + + HttpListenerRequest req = ctx.Request; + requestReceived(req); + + using (HttpListenerResponse resp = ctx.Response) + { + resp.ContentType = "text/plain"; + resp.ContentEncoding = Encoding.UTF8; + resp.ContentLength64 = 0; + } + } + } + } + [Fact] public void CoreFxSignedXmlUsesSha256ByDefault() { diff --git a/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs b/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs index 676247bba16e79..0f7257bcebc699 100644 --- a/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs +++ b/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs @@ -948,11 +948,13 @@ public abstract partial class XmlResolver { protected XmlResolver() { } public virtual System.Net.ICredentials Credentials { set { } } + public static System.Xml.XmlResolver ThrowingResolver { get { throw null; } } public abstract object? GetEntity(System.Uri absoluteUri, string? role, System.Type? ofObjectToReturn); public virtual System.Threading.Tasks.Task GetEntityAsync(System.Uri absoluteUri, string? role, System.Type? ofObjectToReturn) { throw null; } public virtual System.Uri ResolveUri(System.Uri? baseUri, string? relativeUri) { throw null; } public virtual bool SupportsType(System.Uri absoluteUri, System.Type? type) { throw null; } } + [System.ObsoleteAttribute("XmlSecureResolver is obsolete. Use XmlResolver.ThrowingResolver instead when attempting to forbid XML external entity resolution.", DiagnosticId = "SYSLIB0047", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public partial class XmlSecureResolver : System.Xml.XmlResolver { public XmlSecureResolver(System.Xml.XmlResolver resolver, string? securityUrl) { } From 95530699dcd9199120352965f3b5c89915c58adf Mon Sep 17 00:00:00 2001 From: Drew Kersnar <18474647+dakersnar@users.noreply.github.com> Date: Fri, 12 Aug 2022 14:56:59 -0500 Subject: [PATCH 35/56] Fix BigInteger.Parse returning incorrect values for exponents above 1000 (#73643) * Fixing problem with invalid parse scientific form of numbers. (#17296) * Preventing OverflowException when parsing scientific form of numbers. (#17296) * Suggestions from review * Added bigger test cases * Adjusted cutoff to 9 digits * Accidental commit Co-authored-by: Maksim Golev --- .../Globalization/FormatProvider.Number.cs | 17 ++++-- .../src/System/Numerics/BigInteger.cs | 4 +- .../src/System/Numerics/BigNumber.cs | 60 +++++++++++++------ .../BigInteger/BigIntegerToStringTests.cs | 36 +++++++++++ 4 files changed, 94 insertions(+), 23 deletions(-) diff --git a/src/libraries/Common/src/System/Globalization/FormatProvider.Number.cs b/src/libraries/Common/src/System/Globalization/FormatProvider.Number.cs index 0e11b9b186d42a..688e587609dee2 100644 --- a/src/libraries/Common/src/System/Globalization/FormatProvider.Number.cs +++ b/src/libraries/Common/src/System/Globalization/FormatProvider.Number.cs @@ -515,17 +515,26 @@ private static unsafe bool ParseNumber(ref char* str, char* strEnd, NumberStyles int exp = 0; do { - exp = exp * 10 + (ch - '0'); - ch = ++p < strEnd ? *p : '\0'; - if (exp > 1000) + // Check if we are about to overflow past our limit of 9 digits + if (exp >= 100_000_000) { - exp = 9999; + // Set exp to Int.MaxValue to signify the requested exponent is too large. This will lead to an OverflowException later. + exp = int.MaxValue; + number.scale = 0; + + // Finish parsing the number, a FormatException could still occur later on. while (char.IsAsciiDigit(ch)) { ch = ++p < strEnd ? *p : '\0'; } + break; } + + exp = exp * 10 + (ch - '0'); + ch = ++p < strEnd ? *p : '\0'; + } while (char.IsAsciiDigit(ch)); + if (negExp) { exp = -exp; diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 6f348d3803785a..4956fac177a403 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -684,7 +684,7 @@ public static bool TryParse([NotNullWhen(true)] string? value, out BigInteger re public static bool TryParse([NotNullWhen(true)] string? value, NumberStyles style, IFormatProvider? provider, out BigInteger result) { - return BigNumber.TryParseBigInteger(value, style, NumberFormatInfo.GetInstance(provider), out result); + return BigNumber.TryParseBigInteger(value, style, NumberFormatInfo.GetInstance(provider), out result) == BigNumber.ParsingStatus.OK; } public static BigInteger Parse(ReadOnlySpan value, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) @@ -699,7 +699,7 @@ public static bool TryParse(ReadOnlySpan value, out BigInteger result) public static bool TryParse(ReadOnlySpan value, NumberStyles style, IFormatProvider? provider, out BigInteger result) { - return BigNumber.TryParseBigInteger(value, style, NumberFormatInfo.GetInstance(provider), out result); + return BigNumber.TryParseBigInteger(value, style, NumberFormatInfo.GetInstance(provider), out result) == BigNumber.ParsingStatus.OK; } public static int Compare(BigInteger left, BigInteger right) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs index ff6fbca60184d9..83fad3f14b8894 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs @@ -288,6 +288,22 @@ internal static class BigNumber | NumberStyles.AllowCurrencySymbol | NumberStyles.AllowHexSpecifier); private static readonly uint[] s_uint32PowersOfTen = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + internal enum ParsingStatus + { + OK, + Failed, + Overflow + } + + [DoesNotReturn] + internal static void ThrowOverflowOrFormatException(ParsingStatus status) => throw GetException(status); + + private static Exception GetException(ParsingStatus status) + { + return status == ParsingStatus.Failed + ? new FormatException(SR.Overflow_ParseBigInteger) + : new OverflowException(SR.Overflow_ParseBigInteger); + } private struct BigNumberBuffer { @@ -325,18 +341,18 @@ internal static bool TryValidateParseStyleInteger(NumberStyles style, [NotNullWh return true; } - internal static bool TryParseBigInteger(string? value, NumberStyles style, NumberFormatInfo info, out BigInteger result) + internal static ParsingStatus TryParseBigInteger(string? value, NumberStyles style, NumberFormatInfo info, out BigInteger result) { if (value == null) { result = default; - return false; + return ParsingStatus.Failed; } return TryParseBigInteger(value.AsSpan(), style, info, out result); } - internal static bool TryParseBigInteger(ReadOnlySpan value, NumberStyles style, NumberFormatInfo info, out BigInteger result) + internal static ParsingStatus TryParseBigInteger(ReadOnlySpan value, NumberStyles style, NumberFormatInfo info, out BigInteger result) { if (!TryValidateParseStyleInteger(style, out ArgumentException? e)) { @@ -347,7 +363,7 @@ internal static bool TryParseBigInteger(ReadOnlySpan value, NumberStyles s if (!FormatProvider.TryStringToBigInteger(value, style, info, bigNumber.digits, out bigNumber.precision, out bigNumber.scale, out bigNumber.sign)) { result = default; - return false; + return ParsingStatus.Failed; } if ((style & NumberStyles.AllowHexSpecifier) != 0) @@ -373,19 +389,22 @@ internal static BigInteger ParseBigInteger(ReadOnlySpan value, NumberStyle { throw e; } - if (!TryParseBigInteger(value, style, info, out BigInteger result)) + + ParsingStatus status = TryParseBigInteger(value, style, info, out BigInteger result); + if (status != ParsingStatus.OK) { - throw new FormatException(SR.Overflow_ParseBigInteger); + ThrowOverflowOrFormatException(status); } + return result; } - private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out BigInteger result) + private static ParsingStatus HexNumberToBigInteger(ref BigNumberBuffer number, out BigInteger result) { if (number.digits == null || number.digits.Length == 0) { result = default; - return false; + return ParsingStatus.Failed; } const int DigitsPerBlock = 8; @@ -480,7 +499,7 @@ private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out BigInt } result = new BigInteger(sign, bits); - return true; + return ParsingStatus.OK; } finally { @@ -499,7 +518,7 @@ private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out BigInt // a divide-and-conquer algorithm with a running time of O(NlogN). // private static int s_naiveThreshold = 20000; - private static bool NumberToBigInteger(ref BigNumberBuffer number, out BigInteger result) + private static ParsingStatus NumberToBigInteger(ref BigNumberBuffer number, out BigInteger result) { int currentBufferSize = 0; @@ -510,10 +529,17 @@ private static bool NumberToBigInteger(ref BigNumberBuffer number, out BigIntege const uint TenPowMaxPartial = 1000000000; int[]? arrayFromPoolForResultBuffer = null; + + if (numberScale == int.MaxValue) + { + result = default; + return ParsingStatus.Overflow; + } + if (numberScale < 0) { result = default; - return false; + return ParsingStatus.Failed; } try @@ -535,7 +561,7 @@ private static bool NumberToBigInteger(ref BigNumberBuffer number, out BigIntege } } - bool Naive(ref BigNumberBuffer number, out BigInteger result) + ParsingStatus Naive(ref BigNumberBuffer number, out BigInteger result) { Span stackBuffer = stackalloc uint[BigIntegerCalculator.StackAllocThreshold]; Span currentBuffer = stackBuffer; @@ -547,7 +573,7 @@ bool Naive(ref BigNumberBuffer number, out BigInteger result) if (!ProcessChunk(digitsChunk.Span, ref currentBuffer)) { result = default; - return false; + return ParsingStatus.Failed; } } @@ -557,7 +583,7 @@ bool Naive(ref BigNumberBuffer number, out BigInteger result) } result = NumberBufferToBigInteger(currentBuffer, number.sign); - return true; + return ParsingStatus.OK; bool ProcessChunk(ReadOnlySpan chunkDigits, ref Span currentBuffer) { @@ -619,7 +645,7 @@ bool ProcessChunk(ReadOnlySpan chunkDigits, ref Span currentBuffer) } } - bool DivideAndConquer(ref BigNumberBuffer number, out BigInteger result) + ParsingStatus DivideAndConquer(ref BigNumberBuffer number, out BigInteger result) { Span currentBuffer; int[]? arrayFromPoolForMultiplier = null; @@ -674,7 +700,7 @@ bool DivideAndConquer(ref BigNumberBuffer number, out BigInteger result) if (digitChar != '0') { result = default; - return false; + return ParsingStatus.Failed; } } } @@ -776,7 +802,7 @@ bool DivideAndConquer(ref BigNumberBuffer number, out BigInteger result) ArrayPool.Shared.Return(arrayFromPoolForMultiplier); } } - return true; + return ParsingStatus.OK; } BigInteger NumberBufferToBigInteger(Span currentBuffer, bool signa) diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/BigIntegerToStringTests.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/BigIntegerToStringTests.cs index 78b47e967784c0..f1f6071127fc51 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/BigIntegerToStringTests.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/BigIntegerToStringTests.cs @@ -439,6 +439,42 @@ public static void CustomFormatPerMille() RunCustomFormatToStringTests(s_random, "#\u2030000000", CultureInfo.CurrentCulture.NumberFormat.NegativeSign, 6, PerMilleSymbolFormatter); } + public static IEnumerable RunFormatScientificNotationToBigIntegerAndViceVersaData() + { + yield return new object[] { "1E+1000", "1E+1000" }; + yield return new object[] { "1E+1001", "1E+1001" }; + yield return new object[] { "1E+10001", "1E+10001" }; + yield return new object[] { "1E+100001", "1E+100001" }; + yield return new object[] { "1E+99999", "1E+99999" }; + } + + [Theory] + [MemberData(nameof(RunFormatScientificNotationToBigIntegerAndViceVersaData))] + public static void RunFormatScientificNotationToBigIntegerAndViceVersa(string testingValue, string expectedResult) + { + BigInteger parsedValue; + string actualResult; + + parsedValue = BigInteger.Parse(testingValue, NumberStyles.AllowExponent); + actualResult = parsedValue.ToString("E0"); + + Assert.Equal(expectedResult, actualResult); + } + + public static IEnumerable RunFormatScientificNotationToBigIntegerThrowsExceptionData() + { + yield return new object[] { "1E+1000000000" }; + yield return new object[] { "1E+2147483647" }; + yield return new object[] { "1E+21474836492" }; + } + + [Theory] + [MemberData(nameof(RunFormatScientificNotationToBigIntegerThrowsExceptionData))] + public static void RunFormatScientificNotationToBigIntegerThrowsException(string testingValue) + { + Assert.Throws(() => BigInteger.Parse(testingValue, NumberStyles.AllowExponent)); + } + [Fact] public static void ToString_InvalidFormat_ThrowsFormatException() { From 2e4824a92ed4ec21d6a946c5163179bb1384bdc8 Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Fri, 12 Aug 2022 23:02:41 +0300 Subject: [PATCH 36/56] Remove executable bits from generated shared libs (#73686) --- .../BuildIntegration/Microsoft.NETCore.Native.Unix.targets | 1 + .../BuildIntegration/Microsoft.NETCore.Native.targets | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets index ad6195b98cda07..64122154d42eea 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets @@ -83,6 +83,7 @@ The .NET Foundation licenses this file to you under the MIT license. + diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets index 5ded3960f2795e..d314741a634586 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets @@ -342,6 +342,9 @@ The .NET Foundation licenses this file to you under the MIT license. + + + ")] + [InlineData("bar", "", "")] + [InlineData("bar", null, "")] + [InlineData("foo.bar", null, "")] + [InlineData("foo:bar", null, "")] + public static void ProcessingInstructionCanBeCreatedAndSerialized(string target, string? data, string expectedOutput) { var xmlDocument = new XmlDocument(); - var newNode = xmlDocument.CreateProcessingInstruction("bar", "foo"); + var newNode = xmlDocument.CreateProcessingInstruction(target, data); - Assert.Equal("", newNode.OuterXml); + Assert.Equal(expectedOutput, newNode.OuterXml); Assert.Equal(XmlNodeType.ProcessingInstruction, newNode.NodeType); } + + [Fact] + public static void NullTargetThrows() + { + var xmlDocument = new XmlDocument(); + Assert.Throws(() => xmlDocument.CreateProcessingInstruction(null, "anyData")); + } + + [Fact] + public static void EmptyTargetThrows() + { + var xmlDocument = new XmlDocument(); + Assert.Throws(() => xmlDocument.CreateProcessingInstruction("", "anyData")); + } } } diff --git a/src/libraries/System.Private.Xml/tests/XmlDocument/XmlNodeTests/ValueTests.cs b/src/libraries/System.Private.Xml/tests/XmlDocument/XmlNodeTests/ValueTests.cs index 40a18cbd810bef..f3f9072c56c8ae 100644 --- a/src/libraries/System.Private.Xml/tests/XmlDocument/XmlNodeTests/ValueTests.cs +++ b/src/libraries/System.Private.Xml/tests/XmlDocument/XmlNodeTests/ValueTests.cs @@ -137,5 +137,60 @@ public static void DocumentFragment() Assert.Null(node.Value); Assert.Throws(() => node.Value = "some value"); } + + [Fact] + public static void CreateProcessingInstructionTranslatesNullIntoEmptyString() + { + var xmlDocument = new XmlDocument(); + XmlProcessingInstruction pi = xmlDocument.CreateProcessingInstruction("test", null); + Assert.Equal(string.Empty, pi.Data); + Assert.Equal(string.Empty, pi.Value); + Assert.Equal(string.Empty, pi.InnerText); + } + + [Fact] + public static void SettingNullDataTranslatesIntoEmptyString() + { + var xmlDocument = new XmlDocument(); + XmlProcessingInstruction pi = xmlDocument.CreateProcessingInstruction("test", "foo"); + Assert.Equal("foo", pi.Data); + Assert.Equal("foo", pi.Value); + Assert.Equal("foo", pi.InnerText); + + pi.Data = null; + Assert.Equal(string.Empty, pi.Data); + Assert.Equal(string.Empty, pi.Value); + Assert.Equal(string.Empty, pi.InnerText); + } + + [Fact] + public static void SettingNullValueTranslatesIntoEmptyString() + { + var xmlDocument = new XmlDocument(); + XmlProcessingInstruction pi = xmlDocument.CreateProcessingInstruction("test", "foo"); + Assert.Equal("foo", pi.Data); + Assert.Equal("foo", pi.Value); + Assert.Equal("foo", pi.InnerText); + + pi.Value = null; + Assert.Equal(string.Empty, pi.Data); + Assert.Equal(string.Empty, pi.Value); + Assert.Equal(string.Empty, pi.InnerText); + } + + [Fact] + public static void SettingNullInnerTextTranslatesIntoEmptyString() + { + var xmlDocument = new XmlDocument(); + XmlProcessingInstruction pi = xmlDocument.CreateProcessingInstruction("test", "foo"); + Assert.Equal("foo", pi.Data); + Assert.Equal("foo", pi.Value); + Assert.Equal("foo", pi.InnerText); + + pi.InnerText = null; + Assert.Equal(string.Empty, pi.Data); + Assert.Equal(string.Empty, pi.Value); + Assert.Equal(string.Empty, pi.InnerText); + } } } diff --git a/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs b/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs index 0f7257bcebc699..126e319d3d446d 100644 --- a/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs +++ b/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs @@ -343,7 +343,7 @@ public event System.Xml.XmlNodeChangedEventHandler NodeRemoving { add { } remove public virtual System.Xml.XmlNode CreateNode(string nodeTypeString, string name, string? namespaceURI) { throw null; } public virtual System.Xml.XmlNode CreateNode(System.Xml.XmlNodeType type, string name, string? namespaceURI) { throw null; } public virtual System.Xml.XmlNode CreateNode(System.Xml.XmlNodeType type, string? prefix, string name, string? namespaceURI) { throw null; } - public virtual System.Xml.XmlProcessingInstruction CreateProcessingInstruction(string target, string data) { throw null; } + public virtual System.Xml.XmlProcessingInstruction CreateProcessingInstruction(string target, string? data) { throw null; } public virtual System.Xml.XmlSignificantWhitespace CreateSignificantWhitespace(string? text) { throw null; } public virtual System.Xml.XmlText CreateTextNode(string? text) { throw null; } public virtual System.Xml.XmlWhitespace CreateWhitespace(string? text) { throw null; } @@ -738,13 +738,15 @@ public XmlParserContext(System.Xml.XmlNameTable? nt, System.Xml.XmlNamespaceMana } public partial class XmlProcessingInstruction : System.Xml.XmlLinkedNode { - protected internal XmlProcessingInstruction(string target, string data, System.Xml.XmlDocument doc) { } + protected internal XmlProcessingInstruction(string target, string? data, System.Xml.XmlDocument doc) { } + [System.Diagnostics.CodeAnalysis.AllowNullAttribute] public string Data { get { throw null; } set { } } + [System.Diagnostics.CodeAnalysis.AllowNullAttribute] public override string InnerText { get { throw null; } set { } } public override string LocalName { get { throw null; } } public override string Name { get { throw null; } } public override System.Xml.XmlNodeType NodeType { get { throw null; } } - public string? Target { get { throw null; } } + public string Target { get { throw null; } } [System.Diagnostics.CodeAnalysis.AllowNullAttribute] public override string Value { get { throw null; } set { } } public override System.Xml.XmlNode CloneNode(bool deep) { throw null; } From 3824cb2931453d7818c6fff0762cc6f1c536a147 Mon Sep 17 00:00:00 2001 From: Katya Sokolova Date: Fri, 12 Aug 2022 23:04:34 +0200 Subject: [PATCH 38/56] Handle web socket downgrade when HTTP/2 not enabled (#73843) * Handle downgrade when HTTP/2 not enabled * Update src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs Co-authored-by: Natalia Kondratyeva * feedback * Force 1.1 for non-secure web request if policy allows Co-authored-by: Natalia Kondratyeva --- .../Http/SocketsHttpHandler/ConnectHelper.cs | 9 +++- .../SocketsHttpHandler/HttpConnectionPool.cs | 15 +++--- .../Net/WebSockets/WebSocketHandle.Managed.cs | 5 +- .../tests/ClientWebSocketTestBase.cs | 5 ++ .../tests/ConnectTest.Http2.cs | 53 +++++++++++++++++++ 5 files changed, 77 insertions(+), 10 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs index 04c5bef5d95b79..d2a6f430a9b7c3 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs @@ -88,7 +88,14 @@ public static async ValueTask EstablishSslConnectionAsync(SslClientAu throw CancellationHelper.CreateOperationCanceledException(e, cancellationToken); } - throw new HttpRequestException(SR.net_http_ssl_connection_failed, e); + HttpRequestException ex = new HttpRequestException(SR.net_http_ssl_connection_failed, e); + if (request.IsExtendedConnectRequest) + { + // Extended connect request is negotiating strictly for ALPN = "h2" because HttpClient is unaware of a possible downgrade. + // At this point, SSL connection for HTTP / 2 failed, and the exception should indicate the reason for the external client / user. + ex.Data["HTTP2_ENABLED"] = false; + } + throw ex; } // Handle race condition if cancellation happens after SSL auth completes but before the registration is disposed diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs index 354b50b59668d0..e4afdd294fca3c 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs @@ -426,7 +426,13 @@ private static void ThrowGetVersionException(HttpRequestMessage request, int des { Debug.Assert(desiredVersion == 2 || desiredVersion == 3); - throw new HttpRequestException(SR.Format(SR.net_http_requested_version_cannot_establish, request.Version, request.VersionPolicy, desiredVersion), inner); + HttpRequestException ex = new HttpRequestException(SR.Format(SR.net_http_requested_version_cannot_establish, request.Version, request.VersionPolicy, desiredVersion), inner); + if (request.IsExtendedConnectRequest && desiredVersion == 2) + { + ex.Data["HTTP2_ENABLED"] = false; + } + + throw ex; } private bool CheckExpirationOnGet(HttpConnectionBase connection) @@ -1122,12 +1128,7 @@ public async ValueTask SendWithVersionDetectionAndRetryAsyn // Throw if fallback is not allowed by the version policy. if (request.VersionPolicy != HttpVersionPolicy.RequestVersionOrLower) { - HttpRequestException exception = new HttpRequestException(SR.Format(SR.net_http_requested_version_server_refused, request.Version, request.VersionPolicy), e); - if (request.IsExtendedConnectRequest) - { - exception.Data["HTTP2_ENABLED"] = false; - } - throw exception; + throw new HttpRequestException(SR.Format(SR.net_http_requested_version_server_refused, request.Version, request.VersionPolicy), e); } if (NetEventSource.Log.IsEnabled()) diff --git a/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs b/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs index 29ddf98cafeb72..6bb365012ef779 100644 --- a/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs +++ b/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs @@ -53,7 +53,8 @@ public async Task ConnectAsync(Uri uri, HttpMessageInvoker? invoker, Cancellatio bool disposeResponse = false; - bool tryDowngrade = false; + // force non-secure request to 1.1 whenever it is possible as HttpClient does + bool tryDowngrade = uri.Scheme == UriScheme.Ws && (options.HttpVersion == HttpVersion.Version11 || options.HttpVersionPolicy == HttpVersionPolicy.RequestVersionOrLower); try { @@ -63,7 +64,7 @@ public async Task ConnectAsync(Uri uri, HttpMessageInvoker? invoker, Cancellatio { HttpRequestMessage request; if (!tryDowngrade && options.HttpVersion >= HttpVersion.Version20 - || (options.HttpVersion == HttpVersion.Version11 && options.HttpVersionPolicy == HttpVersionPolicy.RequestVersionOrHigher)) + || (options.HttpVersion == HttpVersion.Version11 && options.HttpVersionPolicy == HttpVersionPolicy.RequestVersionOrHigher && uri.Scheme == UriScheme.Wss)) { if (options.HttpVersion > HttpVersion.Version20 && options.HttpVersionPolicy != HttpVersionPolicy.RequestVersionOrLower) { diff --git a/src/libraries/System.Net.WebSockets.Client/tests/ClientWebSocketTestBase.cs b/src/libraries/System.Net.WebSockets.Client/tests/ClientWebSocketTestBase.cs index 203040e4ffc2dd..a32587bc862fc9 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/ClientWebSocketTestBase.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/ClientWebSocketTestBase.cs @@ -26,6 +26,11 @@ public class ClientWebSocketTestBase new object[] { o[0], false }, new object[] { o[0], true } }).ToArray(); + public static readonly object[][] SecureEchoServersAndBoolean = new object[][] + { + new object[] { Test.Common.Configuration.WebSockets.SecureRemoteEchoServer, false }, + new object[] { Test.Common.Configuration.WebSockets.SecureRemoteEchoServer, true } + }; public const int TimeOutMilliseconds = 30000; public const int CloseDescriptionMaxLength = 123; diff --git a/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.Http2.cs b/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.Http2.cs index aa14fd93ab2927..1ec2f1ee39fa45 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.Http2.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.Http2.cs @@ -96,6 +96,59 @@ await Http2LoopbackServer.CreateClientAndServerAsync(async uri => ); } + [OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))] + [Theory] + [MemberData(nameof(SecureEchoServersAndBoolean))] + [SkipOnPlatform(TestPlatforms.Browser, "System.Net.Sockets is not supported on this platform")] + public async Task ConnectAsync_Http11Server_DowngradeFail(Uri server, bool useHandler) + { + using (var cws = new ClientWebSocket()) + using (var cts = new CancellationTokenSource(TimeOutMilliseconds)) + { + cws.Options.HttpVersion = HttpVersion.Version20; + cws.Options.HttpVersionPolicy = Http.HttpVersionPolicy.RequestVersionExact; + Task t; + if (useHandler) + { + var handler = new SocketsHttpHandler(); + t = cws.ConnectAsync(server, new HttpMessageInvoker(handler), cts.Token); + } + else + { + t = cws.ConnectAsync(server, cts.Token); + } + var ex = await Assert.ThrowsAnyAsync(() => t); + Assert.IsType(ex.InnerException); + Assert.True(ex.InnerException.Data.Contains("HTTP2_ENABLED")); + Assert.Equal(WebSocketState.Closed, cws.State); + } + } + + [OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))] + [Theory] + [MemberData(nameof(EchoServersAndBoolean))] + [SkipOnPlatform(TestPlatforms.Browser, "System.Net.Sockets is not supported on this platform")] + public async Task ConnectAsync_Http11Server_DowngradeSuccess(Uri server, bool useHandler) + { + using (var cws = new ClientWebSocket()) + using (var cts = new CancellationTokenSource(TimeOutMilliseconds)) + { + cws.Options.HttpVersion = HttpVersion.Version20; + cws.Options.HttpVersionPolicy = Http.HttpVersionPolicy.RequestVersionOrLower; + if (useHandler) + { + var handler = new SocketsHttpHandler(); + await cws.ConnectAsync(server, new HttpMessageInvoker(handler), cts.Token); + } + else + { + await cws.ConnectAsync(server, cts.Token); + } + Assert.Equal(WebSocketState.Open, cws.State); + } + } + + [Theory] [InlineData(false)] [InlineData(true)] From c302b7b9af154d17d154dde4ad37e47bc3792ecf Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 12 Aug 2022 23:33:13 +0200 Subject: [PATCH 39/56] JIT: Fix STORE_DYN_BLK morphing (#73844) Make sure to properly call SetIndirExceptionFlags and to properly mark defs (that will otherwise hit asserts in rationalization). --- src/coreclr/jit/gentree.cpp | 2 ++ src/coreclr/jit/morphblock.cpp | 8 +++-- .../JitBlue/Runtime_73821/Runtime_73821.cs | 35 +++++++++++++++++++ .../Runtime_73821/Runtime_73821.csproj | 10 ++++++ 4 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_73821/Runtime_73821.cs create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_73821/Runtime_73821.csproj diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 5338baccb0147a..c43f4735533094 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -7541,6 +7541,8 @@ GenTree* Compiler::gtNewStructVal(ClassLayout* layout, GenTree* addr) blkNode = gtNewObjNode(layout, addr); } + blkNode->SetIndirExceptionFlags(this); + return blkNode; } diff --git a/src/coreclr/jit/morphblock.cpp b/src/coreclr/jit/morphblock.cpp index e5f454db081d95..a976ded54d7f83 100644 --- a/src/coreclr/jit/morphblock.cpp +++ b/src/coreclr/jit/morphblock.cpp @@ -1550,14 +1550,18 @@ GenTree* Compiler::fgMorphStoreDynBlock(GenTreeStoreDynBlk* tree) if (size != 0) { GenTree* lhs = gtNewBlockVal(tree->Addr(), static_cast(size)); - lhs->SetIndirExceptionFlags(this); - GenTree* asg = gtNewAssignNode(lhs, tree->Data()); asg->gtFlags |= (tree->gtFlags & (GTF_ALL_EFFECT | GTF_BLK_VOLATILE | GTF_BLK_UNALIGNED)); INDEBUG(asg->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); JITDUMP("MorphStoreDynBlock: transformed STORE_DYN_BLK into ASG(BLK, Data())\n"); + GenTree* lclVarTree = fgIsIndirOfAddrOfLocal(lhs); + if (lclVarTree != nullptr) + { + lclVarTree->gtFlags |= GTF_VAR_DEF; + } + return tree->OperIsCopyBlkOp() ? fgMorphCopyBlock(asg) : fgMorphInitBlock(asg); } } diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_73821/Runtime_73821.cs b/src/tests/JIT/Regression/JitBlue/Runtime_73821/Runtime_73821.cs new file mode 100644 index 00000000000000..540e8fae97eb7b --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_73821/Runtime_73821.cs @@ -0,0 +1,35 @@ +// 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; + +unsafe class Runtime_73821 +{ + public struct S + { + public int F; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static S Test1(int val) + { + S s; + int size = sizeof(S); + Unsafe.CopyBlockUnaligned(&s, &val, (uint)size); + return s; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int Test2(int val) + { + int val2; + int size = sizeof(int); + Unsafe.CopyBlockUnaligned(&val2, &val, (uint)size); + return val2; + } + + static int Main() + { + return Test1(33).F + Test2(67); + } +} diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_73821/Runtime_73821.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_73821/Runtime_73821.csproj new file mode 100644 index 00000000000000..cf94135633b19a --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_73821/Runtime_73821.csproj @@ -0,0 +1,10 @@ + + + Exe + True + true + + + + + \ No newline at end of file From c4ea2caaa63322da54e289f490ba3bb4820e9303 Mon Sep 17 00:00:00 2001 From: Tlakaelel Axayakatl Ceja Date: Fri, 12 Aug 2022 14:51:20 -0700 Subject: [PATCH 40/56] Add annotations to System.Private.DataContractSerialization (#73337) Add annotations to System.Private.DataContractSerialization --- ...m.Private.DataContractSerialization.csproj | 3 +- .../Runtime/Serialization/AccessorBuilder.cs | 5 ++ .../Serialization/ClassDataContract.cs | 27 ++++++++ .../Runtime/Serialization/CodeGenerator.cs | 2 + .../Serialization/CollectionDataContract.cs | 44 +++++++++++- .../Runtime/Serialization/DataContract.cs | 51 ++++++++++++++ .../Serialization/DataContractResolver.cs | 2 + .../Serialization/DataContractSerializer.cs | 28 ++++++++ .../Runtime/Serialization/DataContractSet.cs | 12 ++++ .../DataContractSurrogateCaller.cs | 3 + .../Runtime/Serialization/DataMember.cs | 4 ++ .../Runtime/Serialization/EnumDataContract.cs | 4 ++ .../GenericParameterDataContract.cs | 2 + .../System/Runtime/Serialization/Globals.cs | 2 + .../Json/DataContractJsonSerializer.cs | 38 +++++++++++ .../Json/JsonByteArrayDataContract.cs | 2 + .../Json/JsonClassDataContract.cs | 6 ++ .../Json/JsonCollectionDataContract.cs | 7 ++ .../Serialization/Json/JsonDataContract.cs | 10 +++ .../Json/JsonEnumDataContract.cs | 4 ++ .../Json/JsonFormatReaderGenerator.cs | 19 ++++++ .../Json/JsonFormatWriterGenerator.cs | 13 ++++ .../Json/JsonObjectDataContract.cs | 3 + .../Json/JsonQNameDataContract.cs | 2 + .../Json/JsonStringDataContract.cs | 2 + .../Serialization/Json/JsonUriDataContract.cs | 2 + .../Serialization/Json/JsonXmlDataContract.cs | 3 + .../Json/ReflectionJsonFormatReader.cs | 7 ++ .../Json/ReflectionJsonFormatWriter.cs | 4 ++ ...lObjectSerializerReadContextComplexJson.cs | 6 ++ ...ObjectSerializerWriteContextComplexJson.cs | 14 ++++ .../KnownTypeDataContractResolver.cs | 2 + .../Serialization/PrimitiveDataContract.cs | 68 +++++++++++++++++++ .../Serialization/ReflectionClassWriter.cs | 5 ++ .../Runtime/Serialization/ReflectionReader.cs | 19 ++++++ .../ReflectionXmlFormatReader.cs | 7 +- .../ReflectionXmlFormatWriter.cs | 6 +- .../Runtime/Serialization/SchemaExporter.cs | 9 +++ .../Serialization/SurrogateDataContract.cs | 4 ++ .../Serialization/XPathQueryGenerator.cs | 4 ++ .../Runtime/Serialization/XmlDataContract.cs | 8 +++ .../XmlFormatGeneratorStatics.cs | 2 + .../Serialization/XmlFormatReaderGenerator.cs | 20 ++++++ .../Serialization/XmlFormatWriterGenerator.cs | 14 ++++ .../Serialization/XmlObjectSerializer.cs | 34 ++++++++++ .../XmlObjectSerializerContext.cs | 17 +++++ .../XmlObjectSerializerReadContext.cs | 22 ++++++ .../XmlObjectSerializerReadContextComplex.cs | 9 +++ .../XmlObjectSerializerWriteContext.cs | 27 ++++++++ .../XmlObjectSerializerWriteContextComplex.cs | 8 +++ .../Serialization/XsdDataContractExporter.cs | 13 ++++ .../ref/System.Runtime.Serialization.Json.cs | 23 +++++++ .../ref/System.Runtime.Serialization.Xml.cs | 43 ++++++++++++ 53 files changed, 688 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System.Private.DataContractSerialization.csproj b/src/libraries/System.Private.DataContractSerialization/src/System.Private.DataContractSerialization.csproj index f8df7d3599de5e..b5fd2b74823358 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System.Private.DataContractSerialization.csproj +++ b/src/libraries/System.Private.DataContractSerialization/src/System.Private.DataContractSerialization.csproj @@ -1,10 +1,11 @@ - + $(NetCoreAppCurrent) $(NoWarn);1634;1691;649 true false + true diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/AccessorBuilder.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/AccessorBuilder.cs index e6af588406a6df..df00abecc6eed4 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/AccessorBuilder.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/AccessorBuilder.cs @@ -27,6 +27,7 @@ internal static class FastInvokerBuilder [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2060:MakeGenericMethod", Justification = "The call to MakeGenericMethod is safe due to the fact that we are preserving the constructors of type which is what Make() is doing.")] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static Func GetMakeNewInstanceFunc( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type) @@ -66,7 +67,9 @@ public static Getter CreateGetter(MemberInfo memberInfo) // Only JIT if dynamic code is supported. if (RuntimeFeature.IsDynamicCodeSupported || (!declaringType.IsValueType && !propertyType.IsValueType)) { +#pragma warning disable IL3050 // AOT compiling should recognize that this call is gated by RuntimeFeature.IsDynamicCodeSupported var createGetterGeneric = s_createGetterInternal.MakeGenericMethod(declaringType, propertyType).CreateDelegate>(); +#pragma warning restore IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling. return createGetterGeneric(propInfo); } else @@ -121,7 +124,9 @@ public static Setter CreateSetter(MemberInfo memberInfo) // Only JIT if dynamic code is supported. if (RuntimeFeature.IsDynamicCodeSupported || (!declaringType.IsValueType && !propertyType.IsValueType)) { +#pragma warning disable IL3050 // AOT compiling should recognize that this call is gated by RuntimeFeature.IsDynamicCodeSupported var createSetterGeneric = s_createSetterInternal.MakeGenericMethod(propInfo.DeclaringType!, propInfo.PropertyType).CreateDelegate>(); +#pragma warning restore IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling. return createSetterGeneric(propInfo); } else diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ClassDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ClassDataContract.cs index 1200e4ee07097c..f412ee7557ed19 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ClassDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ClassDataContract.cs @@ -41,12 +41,14 @@ internal sealed class ClassDataContract : DataContract DynamicallyAccessedMemberTypes.PublicProperties; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal ClassDataContract(Type type) : base(new ClassDataContractCriticalHelper(type)) { InitClassDataContract(); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private ClassDataContract(Type type, XmlDictionaryString ns, string[] memberNames) : base(new ClassDataContractCriticalHelper(type, ns, memberNames)) { InitClassDataContract(); @@ -75,6 +77,7 @@ internal List? Members public XmlDictionaryString?[]? ChildElementNamespaces { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_childElementNamespaces == null) @@ -133,6 +136,7 @@ internal MethodInfo? ExtensionDataSetMethod public override DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { return _helper.KnownDataContracts; } } @@ -204,6 +208,8 @@ internal MethodInfo? GetKeyValuePairMethodInfo } private Func? _makeNewInstance; + [UnconditionalSuppressMessage("AOT Analysis", "IL3050:RequiresDynamicCode", + Justification = "Cannot annotate a field, annotating the usage instead")] private Func MakeNewInstance => _makeNewInstance ??= FastInvokerBuilder.GetMakeNewInstanceFunc(UnderlyingType); internal bool CreateNewInstanceViaDefaultConstructor([NotNullWhen(true)] out object? obj) @@ -229,6 +235,7 @@ internal bool CreateNewInstanceViaDefaultConstructor([NotNullWhen(true)] out obj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private XmlFormatClassWriterDelegate CreateXmlFormatWriterDelegate() { return new XmlFormatWriterGenerator().GenerateClassWriter(this); @@ -237,6 +244,7 @@ private XmlFormatClassWriterDelegate CreateXmlFormatWriterDelegate() internal XmlFormatClassWriterDelegate XmlFormatWriterDelegate { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_helper.XmlFormatWriterDelegate == null) @@ -259,6 +267,7 @@ internal XmlFormatClassWriterDelegate XmlFormatWriterDelegate } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private XmlFormatClassReaderDelegate CreateXmlFormatReaderDelegate() { return new XmlFormatReaderGenerator().GenerateClassReader(this); @@ -267,6 +276,7 @@ private XmlFormatClassReaderDelegate CreateXmlFormatReaderDelegate() internal XmlFormatClassReaderDelegate XmlFormatReaderDelegate { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_helper.XmlFormatReaderDelegate == null) @@ -289,6 +299,7 @@ internal XmlFormatClassReaderDelegate XmlFormatReaderDelegate } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static ClassDataContract CreateClassDataContractForKeyValue(Type type, XmlDictionaryString ns, string[] memberNames) { ClassDataContract? cdc = (ClassDataContract?)DataContract.GetDataContractFromGeneratedAssembly(type); @@ -323,6 +334,7 @@ internal static void CheckAndAddMember(List members, DataMember memb } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static XmlDictionaryString? GetChildNamespaceToDeclare(DataContract dataContract, Type childType, XmlDictionary dictionary) { childType = DataContract.UnwrapNullableType(childType); @@ -446,6 +458,7 @@ internal static bool IsNonSerializedMember(Type type, string memberName) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private XmlDictionaryString?[]? CreateChildElementNamespaces() { if (Members == null) @@ -474,6 +487,7 @@ private void EnsureMethodsImported() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { Debug.Assert(context != null); @@ -486,6 +500,7 @@ public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, Xml } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) { if (_isScriptObject) @@ -693,6 +708,8 @@ private sealed class ClassDataContractCriticalHelper : DataContract.DataContract { private static Type[]? s_serInfoCtorArgs; private static readonly MethodInfo s_getKeyValuePairMethod = typeof(KeyValuePairAdapter<,>).GetMethod("GetKeyValuePair", Globals.ScanAllMembers)!; + [UnconditionalSuppressMessage("AOT Analysis", "IL3050:RequiresDynamicCode", + Justification = "Cannot annotate a field, annotating the method that uses the field instead")] private static readonly ConstructorInfo s_ctorGenericMethod = typeof(KeyValuePairAdapter<,>).GetConstructor(Globals.ScanAllMembers, new Type[] { typeof(KeyValuePair<,>).MakeGenericType(typeof(KeyValuePairAdapter<,>).GetGenericArguments()) })!; private ClassDataContract? _baseContract; @@ -725,6 +742,7 @@ private sealed class ClassDataContractCriticalHelper : DataContract.DataContract public XmlDictionaryString[]? MemberNamespaces; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal ClassDataContractCriticalHelper([DynamicallyAccessedMembers(DataContractPreserveMemberTypes)] Type type) : base(type) { @@ -839,6 +857,7 @@ internal ClassDataContractCriticalHelper([DynamicallyAccessedMembers(DataContrac } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal ClassDataContractCriticalHelper( [DynamicallyAccessedMembers(DataContractPreserveMemberTypes)] Type type, XmlDictionaryString ns, string[] memberNames) : base(type) @@ -915,6 +934,7 @@ private void EnsureIsReferenceImported(Type type) [MemberNotNull(nameof(_members))] [MemberNotNull(nameof(Members))] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ImportDataMembers() { Type type = this.UnderlyingType; @@ -1093,6 +1113,7 @@ private static bool CanSerializeMember(FieldInfo? field) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static bool SetIfGetOnlyCollection(DataMember memberContract) { //OK to call IsCollection here since the use of surrogated collection types is not supported in get-only scenarios @@ -1167,6 +1188,7 @@ private void SetIfMembersHaveConflict(List members) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private XmlQualifiedName GetStableNameAndSetHasDataContract(Type type) { return DataContract.GetStableName(type, out _hasDataContract); @@ -1337,6 +1359,7 @@ internal override DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_knownDataContracts != null) @@ -1386,6 +1409,7 @@ internal bool IsNonAttributedType } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void SetKeyValuePairAdapterFlags( [DynamicallyAccessedMembers(DataContractPreserveMemberTypes)] Type type) @@ -1510,6 +1534,7 @@ public int Compare(Member x, Member y) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal ClassDataContractCriticalHelper Clone() { ClassDataContractCriticalHelper clonedHelper = new ClassDataContractCriticalHelper(this.UnderlyingType); @@ -1572,6 +1597,7 @@ internal Type ObjectType } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal ClassDataContract Clone() { ClassDataContract clonedDc = new ClassDataContract(this.UnderlyingType); @@ -1586,6 +1612,7 @@ internal ClassDataContract Clone() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void UpdateNamespaceAndMembers(Type type, XmlDictionaryString ns, string[] memberNames) { this.StableName = new XmlQualifiedName(GetStableName(type).Name, ns.Value); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs index b39d1af3d439ff..f7d61607657414 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs @@ -121,6 +121,7 @@ internal void BeginMethod(DynamicMethod dynamicMethod, Type delegateType, string InitILGeneration(methodName, argTypes); } + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void BeginMethod(string methodName, Type delegateType, bool allowPrivateMemberAccess) { MethodInfo signature = JsonFormatWriterGenerator.GetInvokeMethod(delegateType); @@ -132,6 +133,7 @@ internal void BeginMethod(string methodName, Type delegateType, bool allowPrivat _delegateType = delegateType; } + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void BeginMethod(Type returnType, string methodName, Type[] argTypes, bool allowPrivateMemberAccess) { _dynamicMethod = new DynamicMethod(methodName, returnType, argTypes, SerializationModule, allowPrivateMemberAccess); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CollectionDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CollectionDataContract.cs index df57f68205cf2e..fde7dd4a8ef8d8 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CollectionDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CollectionDataContract.cs @@ -134,12 +134,14 @@ internal sealed class CollectionDataContract : DataContract private CollectionDataContractCriticalHelper _helper; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal CollectionDataContract(Type type) : base(new CollectionDataContractCriticalHelper(type)) { InitCollectionDataContract(this); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private CollectionDataContract(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, string? deserializationExceptionMessage) : base(new CollectionDataContractCriticalHelper(type, kind, itemType, getEnumeratorMethod, deserializationExceptionMessage)) { @@ -147,6 +149,7 @@ private CollectionDataContract(Type type, CollectionKind kind, Type itemType, Me } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private CollectionDataContract(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, MethodInfo? addMethod, ConstructorInfo? constructor) : base(new CollectionDataContractCriticalHelper(type, kind, itemType, getEnumeratorMethod, addMethod, constructor)) { @@ -154,6 +157,7 @@ private CollectionDataContract(Type type, CollectionKind kind, Type itemType, Me } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private CollectionDataContract(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, MethodInfo? addMethod, ConstructorInfo? constructor, bool isConstructorCheckRequired) : base(new CollectionDataContractCriticalHelper(type, kind, itemType, getEnumeratorMethod, addMethod, constructor, isConstructorCheckRequired)) { @@ -161,6 +165,7 @@ private CollectionDataContract(Type type, CollectionKind kind, Type itemType, Me } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private CollectionDataContract(Type type, string invalidCollectionInSharedContractMessage) : base(new CollectionDataContractCriticalHelper(type, invalidCollectionInSharedContractMessage)) { InitCollectionDataContract(GetSharedTypeContract(type)); @@ -169,6 +174,7 @@ private CollectionDataContract(Type type, string invalidCollectionInSharedContra [MemberNotNull(nameof(_helper))] [MemberNotNull(nameof(_collectionItemName))] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void InitCollectionDataContract(DataContract? sharedTypeContract) { _helper = (base.Helper as CollectionDataContractCriticalHelper)!; @@ -202,6 +208,7 @@ public Type ItemType public DataContract ItemContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { return _itemContract ?? _helper.ItemContract; @@ -261,6 +268,7 @@ internal bool IsDictionary public XmlDictionaryString? ChildElementNamespace { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_childElementNamespace == null) @@ -319,6 +327,7 @@ internal ConstructorInfo? Constructor public override DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { return _helper.KnownDataContracts; } @@ -343,6 +352,7 @@ internal bool IsReadOnlyContract } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private XmlFormatCollectionWriterDelegate CreateXmlFormatWriterDelegate() { return new XmlFormatWriterGenerator().GenerateCollectionWriter(this); @@ -351,6 +361,7 @@ private XmlFormatCollectionWriterDelegate CreateXmlFormatWriterDelegate() internal XmlFormatCollectionWriterDelegate XmlFormatWriterDelegate { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_helper.XmlFormatWriterDelegate == null) @@ -373,6 +384,7 @@ internal XmlFormatCollectionWriterDelegate XmlFormatWriterDelegate } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private XmlFormatCollectionReaderDelegate CreateXmlFormatReaderDelegate() { return new XmlFormatReaderGenerator().GenerateCollectionReader(this); @@ -381,6 +393,7 @@ private XmlFormatCollectionReaderDelegate CreateXmlFormatReaderDelegate() internal XmlFormatCollectionReaderDelegate XmlFormatReaderDelegate { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_helper.XmlFormatReaderDelegate == null) @@ -408,6 +421,7 @@ internal XmlFormatCollectionReaderDelegate XmlFormatReaderDelegate } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private XmlFormatGetOnlyCollectionReaderDelegate CreateXmlFormatGetOnlyCollectionReaderDelegate() { return new XmlFormatReaderGenerator().GenerateGetOnlyCollectionReader(this); @@ -417,6 +431,7 @@ private XmlFormatGetOnlyCollectionReaderDelegate CreateXmlFormatGetOnlyCollectio internal XmlFormatGetOnlyCollectionReaderDelegate XmlFormatGetOnlyCollectionReaderDelegate { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_helper.XmlFormatGetOnlyCollectionReaderDelegate == null) @@ -453,17 +468,20 @@ internal XmlFormatGetOnlyCollectionReaderDelegate XmlFormatGetOnlyCollectionRead } } + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void IncrementCollectionCount(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context) { _helper.IncrementCollectionCount(xmlWriter, obj, context); } + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal IEnumerator GetEnumeratorForCollection(object obj) { return _helper.GetEnumeratorForCollection(obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal Type GetCollectionElementType() { return _helper.GetCollectionElementType(); @@ -511,6 +529,7 @@ private sealed class CollectionDataContractCriticalHelper : DataContract.DataCon }; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void Init(CollectionKind kind, Type? itemType, CollectionDataContractAttribute? collectionContractAttribute) { _kind = kind; @@ -567,6 +586,7 @@ private void Init(CollectionKind kind, Type? itemType, CollectionDataContractAtt // array [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal CollectionDataContractCriticalHelper( [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] Type type) : base(type) @@ -581,6 +601,7 @@ internal CollectionDataContractCriticalHelper( // read-only collection [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal CollectionDataContractCriticalHelper( [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] Type type, @@ -602,6 +623,7 @@ internal CollectionDataContractCriticalHelper( // collection [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal CollectionDataContractCriticalHelper( [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] Type type, @@ -625,6 +647,7 @@ internal CollectionDataContractCriticalHelper( // collection [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal CollectionDataContractCriticalHelper(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, MethodInfo? addMethod, ConstructorInfo? constructor, bool isConstructorCheckRequired) : this(type, kind, itemType, getEnumeratorMethod, addMethod, constructor) { @@ -632,6 +655,7 @@ internal CollectionDataContractCriticalHelper(Type type, CollectionKind kind, Ty } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal CollectionDataContractCriticalHelper( [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] Type type, @@ -655,6 +679,7 @@ internal Type ItemType internal DataContract ItemContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_itemContract == null) @@ -748,6 +773,7 @@ internal bool IsItemTypeNullable internal override DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (!_isKnownTypeAttributeChecked && UnderlyingType != null) @@ -797,6 +823,7 @@ internal XmlFormatGetOnlyCollectionReaderDelegate? XmlFormatGetOnlyCollectionRea private static void DummyIncrementCollectionCount(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context) { } + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void IncrementCollectionCount(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context) { if (_incrementCollectionCountDelegate == null) @@ -842,6 +869,7 @@ internal void IncrementCollectionCount(XmlWriterDelegator xmlWriter, object obj, [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2060:MakeGenericMethod", Justification = "The call to MakeGenericMethod is safe due to the fact that CollectionDataContractCriticalHelper.BuildIncrementCollectionCountDelegate is not annotated.")] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static MethodInfo GetBuildIncrementCollectionCountGenericDelegate(Type type) => BuildIncrementCollectionCountDelegateMethod.MakeGenericMethod(type); private static MethodInfo? s_buildIncrementCollectionCountDelegateMethod; @@ -861,6 +889,7 @@ private static IncrementCollectionCountDelegate BuildIncrementCollectionCountDel private CreateGenericDictionaryEnumeratorDelegate? _createGenericDictionaryEnumeratorDelegate; + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal IEnumerator GetEnumeratorForCollection(object obj) { IEnumerator enumerator = ((IEnumerable)obj).GetEnumerator(); @@ -883,11 +912,12 @@ internal IEnumerator GetEnumeratorForCollection(object obj) return enumerator; [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2060:MakeGenericMethod", - Justification = "The call to MakeGenericMethod is safe due to the fact that CollectionDataContractCriticalHelper.BuildCreateGenericDictionaryEnumerator is not annotated.")] + Justification = "The call to MakeGenericMethod is safe due to the fact that CollectionDataContractCriticalHelper.BuildCreateGenericDictionaryEnumerator is not annotated.")] static MethodInfo GetBuildCreateGenericDictionaryEnumeratorGenericMethod(Type[] keyValueTypes) => GetBuildCreateGenericDictionaryEnumeratorMethodInfo.MakeGenericMethod(keyValueTypes[0], keyValueTypes[1]); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal Type GetCollectionElementType() { Debug.Assert(Kind != CollectionKind.Array, "GetCollectionElementType should not be called on Arrays"); @@ -956,6 +986,7 @@ private static CreateGenericDictionaryEnumeratorDelegate BuildCreateGenericDicti } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private DataContract? GetSharedTypeContract(Type type) { if (type.IsDefined(Globals.TypeOfCollectionDataContractAttribute, false)) @@ -977,24 +1008,28 @@ internal static bool IsCollectionInterface(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static bool IsCollection(Type type) { return IsCollection(type, out _); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static bool IsCollection(Type type, [NotNullWhen(true)] out Type? itemType) { return IsCollectionHelper(type, out itemType, true /*constructorRequired*/); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static bool IsCollection(Type type, bool constructorRequired) { return IsCollectionHelper(type, out _, constructorRequired); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static bool IsCollectionHelper(Type type, [NotNullWhen(true)] out Type? itemType, bool constructorRequired) { if (type.IsArray && DataContract.GetBuiltInDataContract(type) == null) @@ -1006,12 +1041,14 @@ private static bool IsCollectionHelper(Type type, [NotNullWhen(true)] out Type? } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static bool TryCreate(Type type, [NotNullWhen(true)] out DataContract? dataContract) { return IsCollectionOrTryCreate(type, tryCreate: true, out dataContract!, out _, constructorRequired: true); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static bool CreateGetOnlyCollectionDataContract(Type type, [NotNullWhen(true)] out DataContract? dataContract) { if (type.IsArray) @@ -1026,6 +1063,7 @@ internal static bool CreateGetOnlyCollectionDataContract(Type type, [NotNullWhen } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static bool TryCreateGetOnlyCollectionDataContract(Type type, [NotNullWhen(true)] out DataContract? dataContract) { dataContract = DataContract.GetDataContractFromGeneratedAssembly(type); @@ -1074,6 +1112,7 @@ private static bool IsArraySegment(Type t) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static bool IsCollectionOrTryCreate(Type type, bool tryCreate, out DataContract? dataContract, out Type itemType, bool constructorRequired) { dataContract = null; @@ -1312,6 +1351,7 @@ internal static bool IsCollectionDataContract(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static bool HandleIfInvalidCollection(Type type, bool tryCreate, bool hasCollectionDataContract, bool createContractWithException, string message, string? param, ref DataContract? dataContract) { if (hasCollectionDataContract) @@ -1565,6 +1605,7 @@ internal bool RequiresMemberAccessForWrite(SecurityException? securityException) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { Debug.Assert(context != null); @@ -1575,6 +1616,7 @@ public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, Xml } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) { Debug.Assert(context != null); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs index f859f2f518ceff..b7bf190cb74c8e 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs @@ -30,6 +30,7 @@ internal abstract class DataContract internal const string SerializerTrimmerWarning = "Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the " + "required types are preserved."; + internal const string SerializerAOTWarning = "Data Contract Serialization and Deserialization might require types that cannot be statically analyzed."; public static Dictionary GetDataContracts() { @@ -70,18 +71,21 @@ internal MethodInfo? ParseMethod } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetDataContract(Type type) { return GetDataContract(type.TypeHandle, type); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type type) { return GetDataContract(typeHandle, type, SerializationMode.SharedContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type? type, SerializationMode mode) { int id = GetId(typeHandle); @@ -90,6 +94,7 @@ internal static DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type? } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetDataContract(int id, RuntimeTypeHandle typeHandle, SerializationMode mode) { DataContract dataContract = GetDataContractSkipValidation(id, typeHandle, null); @@ -97,12 +102,14 @@ internal static DataContract GetDataContract(int id, RuntimeTypeHandle typeHandl } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetDataContractSkipValidation(int id, RuntimeTypeHandle typeHandle, Type? type) { return DataContractCriticalHelper.GetDataContractSkipValidation(id, typeHandle, type); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetGetOnlyCollectionDataContract(int id, RuntimeTypeHandle typeHandle, Type? type, SerializationMode mode) { DataContract dataContract = GetGetOnlyCollectionDataContractSkipValidation(id, typeHandle, type); @@ -115,6 +122,7 @@ internal static DataContract GetGetOnlyCollectionDataContract(int id, RuntimeTyp } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetGetOnlyCollectionDataContractSkipValidation(int id, RuntimeTypeHandle typeHandle, Type? type) { return DataContractCriticalHelper.GetGetOnlyCollectionDataContractSkipValidation(id, typeHandle, type); @@ -136,18 +144,21 @@ internal static int GetId(RuntimeTypeHandle typeHandle) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static DataContract? GetBuiltInDataContract(Type type) { return DataContractCriticalHelper.GetBuiltInDataContract(type); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static DataContract? GetBuiltInDataContract(string name, string ns) { return DataContractCriticalHelper.GetBuiltInDataContract(name, ns); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static DataContract? GetBuiltInDataContract(string typeName) { return DataContractCriticalHelper.GetBuiltInDataContract(typeName); @@ -203,18 +214,21 @@ internal Type TypeForInitialization } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(this.GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) { throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(this.GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(this.GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); @@ -255,6 +269,7 @@ public XmlQualifiedName StableName public virtual DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { return _helper.KnownDataContracts; } @@ -379,6 +394,7 @@ internal class DataContractCriticalHelper private Type _typeForInitialization; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetDataContractSkipValidation(int id, RuntimeTypeHandle typeHandle, Type? type) { DataContract dataContract = s_dataContractCache[id]; @@ -395,6 +411,7 @@ internal static DataContract GetDataContractSkipValidation(int id, RuntimeTypeHa } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetGetOnlyCollectionDataContractSkipValidation(int id, RuntimeTypeHandle typeHandle, Type? type) { DataContract dataContract = s_dataContractCache[id]; @@ -475,6 +492,7 @@ internal static int GetId(RuntimeTypeHandle typeHandle) // check whether a corresponding update is required in ClassDataContract.IsNonAttributedTypeValidForSerialization [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static DataContract CreateDataContract(int id, RuntimeTypeHandle typeHandle, Type? type) { DataContract? dataContract = s_dataContractCache[id]; @@ -504,6 +522,7 @@ private static DataContract CreateDataContract(int id, RuntimeTypeHandle typeHan } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static DataContract CreateDataContract(Type type) { type = UnwrapNullableType(type); @@ -559,6 +578,7 @@ private static void AssignDataContractToId(DataContract dataContract, int id) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static DataContract CreateGetOnlyCollectionDataContract(int id, RuntimeTypeHandle typeHandle, Type? type) { DataContract? dataContract = null; @@ -581,6 +601,7 @@ private static DataContract CreateGetOnlyCollectionDataContract(int id, RuntimeT // This method returns adapter types used at runtime to create DataContract. [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static Type GetDataContractAdapterType(Type type) { // Replace the DataTimeOffset ISerializable type passed in with the internal DateTimeOffsetAdapter DataContract type. @@ -629,6 +650,7 @@ private static RuntimeTypeHandle GetDataContractAdapterTypeHandle(RuntimeTypeHan } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static DataContract? GetBuiltInDataContract(Type type) { if (type.IsInterface && !CollectionDataContract.IsCollectionInterface(type)) @@ -649,6 +671,7 @@ private static RuntimeTypeHandle GetDataContractAdapterTypeHandle(RuntimeTypeHan } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static DataContract? GetBuiltInDataContract(string name, string ns) { lock (s_initBuiltInContractsLock) @@ -667,6 +690,7 @@ private static RuntimeTypeHandle GetDataContractAdapterTypeHandle(RuntimeTypeHan } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static DataContract? GetBuiltInDataContract(string typeName) { if (!typeName.StartsWith("System.", StringComparison.Ordinal)) @@ -745,6 +769,7 @@ private static RuntimeTypeHandle GetDataContractAdapterTypeHandle(RuntimeTypeHan } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static bool TryCreateBuiltInDataContract(Type type, [NotNullWhen(true)] out DataContract? dataContract) { if (type.IsEnum) // Type.GetTypeCode will report Enums as TypeCode.IntXX @@ -827,6 +852,7 @@ public static bool TryCreateBuiltInDataContract(Type type, [NotNullWhen(true)] o } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static bool TryCreateBuiltInDataContract(string name, string ns, [NotNullWhen(true)] out DataContract? dataContract) { dataContract = null; @@ -1094,6 +1120,7 @@ internal XmlQualifiedName StableName internal virtual DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { return null; } set { /* do nothing */ } } @@ -1194,6 +1221,7 @@ internal void ThrowInvalidDataContractException(string message) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static bool IsTypeSerializable(Type type, HashSet? previousCollectionTypes = null) { Type? itemType; @@ -1297,12 +1325,14 @@ internal static bool IsValidNCName(string name) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static XmlQualifiedName GetStableName(Type type) { return GetStableName(type, out _); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static XmlQualifiedName GetStableName(Type type, out bool hasDataContract) { type = UnwrapRedundantNullableType(type); @@ -1330,6 +1360,7 @@ internal static XmlQualifiedName GetStableName(Type type, out bool hasDataContra } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static XmlQualifiedName GetDCTypeStableName(Type type, DataContractAttribute dataContractAttribute) { string? name, ns; @@ -1359,6 +1390,7 @@ private static XmlQualifiedName GetDCTypeStableName(Type type, DataContractAttri } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static XmlQualifiedName GetNonDCTypeStableName(Type type) { string? name, ns; @@ -1381,6 +1413,7 @@ private static XmlQualifiedName GetNonDCTypeStableName(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static bool TryGetBuiltInXmlAndArrayTypeStableName(Type type, [NotNullWhen(true)] out XmlQualifiedName? stableName) { stableName = null; @@ -1421,6 +1454,7 @@ internal static bool TryGetDCAttribute(Type type, [NotNullWhen(true)] out DataCo } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static XmlQualifiedName GetCollectionStableName(Type type, Type itemType, out CollectionDataContractAttribute? collectionContractAttribute) { string? name, ns; @@ -1466,6 +1500,7 @@ internal static XmlQualifiedName GetCollectionStableName(Type type, Type itemTyp } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static string GetArrayPrefix(ref Type itemType) { string arrayOfPrefix = string.Empty; @@ -1485,12 +1520,14 @@ internal static string GetCollectionNamespace(string elementNs) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static XmlQualifiedName GetDefaultStableName(Type type) { return CreateQualifiedName(GetDefaultStableLocalName(type), GetDefaultStableNamespace(type)); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static string GetDefaultStableLocalName(Type type) { if (type.IsGenericParameter) @@ -1622,6 +1659,7 @@ internal static string GetDefaultStableNamespace(string? clrNs) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static void GetDefaultStableName(string fullTypeName, out string localName, out string ns) { CodeTypeReference typeReference = new CodeTypeReference(fullTypeName); @@ -1629,6 +1667,7 @@ internal static void GetDefaultStableName(string fullTypeName, out string localN } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static void GetDefaultStableName(CodeTypeReference typeReference, out string localName, out string ns) { string fullTypeName = typeReference.BaseType; @@ -1893,6 +1932,7 @@ private static byte[] ComputeHash(byte[] namespaces) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static string ExpandGenericParameters(string format, Type type) { GenericNameProvider genericNameProviderForType = new GenericNameProvider(type); @@ -1900,6 +1940,7 @@ private static string ExpandGenericParameters(string format, Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static string ExpandGenericParameters(string format, IGenericNameProvider genericNameProvider) { string? digest = null; @@ -1953,6 +1994,7 @@ internal static bool IsTypeNullable(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContractDictionary? ImportKnownTypeAttributes(Type type) { DataContractDictionary? knownDataContracts = null; @@ -1962,6 +2004,7 @@ internal static bool IsTypeNullable(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static void ImportKnownTypeAttributes(Type? type, Dictionary typesChecked, ref DataContractDictionary? knownDataContracts) { while (type != null && DataContract.IsTypeSerializable(type)) @@ -2056,6 +2099,7 @@ private static void ImportKnownTypeAttributes(Type? type, Dictionary } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static void CheckAndAdd(Type type, Dictionary typesChecked, [NotNullIfNotNull(nameof(nameToDataContractTable))] ref DataContractDictionary? nameToDataContractTable) { type = DataContract.UnwrapNullableType(type); @@ -2197,13 +2241,16 @@ internal interface IGenericNameProvider int GetParameterCount(); IList GetNestedParameterCounts(); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] string GetParameterName(int paramIndex); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] string GetNamespaces(); string GetGenericTypeName(); bool ParametersFromBuiltInNamespaces { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get; } } @@ -2240,12 +2287,14 @@ public IList GetNestedParameterCounts() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public string GetParameterName(int paramIndex) { return GetStableName(paramIndex).Name; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public string GetNamespaces() { StringBuilder namespaces = new StringBuilder(); @@ -2262,6 +2311,7 @@ public string GetGenericTypeName() public bool ParametersFromBuiltInNamespaces { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { bool parametersFromBuiltInNamespaces = true; @@ -2277,6 +2327,7 @@ public bool ParametersFromBuiltInNamespaces } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private XmlQualifiedName GetStableName(int i) { object o = _genericParams[i]; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractResolver.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractResolver.cs index 2b9345dc2c5f23..c1426d4b474cf9 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractResolver.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractResolver.cs @@ -10,8 +10,10 @@ namespace System.Runtime.Serialization public abstract class DataContractResolver { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public abstract bool TryResolveType(Type type, Type? declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString? typeName, out XmlDictionaryString? typeNamespace); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public abstract Type? ResolveName(string typeName, string? typeNamespace, Type? declaredType, DataContractResolver knownTypeResolver); } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs index cd8b099284e332..f798ef38e082d9 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs @@ -170,6 +170,7 @@ public ReadOnlyCollection KnownTypes internal override DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (this.knownDataContracts == null && this.knownTypeList != null) @@ -218,6 +219,7 @@ public bool SerializeReadOnlyTypes private DataContract RootContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_rootContract == null) @@ -230,12 +232,14 @@ private DataContract RootContract } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void InternalWriteObject(XmlWriterDelegator writer, object? graph) { InternalWriteObject(writer, graph, null); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void InternalWriteObject(XmlWriterDelegator writer, object? graph, DataContractResolver? dataContractResolver) { InternalWriteStartObject(writer, graph); @@ -244,102 +248,119 @@ internal override void InternalWriteObject(XmlWriterDelegator writer, object? gr } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteObject(XmlWriter writer, object? graph) { WriteObjectHandleExceptions(new XmlWriterDelegator(writer), graph); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteStartObject(XmlWriter writer, object? graph) { WriteStartObjectHandleExceptions(new XmlWriterDelegator(writer), graph); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteObjectContent(XmlWriter writer, object? graph) { WriteObjectContentHandleExceptions(new XmlWriterDelegator(writer), graph); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteEndObject(XmlWriter writer) { WriteEndObjectHandleExceptions(new XmlWriterDelegator(writer)); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteStartObject(XmlDictionaryWriter writer, object? graph) { WriteStartObjectHandleExceptions(new XmlWriterDelegator(writer), graph); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteObjectContent(XmlDictionaryWriter writer, object? graph) { WriteObjectContentHandleExceptions(new XmlWriterDelegator(writer), graph); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteEndObject(XmlDictionaryWriter writer) { WriteEndObjectHandleExceptions(new XmlWriterDelegator(writer)); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public void WriteObject(XmlDictionaryWriter writer, object? graph, DataContractResolver? dataContractResolver) { WriteObjectHandleExceptions(new XmlWriterDelegator(writer), graph, dataContractResolver); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadObject(XmlReader reader) { return ReadObjectHandleExceptions(new XmlReaderDelegator(reader), true /*verifyObjectName*/); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadObject(XmlReader reader, bool verifyObjectName) { return ReadObjectHandleExceptions(new XmlReaderDelegator(reader), verifyObjectName); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override bool IsStartObject(XmlReader reader) { return IsStartObjectHandleExceptions(new XmlReaderDelegator(reader)); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadObject(XmlDictionaryReader reader, bool verifyObjectName) { return ReadObjectHandleExceptions(new XmlReaderDelegator(reader), verifyObjectName); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override bool IsStartObject(XmlDictionaryReader reader) { return IsStartObjectHandleExceptions(new XmlReaderDelegator(reader)); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public object? ReadObject(XmlDictionaryReader reader, bool verifyObjectName, DataContractResolver? dataContractResolver) { return ReadObjectHandleExceptions(new XmlReaderDelegator(reader), verifyObjectName, dataContractResolver); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void InternalWriteStartObject(XmlWriterDelegator writer, object? graph) { WriteRootElement(writer, RootContract, _rootName, _rootNamespace, _needsContractNsAtRoot); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void InternalWriteObjectContent(XmlWriterDelegator writer, object? graph) { InternalWriteObjectContent(writer, graph, null); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void InternalWriteObjectContent(XmlWriterDelegator writer, object? graph, DataContractResolver? dataContractResolver) { if (MaxItemsInObjectGraph == 0) @@ -396,6 +417,7 @@ internal void InternalWriteObjectContent(XmlWriterDelegator writer, object? grap } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetDataContract(DataContract declaredTypeContract, Type declaredType, Type objectType) { if (declaredType.IsInterface && CollectionDataContract.IsCollectionInterface(declaredType)) @@ -413,6 +435,7 @@ internal static DataContract GetDataContract(DataContract declaredTypeContract, } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void InternalWriteEndObject(XmlWriterDelegator writer) { if (!IsRootXmlAny(_rootName, RootContract)) @@ -422,12 +445,14 @@ internal override void InternalWriteEndObject(XmlWriterDelegator writer) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override object? InternalReadObject(XmlReaderDelegator xmlReader, bool verifyObjectName) { return InternalReadObject(xmlReader, verifyObjectName, null); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override object? InternalReadObject(XmlReaderDelegator xmlReader, bool verifyObjectName, DataContractResolver? dataContractResolver) { if (MaxItemsInObjectGraph == 0) @@ -476,6 +501,7 @@ internal override void InternalWriteEndObject(XmlWriterDelegator writer) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override bool InternalIsStartObject(XmlReaderDelegator reader) { return IsRootElement(reader, RootContract, _rootName, _rootNamespace); @@ -493,6 +519,7 @@ internal override bool InternalIsStartObject(XmlReaderDelegator reader) [return: NotNullIfNotNull(nameof(oldObj))] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static object? SurrogateToDataContractType(ISerializationSurrogateProvider serializationSurrogateProvider, object? oldObj, Type surrogatedDeclaredType, ref Type objType) { object? obj = DataContractSurrogateCaller.GetObjectToSerialize(serializationSurrogateProvider, oldObj, objType, surrogatedDeclaredType); @@ -504,6 +531,7 @@ internal override bool InternalIsStartObject(XmlReaderDelegator reader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static Type GetSurrogatedType(ISerializationSurrogateProvider serializationSurrogateProvider, Type type) { return DataContractSurrogateCaller.GetDataContractType(serializationSurrogateProvider, DataContract.UnwrapNullableType(type)); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSet.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSet.cs index 41bae413f816aa..08b6a90a16e756 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSet.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSet.cs @@ -32,6 +32,7 @@ internal DataContractSet(IDataContractSurrogate dataContractSurrogate, ICollecti #endif [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal DataContractSet(DataContractSet dataContractSet) { ArgumentNullException.ThrowIfNull(dataContractSet); @@ -65,6 +66,7 @@ internal DataContractSet(DataContractSet dataContractSet) #endif [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void Add(Type type) { DataContract dataContract = GetDataContract(type); @@ -79,12 +81,14 @@ internal static void EnsureTypeNotGeneric(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void Add(DataContract dataContract) { Add(dataContract.StableName, dataContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public void Add(XmlQualifiedName name, DataContract dataContract) { if (dataContract.IsBuiltInDataContract) @@ -93,6 +97,7 @@ public void Add(XmlQualifiedName name, DataContract dataContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void InternalAdd(XmlQualifiedName name, DataContract dataContract) { DataContract? dataContractInSet; @@ -129,6 +134,7 @@ internal void InternalAdd(XmlQualifiedName name, DataContract dataContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void AddClassDataContract(ClassDataContract classDataContract) { if (classDataContract.BaseContract != null) @@ -162,6 +168,7 @@ private void AddClassDataContract(ClassDataContract classDataContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void AddCollectionDataContract(CollectionDataContract collectionDataContract) { if (collectionDataContract.IsDictionary) @@ -179,12 +186,14 @@ private void AddCollectionDataContract(CollectionDataContract collectionDataCont } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void AddXmlDataContract(XmlDataContract xmlDataContract) { AddKnownDataContracts(xmlDataContract.KnownDataContracts); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void AddKnownDataContracts(DataContractDictionary? knownDataContracts) { if (knownDataContracts != null) @@ -197,6 +206,7 @@ private void AddKnownDataContracts(DataContractDictionary? knownDataContracts) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetDataContract(Type clrType) { #if SUPPORT_SURROGATE @@ -227,6 +237,7 @@ internal static DataContract GetDataContract(Type clrType) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetMemberTypeDataContract(DataMember dataMember) { Type dataMemberType = dataMember.MemberType; @@ -252,6 +263,7 @@ internal static DataContract GetMemberTypeDataContract(DataMember dataMember) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetItemTypeDataContract(CollectionDataContract collectionContract) { if (collectionContract.ItemType != null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSurrogateCaller.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSurrogateCaller.cs index 03d145ba53b884..f31813e05acf68 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSurrogateCaller.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSurrogateCaller.cs @@ -12,6 +12,7 @@ namespace System.Runtime.Serialization internal static class DataContractSurrogateCaller { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static Type GetDataContractType(ISerializationSurrogateProvider surrogateProvider, Type type) { if (DataContract.GetBuiltInDataContract(type) != null) @@ -21,6 +22,7 @@ internal static Type GetDataContractType(ISerializationSurrogateProvider surroga [return: NotNullIfNotNull(nameof(obj))] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static object? GetObjectToSerialize(ISerializationSurrogateProvider surrogateProvider, object? obj, Type objType, Type membertype) { if (obj == null) @@ -32,6 +34,7 @@ internal static Type GetDataContractType(ISerializationSurrogateProvider surroga [return: NotNullIfNotNull(nameof(obj))] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static object? GetDeserializedObject(ISerializationSurrogateProvider surrogateProvider, object? obj, Type objType, Type memberType) { if (obj == null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataMember.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataMember.cs index 251e896e864880..ad63d673006aa8 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataMember.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataMember.cs @@ -90,6 +90,7 @@ internal Type MemberType internal DataContract MemberTypeContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { return _helper.MemberTypeContract; } } @@ -97,6 +98,7 @@ internal DataContract MemberTypeContract internal PrimitiveDataContract? MemberPrimitiveContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { return _helper.MemberPrimitiveContract; @@ -210,6 +212,7 @@ internal Type MemberType internal DataContract MemberTypeContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_memberTypeContract == null) @@ -249,6 +252,7 @@ internal DataMember? ConflictingMember internal PrimitiveDataContract? MemberPrimitiveContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_memberPrimitiveContract == PrimitiveDataContract.NullContract) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/EnumDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/EnumDataContract.cs index 9014a71b47d401..0d696455e1d135 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/EnumDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/EnumDataContract.cs @@ -22,6 +22,7 @@ internal sealed class EnumDataContract : DataContract public XmlQualifiedName? BaseContractName { get; set; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal EnumDataContract(Type type) : base(new EnumDataContractCriticalHelper(type)) { _helper = (base.Helper as EnumDataContractCriticalHelper)!; @@ -98,6 +99,7 @@ internal static void Add(Type type, string localName) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal EnumDataContractCriticalHelper( [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] Type type) : base(type) @@ -360,12 +362,14 @@ internal long GetEnumValueFromString(string value) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { WriteEnumValue(xmlWriter, obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) { object obj = ReadEnumValue(xmlReader); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/GenericParameterDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/GenericParameterDataContract.cs index 8f5bd20805c9de..ddcc9e1182a15f 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/GenericParameterDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/GenericParameterDataContract.cs @@ -12,6 +12,7 @@ internal sealed class GenericParameterDataContract : DataContract private readonly GenericParameterDataContractCriticalHelper _helper; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal GenericParameterDataContract(Type type) : base(new GenericParameterDataContractCriticalHelper(type)) { @@ -37,6 +38,7 @@ private sealed class GenericParameterDataContractCriticalHelper : DataContract.D private readonly int _parameterPosition; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal GenericParameterDataContractCriticalHelper( [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] Type type) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Globals.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Globals.cs index 48167f42d04f67..8ef893fdf3e856 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Globals.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Globals.cs @@ -299,6 +299,7 @@ internal static partial class Globals internal static Type TypeOfHashtable { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get => s_typeOfHashtable ??= TypeOfDictionaryGeneric.MakeGenericType(TypeOfObject, TypeOfObject); } @@ -321,6 +322,7 @@ internal static Type TypeOfHashtable private static readonly Type? s_typeOfScriptObject; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static ClassDataContract CreateScriptObjectClassDataContract() { Debug.Assert(s_typeOfScriptObject != null); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs index 4ab7ae4006ca95..ed81d44d7b4f2a 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs @@ -35,42 +35,49 @@ public sealed class DataContractJsonSerializer : XmlObjectSerializer [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public DataContractJsonSerializer(Type type) : this(type, (IEnumerable?)null) { } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public DataContractJsonSerializer(Type type, string? rootName) : this(type, rootName, null) { } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public DataContractJsonSerializer(Type type, XmlDictionaryString? rootName) : this(type, rootName, null) { } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public DataContractJsonSerializer(Type type, IEnumerable? knownTypes) : this(type, null, knownTypes, int.MaxValue, false, false) { } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public DataContractJsonSerializer(Type type, string? rootName, IEnumerable? knownTypes) : this(type, new DataContractJsonSerializerSettings() { RootName = rootName, KnownTypes = knownTypes }) { } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public DataContractJsonSerializer(Type type, XmlDictionaryString? rootName, IEnumerable? knownTypes) : this(type, rootName, knownTypes, int.MaxValue, false, false) { } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public DataContractJsonSerializer(Type type, DataContractJsonSerializerSettings? settings) { settings ??= new DataContractJsonSerializerSettings(); @@ -81,6 +88,7 @@ public DataContractJsonSerializer(Type type, DataContractJsonSerializerSettings? } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal DataContractJsonSerializer(Type type, XmlDictionaryString? rootName, IEnumerable? knownTypes, @@ -119,6 +127,7 @@ public ReadOnlyCollection KnownTypes internal override DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (this.knownDataContracts == null && this.knownTypeList != null) @@ -174,6 +183,7 @@ public bool UseSimpleDictionaryFormat private DataContract RootContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_rootContract == null) @@ -194,6 +204,7 @@ private XmlDictionaryString RootName } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override bool IsStartObject(XmlReader reader) { // No need to pass in DateTimeFormat to JsonReaderDelegator: no DateTimes will be read in IsStartObject @@ -201,6 +212,7 @@ public override bool IsStartObject(XmlReader reader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override bool IsStartObject(XmlDictionaryReader reader) { // No need to pass in DateTimeFormat to JsonReaderDelegator: no DateTimes will be read in IsStartObject @@ -208,6 +220,7 @@ public override bool IsStartObject(XmlDictionaryReader reader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadObject(Stream stream) { ArgumentNullException.ThrowIfNull(stream); @@ -216,30 +229,35 @@ public override bool IsStartObject(XmlDictionaryReader reader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadObject(XmlReader reader) { return ReadObjectHandleExceptions(new JsonReaderDelegator(reader, this.DateTimeFormat), true); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadObject(XmlReader reader, bool verifyObjectName) { return ReadObjectHandleExceptions(new JsonReaderDelegator(reader, this.DateTimeFormat), verifyObjectName); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadObject(XmlDictionaryReader reader) { return ReadObjectHandleExceptions(new JsonReaderDelegator(reader, this.DateTimeFormat), true); // verifyObjectName } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadObject(XmlDictionaryReader reader, bool verifyObjectName) { return ReadObjectHandleExceptions(new JsonReaderDelegator(reader, this.DateTimeFormat), verifyObjectName); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteEndObject(XmlWriter writer) { // No need to pass in DateTimeFormat to JsonWriterDelegator: no DateTimes will be written in end object @@ -247,6 +265,7 @@ public override void WriteEndObject(XmlWriter writer) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteEndObject(XmlDictionaryWriter writer) { // No need to pass in DateTimeFormat to JsonWriterDelegator: no DateTimes will be written in end object @@ -254,6 +273,7 @@ public override void WriteEndObject(XmlDictionaryWriter writer) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteObject(Stream stream, object? graph) { ArgumentNullException.ThrowIfNull(stream); @@ -264,30 +284,35 @@ public override void WriteObject(Stream stream, object? graph) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteObject(XmlWriter writer, object? graph) { WriteObjectHandleExceptions(new JsonWriterDelegator(writer, this.DateTimeFormat), graph); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteObject(XmlDictionaryWriter writer, object? graph) { WriteObjectHandleExceptions(new JsonWriterDelegator(writer, this.DateTimeFormat), graph); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteObjectContent(XmlWriter writer, object? graph) { WriteObjectContentHandleExceptions(new JsonWriterDelegator(writer, this.DateTimeFormat), graph); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteObjectContent(XmlDictionaryWriter writer, object? graph) { WriteObjectContentHandleExceptions(new JsonWriterDelegator(writer, this.DateTimeFormat), graph); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteStartObject(XmlWriter writer, object? graph) { // No need to pass in DateTimeFormat to JsonWriterDelegator: no DateTimes will be written in start object @@ -295,6 +320,7 @@ public override void WriteStartObject(XmlWriter writer, object? graph) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteStartObject(XmlDictionaryWriter writer, object? graph) { // No need to pass in DateTimeFormat to JsonWriterDelegator: no DateTimes will be written in start object @@ -358,6 +384,7 @@ internal static bool IsJsonLocalName(XmlReaderDelegator reader, string elementNa } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static object? ReadJsonValue(DataContract contract, XmlReaderDelegator reader, XmlObjectSerializerReadContextComplexJson? context) { return JsonDataContract.GetJsonDataContract(contract).ReadJsonValue(reader, context); @@ -369,6 +396,7 @@ internal static void WriteJsonNull(XmlWriterDelegator writer) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static void WriteJsonValue(JsonDataContract contract, XmlWriterDelegator writer, object graph, XmlObjectSerializerWriteContextComplexJson? context, RuntimeTypeHandle declaredTypeHandle) { contract.WriteJsonValue(writer, graph, context, declaredTypeHandle); @@ -385,6 +413,7 @@ internal static void WriteJsonValue(JsonDataContract contract, XmlWriterDelegato } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override bool InternalIsStartObject(XmlReaderDelegator reader) { if (IsRootElement(reader, RootContract, RootName, XmlDictionaryString.Empty)) @@ -396,6 +425,7 @@ internal override bool InternalIsStartObject(XmlReaderDelegator reader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override object? InternalReadObject(XmlReaderDelegator xmlReader, bool verifyObjectName) { if (MaxItemsInObjectGraph == 0) @@ -426,12 +456,14 @@ internal override bool InternalIsStartObject(XmlReaderDelegator reader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void InternalWriteEndObject(XmlWriterDelegator writer) { writer.WriteEndElement(); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void InternalWriteObject(XmlWriterDelegator writer, object? graph) { InternalWriteStartObject(writer, graph); @@ -440,6 +472,7 @@ internal override void InternalWriteObject(XmlWriterDelegator writer, object? gr } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void InternalWriteObjectContent(XmlWriterDelegator writer, object? graph) { if (MaxItemsInObjectGraph == 0) @@ -488,6 +521,7 @@ internal override void InternalWriteObjectContent(XmlWriterDelegator writer, obj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void InternalWriteStartObject(XmlWriterDelegator writer, object? graph) { if (_rootNameRequiresMapping) @@ -502,6 +536,7 @@ internal override void InternalWriteStartObject(XmlWriterDelegator writer, objec } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void AddCollectionItemTypeToKnownTypes(Type knownType) { Type? itemType; @@ -519,6 +554,7 @@ private void AddCollectionItemTypeToKnownTypes(Type knownType) [MemberNotNull(nameof(_rootType))] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void Initialize(Type type, IEnumerable? knownTypes, int maxItemsInObjectGraph, @@ -559,6 +595,7 @@ private void Initialize(Type type, [MemberNotNull(nameof(_rootType))] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void Initialize(Type type, XmlDictionaryString? rootName, IEnumerable? knownTypes, @@ -587,6 +624,7 @@ internal static void CheckIfTypeIsReference(DataContract dataContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetDataContract(DataContract declaredTypeContract, Type declaredType, Type objectType) { DataContract contract = DataContractSerializer.GetDataContract(declaredTypeContract, declaredType, objectType); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonByteArrayDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonByteArrayDataContract.cs index 5518442c8649da..dfd95ac53e3ed6 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonByteArrayDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonByteArrayDataContract.cs @@ -13,12 +13,14 @@ namespace System.Runtime.Serialization.Json internal sealed class JsonByteArrayDataContract : JsonDataContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonByteArrayDataContract(ByteArrayDataContract traditionalByteArrayDataContract) : base(traditionalByteArrayDataContract) { } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { if (context == null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonClassDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonClassDataContract.cs index 6ff59d9eaa7f2b..be8c47db186099 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonClassDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonClassDataContract.cs @@ -15,6 +15,7 @@ internal sealed class JsonClassDataContract : JsonDataContract private readonly JsonClassDataContractCriticalHelper _helper; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonClassDataContract(ClassDataContract traditionalDataContract) : base(new JsonClassDataContractCriticalHelper(traditionalDataContract)) { @@ -24,6 +25,7 @@ public JsonClassDataContract(ClassDataContract traditionalDataContract) internal JsonFormatClassReaderDelegate JsonFormatReaderDelegate { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_helper.JsonFormatReaderDelegate == null) @@ -54,6 +56,7 @@ internal JsonFormatClassReaderDelegate JsonFormatReaderDelegate internal JsonFormatClassWriterDelegate JsonFormatWriterDelegate { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_helper.JsonFormatWriterDelegate == null) @@ -88,6 +91,7 @@ internal JsonFormatClassWriterDelegate JsonFormatWriterDelegate private ClassDataContract TraditionalClassDataContract => _helper.TraditionalClassDataContract; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { jsonReader.Read(); @@ -97,6 +101,7 @@ internal JsonFormatClassWriterDelegate JsonFormatWriterDelegate } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteJsonValueCore(XmlWriterDelegator jsonWriter, object obj, XmlObjectSerializerWriteContextComplexJson? context, RuntimeTypeHandle declaredTypeHandle) { Debug.Assert(context != null); @@ -113,6 +118,7 @@ private sealed class JsonClassDataContractCriticalHelper : JsonDataContractCriti private readonly string _typeName; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonClassDataContractCriticalHelper(ClassDataContract traditionalDataContract) : base(traditionalDataContract) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonCollectionDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonCollectionDataContract.cs index 9d0a40cc5d1c9a..74cf2f047883e5 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonCollectionDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonCollectionDataContract.cs @@ -15,6 +15,7 @@ internal sealed class JsonCollectionDataContract : JsonDataContract private readonly JsonCollectionDataContractCriticalHelper _helper; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonCollectionDataContract(CollectionDataContract traditionalDataContract) : base(new JsonCollectionDataContractCriticalHelper(traditionalDataContract)) { @@ -24,6 +25,7 @@ public JsonCollectionDataContract(CollectionDataContract traditionalDataContract internal JsonFormatCollectionReaderDelegate JsonFormatReaderDelegate { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_helper.JsonFormatReaderDelegate == null) @@ -54,6 +56,7 @@ internal JsonFormatCollectionReaderDelegate JsonFormatReaderDelegate internal JsonFormatGetOnlyCollectionReaderDelegate JsonFormatGetOnlyReaderDelegate { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_helper.JsonFormatGetOnlyReaderDelegate == null) @@ -90,6 +93,7 @@ internal JsonFormatGetOnlyCollectionReaderDelegate JsonFormatGetOnlyReaderDelega internal JsonFormatCollectionWriterDelegate JsonFormatWriterDelegate { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_helper.JsonFormatWriterDelegate == null) @@ -120,6 +124,7 @@ internal JsonFormatCollectionWriterDelegate JsonFormatWriterDelegate private CollectionDataContract TraditionalCollectionDataContract => _helper.TraditionalCollectionDataContract; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { Debug.Assert(context != null); @@ -141,6 +146,7 @@ internal JsonFormatCollectionWriterDelegate JsonFormatWriterDelegate } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteJsonValueCore(XmlWriterDelegator jsonWriter, object obj, XmlObjectSerializerWriteContextComplexJson? context, RuntimeTypeHandle declaredTypeHandle) { Debug.Assert(context != null); @@ -157,6 +163,7 @@ private sealed class JsonCollectionDataContractCriticalHelper : JsonDataContract private readonly CollectionDataContract _traditionalCollectionDataContract; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonCollectionDataContractCriticalHelper(CollectionDataContract traditionalDataContract) : base(traditionalDataContract) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonDataContract.cs index 4330703dc3ab89..a9783115ccd084 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonDataContract.cs @@ -16,6 +16,7 @@ internal class JsonDataContract private readonly JsonDataContractCriticalHelper _helper; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected JsonDataContract(DataContract traditionalDataContract) { _helper = new JsonDataContractCriticalHelper(traditionalDataContract); @@ -63,12 +64,14 @@ internal static JsonReadWriteDelegates GetReadWriteDelegatesFromGeneratedAssembl } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static JsonDataContract GetJsonDataContract(DataContract traditionalDataContract) { return JsonDataContractCriticalHelper.GetJsonDataContract(traditionalDataContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public object? ReadJsonValue(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { PushKnownDataContracts(context); @@ -78,12 +81,14 @@ public static JsonDataContract GetJsonDataContract(DataContract traditionalDataC } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { return TraditionalDataContract.ReadXmlValue(jsonReader, context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public void WriteJsonValue(XmlWriterDelegator jsonWriter, object obj, XmlObjectSerializerWriteContextComplexJson? context, RuntimeTypeHandle declaredTypeHandle) { PushKnownDataContracts(context); @@ -92,6 +97,7 @@ public void WriteJsonValue(XmlWriterDelegator jsonWriter, object obj, XmlObjectS } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual void WriteJsonValueCore(XmlWriterDelegator jsonWriter, object obj, XmlObjectSerializerWriteContextComplexJson? context, RuntimeTypeHandle declaredTypeHandle) { TraditionalDataContract.WriteXmlValue(jsonWriter, obj, context); @@ -149,6 +155,7 @@ internal class JsonDataContractCriticalHelper private readonly string _typeName; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal JsonDataContractCriticalHelper(DataContract traditionalDataContract) { _traditionalDataContract = traditionalDataContract; @@ -163,6 +170,7 @@ internal JsonDataContractCriticalHelper(DataContract traditionalDataContract) internal virtual string TypeName => _typeName; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static JsonDataContract GetJsonDataContract(DataContract traditionalDataContract) { int id = JsonDataContractCriticalHelper.GetId(traditionalDataContract.UnderlyingType.TypeHandle); @@ -209,6 +217,7 @@ internal static int GetId(RuntimeTypeHandle typeHandle) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static JsonDataContract CreateJsonDataContract(int id, DataContract traditionalDataContract) { lock (s_createDataContractLock) @@ -273,6 +282,7 @@ private static JsonDataContract CreateJsonDataContract(int id, DataContract trad } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void AddCollectionItemContractsToKnownDataContracts() { if (_traditionalDataContract.KnownDataContracts != null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEnumDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEnumDataContract.cs index 76f774cde91f09..5303f259d84a3b 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEnumDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEnumDataContract.cs @@ -10,6 +10,7 @@ internal sealed class JsonEnumDataContract : JsonDataContract private readonly JsonEnumDataContractCriticalHelper _helper; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonEnumDataContract(EnumDataContract traditionalDataContract) : base(new JsonEnumDataContractCriticalHelper(traditionalDataContract)) { @@ -19,6 +20,7 @@ public JsonEnumDataContract(EnumDataContract traditionalDataContract) public bool IsULong => _helper.IsULong; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { object enumValue; @@ -36,6 +38,7 @@ public JsonEnumDataContract(EnumDataContract traditionalDataContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteJsonValueCore(XmlWriterDelegator jsonWriter, object obj, XmlObjectSerializerWriteContextComplexJson? context, RuntimeTypeHandle declaredTypeHandle) { if (IsULong) @@ -53,6 +56,7 @@ private sealed class JsonEnumDataContractCriticalHelper : JsonDataContractCritic private readonly bool _isULong; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonEnumDataContractCriticalHelper(EnumDataContract traditionalEnumDataContract) : base(traditionalEnumDataContract) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatReaderGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatReaderGenerator.cs index ceee570b7475ef..8af5ffd850ce98 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatReaderGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatReaderGenerator.cs @@ -27,18 +27,21 @@ public JsonFormatReaderGenerator() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonFormatClassReaderDelegate GenerateClassReader(ClassDataContract classContract) { return _helper.GenerateClassReader(classContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonFormatCollectionReaderDelegate GenerateCollectionReader(CollectionDataContract collectionContract) { return _helper.GenerateCollectionReader(collectionContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader(CollectionDataContract collectionContract) { return _helper.GenerateGetOnlyCollectionReader(collectionContract); @@ -56,6 +59,7 @@ private sealed class CriticalHelper private ArgBuilder _emptyDictionaryStringArg = null!; // initialized in InitArgs [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonFormatClassReaderDelegate GenerateClassReader(ClassDataContract classContract) { _ilg = new CodeGenerator(); @@ -124,6 +128,7 @@ public JsonFormatClassReaderDelegate GenerateClassReader(ClassDataContract class } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonFormatCollectionReaderDelegate GenerateCollectionReader(CollectionDataContract collectionContract) { _ilg = GenerateCollectionReaderHelper(collectionContract, false /*isGetOnlyCollection*/); @@ -134,6 +139,7 @@ public JsonFormatCollectionReaderDelegate GenerateCollectionReader(CollectionDat } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader(CollectionDataContract collectionContract) { _ilg = GenerateCollectionReaderHelper(collectionContract, true /*isGetOnlyCollection*/); @@ -141,6 +147,7 @@ public JsonFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader return (JsonFormatGetOnlyCollectionReaderDelegate)_ilg.EndMethod(); } + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private CodeGenerator GenerateCollectionReaderHelper(CollectionDataContract collectionContract, bool isGetOnlyCollection) { _ilg = new CodeGenerator(); @@ -172,6 +179,7 @@ private CodeGenerator GenerateCollectionReaderHelper(CollectionDataContract coll return _ilg; } + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static void BeginMethod(CodeGenerator ilg, string methodName, Type delegateType, bool allowPrivateMemberAccess) { MethodInfo signature = JsonFormatWriterGenerator.GetInvokeMethod(delegateType); @@ -276,6 +284,7 @@ private bool InvokeFactoryMethod(ClassDataContract classContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ReadClass(ClassDataContract classContract) { if (classContract.HasExtensionData) @@ -301,6 +310,7 @@ private void ReadClass(ClassDataContract classContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ReadMembers(ClassDataContract classContract, LocalBuilder? extensionDataLocal) { int memberCount = classContract.MemberNames!.Length; @@ -349,6 +359,7 @@ private void ReadMembers(ClassDataContract classContract, LocalBuilder? extensio } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private int ReadMembers(ClassDataContract classContract, BitFlagsGenerator expectedElements, Label[] memberLabels, Label throwDuplicateMemberLabel, LocalBuilder memberIndexLocal) { @@ -457,6 +468,7 @@ private void ReadISerializable(ClassDataContract classContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private LocalBuilder ReadValue(Type type, string name) { LocalBuilder value = _ilg.DeclareLocal(type, "valueRead"); @@ -572,6 +584,7 @@ private void InternalDeserialize(LocalBuilder value, Type type, string name) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WrapNullableObject(LocalBuilder innerValue, LocalBuilder outerValue, int nullables) { Type innerType = innerValue.LocalType, outerType = outerValue.LocalType; @@ -587,6 +600,7 @@ private void WrapNullableObject(LocalBuilder innerValue, LocalBuilder outerValue } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ReadCollection(CollectionDataContract collectionContract) { Type type = collectionContract.UnderlyingType; @@ -717,6 +731,7 @@ private void ReadCollection(CollectionDataContract collectionContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ReadSimpleDictionary(CollectionDataContract collectionContract, Type keyValueType) { Type[] keyValueTypes = keyValueType.GetGenericArguments(); @@ -810,6 +825,7 @@ private void ReadSimpleDictionary(CollectionDataContract collectionContract, Typ } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ReadGetOnlyCollection(CollectionDataContract collectionContract) { Type type = collectionContract.UnderlyingType; @@ -899,6 +915,7 @@ private void ReadGetOnlyCollection(CollectionDataContract collectionContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private bool TryReadPrimitiveArray(Type itemType) { PrimitiveDataContract? primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(itemType); @@ -950,6 +967,7 @@ private bool TryReadPrimitiveArray(Type itemType) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private LocalBuilder ReadCollectionItem(CollectionDataContract collectionContract, Type itemType) { if (collectionContract.Kind == CollectionKind.Dictionary || collectionContract.Kind == CollectionKind.GenericDictionary) @@ -973,6 +991,7 @@ private LocalBuilder ReadCollectionItem(CollectionDataContract collectionContrac } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void StoreCollectionValue(LocalBuilder collection, LocalBuilder value, CollectionDataContract collectionContract) { if (collectionContract.Kind == CollectionKind.GenericDictionary || collectionContract.Kind == CollectionKind.Dictionary) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs index afe02b062b6a7b..2cef510cefa9c3 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs @@ -25,12 +25,14 @@ public JsonFormatWriterGenerator() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal JsonFormatClassWriterDelegate GenerateClassWriter(ClassDataContract classContract) { return _helper.GenerateClassWriter(classContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal JsonFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionDataContract collectionContract) { return _helper.GenerateCollectionWriter(collectionContract); @@ -58,6 +60,7 @@ private sealed class CriticalHelper private int _childElementIndex; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal JsonFormatClassWriterDelegate GenerateClassWriter(ClassDataContract classContract) { _ilg = new CodeGenerator(); @@ -84,6 +87,7 @@ internal JsonFormatClassWriterDelegate GenerateClassWriter(ClassDataContract cla } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal JsonFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionDataContract collectionContract) { _ilg = new CodeGenerator(); @@ -112,6 +116,7 @@ internal JsonFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionD return (JsonFormatCollectionWriterDelegate)_ilg.EndMethod(); } + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static void BeginMethod(CodeGenerator ilg, string methodName, Type delegateType, bool allowPrivateMemberAccess) { MethodInfo signature = GetInvokeMethod(delegateType); @@ -125,6 +130,7 @@ private static void BeginMethod(CodeGenerator ilg, string methodName, Type deleg } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void InitArgs(Type objType) { _xmlWriterArg = _ilg.GetArg(0); @@ -207,6 +213,7 @@ private void InvokeOnSerialized(ClassDataContract classContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WriteClass(ClassDataContract classContract) { InvokeOnSerializing(classContract); @@ -236,6 +243,7 @@ private void WriteClass(ClassDataContract classContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private int WriteMembers(ClassDataContract classContract, LocalBuilder? extensionDataLocal, ClassDataContract derivedMostClassContract) { int memberCount = (classContract.BaseContract == null) ? 0 : @@ -311,6 +319,7 @@ private LocalBuilder LoadMemberValue(DataMember member) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WriteCollection(CollectionDataContract collectionContract) { LocalBuilder itemName = _ilg.DeclareLocal(typeof(XmlDictionaryString), "itemName"); @@ -514,6 +523,7 @@ private void WriteCollection(CollectionDataContract collectionContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private bool TryWritePrimitive(Type type, LocalBuilder? value, MemberInfo? memberInfo, LocalBuilder? arrayItemIndex, LocalBuilder? name, int nameIndex) { PrimitiveDataContract? primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(type); @@ -561,6 +571,7 @@ private bool TryWritePrimitive(Type type, LocalBuilder? value, MemberInfo? membe } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private bool TryWritePrimitiveArray(Type type, Type itemType, LocalBuilder value, LocalBuilder itemName) { PrimitiveDataContract? primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(itemType); @@ -627,6 +638,7 @@ private void WriteObjectAttribute() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WriteValue(LocalBuilder memberValue) { Type memberType = memberValue.LocalType; @@ -722,6 +734,7 @@ private void InternalSerialize(MethodInfo methodInfo, LocalBuilder memberValue, } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private LocalBuilder UnwrapNullableObject(LocalBuilder memberValue)// Leaves !HasValue on stack { Type memberType = memberValue.LocalType; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs index 078c4c51793393..7a08e94154d9b1 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs @@ -11,12 +11,14 @@ namespace System.Runtime.Serialization.Json internal sealed class JsonObjectDataContract : JsonDataContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonObjectDataContract(DataContract traditionalDataContract) : base(traditionalDataContract) { } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { object? obj; @@ -54,6 +56,7 @@ public JsonObjectDataContract(DataContract traditionalDataContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteJsonValueCore(XmlWriterDelegator jsonWriter, object obj, XmlObjectSerializerWriteContextComplexJson? context, RuntimeTypeHandle declaredTypeHandle) { jsonWriter.WriteAttributeString(null, JsonGlobals.typeString, null, JsonGlobals.objectString); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonQNameDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonQNameDataContract.cs index 8bc4a2d1f50424..c00625e3590030 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonQNameDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonQNameDataContract.cs @@ -13,12 +13,14 @@ namespace System.Runtime.Serialization.Json internal sealed class JsonQNameDataContract : JsonDataContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonQNameDataContract(QNameDataContract traditionalQNameDataContract) : base(traditionalQNameDataContract) { } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { if (context == null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonStringDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonStringDataContract.cs index e65c58c9caed53..3e6e8f24d623e7 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonStringDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonStringDataContract.cs @@ -13,12 +13,14 @@ namespace System.Runtime.Serialization.Json internal sealed class JsonStringDataContract : JsonDataContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonStringDataContract(StringDataContract traditionalStringDataContract) : base(traditionalStringDataContract) { } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { if (context == null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonUriDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonUriDataContract.cs index bd4b68daf4103a..56540cca5bc335 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonUriDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonUriDataContract.cs @@ -8,12 +8,14 @@ namespace System.Runtime.Serialization.Json internal sealed class JsonUriDataContract : JsonDataContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonUriDataContract(UriDataContract traditionalUriDataContract) : base(traditionalUriDataContract) { } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { if (context == null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonXmlDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonXmlDataContract.cs index 99640f43461ac6..e085fb5cf74f94 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonXmlDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonXmlDataContract.cs @@ -12,12 +12,14 @@ namespace System.Runtime.Serialization.Json internal sealed class JsonXmlDataContract : JsonDataContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonXmlDataContract(XmlDataContract traditionalXmlDataContract) : base(traditionalXmlDataContract) { } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { string xmlContent = jsonReader.ReadElementContentAsString(); @@ -41,6 +43,7 @@ public JsonXmlDataContract(XmlDataContract traditionalXmlDataContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteJsonValueCore(XmlWriterDelegator jsonWriter, object obj, XmlObjectSerializerWriteContextComplexJson? context, RuntimeTypeHandle declaredTypeHandle) { DataContractSerializer dataContractSerializer = new DataContractSerializer(Type.GetTypeFromHandle(declaredTypeHandle)!, diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatReader.cs index 4161f53a29aece..971d808646e5f5 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatReader.cs @@ -27,6 +27,7 @@ public ReflectionJsonClassReader(ClassDataContract classDataContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public object ReflectionReadClass(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContextComplexJson? context, XmlDictionaryString emptyDictionaryString, XmlDictionaryString[]? memberNames) { Debug.Assert(_classContract != null); @@ -39,12 +40,14 @@ internal sealed class ReflectionJsonCollectionReader private readonly ReflectionReader _reflectionReader = new ReflectionJsonReader(); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public object ReflectionReadCollection(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContextComplexJson context, XmlDictionaryString emptyDictionaryString, XmlDictionaryString itemName, CollectionDataContract collectionContract) { return _reflectionReader.ReflectionReadCollection(xmlReader, context, itemName, emptyDictionaryString/*itemNamespace*/, collectionContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public void ReflectionReadGetOnlyCollection(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContextComplexJson context, XmlDictionaryString emptyDictionaryString, XmlDictionaryString itemName, CollectionDataContract collectionContract) { _reflectionReader.ReflectionReadGetOnlyCollection(xmlReader, context, itemName, emptyDictionaryString/*itemNamespace*/, collectionContract); @@ -54,6 +57,7 @@ public void ReflectionReadGetOnlyCollection(XmlReaderDelegator xmlReader, XmlObj internal sealed class ReflectionJsonReader : ReflectionReader { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override void ReflectionReadMembers(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString[] memberNames, XmlDictionaryString[]? memberNamespaces, ClassDataContract classContract, ref object obj) { var jsonContext = context as XmlObjectSerializerReadContextComplexJson; @@ -108,6 +112,7 @@ protected override string GetCollectionContractNamespace(CollectionDataContract } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override object? ReflectionReadDictionaryItem(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, CollectionDataContract collectionContract) { var jsonContext = context as XmlObjectSerializerReadContextComplexJson; @@ -120,6 +125,7 @@ protected override string GetCollectionContractNamespace(CollectionDataContract } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override bool ReflectionReadSpecialCollection(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, CollectionDataContract collectionContract, object? resultCollection) { var jsonContext = context as XmlObjectSerializerReadContextComplexJson; @@ -136,6 +142,7 @@ protected override bool ReflectionReadSpecialCollection(XmlReaderDelegator xmlRe } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ReadSimpleDictionary(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, CollectionDataContract collectionContract, Type keyValueType, object? dictionary) { Type[] keyValueTypes = keyValueType.GetGenericArguments(); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatWriter.cs index 74fbefe525979e..6d34048f6f213b 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatWriter.cs @@ -19,12 +19,14 @@ internal sealed class ReflectionJsonFormatWriter private readonly ReflectionJsonClassWriter _reflectionClassWriter = new ReflectionJsonClassWriter(); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public void ReflectionWriteClass(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContextComplexJson context, ClassDataContract classContract, XmlDictionaryString[]? memberNames) { _reflectionClassWriter.ReflectionWriteClass(xmlWriter, obj, context, classContract, memberNames); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static void ReflectionWriteCollection(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContextComplexJson context, CollectionDataContract collectionContract) { JsonWriterDelegator? jsonWriter = xmlWriter as JsonWriterDelegator; @@ -138,6 +140,7 @@ private static void ReflectionWriteObjectAttribute(XmlWriterDelegator xmlWriter) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static bool ReflectionTryWritePrimitiveArray(JsonWriterDelegator jsonWriter, object obj, Type underlyingType, Type itemType, XmlDictionaryString collectionItemName) { PrimitiveDataContract? primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(itemType); @@ -195,6 +198,7 @@ private static void ReflectionWriteArrayAttribute(XmlWriterDelegator xmlWriter) internal sealed class ReflectionJsonClassWriter : ReflectionClassWriter { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override int ReflectionWriteMembers(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context, ClassDataContract classContract, ClassDataContract derivedMostClassContract, int childElementIndex, XmlDictionaryString[]? memberNames) { Debug.Assert(memberNames != null); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerReadContextComplexJson.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerReadContextComplexJson.cs index 9c61887a150b4e..0a89fda737fc38 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerReadContextComplexJson.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerReadContextComplexJson.cs @@ -32,12 +32,14 @@ internal static XmlObjectSerializerReadContextComplexJson CreateContext(DataCont } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override object? ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader) { return DataContractJsonSerializer.ReadJsonValue(dataContract, reader, this); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public int GetJsonMemberIndex(XmlReaderDelegator xmlReader, XmlDictionaryString[] memberNames, int memberIndex, ExtensionDataObject? extensionData) { int length = memberNames.Length; @@ -256,6 +258,7 @@ protected override XmlReaderDelegator CreateReaderDelegatorForReader(XmlReader x } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type? type) { DataContract dataContract = base.GetDataContract(typeHandle, type); @@ -264,6 +267,7 @@ internal override DataContract GetDataContract(RuntimeTypeHandle typeHandle, Typ } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override DataContract GetDataContractSkipValidation(int typeId, RuntimeTypeHandle typeHandle, Type? type) { DataContract dataContract = base.GetDataContractSkipValidation(typeId, typeHandle, type); @@ -272,6 +276,7 @@ internal override DataContract GetDataContractSkipValidation(int typeId, Runtime } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override DataContract GetDataContract(int id, RuntimeTypeHandle typeHandle) { DataContract dataContract = base.GetDataContract(id, typeHandle); @@ -342,6 +347,7 @@ private static bool IsBitSet(byte[] bytes, int bitIndex) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override DataContract? ResolveDataContractFromRootDataContract(XmlQualifiedName typeQName) { return XmlObjectSerializerWriteContextComplexJson.ResolveJsonDataContractFromRootDataContract(this, typeQName, rootTypeDataContract!); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerWriteContextComplexJson.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerWriteContextComplexJson.cs index 8a644b13bec9e4..d43d2563909e12 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerWriteContextComplexJson.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerWriteContextComplexJson.cs @@ -91,6 +91,7 @@ internal static string TruncateDefaultDataContractNamespace(string dataContractN } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override bool WriteTypeInfo(XmlWriterDelegator writer, DataContract contract, DataContract declaredContract) { if (!((object.ReferenceEquals(contract.Name, declaredContract.Name) && @@ -142,6 +143,7 @@ private static void WriteTypeInfo(XmlWriterDelegator writer, string typeInformat } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override void WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle declaredTypeHandle) { JsonDataContract jsonDataContract = JsonDataContract.GetJsonDataContract(dataContract); @@ -164,6 +166,7 @@ internal static XmlDictionaryString CollectionItemName } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override void SerializeWithXsiType(XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle objectTypeHandle, Type? objectType, int declaredTypeID, RuntimeTypeHandle declaredTypeHandle, Type declaredType) { DataContract dataContract; @@ -196,6 +199,7 @@ protected override void SerializeWithXsiType(XmlWriterDelegator xmlWriter, objec } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void HandleCollectionAssignedToObject(Type declaredType, ref DataContract dataContract, ref object obj, ref bool verifyKnownType) { if ((declaredType != dataContract.UnderlyingType) && (dataContract is CollectionDataContract)) @@ -231,6 +235,7 @@ private void HandleCollectionAssignedToObject(Type declaredType, ref DataContrac } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void SerializeWithXsiTypeAtTopLevel(DataContract dataContract, XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle originalDeclaredTypeHandle, Type graphType) { bool verifyKnownType = false; @@ -252,6 +257,7 @@ internal override void SerializeWithXsiTypeAtTopLevel(DataContract dataContract, } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void VerifyType(DataContract dataContract, Type declaredType) { bool knownTypesAddedInCurrentScope = false; @@ -279,6 +285,7 @@ internal static void WriteJsonNameWithMapping(XmlWriterDelegator xmlWriter, XmlD } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void WriteExtensionDataTypeInfo(XmlWriterDelegator xmlWriter, IDataNode dataNode) { Type dataType = dataNode.DataType; @@ -322,6 +329,7 @@ internal static void VerifyObjectCompatibilityWithInterface(DataContract contrac } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void WriteJsonISerializable(XmlWriterDelegator xmlWriter, ISerializable obj) { Type objType = obj.GetType(); @@ -339,6 +347,7 @@ internal void WriteJsonISerializable(XmlWriterDelegator xmlWriter, ISerializable [return: NotNullIfNotNull(nameof(oldItemContract))] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract? GetRevisedItemContract(DataContract oldItemContract) { if ((oldItemContract != null) && @@ -351,6 +360,7 @@ internal void WriteJsonISerializable(XmlWriterDelegator xmlWriter, ISerializable } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type? type) { DataContract dataContract = base.GetDataContract(typeHandle, type); @@ -359,6 +369,7 @@ internal override DataContract GetDataContract(RuntimeTypeHandle typeHandle, Typ } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override DataContract GetDataContractSkipValidation(int typeId, RuntimeTypeHandle typeHandle, Type? type) { DataContract dataContract = base.GetDataContractSkipValidation(typeId, typeHandle, type); @@ -367,6 +378,7 @@ internal override DataContract GetDataContractSkipValidation(int typeId, Runtime } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override DataContract GetDataContract(int id, RuntimeTypeHandle typeHandle) { DataContract dataContract = base.GetDataContract(id, typeHandle); @@ -375,12 +387,14 @@ internal override DataContract GetDataContract(int id, RuntimeTypeHandle typeHan } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override DataContract? ResolveDataContractFromRootDataContract(XmlQualifiedName typeQName) { return XmlObjectSerializerWriteContextComplexJson.ResolveJsonDataContractFromRootDataContract(this, typeQName, rootTypeDataContract!); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract? ResolveJsonDataContractFromRootDataContract(XmlObjectSerializerContext context, XmlQualifiedName typeQName, DataContract rootTypeDataContract) { if (rootTypeDataContract.StableName == typeQName) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/KnownTypeDataContractResolver.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/KnownTypeDataContractResolver.cs index ab1d030913b578..391417d733f22c 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/KnownTypeDataContractResolver.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/KnownTypeDataContractResolver.cs @@ -18,6 +18,7 @@ internal KnownTypeDataContractResolver(XmlObjectSerializerContext context) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override bool TryResolveType(Type type, Type? declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString? typeName, out XmlDictionaryString? typeNamespace) { if (type == null) @@ -49,6 +50,7 @@ public override bool TryResolveType(Type type, Type? declaredType, DataContractR } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override Type? ResolveName(string typeName, string? typeNamespace, Type? declaredType, DataContractResolver knownTypeResolver) { if (typeName == null || typeNamespace == null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/PrimitiveDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/PrimitiveDataContract.cs index 8cd388f9601b03..538d8b43c1f69b 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/PrimitiveDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/PrimitiveDataContract.cs @@ -25,12 +25,14 @@ protected PrimitiveDataContract( } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static PrimitiveDataContract? GetPrimitiveDataContract(Type type) { return DataContract.GetBuiltInDataContract(type) as PrimitiveDataContract; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static PrimitiveDataContract? GetPrimitiveDataContract(string name, string ns) { return DataContract.GetBuiltInDataContract(name, ns) as PrimitiveDataContract; @@ -90,6 +92,7 @@ internal MethodInfo XmlFormatContentWriterMethod _helper.XmlFormatReaderMethod ??= typeof(XmlReaderDelegator).GetMethod(ReadMethodName, Globals.ScanAllMembers)!; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { xmlWriter.WriteAnyType(obj); @@ -163,12 +166,14 @@ internal CharDataContract(XmlDictionaryString name, XmlDictionaryString ns) : ba internal override string ReadMethodName { get { return "ReadElementContentAsChar"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteChar((char)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsChar() @@ -176,6 +181,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteChar((char)obj!, name, ns); @@ -197,12 +203,14 @@ public BooleanDataContract() : base(typeof(bool), DictionaryGlobals.BooleanLocal internal override string ReadMethodName { get { return "ReadElementContentAsBoolean"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteBoolean((bool)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsBoolean() @@ -210,6 +218,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteBoolean((bool)obj!, name, ns); @@ -226,12 +235,14 @@ public SignedByteDataContract() : base(typeof(sbyte), DictionaryGlobals.SignedBy internal override string ReadMethodName { get { return "ReadElementContentAsSignedByte"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteSignedByte((sbyte)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsSignedByte() @@ -239,6 +250,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteSignedByte((sbyte)obj!, name, ns); @@ -255,12 +267,14 @@ public UnsignedByteDataContract() : base(typeof(byte), DictionaryGlobals.Unsigne internal override string ReadMethodName { get { return "ReadElementContentAsUnsignedByte"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteUnsignedByte((byte)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsUnsignedByte() @@ -268,6 +282,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteUnsignedByte((byte)obj!, name, ns); @@ -284,12 +299,14 @@ public ShortDataContract() : base(typeof(short), DictionaryGlobals.ShortLocalNam internal override string ReadMethodName { get { return "ReadElementContentAsShort"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteShort((short)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsShort() @@ -297,6 +314,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteShort((short)obj!, name, ns); @@ -313,12 +331,14 @@ public UnsignedShortDataContract() : base(typeof(ushort), DictionaryGlobals.Unsi internal override string ReadMethodName { get { return "ReadElementContentAsUnsignedShort"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteUnsignedShort((ushort)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsUnsignedShort() @@ -326,6 +346,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteUnsignedShort((ushort)obj!, name, ns); @@ -364,18 +385,21 @@ internal override string WriteMethodName } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { throw new NotImplementedException(); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { throw new NotImplementedException(); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { throw new NotImplementedException(); @@ -392,12 +416,14 @@ public IntDataContract() : base(typeof(int), DictionaryGlobals.IntLocalName, Dic internal override string ReadMethodName { get { return "ReadElementContentAsInt"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteInt((int)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsInt() @@ -405,6 +431,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteInt((int)obj!, name, ns); @@ -421,12 +448,14 @@ public UnsignedIntDataContract() : base(typeof(uint), DictionaryGlobals.Unsigned internal override string ReadMethodName { get { return "ReadElementContentAsUnsignedInt"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteUnsignedInt((uint)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsUnsignedInt() @@ -434,6 +463,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteUnsignedInt((uint)obj!, name, ns); @@ -454,12 +484,14 @@ internal LongDataContract(XmlDictionaryString name, XmlDictionaryString ns) : ba internal override string ReadMethodName { get { return "ReadElementContentAsLong"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteLong((long)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsLong() @@ -467,6 +499,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteLong((long)obj!, name, ns); @@ -508,12 +541,14 @@ public UnsignedLongDataContract() : base(typeof(ulong), DictionaryGlobals.Unsign internal override string ReadMethodName { get { return "ReadElementContentAsUnsignedLong"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteUnsignedLong((ulong)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsUnsignedLong() @@ -521,6 +556,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteUnsignedLong((ulong)obj!, name, ns); @@ -537,12 +573,14 @@ public FloatDataContract() : base(typeof(float), DictionaryGlobals.FloatLocalNam internal override string ReadMethodName { get { return "ReadElementContentAsFloat"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteFloat((float)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsFloat() @@ -550,6 +588,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteFloat((float)obj!, name, ns); @@ -566,12 +605,14 @@ public DoubleDataContract() : base(typeof(double), DictionaryGlobals.DoubleLocal internal override string ReadMethodName { get { return "ReadElementContentAsDouble"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteDouble((double)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsDouble() @@ -579,6 +620,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteDouble((double)obj!, name, ns); @@ -595,12 +637,14 @@ public DecimalDataContract() : base(typeof(decimal), DictionaryGlobals.DecimalLo internal override string ReadMethodName { get { return "ReadElementContentAsDecimal"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteDecimal((decimal)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsDecimal() @@ -608,6 +652,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteDecimal((decimal)obj!, name, ns); @@ -628,12 +673,14 @@ public DateTimeDataContract() : base(typeof(DateTime), DictionaryGlobals.DateTim internal override string ReadMethodName { get { return "ReadElementContentAsDateTime"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteDateTime((DateTime)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsDateTime() @@ -641,6 +688,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteDateTime((DateTime)obj!, name, ns); @@ -661,12 +709,14 @@ internal StringDataContract(XmlDictionaryString name, XmlDictionaryString ns) : internal override string ReadMethodName { get { return "ReadElementContentAsString"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteString((string)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { if (context == null) @@ -680,6 +730,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { context.WriteString(xmlWriter, (string?)obj, name, ns); @@ -796,12 +847,14 @@ public ByteArrayDataContract() : base(typeof(byte[]), DictionaryGlobals.ByteArra internal override string ReadMethodName { get { return "ReadElementContentAsBase64"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteBase64((byte[])obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { if (context == null) @@ -815,6 +868,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteStartElement(name, ns); @@ -833,12 +887,14 @@ public ObjectDataContract() : base(typeof(object), DictionaryGlobals.ObjectLocal internal override string ReadMethodName { get { return "ReadElementContentAsAnyType"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { // write nothing } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { object obj; @@ -890,12 +946,14 @@ internal TimeSpanDataContract(XmlDictionaryString name, XmlDictionaryString ns) internal override string ReadMethodName { get { return "ReadElementContentAsTimeSpan"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteTimeSpan((TimeSpan)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsTimeSpan() @@ -903,6 +961,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator writer, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { writer.WriteTimeSpan((TimeSpan)obj!, name, ns); @@ -928,12 +987,14 @@ internal GuidDataContract(XmlDictionaryString name, XmlDictionaryString ns) : ba internal override string ReadMethodName { get { return "ReadElementContentAsGuid"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteGuid((Guid)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsGuid() @@ -941,6 +1002,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteGuid((Guid)obj!, name, ns); @@ -962,12 +1024,14 @@ public UriDataContract() : base(typeof(Uri), DictionaryGlobals.UriLocalName, Dic internal override string ReadMethodName { get { return "ReadElementContentAsUri"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteUri((Uri)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { if (context == null) @@ -981,6 +1045,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator writer, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { writer.WriteUri((Uri?)obj, name, ns); @@ -1002,12 +1067,14 @@ internal override bool IsPrimitive } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteQName((XmlQualifiedName)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { if (context == null) @@ -1021,6 +1088,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator writer, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { context.WriteQName(writer, (XmlQualifiedName?)obj, name, ns); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionClassWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionClassWriter.cs index 3cc3c97a7261e0..120986ebb66fba 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionClassWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionClassWriter.cs @@ -18,6 +18,7 @@ namespace System.Runtime.Serialization internal abstract class ReflectionClassWriter { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public void ReflectionWriteClass(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context, ClassDataContract classContract, XmlDictionaryString[]? memberNames) { InvokeOnSerializing(obj, context, classContract); @@ -40,6 +41,7 @@ public void ReflectionWriteClass(XmlWriterDelegator xmlWriter, object obj, XmlOb } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static void ReflectionWriteValue(XmlWriterDelegator xmlWriter, XmlObjectSerializerWriteContext context, Type type, object? value, bool writeXsiType, PrimitiveDataContract? primitiveContractForParamType) { Type memberType = type; @@ -104,6 +106,7 @@ public static void ReflectionWriteValue(XmlWriterDelegator xmlWriter, XmlObjectS } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected abstract int ReflectionWriteMembers(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context, ClassDataContract classContract, ClassDataContract derivedMostClassContract, int childElementIndex, XmlDictionaryString[]? memberNames); protected static object? ReflectionGetMemberValue(object obj, DataMember dataMember) @@ -112,6 +115,7 @@ public static void ReflectionWriteValue(XmlWriterDelegator xmlWriter, XmlObjectS } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected static bool ReflectionTryWritePrimitive(XmlWriterDelegator xmlWriter, XmlObjectSerializerWriteContext context, Type type, object? value, XmlDictionaryString name, XmlDictionaryString? ns, PrimitiveDataContract? primitiveContract) { if (primitiveContract == null || primitiveContract.UnderlyingType == Globals.TypeOfObject) @@ -164,6 +168,7 @@ private static object ResolveAdapterType(object obj, ClassDataContract classCont } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static void ReflectionInternalSerialize(XmlWriterDelegator xmlWriter, XmlObjectSerializerWriteContext context, object obj, bool isDeclaredType, bool writeXsiType, Type memberType, bool isNullableOfT = false) { if (isNullableOfT) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionReader.cs index b28f41784478c8..b559ba071bc8b4 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionReader.cs @@ -30,6 +30,7 @@ internal abstract class ReflectionReader s_getCollectionSetItemDelegateMethod ??= typeof(ReflectionReader).GetMethod(nameof(GetCollectionSetItemDelegate), BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static)!; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public object ReflectionReadClass(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context, XmlDictionaryString[]? memberNames, XmlDictionaryString[]? memberNamespaces, ClassDataContract classContract) { Debug.Assert(context != null); @@ -61,6 +62,7 @@ public object ReflectionReadClass(XmlReaderDelegator xmlReader, XmlObjectSeriali } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public void ReflectionReadGetOnlyCollection(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString collectionItemName, XmlDictionaryString collectionItemNamespace, CollectionDataContract collectionContract) { object? resultCollection = context.GetCollectionMember(); @@ -83,12 +85,14 @@ public void ReflectionReadGetOnlyCollection(XmlReaderDelegator xmlReader, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public object ReflectionReadCollection(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString collectionItemName, XmlDictionaryString collectionItemNamespace, CollectionDataContract collectionContract) { return ReflectionReadCollectionCore(xmlReader, context, collectionItemName, collectionItemNamespace, collectionContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private object ReflectionReadCollectionCore(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString collectionItemName, XmlDictionaryString collectionItemNamespace, CollectionDataContract collectionContract) { bool isArray = (collectionContract.Kind == CollectionKind.Array); @@ -114,6 +118,7 @@ private object ReflectionReadCollectionCore(XmlReaderDelegator xmlReader, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private CollectionReadItemDelegate GetCollectionReadItemDelegate(CollectionDataContract collectionContract) { CollectionReadItemDelegate collectionReadItemDelegate; @@ -136,6 +141,7 @@ private CollectionReadItemDelegate GetCollectionReadItemDelegate(CollectionDataC } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private object ReadCollectionItems(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString collectionItemName, XmlDictionaryString collectionItemNamespace, CollectionDataContract collectionContract, object resultCollection, bool isReadOnlyCollection) { string itemName = GetCollectionContractItemName(collectionContract); @@ -182,14 +188,17 @@ private object ReadCollectionItems(XmlReaderDelegator xmlReader, XmlObjectSerial } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected abstract void ReflectionReadMembers(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString[] memberNames, XmlDictionaryString[]? memberNamespaces, ClassDataContract classContract, ref object obj); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected abstract object? ReflectionReadDictionaryItem(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, CollectionDataContract collectionContract); protected abstract string GetCollectionContractItemName(CollectionDataContract collectionContract); protected abstract string GetCollectionContractNamespace(CollectionDataContract collectionContract); protected abstract string GetClassContractNamespace(ClassDataContract classContract); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected virtual bool ReflectionReadSpecialCollection(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, CollectionDataContract collectionContract, object? resultCollection) { return false; @@ -208,6 +217,7 @@ protected int ReflectionGetMembers(ClassDataContract classContract, DataMember[] } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected void ReflectionReadMember(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, ClassDataContract classContract, ref object obj, int memberIndex, DataMember[] members) { DataMember dataMember = members[memberIndex]; @@ -231,6 +241,7 @@ protected void ReflectionReadMember(XmlReaderDelegator xmlReader, XmlObjectSeria } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected object? ReflectionReadValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, Type type, string name, string ns, PrimitiveDataContract? primitiveContractForOriginalType = null) { object? value; @@ -258,6 +269,7 @@ protected void ReflectionReadMember(XmlReaderDelegator xmlReader, XmlObjectSeria } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private object? ReadItemOfPrimitiveType(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, Type type, string name, string ns, PrimitiveDataContract? primitiveContract, int nullables) { object? value; @@ -312,6 +324,7 @@ protected void ReflectionReadMember(XmlReaderDelegator xmlReader, XmlObjectSeria } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static object ReadISerializable(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, ClassDataContract classContract) { object obj; @@ -324,6 +337,7 @@ private static object ReadISerializable(XmlReaderDelegator xmlReader, XmlObjectS // This method is a perf optimization for collections. The original method is ReflectionReadValue. [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private CollectionReadItemDelegate GetReflectionReadValueDelegate(Type type) { int nullables = 0; @@ -361,6 +375,7 @@ private static void ReflectionSetMemberValue(ref object obj, object? memberValue } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private object? ReflectionReadValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, DataMember dataMember, string ns) { Type type = dataMember.MemberType; @@ -370,6 +385,7 @@ private static void ReflectionSetMemberValue(ref object obj, object? memberValue } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private object? ReflectionInternalDeserialize(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, CollectionDataContract? collectionContract, Type type, string name, string ns) { return context.InternalDeserialize(xmlReader, DataContract.GetId(type.TypeHandle), type.TypeHandle, name, ns); @@ -458,6 +474,7 @@ private static bool IsArrayLikeCollection(CollectionDataContract collectionContr } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static object ReflectionCreateCollection(CollectionDataContract collectionContract) { if (IsArrayLikeCollection(collectionContract)) @@ -505,6 +522,7 @@ private static object ReflectionCreateCollection(CollectionDataContract collecti return ((KeyValue)o).Value; } + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static CollectionSetItemDelegate GetCollectionSetItemDelegate(CollectionDataContract collectionContract, object resultCollectionObject, bool isReadOnlyCollection) { if (isReadOnlyCollection && collectionContract.Kind == CollectionKind.Array) @@ -604,6 +622,7 @@ private static CollectionSetItemDelegate GetCollectionSetItemDelegate(Collect } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static bool ReflectionTryReadPrimitiveArray(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString collectionItemName, XmlDictionaryString collectionItemNamespace, Type type, Type itemType, int arraySize, [NotNullWhen(true)] out object? resultArray) { resultArray = null; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatReader.cs index 98e3c1ea9a4ff0..2915cf01b42ef0 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatReader.cs @@ -27,23 +27,24 @@ public ReflectionXmlClassReader(ClassDataContract classDataContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public object ReflectionReadClass(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context, XmlDictionaryString[]? memberNames, XmlDictionaryString[]? memberNamespaces) { return _reflectionReader.ReflectionReadClass(xmlReader, context, memberNames, memberNamespaces, _classContract); } } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal sealed class ReflectionXmlCollectionReader { private readonly ReflectionReader _reflectionReader = new ReflectionXmlReader(); - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public object ReflectionReadCollection(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString itemName, XmlDictionaryString itemNamespace, CollectionDataContract collectionContract) { return _reflectionReader.ReflectionReadCollection(xmlReader, context, itemName, itemNamespace/*itemNamespace*/, collectionContract); } - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public void ReflectionReadGetOnlyCollection(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString itemName, XmlDictionaryString itemNs, CollectionDataContract collectionContract) { _reflectionReader.ReflectionReadGetOnlyCollection(xmlReader, context, itemName, itemNs, collectionContract); @@ -53,6 +54,7 @@ public void ReflectionReadGetOnlyCollection(XmlReaderDelegator xmlReader, XmlObj internal sealed class ReflectionXmlReader : ReflectionReader { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override void ReflectionReadMembers(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString[] memberNames, XmlDictionaryString[]? memberNamespaces, ClassDataContract classContract, ref object obj) { Debug.Assert(memberNamespaces != null); @@ -115,6 +117,7 @@ protected override string GetCollectionContractNamespace(CollectionDataContract } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override object? ReflectionReadDictionaryItem(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, CollectionDataContract collectionContract) { Debug.Assert(collectionContract.Kind == CollectionKind.Dictionary || collectionContract.Kind == CollectionKind.GenericDictionary); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatWriter.cs index dc70a9cb4ae833..e2a2694060fd3c 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatWriter.cs @@ -11,17 +11,17 @@ namespace System.Runtime.Serialization { + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal sealed class ReflectionXmlFormatWriter { private readonly ReflectionXmlClassWriter _reflectionClassWriter = new ReflectionXmlClassWriter(); - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public void ReflectionWriteClass(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context, ClassDataContract classContract) { _reflectionClassWriter.ReflectionWriteClass(xmlWriter, obj, context, classContract, null/*memberNames*/); } - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public static void ReflectionWriteCollection(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context, CollectionDataContract collectionDataContract) { XmlDictionaryString ns = collectionDataContract.Namespace; @@ -88,7 +88,6 @@ public static void ReflectionWriteCollection(XmlWriterDelegator xmlWriter, objec } } - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private static bool ReflectionTryWritePrimitiveArray(XmlWriterDelegator xmlWriter, object obj, Type type, Type itemType, XmlDictionaryString collectionItemName, XmlDictionaryString itemNamespace) { PrimitiveDataContract? primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(itemType); @@ -129,6 +128,7 @@ private static bool ReflectionTryWritePrimitiveArray(XmlWriterDelegator xmlWrite internal sealed class ReflectionXmlClassWriter : ReflectionClassWriter { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override int ReflectionWriteMembers(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context, ClassDataContract classContract, ClassDataContract derivedMostClassContract, int childElementIndex, XmlDictionaryString[]? emptyStringArray) { int memberCount = (classContract.BaseContract == null) ? 0 : diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaExporter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaExporter.cs index 1c76e5760047ef..52a40d34927904 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaExporter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaExporter.cs @@ -36,6 +36,7 @@ private XmlSchemaSet Schemas private XmlDocument XmlDoc => _xmlDoc ??= new XmlDocument(); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void Export() { try @@ -72,6 +73,7 @@ private void ExportSerializationSchema() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ExportDataContract(DataContract dataContract) { if (dataContract.IsBuiltInDataContract) @@ -112,6 +114,7 @@ private XmlSchemaElement ExportTopLevelElement(DataContract dataContract, XmlSch } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ExportClassDataContract(ClassDataContract classDataContract, XmlSchema schema) { XmlSchemaComplexType type = new XmlSchemaComplexType(); @@ -243,6 +246,7 @@ private static XmlElement ExportActualType(XmlQualifiedName typeName, XmlDocumen } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private XmlElement ExportGenericInfo(Type clrType, string elementName, string elementNs) { Type? itemType; @@ -336,6 +340,7 @@ private XmlElement ExportGenericInfo(Type clrType, string elementName, string el } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ExportCollectionDataContract(CollectionDataContract collectionDataContract, XmlSchema schema) { XmlSchemaComplexType type = new XmlSchemaComplexType(); @@ -438,6 +443,7 @@ internal static long GetDefaultEnumValue(bool isFlags, int index) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ExportISerializableDataContract(ClassDataContract dataContract, XmlSchema schema) { XmlSchemaComplexType type = new XmlSchemaComplexType(); @@ -478,6 +484,7 @@ private static XmlSchemaComplexContentExtension CreateTypeContent(XmlSchemaCompl } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ExportXmlDataContract(XmlDataContract dataContract) { XmlQualifiedName? typeQName; @@ -565,6 +572,7 @@ private static void ReprocessAll(XmlSchemaSet schemas)// and remove duplicate it } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static void GetXmlTypeInfo(Type type, out XmlQualifiedName stableName, out XmlSchemaType? xsdType, out bool hasRoot) { if (IsSpecialXmlType(type, out stableName!, out xsdType, out hasRoot)) @@ -577,6 +585,7 @@ internal static void GetXmlTypeInfo(Type type, out XmlQualifiedName stableName, } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static bool InvokeSchemaProviderMethod(Type clrType, XmlSchemaSet schemas, out XmlQualifiedName stableName, out XmlSchemaType? xsdType, out bool hasRoot) { xsdType = null; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SurrogateDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SurrogateDataContract.cs index 02b82aaee0d804..df47c081184ae0 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SurrogateDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SurrogateDataContract.cs @@ -14,6 +14,7 @@ internal sealed class SurrogateDataContract : DataContract private readonly SurrogateDataContractCriticalHelper _helper; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal SurrogateDataContract(Type type, ISerializationSurrogate serializationSurrogate) : base(new SurrogateDataContractCriticalHelper(type, serializationSurrogate)) { @@ -26,6 +27,7 @@ internal ISerializationSurrogate SerializationSurrogate } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { Debug.Assert(context != null); @@ -62,6 +64,7 @@ private void SerializationSurrogateGetObjectData(object obj, SerializationInfo s } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) { Debug.Assert(context != null); @@ -87,6 +90,7 @@ private sealed class SurrogateDataContractCriticalHelper : DataContract.DataCont private readonly ISerializationSurrogate serializationSurrogate; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal SurrogateDataContractCriticalHelper( [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] Type type, diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XPathQueryGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XPathQueryGenerator.cs index 364038c247a3e2..f1e6ced3e84aef 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XPathQueryGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XPathQueryGenerator.cs @@ -19,6 +19,7 @@ public static class XPathQueryGenerator private const string NsSeparator = ":"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static string CreateFromDataContractSerializer(Type type, MemberInfo[] pathToMember, out XmlNamespaceManager namespaces) { return CreateFromDataContractSerializer(type, pathToMember, null, out namespaces); @@ -26,6 +27,7 @@ public static string CreateFromDataContractSerializer(Type type, MemberInfo[] pa // Here you can provide your own root element Xpath which will replace the Xpath of the top level element [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static string CreateFromDataContractSerializer(Type type, MemberInfo[] pathToMember, StringBuilder? rootElementXpath, out XmlNamespaceManager namespaces) { ArgumentNullException.ThrowIfNull(type); @@ -54,6 +56,7 @@ public static string CreateFromDataContractSerializer(Type type, MemberInfo[] pa } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static DataContract ProcessDataContract(DataContract contract, ExportContext context, MemberInfo memberNode) { if (contract is ClassDataContract) @@ -64,6 +67,7 @@ private static DataContract ProcessDataContract(DataContract contract, ExportCon } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static DataContract ProcessClassDataContract(ClassDataContract contract, ExportContext context, MemberInfo memberNode) { string prefix = context.SetNamespace(contract.Namespace!.Value); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlDataContract.cs index 9ca8757798163f..5ab510e1def2ba 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlDataContract.cs @@ -22,6 +22,7 @@ internal sealed class XmlDataContract : DataContract private readonly XmlDataContractCriticalHelper _helper; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal XmlDataContract(Type type) : base(new XmlDataContractCriticalHelper(type)) { _helper = (base.Helper as XmlDataContractCriticalHelper)!; @@ -30,6 +31,7 @@ internal XmlDataContract(Type type) : base(new XmlDataContractCriticalHelper(typ public override DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { return _helper.KnownDataContracts; } @@ -86,6 +88,7 @@ internal bool IsTopLevelElementNullable internal CreateXmlSerializableDelegate CreateXmlSerializableDelegate { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { // We create XmlSerializableDelegate via CodeGen when CodeGen is enabled; @@ -127,6 +130,7 @@ private sealed class XmlDataContractCriticalHelper : DataContract.DataContractCr private XmlSchemaType? _xsdType; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal XmlDataContractCriticalHelper( [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] Type type) : base(type) @@ -174,6 +178,7 @@ internal XmlDataContractCriticalHelper( internal override DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (!_isKnownTypeAttributeChecked && UnderlyingType != null) @@ -254,6 +259,7 @@ internal CreateXmlSerializableDelegate? CreateXmlSerializableDelegate } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal CreateXmlSerializableDelegate GenerateCreateXmlSerializableDelegate() { Type type = this.UnderlyingType; @@ -371,6 +377,7 @@ internal IXmlSerializable ReflectionCreateXmlSerializable(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { if (context == null) @@ -380,6 +387,7 @@ public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, Xml } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) { object? o; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatGeneratorStatics.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatGeneratorStatics.cs index 6791ee9e45f586..0f861c09d2c879 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatGeneratorStatics.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatGeneratorStatics.cs @@ -206,6 +206,7 @@ internal static PropertyInfo NodeTypeProperty internal static ConstructorInfo HashtableCtor { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (s_hashtableCtor == null) @@ -759,6 +760,7 @@ internal static MethodInfo GetDefaultValueMethod [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2060:MakeGenericMethod", Justification = "The call to MakeGenericMethod is safe due to the fact that XmlObjectSerializerWriteContext.GetDefaultValue is not annotated.")] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static object? GetDefaultValue(Type type) { return GetDefaultValueMethod.MakeGenericMethod(type).Invoke(null, Array.Empty()); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatReaderGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatReaderGenerator.cs index 50ed91a7377f49..ed132bfa10589f 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatReaderGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatReaderGenerator.cs @@ -31,18 +31,21 @@ public XmlFormatReaderGenerator() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public XmlFormatClassReaderDelegate GenerateClassReader(ClassDataContract classContract) { return _helper.GenerateClassReader(classContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public XmlFormatCollectionReaderDelegate GenerateCollectionReader(CollectionDataContract collectionContract) { return _helper.GenerateCollectionReader(collectionContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public XmlFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader(CollectionDataContract collectionContract) { return _helper.GenerateGetOnlyCollectionReader(collectionContract); @@ -65,12 +68,14 @@ private sealed class CriticalHelper private ArgBuilder? _collectionContractArg; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static XmlFormatClassReaderDelegate CreateReflectionXmlClassReader(ClassDataContract classContract) { return new ReflectionXmlClassReader(classContract).ReflectionReadClass; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public XmlFormatClassReaderDelegate GenerateClassReader(ClassDataContract classContract) { if (DataContractSerializer.Option == SerializationOption.ReflectionOnly) @@ -161,12 +166,14 @@ public XmlFormatClassReaderDelegate GenerateClassReader(ClassDataContract classC } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static XmlFormatCollectionReaderDelegate CreateReflectionXmlCollectionReader() { return new ReflectionXmlCollectionReader().ReflectionReadCollection; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public XmlFormatCollectionReaderDelegate GenerateCollectionReader(CollectionDataContract collectionContract) { if (DataContractSerializer.Option == SerializationOption.ReflectionOnly) @@ -184,12 +191,14 @@ public XmlFormatCollectionReaderDelegate GenerateCollectionReader(CollectionData } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static XmlFormatGetOnlyCollectionReaderDelegate CreateReflectionReadGetOnlyCollectionReader() { return new ReflectionXmlCollectionReader().ReflectionReadGetOnlyCollection; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public XmlFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader(CollectionDataContract collectionContract) { if (DataContractSerializer.Option == SerializationOption.ReflectionOnly) @@ -204,6 +213,7 @@ public XmlFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader( } } + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private CodeGenerator GenerateCollectionReaderHelper(CollectionDataContract collectionContract, bool isGetOnlyCollection) { _ilg = new CodeGenerator(); @@ -340,6 +350,7 @@ private bool InvokeFactoryMethod(ClassDataContract classContract, LocalBuilder? } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ReadClass(ClassDataContract classContract) { if (classContract.HasExtensionData) @@ -365,6 +376,7 @@ private void ReadClass(ClassDataContract classContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ReadMembers(ClassDataContract classContract, LocalBuilder? extensionDataLocal) { int memberCount = classContract.MemberNames!.Length; @@ -397,6 +409,7 @@ private void ReadMembers(ClassDataContract classContract, LocalBuilder? extensio } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private int ReadMembers(ClassDataContract classContract, bool[] requiredMembers, Label[] memberLabels, LocalBuilder memberIndexLocal, LocalBuilder? requiredIndexLocal) { Debug.Assert(_objectLocal != null); @@ -485,6 +498,7 @@ private void ReadISerializable(ClassDataContract classContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private LocalBuilder ReadValue(Type type, string name, string ns) { LocalBuilder value = _ilg.DeclareLocal(type, "valueRead"); @@ -592,6 +606,7 @@ private void InternalDeserialize(LocalBuilder value, Type type, string name, str } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WrapNullableObject(LocalBuilder innerValue, LocalBuilder outerValue, int nullables) { Type innerType = innerValue.LocalType, outerType = outerValue.LocalType; @@ -608,6 +623,7 @@ private void WrapNullableObject(LocalBuilder innerValue, LocalBuilder outerValue [MemberNotNull(nameof(_objectLocal))] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ReadCollection(CollectionDataContract collectionContract) { Type type = collectionContract.UnderlyingType; @@ -749,6 +765,7 @@ private void ReadCollection(CollectionDataContract collectionContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ReadGetOnlyCollection(CollectionDataContract collectionContract) { Type type = collectionContract.UnderlyingType; @@ -812,6 +829,7 @@ private void ReadGetOnlyCollection(CollectionDataContract collectionContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private bool TryReadPrimitiveArray(Type type, Type itemType, LocalBuilder size) { Debug.Assert(_objectLocal != null); @@ -862,6 +880,7 @@ private bool TryReadPrimitiveArray(Type type, Type itemType, LocalBuilder size) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private LocalBuilder ReadCollectionItem(CollectionDataContract collectionContract, Type itemType, string itemName, string itemNs) { if (collectionContract.Kind == CollectionKind.Dictionary || collectionContract.Kind == CollectionKind.GenericDictionary) @@ -884,6 +903,7 @@ private LocalBuilder ReadCollectionItem(CollectionDataContract collectionContrac } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void StoreCollectionValue(LocalBuilder collection, LocalBuilder value, CollectionDataContract collectionContract) { Debug.Assert(collectionContract.AddMethod != null); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatWriterGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatWriterGenerator.cs index b510882ffec99d..af674cb274d862 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatWriterGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatWriterGenerator.cs @@ -28,12 +28,14 @@ public XmlFormatWriterGenerator() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal XmlFormatClassWriterDelegate GenerateClassWriter(ClassDataContract classContract) { return _helper.GenerateClassWriter(classContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal XmlFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionDataContract collectionContract) { return _helper.GenerateCollectionWriter(collectionContract); @@ -60,12 +62,14 @@ private sealed class CriticalHelper private int _childElementIndex; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static XmlFormatClassWriterDelegate CreateReflectionXmlFormatClassWriterDelegate() { return new ReflectionXmlFormatWriter().ReflectionWriteClass; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal XmlFormatClassWriterDelegate GenerateClassWriter(ClassDataContract classContract) { if (DataContractSerializer.Option == SerializationOption.ReflectionOnly) @@ -98,12 +102,14 @@ internal XmlFormatClassWriterDelegate GenerateClassWriter(ClassDataContract clas } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static XmlFormatCollectionWriterDelegate CreateReflectionXmlFormatCollectionWriterDelegate() { return ReflectionXmlFormatWriter.ReflectionWriteCollection; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal XmlFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionDataContract collectionContract) { if (DataContractSerializer.Option == SerializationOption.ReflectionOnly) @@ -136,6 +142,7 @@ internal XmlFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionDa } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void InitArgs(Type objType) { _xmlWriterArg = _ilg.GetArg(0); @@ -202,6 +209,7 @@ private void InvokeOnSerialized(ClassDataContract classContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WriteClass(ClassDataContract classContract) { InvokeOnSerializing(classContract); @@ -255,6 +263,7 @@ private void WriteClass(ClassDataContract classContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private int WriteMembers(ClassDataContract classContract, LocalBuilder? extensionDataLocal, ClassDataContract derivedMostClassContract) { int memberCount = (classContract.BaseContract == null) ? 0 : @@ -339,6 +348,7 @@ private LocalBuilder LoadMemberValue(DataMember member) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WriteCollection(CollectionDataContract collectionContract) { LocalBuilder itemNamespace = _ilg.DeclareLocal(typeof(XmlDictionaryString), "itemNamespace"); @@ -505,6 +515,7 @@ private void WriteCollection(CollectionDataContract collectionContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private bool TryWritePrimitive(Type type, LocalBuilder? value, MemberInfo? memberInfo, LocalBuilder? arrayItemIndex, LocalBuilder ns, LocalBuilder? name, int nameIndex) { PrimitiveDataContract? primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(type); @@ -552,6 +563,7 @@ private bool TryWritePrimitive(Type type, LocalBuilder? value, MemberInfo? membe } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private bool TryWritePrimitiveArray(Type type, Type itemType, LocalBuilder value, LocalBuilder itemName, LocalBuilder itemNamespace) { PrimitiveDataContract? primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(itemType); @@ -598,6 +610,7 @@ private bool TryWritePrimitiveArray(Type type, Type itemType, LocalBuilder value } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WriteValue(LocalBuilder memberValue, bool writeXsiType) { Type memberType = memberValue.LocalType; @@ -682,6 +695,7 @@ private void InternalSerialize(MethodInfo methodInfo, LocalBuilder memberValue, [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private LocalBuilder UnwrapNullableObject(LocalBuilder memberValue)// Leaves !HasValue on stack { Type memberType = memberValue.LocalType; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializer.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializer.cs index 8ba00fd7041759..2311de112f5f6e 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializer.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializer.cs @@ -17,13 +17,17 @@ namespace System.Runtime.Serialization public abstract class XmlObjectSerializer { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public abstract void WriteStartObject(XmlDictionaryWriter writer, object? graph); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public abstract void WriteObjectContent(XmlDictionaryWriter writer, object? graph); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public abstract void WriteEndObject(XmlDictionaryWriter writer); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual void WriteObject(Stream stream, object? graph) { ArgumentNullException.ThrowIfNull(stream); @@ -34,6 +38,7 @@ public virtual void WriteObject(Stream stream, object? graph) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual void WriteObject(XmlWriter writer, object? graph) { ArgumentNullException.ThrowIfNull(writer); @@ -42,6 +47,7 @@ public virtual void WriteObject(XmlWriter writer, object? graph) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual void WriteStartObject(XmlWriter writer, object? graph) { ArgumentNullException.ThrowIfNull(writer); @@ -50,6 +56,7 @@ public virtual void WriteStartObject(XmlWriter writer, object? graph) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual void WriteObjectContent(XmlWriter writer, object? graph) { ArgumentNullException.ThrowIfNull(writer); @@ -58,6 +65,7 @@ public virtual void WriteObjectContent(XmlWriter writer, object? graph) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual void WriteEndObject(XmlWriter writer) { ArgumentNullException.ThrowIfNull(writer); @@ -66,18 +74,21 @@ public virtual void WriteEndObject(XmlWriter writer) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual void WriteObject(XmlDictionaryWriter writer, object? graph) { WriteObjectHandleExceptions(new XmlWriterDelegator(writer), graph); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void WriteObjectHandleExceptions(XmlWriterDelegator writer, object? graph) { WriteObjectHandleExceptions(writer, graph, null); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void WriteObjectHandleExceptions(XmlWriterDelegator writer, object? graph, DataContractResolver? dataContractResolver) { ArgumentNullException.ThrowIfNull(writer); @@ -99,6 +110,7 @@ internal void WriteObjectHandleExceptions(XmlWriterDelegator writer, object? gra internal virtual DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { return null; @@ -106,6 +118,7 @@ internal virtual DataContractDictionary? KnownDataContracts } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void InternalWriteObject(XmlWriterDelegator writer, object? graph) { WriteStartObject(writer.Writer, graph); @@ -114,24 +127,28 @@ internal virtual void InternalWriteObject(XmlWriterDelegator writer, object? gra } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void InternalWriteObject(XmlWriterDelegator writer, object? graph, DataContractResolver? dataContractResolver) { InternalWriteObject(writer, graph); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void InternalWriteStartObject(XmlWriterDelegator writer, object? graph) { DiagnosticUtility.DebugAssert("XmlObjectSerializer.InternalWriteStartObject should never get called"); throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException()); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void InternalWriteObjectContent(XmlWriterDelegator writer, object? graph) { DiagnosticUtility.DebugAssert("XmlObjectSerializer.InternalWriteObjectContent should never get called"); throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException()); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void InternalWriteEndObject(XmlWriterDelegator writer) { DiagnosticUtility.DebugAssert("XmlObjectSerializer.InternalWriteEndObject should never get called"); @@ -139,6 +156,7 @@ internal virtual void InternalWriteEndObject(XmlWriterDelegator writer) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void WriteStartObjectHandleExceptions(XmlWriterDelegator writer, object? graph) { ArgumentNullException.ThrowIfNull(writer); @@ -158,6 +176,7 @@ internal void WriteStartObjectHandleExceptions(XmlWriterDelegator writer, object } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void WriteObjectContentHandleExceptions(XmlWriterDelegator writer, object? graph) { ArgumentNullException.ThrowIfNull(writer); @@ -179,6 +198,7 @@ internal void WriteObjectContentHandleExceptions(XmlWriterDelegator writer, obje } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void WriteEndObjectHandleExceptions(XmlWriterDelegator writer) { ArgumentNullException.ThrowIfNull(writer); @@ -244,6 +264,7 @@ internal static bool IsContractDeclared(DataContract contract, DataContract decl } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual object? ReadObject(Stream stream) { ArgumentNullException.ThrowIfNull(stream); @@ -252,6 +273,7 @@ internal static bool IsContractDeclared(DataContract contract, DataContract decl } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual object? ReadObject(XmlReader reader) { ArgumentNullException.ThrowIfNull(reader); @@ -260,12 +282,14 @@ internal static bool IsContractDeclared(DataContract contract, DataContract decl } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual object? ReadObject(XmlDictionaryReader reader) { return ReadObjectHandleExceptions(new XmlReaderDelegator(reader), true /*verifyObjectName*/); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual object? ReadObject(XmlReader reader, bool verifyObjectName) { ArgumentNullException.ThrowIfNull(reader); @@ -274,9 +298,11 @@ internal static bool IsContractDeclared(DataContract contract, DataContract decl } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public abstract object? ReadObject(XmlDictionaryReader reader, bool verifyObjectName); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual bool IsStartObject(XmlReader reader) { ArgumentNullException.ThrowIfNull(reader); @@ -285,21 +311,25 @@ public virtual bool IsStartObject(XmlReader reader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public abstract bool IsStartObject(XmlDictionaryReader reader); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual object? InternalReadObject(XmlReaderDelegator reader, bool verifyObjectName) { return ReadObject(reader.UnderlyingReader, verifyObjectName); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual object? InternalReadObject(XmlReaderDelegator reader, bool verifyObjectName, DataContractResolver? dataContractResolver) { return InternalReadObject(reader, verifyObjectName); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual bool InternalIsStartObject(XmlReaderDelegator reader) { DiagnosticUtility.DebugAssert("XmlObjectSerializer.InternalIsStartObject should never get called"); @@ -307,12 +337,14 @@ internal virtual bool InternalIsStartObject(XmlReaderDelegator reader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal object? ReadObjectHandleExceptions(XmlReaderDelegator reader, bool verifyObjectName) { return ReadObjectHandleExceptions(reader, verifyObjectName, null); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal object? ReadObjectHandleExceptions(XmlReaderDelegator reader, bool verifyObjectName, DataContractResolver? dataContractResolver) { ArgumentNullException.ThrowIfNull(reader); @@ -332,6 +364,7 @@ internal virtual bool InternalIsStartObject(XmlReaderDelegator reader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal bool IsStartObjectHandleExceptions(XmlReaderDelegator reader) { ArgumentNullException.ThrowIfNull(reader); @@ -361,6 +394,7 @@ internal static bool IsStartElement(XmlReaderDelegator reader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static bool IsRootElement(XmlReaderDelegator reader, DataContract contract, XmlDictionaryString? name, XmlDictionaryString? ns) { reader.MoveToElement(); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerContext.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerContext.cs index 66ce7c6f8f8c06..5c4fb25fac5b9b 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerContext.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerContext.cs @@ -99,12 +99,14 @@ protected DataContractResolver? DataContractResolver _knownTypeResolver ??= new KnownTypeDataContractResolver(this); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal DataContract GetDataContract(Type type) { return GetDataContract(type.TypeHandle, type); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type? type) { if (IsGetOnlyCollection) @@ -118,6 +120,7 @@ internal virtual DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual DataContract GetDataContractSkipValidation(int typeId, RuntimeTypeHandle typeHandle, Type? type) { if (IsGetOnlyCollection) @@ -131,6 +134,7 @@ internal virtual DataContract GetDataContractSkipValidation(int typeId, RuntimeT } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual DataContract GetDataContract(int id, RuntimeTypeHandle typeHandle) { if (IsGetOnlyCollection) @@ -144,6 +148,7 @@ internal virtual DataContract GetDataContract(int id, RuntimeTypeHandle typeHand } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void CheckIfTypeSerializable(Type memberType, bool isMemberTypeSerializable) { if (!isMemberTypeSerializable) @@ -151,6 +156,7 @@ internal virtual void CheckIfTypeSerializable(Type memberType, bool isMemberType } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual Type GetSurrogatedType(Type type) { return type; @@ -158,6 +164,7 @@ internal virtual Type GetSurrogatedType(Type type) internal virtual DataContractDictionary? SerializerKnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { // This field must be initialized during construction by serializers using data contracts. @@ -171,6 +178,7 @@ internal virtual DataContractDictionary? SerializerKnownDataContracts } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private DataContract? GetDataContractFromSerializerKnownTypes(XmlQualifiedName qname) { DataContractDictionary? serializerKnownDataContracts = this.SerializerKnownDataContracts; @@ -181,6 +189,7 @@ internal virtual DataContractDictionary? SerializerKnownDataContracts } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContractDictionary? GetDataContractsForKnownTypes(IList knownTypeList) { if (knownTypeList == null) return null; @@ -198,6 +207,7 @@ internal virtual DataContractDictionary? SerializerKnownDataContracts } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal bool IsKnownType(DataContract dataContract, DataContractDictionary? knownDataContracts, Type? declaredType) { bool knownTypesAddedInCurrentScope = false; @@ -217,6 +227,7 @@ internal bool IsKnownType(DataContract dataContract, DataContractDictionary? kno } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal bool IsKnownType(DataContract dataContract, Type? declaredType) { DataContract? knownContract = ResolveDataContractFromKnownTypes(dataContract.StableName.Name, dataContract.StableName.Namespace, null /*memberTypeContract*/, declaredType); @@ -224,6 +235,7 @@ internal bool IsKnownType(DataContract dataContract, Type? declaredType) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal Type? ResolveNameFromKnownTypes(XmlQualifiedName typeName) { DataContract? dataContract = ResolveDataContractFromKnownTypes(typeName); @@ -231,12 +243,14 @@ internal bool IsKnownType(DataContract dataContract, Type? declaredType) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private DataContract? ResolveDataContractFromKnownTypes(XmlQualifiedName typeName) => PrimitiveDataContract.GetPrimitiveDataContract(typeName.Name, typeName.Namespace) ?? scopedKnownTypes.GetDataContract(typeName) ?? GetDataContractFromSerializerKnownTypes(typeName); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected DataContract? ResolveDataContractFromKnownTypes(string typeName, string? typeNs, DataContract? memberTypeContract, Type? declaredType) { XmlQualifiedName qname = new XmlQualifiedName(typeName, typeNs); @@ -270,6 +284,7 @@ internal bool IsKnownType(DataContract dataContract, Type? declaredType) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected virtual DataContract? ResolveDataContractFromRootDataContract(XmlQualifiedName typeQName) { CollectionDataContract? collectionContract = rootTypeDataContract as CollectionDataContract; @@ -286,6 +301,7 @@ internal bool IsKnownType(DataContract dataContract, Type? declaredType) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void PushKnownTypes(DataContract dc) { if (dc != null && dc.KnownDataContracts != null) @@ -295,6 +311,7 @@ internal void PushKnownTypes(DataContract dc) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void PopKnownTypes(DataContract dc) { if (dc != null && dc.KnownDataContracts != null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContext.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContext.cs index 09fb6e269353de..0a4a686e3d7efa 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContext.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContext.cs @@ -82,6 +82,7 @@ internal XmlObjectSerializerReadContext(DataContractSerializer serializer, DataC } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual object? InternalDeserialize(XmlReaderDelegator xmlReader, int id, RuntimeTypeHandle declaredTypeHandle, string name, string ns) { DataContract dataContract = GetDataContract(id, declaredTypeHandle); @@ -89,6 +90,7 @@ internal XmlObjectSerializerReadContext(DataContractSerializer serializer, DataC } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual object? InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, string name, string ns) { DataContract dataContract = GetDataContract(declaredType); @@ -96,6 +98,7 @@ internal XmlObjectSerializerReadContext(DataContractSerializer serializer, DataC } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual object? InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract? dataContract, string? name, string? ns) { dataContract ??= GetDataContract(declaredType); @@ -128,6 +131,7 @@ protected bool TryHandleNullOrRef(XmlReaderDelegator reader, Type declaredType, } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected object? InternalDeserialize(XmlReaderDelegator reader, string? name, string? ns, Type declaredType, ref DataContract dataContract) { object? retObj = null; @@ -208,6 +212,7 @@ internal static bool MoveToNextElement(XmlReaderDelegator xmlReader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal int GetMemberIndex(XmlReaderDelegator xmlReader, XmlDictionaryString[] memberNames, XmlDictionaryString[] memberNamespaces, int memberIndex, ExtensionDataObject? extensionData) { for (int i = memberIndex + 1; i < memberNames.Length; i++) @@ -220,6 +225,7 @@ internal int GetMemberIndex(XmlReaderDelegator xmlReader, XmlDictionaryString[] } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal int GetMemberIndexWithRequiredMembers(XmlReaderDelegator xmlReader, XmlDictionaryString[] memberNames, XmlDictionaryString[] memberNamespaces, int memberIndex, int requiredIndex, ExtensionDataObject? extensionData) { for (int i = memberIndex + 1; i < memberNames.Length; i++) @@ -251,6 +257,7 @@ internal static void ThrowRequiredMemberMissingException(XmlReaderDelegator xmlR } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected void HandleMemberNotFound(XmlReaderDelegator xmlReader, ExtensionDataObject? extensionData, int memberIndex) { xmlReader.MoveToContent(); @@ -264,6 +271,7 @@ protected void HandleMemberNotFound(XmlReaderDelegator xmlReader, ExtensionDataO } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void HandleUnknownElement(XmlReaderDelegator xmlReader, ExtensionDataObject extensionData, int memberIndex) { extensionData.Members ??= new List(); @@ -277,6 +285,7 @@ internal void SkipUnknownElement(XmlReaderDelegator xmlReader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal string ReadIfNullOrRef(XmlReaderDelegator xmlReader, Type memberType, bool isMemberTypeSerializable) { Debug.Assert(attributes != null); @@ -453,6 +462,7 @@ internal void CheckEndOfArray(XmlReaderDelegator xmlReader, int arraySize, XmlDi } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal object? ReadIXmlSerializable(XmlReaderDelegator xmlReader, XmlDataContract xmlDataContract, bool isMemberType) { _xmlSerializableReader ??= new XmlSerializableReader(); @@ -460,12 +470,14 @@ internal void CheckEndOfArray(XmlReaderDelegator xmlReader, int arraySize, XmlDi } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static object? ReadRootIXmlSerializable(XmlReaderDelegator xmlReader, XmlDataContract xmlDataContract, bool isMemberType) { return ReadIXmlSerializable(new XmlSerializableReader(), xmlReader, xmlDataContract, isMemberType); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static object? ReadIXmlSerializable(XmlSerializableReader xmlSerializableReader, XmlReaderDelegator xmlReader, XmlDataContract xmlDataContract, bool isMemberType) { object? obj = null; @@ -497,6 +509,7 @@ internal void CheckEndOfArray(XmlReaderDelegator xmlReader, int arraySize, XmlDi } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public SerializationInfo ReadSerializationInfo(XmlReaderDelegator xmlReader, Type type) { var serInfo = new SerializationInfo(type, XmlObjectSerializer.FormatterConverter); @@ -541,6 +554,7 @@ public SerializationInfo ReadSerializationInfo(XmlReaderDelegator xmlReader, Typ } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected virtual DataContract? ResolveDataContractFromTypeName() { Debug.Assert(attributes != null); @@ -549,6 +563,7 @@ public SerializationInfo ReadSerializationInfo(XmlReaderDelegator xmlReader, Typ } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private ExtensionDataMember ReadExtensionDataMember(XmlReaderDelegator xmlReader, int memberIndex) { var member = new ExtensionDataMember(xmlReader.LocalName, xmlReader.NamespaceURI) @@ -561,6 +576,7 @@ private ExtensionDataMember ReadExtensionDataMember(XmlReaderDelegator xmlReader } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public IDataNode? ReadExtensionDataValue(XmlReaderDelegator xmlReader) { ReadAttributes(xmlReader); @@ -659,6 +675,7 @@ protected virtual void StartReadExtensionDataValue(XmlReaderDelegator xmlReader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private IDataNode ReadExtensionDataValue(XmlReaderDelegator xmlReader, string? dataContractName, string? dataContractNamespace) { Debug.Assert(attributes != null); @@ -733,6 +750,7 @@ private IDataNode ReadUnknownPrimitiveData(XmlReaderDelegator xmlReader, Type ty } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private ClassDataNode ReadUnknownClassData(XmlReaderDelegator xmlReader, string? dataContractName, string? dataContractNamespace) { var dataNode = new ClassDataNode(); @@ -753,6 +771,7 @@ private ClassDataNode ReadUnknownClassData(XmlReaderDelegator xmlReader, string? } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private CollectionDataNode ReadUnknownCollectionData(XmlReaderDelegator xmlReader, string? dataContractName, string? dataContractNamespace) { Debug.Assert(attributes != null); @@ -809,6 +828,7 @@ private CollectionDataNode ReadUnknownCollectionData(XmlReaderDelegator xmlReade } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private ISerializableDataNode ReadUnknownISerializableData(XmlReaderDelegator xmlReader, string? dataContractName, string? dataContractNamespace) { Debug.Assert(attributes != null); @@ -887,6 +907,7 @@ private IDataNode ReadUnknownXmlData(XmlReaderDelegator xmlReader, string? dataC // items be unqualified. If the XML only contains elements (no attributes or other nodes) is recognized as a // class/class hierarchy. Otherwise it is deserialized as XML. [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private IDataNode ReadAndResolveUnknownXmlData(XmlReaderDelegator xmlReader, IDictionary? namespaces, string? dataContractName, string? dataContractNamespace) { @@ -1033,6 +1054,7 @@ internal static Exception CreateSerializationException(string message) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected virtual object? ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader) { return dataContract.ReadXmlValue(reader, this); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContextComplex.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContextComplex.cs index 53f4021549ec10..8867f198f62234 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContextComplex.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContextComplex.cs @@ -32,6 +32,7 @@ internal override SerializationMode Mode } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override object? InternalDeserialize(XmlReaderDelegator xmlReader, int declaredTypeID, RuntimeTypeHandle declaredTypeHandle, string name, string ns) { if (_mode == SerializationMode.SharedContract) @@ -48,6 +49,7 @@ internal override SerializationMode Mode } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override object? InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, string name, string ns) { if (_mode == SerializationMode.SharedContract) @@ -64,6 +66,7 @@ internal override SerializationMode Mode } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override object? InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract? dataContract, string? name, string? ns) { if (_mode == SerializationMode.SharedContract) @@ -80,6 +83,7 @@ internal override SerializationMode Mode } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private object? InternalDeserializeInSharedTypeMode(XmlReaderDelegator xmlReader, int declaredTypeID, Type declaredType, string? name, string? ns) { Debug.Assert(attributes != null); @@ -122,6 +126,7 @@ internal override SerializationMode Mode } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private object? InternalDeserializeWithSurrogate(XmlReaderDelegator xmlReader, Type declaredType, DataContract? surrogateDataContract, string? name, string? ns) { Debug.Assert(_serializationSurrogateProvider != null); @@ -149,6 +154,7 @@ private static Type ResolveDataContractTypeInSharedTypeMode(string assemblyName, } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private DataContract? ResolveDataContractInSharedTypeMode(string assemblyName, string typeName, out Assembly assembly, out Type type) { type = ResolveDataContractTypeInSharedTypeMode(assemblyName, typeName, out assembly); @@ -161,6 +167,7 @@ private static Type ResolveDataContractTypeInSharedTypeMode(string assemblyName, } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override DataContract? ResolveDataContractFromTypeName() { Debug.Assert(attributes != null); @@ -180,6 +187,7 @@ private static Type ResolveDataContractTypeInSharedTypeMode(string assemblyName, } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void CheckIfTypeSerializable(Type memberType, bool isMemberTypeSerializable) { if (_serializationSurrogateProvider != null) @@ -196,6 +204,7 @@ internal override void CheckIfTypeSerializable(Type memberType, bool isMemberTyp } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override Type GetSurrogatedType(Type type) { if (_serializationSurrogateProvider == null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContext.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContext.cs index 00abd388dc0920..727d65a5238389 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContext.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContext.cs @@ -80,6 +80,7 @@ internal void ResetIsGetOnlyCollection() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void InternalSerializeReference(XmlWriterDelegator xmlWriter, object obj, bool isDeclaredType, bool writeXsiType, int declaredTypeID, RuntimeTypeHandle declaredTypeHandle) { if (!OnHandleReference(xmlWriter, obj, true /*canContainCyclicReference*/)) @@ -88,6 +89,7 @@ internal void InternalSerializeReference(XmlWriterDelegator xmlWriter, object ob } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void InternalSerialize(XmlWriterDelegator xmlWriter, object obj, bool isDeclaredType, bool writeXsiType, int declaredTypeID, RuntimeTypeHandle declaredTypeHandle) { if (writeXsiType) @@ -118,6 +120,7 @@ internal virtual void InternalSerialize(XmlWriterDelegator xmlWriter, object obj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle declaredTypeHandle) { if (OnHandleIsReference(xmlWriter, dataContract, obj)) @@ -135,6 +138,7 @@ internal void SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelega } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void SerializeWithXsiTypeAtTopLevel(DataContract dataContract, XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle originalDeclaredTypeHandle, Type graphType) { Debug.Assert(rootTypeDataContract != null); @@ -157,6 +161,7 @@ internal virtual void SerializeWithXsiTypeAtTopLevel(DataContract dataContract, } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected virtual void SerializeWithXsiType(XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle objectTypeHandle, Type? objectType, int declaredTypeID, RuntimeTypeHandle declaredTypeHandle, Type declaredType) { bool verifyKnownType = false; @@ -221,6 +226,7 @@ internal bool OnHandleIsReference(XmlWriterDelegator xmlWriter, DataContract con } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected void SerializeAndVerifyType(DataContract dataContract, XmlWriterDelegator xmlWriter, object obj, bool verifyKnownType, RuntimeTypeHandle declaredTypeHandle, Type declaredType) { bool knownTypesAddedInCurrentScope = false; @@ -281,6 +287,7 @@ internal virtual void WriteString(XmlWriterDelegator xmlWriter, string value) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void WriteString(XmlWriterDelegator xmlWriter, string? value, XmlDictionaryString name, XmlDictionaryString? ns) { if (value == null) @@ -299,6 +306,7 @@ internal virtual void WriteBase64(XmlWriterDelegator xmlWriter, byte[] value) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void WriteBase64(XmlWriterDelegator xmlWriter, byte[] value, XmlDictionaryString name, XmlDictionaryString ns) { if (value == null) @@ -317,6 +325,7 @@ internal virtual void WriteUri(XmlWriterDelegator xmlWriter, Uri value) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void WriteUri(XmlWriterDelegator xmlWriter, Uri value, XmlDictionaryString name, XmlDictionaryString ns) { if (value == null) @@ -335,6 +344,7 @@ internal virtual void WriteQName(XmlWriterDelegator xmlWriter, XmlQualifiedName } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void WriteQName(XmlWriterDelegator xmlWriter, XmlQualifiedName? value, XmlDictionaryString name, XmlDictionaryString? ns) { if (value == null) @@ -385,6 +395,7 @@ internal virtual void OnEndHandleReference(XmlWriterDelegator xmlWriter, object } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void WriteNull(XmlWriterDelegator xmlWriter, Type memberType, bool isMemberTypeSerializable) { CheckIfTypeSerializable(memberType, isMemberTypeSerializable); @@ -392,6 +403,7 @@ internal void WriteNull(XmlWriterDelegator xmlWriter, Type memberType, bool isMe } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void WriteNull(XmlWriterDelegator xmlWriter, Type memberType, bool isMemberTypeSerializable, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteStartElement(name, ns); @@ -495,6 +507,7 @@ internal static void GetObjectData(ISerializable obj, SerializationInfo serInfo, } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public void WriteISerializable(XmlWriterDelegator xmlWriter, ISerializable obj) { Type objType = obj.GetType(); @@ -511,6 +524,7 @@ public void WriteISerializable(XmlWriterDelegator xmlWriter, ISerializable obj) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void WriteSerializationInfo(XmlWriterDelegator xmlWriter, Type objType, SerializationInfo serInfo) { if (DataContract.GetClrTypeFullName(objType) != serInfo.FullTypeName) @@ -552,6 +566,7 @@ internal void WriteSerializationInfo(XmlWriterDelegator xmlWriter, Type objType, } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected virtual void WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle declaredTypeHandle) { dataContract.WriteXmlValue(xmlWriter, obj, this); @@ -563,6 +578,7 @@ protected virtual void WriteNull(XmlWriterDelegator xmlWriter) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WriteResolvedTypeInfo(XmlWriterDelegator writer, Type objectType, Type declaredType) { XmlDictionaryString? typeName, typeNamespace; @@ -573,6 +589,7 @@ private void WriteResolvedTypeInfo(XmlWriterDelegator writer, Type objectType, T } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private bool ResolveType(Type objectType, Type declaredType, [NotNullWhen(true)] out XmlDictionaryString? typeName, [NotNullWhen(true)] out XmlDictionaryString? typeNamespace) { Debug.Assert(DataContractResolver != null); @@ -600,6 +617,7 @@ private bool ResolveType(Type objectType, Type declaredType, [NotNullWhen(true)] } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected virtual bool WriteTypeInfo(XmlWriterDelegator writer, DataContract contract, DataContract declaredContract) { if (!XmlObjectSerializer.IsContractDeclared(contract, declaredContract)) @@ -629,6 +647,7 @@ protected virtual void WriteTypeInfo(XmlWriterDelegator writer, XmlDictionaryStr } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public void WriteExtensionData(XmlWriterDelegator xmlWriter, ExtensionDataObject? extensionData, int memberIndex) { if (IgnoreExtensionDataObject || extensionData == null) @@ -649,6 +668,7 @@ public void WriteExtensionData(XmlWriterDelegator xmlWriter, ExtensionDataObject } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WriteExtensionDataMember(XmlWriterDelegator xmlWriter, ExtensionDataMember member) { xmlWriter.WriteStartElement(member.Name, member.Namespace); @@ -658,6 +678,7 @@ private void WriteExtensionDataMember(XmlWriterDelegator xmlWriter, ExtensionDat } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void WriteExtensionDataTypeInfo(XmlWriterDelegator xmlWriter, IDataNode dataNode) { if (dataNode.DataContractName != null) @@ -667,6 +688,7 @@ internal virtual void WriteExtensionDataTypeInfo(XmlWriterDelegator xmlWriter, I } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void WriteExtensionDataValue(XmlWriterDelegator xmlWriter, IDataNode? dataNode) { IncrementItemCount(1); @@ -709,6 +731,7 @@ internal void WriteExtensionDataValue(XmlWriterDelegator xmlWriter, IDataNode? d } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal bool TryWriteDeserializedExtensionData(XmlWriterDelegator xmlWriter, IDataNode dataNode) { object? o = dataNode.Value; @@ -721,6 +744,7 @@ internal bool TryWriteDeserializedExtensionData(XmlWriterDelegator xmlWriter, ID } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WriteExtensionClassData(XmlWriterDelegator xmlWriter, ClassDataNode dataNode) { if (!TryWriteDeserializedExtensionData(xmlWriter, dataNode)) @@ -739,6 +763,7 @@ private void WriteExtensionClassData(XmlWriterDelegator xmlWriter, ClassDataNode } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WriteExtensionCollectionData(XmlWriterDelegator xmlWriter, CollectionDataNode dataNode) { if (!TryWriteDeserializedExtensionData(xmlWriter, dataNode)) @@ -761,6 +786,7 @@ private void WriteExtensionCollectionData(XmlWriterDelegator xmlWriter, Collecti } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WriteExtensionISerializableData(XmlWriterDelegator xmlWriter, ISerializableDataNode dataNode) { if (!TryWriteDeserializedExtensionData(xmlWriter, dataNode)) @@ -785,6 +811,7 @@ private void WriteExtensionISerializableData(XmlWriterDelegator xmlWriter, ISeri } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WriteExtensionXmlData(XmlWriterDelegator xmlWriter, XmlDataNode dataNode) { if (!TryWriteDeserializedExtensionData(xmlWriter, dataNode)) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContextComplex.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContextComplex.cs index b1abf32a8ee00f..687013b8e8dd57 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContextComplex.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContextComplex.cs @@ -62,6 +62,7 @@ internal override void WriteString(XmlWriterDelegator xmlWriter, string value) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void WriteString(XmlWriterDelegator xmlWriter, string? value, XmlDictionaryString name, XmlDictionaryString? ns) { if (value == null) @@ -82,6 +83,7 @@ internal override void WriteBase64(XmlWriterDelegator xmlWriter, byte[] value) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void WriteBase64(XmlWriterDelegator xmlWriter, byte[] value, XmlDictionaryString name, XmlDictionaryString ns) { if (value == null) @@ -102,6 +104,7 @@ internal override void WriteUri(XmlWriterDelegator xmlWriter, Uri value) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void WriteUri(XmlWriterDelegator xmlWriter, Uri value, XmlDictionaryString name, XmlDictionaryString ns) { if (value == null) @@ -122,6 +125,7 @@ internal override void WriteQName(XmlWriterDelegator xmlWriter, XmlQualifiedName } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void WriteQName(XmlWriterDelegator xmlWriter, XmlQualifiedName? value, XmlDictionaryString name, XmlDictionaryString? ns) { if (value == null) @@ -139,6 +143,7 @@ internal override void WriteQName(XmlWriterDelegator xmlWriter, XmlQualifiedName } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void InternalSerialize(XmlWriterDelegator xmlWriter, object obj, bool isDeclaredType, bool writeXsiType, int declaredTypeID, RuntimeTypeHandle declaredTypeHandle) { if (_serializationSurrogateProvider == null) @@ -177,6 +182,7 @@ internal override void OnEndHandleReference(XmlWriterDelegator xmlWriter, object } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void CheckIfTypeSerializable(Type memberType, bool isMemberTypeSerializable) { if (_serializationSurrogateProvider != null) @@ -193,6 +199,7 @@ internal override void CheckIfTypeSerializable(Type memberType, bool isMemberTyp } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override Type GetSurrogatedType(Type type) { if (_serializationSurrogateProvider == null) @@ -216,6 +223,7 @@ internal override Type GetSurrogatedType(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void InternalSerializeWithSurrogate(XmlWriterDelegator xmlWriter, object obj, bool isDeclaredType, bool writeXsiType, int declaredTypeID, RuntimeTypeHandle declaredTypeHandle) { RuntimeTypeHandle objTypeHandle = isDeclaredType ? declaredTypeHandle : obj.GetType().TypeHandle; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XsdDataContractExporter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XsdDataContractExporter.cs index ad147ba1af8fd7..de8b5373ed0655 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XsdDataContractExporter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XsdDataContractExporter.cs @@ -62,6 +62,7 @@ private static DataContractSet DataContractSet } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public void Export(ICollection assemblies) { ArgumentNullException.ThrowIfNull(assemblies); @@ -89,6 +90,7 @@ public void Export(ICollection assemblies) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public void Export(ICollection types) { ArgumentNullException.ThrowIfNull(types); @@ -113,6 +115,7 @@ public void Export(ICollection types) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public void Export(Type type) { ArgumentNullException.ThrowIfNull(type); @@ -131,6 +134,7 @@ public void Export(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public XmlQualifiedName GetSchemaTypeName(Type type) { ArgumentNullException.ThrowIfNull(type); @@ -145,6 +149,7 @@ public XmlQualifiedName GetSchemaTypeName(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public XmlSchemaType? GetSchemaType(Type type) { ArgumentNullException.ThrowIfNull(type); @@ -159,6 +164,7 @@ public XmlQualifiedName GetSchemaTypeName(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public XmlQualifiedName? GetRootElementName(Type type) { ArgumentNullException.ThrowIfNull(type); @@ -187,6 +193,7 @@ private static Type GetSurrogatedType(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static void CheckAndAddType(Type type) { type = GetSurrogatedType(type); @@ -195,12 +202,14 @@ private static void CheckAndAddType(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static void AddType(Type type) { DataContractSet.Add(type); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void Export() { AddKnownTypes(); @@ -209,6 +218,7 @@ private void Export() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void AddKnownTypes() { if (Options != null) @@ -229,6 +239,7 @@ private void AddKnownTypes() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public bool CanExport(ICollection assemblies) { ArgumentNullException.ThrowIfNull(assemblies); @@ -261,6 +272,7 @@ public bool CanExport(ICollection assemblies) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public bool CanExport(ICollection types) { ArgumentNullException.ThrowIfNull(types); @@ -290,6 +302,7 @@ public bool CanExport(ICollection types) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public bool CanExport(Type type) { ArgumentNullException.ThrowIfNull(type); diff --git a/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.cs b/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.cs index 014c498f6eace0..ff1b2a25d75d2a 100644 --- a/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.cs +++ b/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.cs @@ -26,18 +26,25 @@ namespace System.Runtime.Serialization.Json public sealed partial class DataContractJsonSerializer : System.Runtime.Serialization.XmlObjectSerializer { [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public DataContractJsonSerializer(System.Type type) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public DataContractJsonSerializer(System.Type type, System.Collections.Generic.IEnumerable? knownTypes) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public DataContractJsonSerializer(System.Type type, System.Runtime.Serialization.Json.DataContractJsonSerializerSettings? settings) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public DataContractJsonSerializer(System.Type type, string? rootName) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public DataContractJsonSerializer(System.Type type, string? rootName, System.Collections.Generic.IEnumerable? knownTypes) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public DataContractJsonSerializer(System.Type type, System.Xml.XmlDictionaryString? rootName) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public DataContractJsonSerializer(System.Type type, System.Xml.XmlDictionaryString? rootName, System.Collections.Generic.IEnumerable? knownTypes) { } public System.Runtime.Serialization.DateTimeFormat? DateTimeFormat { get { throw null; } } public System.Runtime.Serialization.EmitTypeInformation EmitTypeInformation { get { throw null; } } @@ -47,36 +54,52 @@ public DataContractJsonSerializer(System.Type type, System.Xml.XmlDictionaryStri public bool SerializeReadOnlyTypes { get { throw null; } } public bool UseSimpleDictionaryFormat { get { throw null; } } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override bool IsStartObject(System.Xml.XmlDictionaryReader reader) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override bool IsStartObject(System.Xml.XmlReader reader) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override object? ReadObject(System.IO.Stream stream) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override object? ReadObject(System.Xml.XmlDictionaryReader reader) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override object? ReadObject(System.Xml.XmlDictionaryReader reader, bool verifyObjectName) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override object? ReadObject(System.Xml.XmlReader reader) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override object? ReadObject(System.Xml.XmlReader reader, bool verifyObjectName) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteEndObject(System.Xml.XmlDictionaryWriter writer) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteEndObject(System.Xml.XmlWriter writer) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteObject(System.IO.Stream stream, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteObject(System.Xml.XmlDictionaryWriter writer, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteObject(System.Xml.XmlWriter writer, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteObjectContent(System.Xml.XmlDictionaryWriter writer, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteObjectContent(System.Xml.XmlWriter writer, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteStartObject(System.Xml.XmlDictionaryWriter writer, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteStartObject(System.Xml.XmlWriter writer, object? graph) { } } public partial class DataContractJsonSerializerSettings diff --git a/src/libraries/System.Runtime.Serialization.Xml/ref/System.Runtime.Serialization.Xml.cs b/src/libraries/System.Runtime.Serialization.Xml/ref/System.Runtime.Serialization.Xml.cs index 6a3d406d810269..94cc20e75fb6ec 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/ref/System.Runtime.Serialization.Xml.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/ref/System.Runtime.Serialization.Xml.cs @@ -10,8 +10,10 @@ public abstract partial class DataContractResolver { protected DataContractResolver() { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public abstract System.Type? ResolveName(string typeName, string? typeNamespace, System.Type? declaredType, System.Runtime.Serialization.DataContractResolver knownTypeResolver); [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public abstract bool TryResolveType(System.Type type, System.Type? declaredType, System.Runtime.Serialization.DataContractResolver knownTypeResolver, out System.Xml.XmlDictionaryString? typeName, out System.Xml.XmlDictionaryString? typeNamespace); } public sealed partial class DataContractSerializer : System.Runtime.Serialization.XmlObjectSerializer @@ -30,32 +32,46 @@ public DataContractSerializer(System.Type type, System.Xml.XmlDictionaryString r public bool PreserveObjectReferences { get { throw null; } } public bool SerializeReadOnlyTypes { get { throw null; } } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override bool IsStartObject(System.Xml.XmlDictionaryReader reader) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override bool IsStartObject(System.Xml.XmlReader reader) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override object? ReadObject(System.Xml.XmlDictionaryReader reader, bool verifyObjectName) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public object? ReadObject(System.Xml.XmlDictionaryReader reader, bool verifyObjectName, System.Runtime.Serialization.DataContractResolver? dataContractResolver) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override object? ReadObject(System.Xml.XmlReader reader) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override object? ReadObject(System.Xml.XmlReader reader, bool verifyObjectName) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteEndObject(System.Xml.XmlDictionaryWriter writer) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteEndObject(System.Xml.XmlWriter writer) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public void WriteObject(System.Xml.XmlDictionaryWriter writer, object? graph, System.Runtime.Serialization.DataContractResolver? dataContractResolver) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteObject(System.Xml.XmlWriter writer, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteObjectContent(System.Xml.XmlDictionaryWriter writer, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteObjectContent(System.Xml.XmlWriter writer, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteStartObject(System.Xml.XmlDictionaryWriter writer, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteStartObject(System.Xml.XmlWriter writer, object? graph) { } } public static partial class DataContractSerializerExtensions @@ -92,36 +108,52 @@ public abstract partial class XmlObjectSerializer { protected XmlObjectSerializer() { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public abstract bool IsStartObject(System.Xml.XmlDictionaryReader reader); [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public virtual bool IsStartObject(System.Xml.XmlReader reader) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public virtual object? ReadObject(System.IO.Stream stream) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public virtual object? ReadObject(System.Xml.XmlDictionaryReader reader) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public abstract object? ReadObject(System.Xml.XmlDictionaryReader reader, bool verifyObjectName); [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public virtual object? ReadObject(System.Xml.XmlReader reader) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public virtual object? ReadObject(System.Xml.XmlReader reader, bool verifyObjectName) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public abstract void WriteEndObject(System.Xml.XmlDictionaryWriter writer); [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public virtual void WriteEndObject(System.Xml.XmlWriter writer) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public virtual void WriteObject(System.IO.Stream stream, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public virtual void WriteObject(System.Xml.XmlDictionaryWriter writer, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public virtual void WriteObject(System.Xml.XmlWriter writer, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public abstract void WriteObjectContent(System.Xml.XmlDictionaryWriter writer, object? graph); [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public virtual void WriteObjectContent(System.Xml.XmlWriter writer, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public abstract void WriteStartObject(System.Xml.XmlDictionaryWriter writer, object? graph); [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public virtual void WriteStartObject(System.Xml.XmlWriter writer, object? graph) { } } public static partial class XmlSerializableServices @@ -133,8 +165,10 @@ public static void WriteNodes(System.Xml.XmlWriter xmlWriter, System.Xml.XmlNode public static partial class XPathQueryGenerator { [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public static string CreateFromDataContractSerializer(System.Type type, System.Reflection.MemberInfo[] pathToMember, System.Text.StringBuilder? rootElementXpath, out System.Xml.XmlNamespaceManager namespaces) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public static string CreateFromDataContractSerializer(System.Type type, System.Reflection.MemberInfo[] pathToMember, out System.Xml.XmlNamespaceManager namespaces) { throw null; } } public partial class XsdDataContractExporter @@ -144,22 +178,31 @@ public XsdDataContractExporter(System.Xml.Schema.XmlSchemaSet? schemas) { } public System.Runtime.Serialization.ExportOptions? Options { get { throw null; } set { } } public System.Xml.Schema.XmlSchemaSet Schemas { get { throw null; } } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public bool CanExport(System.Collections.Generic.ICollection assemblies) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public bool CanExport(System.Collections.Generic.ICollection types) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public bool CanExport(System.Type type) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public void Export(System.Collections.Generic.ICollection assemblies) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public void Export(System.Collections.Generic.ICollection types) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public void Export(System.Type type) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public System.Xml.XmlQualifiedName? GetRootElementName(System.Type type) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public System.Xml.Schema.XmlSchemaType? GetSchemaType(System.Type type) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public System.Xml.XmlQualifiedName GetSchemaTypeName(System.Type type) { throw null; } } } From 5eb776dc21d89063d66287461313c1e79073b57b Mon Sep 17 00:00:00 2001 From: Drew Kersnar <18474647+dakersnar@users.noreply.github.com> Date: Fri, 12 Aug 2022 17:15:56 -0500 Subject: [PATCH 41/56] Add AllBitsSet DIM (#72961) * Add AllBitsSet Dim * Apply suggestion from code review * Add unit test for DIM * Apply suggestions from code review Co-authored-by: Tanner Gooding * Apply suggestions from code review * Fix typo causing compile errors Co-authored-by: Tanner Gooding --- .../src/System/Numerics/IBinaryNumber.cs | 2 +- .../System.Runtime/ref/System.Runtime.cs | 2 +- .../tests/System.Runtime.Tests.csproj | 2 + .../System/Numerics/GenericMathDimHelpers.cs | 99 +++++++++++++++++++ .../System/Numerics/IBinaryNumberTests.cs | 17 ++++ 5 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 src/libraries/System.Runtime/tests/System/Numerics/GenericMathDimHelpers.cs create mode 100644 src/libraries/System.Runtime/tests/System/Numerics/IBinaryNumberTests.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryNumber.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryNumber.cs index 22226f63ed3fd9..9b45fb59cc73b1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryNumber.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryNumber.cs @@ -11,7 +11,7 @@ public interface IBinaryNumber where TSelf : IBinaryNumber { /// Gets an instance of the binary type in which all bits are set. - static abstract TSelf AllBitsSet { get; } // TODO: add the DIM once https://github.com/dotnet/linker/issues/2865 is fixed + static virtual TSelf AllBitsSet => ~TSelf.Zero; /// Determines if a value is a power of two. /// The value to be checked. diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 1212c55a7d9630..5d04d15a81dc9b 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -10259,7 +10259,7 @@ public partial interface IBinaryInteger : System.IComparable, System.ICom } public partial interface IBinaryNumber : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators where TSelf : System.Numerics.IBinaryNumber { - static abstract TSelf AllBitsSet { get; } + static virtual TSelf AllBitsSet { get { throw null; } } static abstract bool IsPow2(TSelf value); static abstract TSelf Log2(TSelf value); } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj index 8ff7eb9bc26756..accc11d4277ca8 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj @@ -262,6 +262,8 @@ + + diff --git a/src/libraries/System.Runtime/tests/System/Numerics/GenericMathDimHelpers.cs b/src/libraries/System.Runtime/tests/System/Numerics/GenericMathDimHelpers.cs new file mode 100644 index 00000000000000..d6b34be3114410 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/Numerics/GenericMathDimHelpers.cs @@ -0,0 +1,99 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Numerics; +using System.Runtime.InteropServices; +using Xunit; + +namespace System.Numerics.Tests +{ + public struct BinaryNumberDimHelper : IBinaryNumber + { + public int Value = 0; + + private BinaryNumberDimHelper(int value) + { + Value = value; + } + + static BinaryNumberDimHelper INumberBase.Zero => new BinaryNumberDimHelper(0); + + static BinaryNumberDimHelper IBitwiseOperators.operator ~(BinaryNumberDimHelper value) + { + return new BinaryNumberDimHelper(~value.Value); + } + + // + // The below are all not used for existing Dim tests, so they stay unimplemented + // + + static BinaryNumberDimHelper IMultiplicativeIdentity.MultiplicativeIdentity => throw new NotImplementedException(); + static BinaryNumberDimHelper INumberBase.One => throw new NotImplementedException(); + static int INumberBase.Radix => throw new NotImplementedException(); + static BinaryNumberDimHelper IAdditiveIdentity.AdditiveIdentity => throw new NotImplementedException(); + static BinaryNumberDimHelper INumberBase.Abs(BinaryNumberDimHelper value) => throw new NotImplementedException(); + static bool INumberBase.IsCanonical(BinaryNumberDimHelper value) => throw new NotImplementedException(); + static bool INumberBase.IsComplexNumber(BinaryNumberDimHelper value) => throw new NotImplementedException(); + static bool INumberBase.IsEvenInteger(BinaryNumberDimHelper value) => throw new NotImplementedException(); + static bool INumberBase.IsFinite(BinaryNumberDimHelper value) => throw new NotImplementedException(); + static bool INumberBase.IsImaginaryNumber(BinaryNumberDimHelper value) => throw new NotImplementedException(); + static bool INumberBase.IsInfinity(BinaryNumberDimHelper value) => throw new NotImplementedException(); + static bool INumberBase.IsInteger(BinaryNumberDimHelper value) => throw new NotImplementedException(); + static bool INumberBase.IsNaN(BinaryNumberDimHelper value) => throw new NotImplementedException(); + static bool INumberBase.IsNegative(BinaryNumberDimHelper value) => throw new NotImplementedException(); + static bool INumberBase.IsNegativeInfinity(BinaryNumberDimHelper value) => throw new NotImplementedException(); + static bool INumberBase.IsNormal(BinaryNumberDimHelper value) => throw new NotImplementedException(); + static bool INumberBase.IsOddInteger(BinaryNumberDimHelper value) => throw new NotImplementedException(); + static bool INumberBase.IsPositive(BinaryNumberDimHelper value) => throw new NotImplementedException(); + static bool INumberBase.IsPositiveInfinity(BinaryNumberDimHelper value) => throw new NotImplementedException(); + static bool IBinaryNumber.IsPow2(BinaryNumberDimHelper value) => throw new NotImplementedException(); + static bool INumberBase.IsRealNumber(BinaryNumberDimHelper value) => throw new NotImplementedException(); + static bool INumberBase.IsSubnormal(BinaryNumberDimHelper value) => throw new NotImplementedException(); + static bool INumberBase.IsZero(BinaryNumberDimHelper value) => throw new NotImplementedException(); + static BinaryNumberDimHelper IBinaryNumber.Log2(BinaryNumberDimHelper value) => throw new NotImplementedException(); + static BinaryNumberDimHelper INumberBase.MaxMagnitude(BinaryNumberDimHelper x, BinaryNumberDimHelper y) => throw new NotImplementedException(); + static BinaryNumberDimHelper INumberBase.MaxMagnitudeNumber(BinaryNumberDimHelper x, BinaryNumberDimHelper y) => throw new NotImplementedException(); + static BinaryNumberDimHelper INumberBase.MinMagnitude(BinaryNumberDimHelper x, BinaryNumberDimHelper y) => throw new NotImplementedException(); + static BinaryNumberDimHelper INumberBase.MinMagnitudeNumber(BinaryNumberDimHelper x, BinaryNumberDimHelper y) => throw new NotImplementedException(); + static BinaryNumberDimHelper INumberBase.Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) => throw new NotImplementedException(); + static BinaryNumberDimHelper INumberBase.Parse(string s, NumberStyles style, IFormatProvider? provider) => throw new NotImplementedException(); + static BinaryNumberDimHelper ISpanParsable.Parse(ReadOnlySpan s, IFormatProvider? provider) => throw new NotImplementedException(); + static BinaryNumberDimHelper IParsable.Parse(string s, IFormatProvider? provider) => throw new NotImplementedException(); + static bool INumberBase.TryConvertToChecked(BinaryNumberDimHelper value, out TOther? result) where TOther : default => throw new NotImplementedException(); + static bool INumberBase.TryConvertToSaturating(BinaryNumberDimHelper value, out TOther? result) where TOther : default => throw new NotImplementedException(); + static bool INumberBase.TryConvertToTruncating(BinaryNumberDimHelper value, out TOther? result) where TOther : default => throw new NotImplementedException(); + static bool INumberBase.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out BinaryNumberDimHelper result) => throw new NotImplementedException(); + static bool INumberBase.TryParse(string? s, NumberStyles style, IFormatProvider? provider, out BinaryNumberDimHelper result) => throw new NotImplementedException(); + static bool ISpanParsable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out BinaryNumberDimHelper result) => throw new NotImplementedException(); + static bool IParsable.TryParse(string? s, IFormatProvider? provider, out BinaryNumberDimHelper result) => throw new NotImplementedException(); + int IComparable.CompareTo(object? obj) => throw new NotImplementedException(); + string IFormattable.ToString(string? format, IFormatProvider? formatProvider) => throw new NotImplementedException(); + bool ISpanFormattable.TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) => throw new NotImplementedException(); + int IComparable.CompareTo(BinaryNumberDimHelper other) => throw new NotImplementedException(); + static bool INumberBase.TryConvertFromChecked(TOther value, out BinaryNumberDimHelper result) => throw new NotImplementedException(); + static bool INumberBase.TryConvertFromSaturating(TOther value, out BinaryNumberDimHelper result) => throw new NotImplementedException(); + static bool INumberBase.TryConvertFromTruncating(TOther value, out BinaryNumberDimHelper result) => throw new NotImplementedException(); + bool IEquatable.Equals(BinaryNumberDimHelper other) => throw new NotImplementedException(); + + static BinaryNumberDimHelper IUnaryPlusOperators.operator +(BinaryNumberDimHelper value) => throw new NotImplementedException(); + static BinaryNumberDimHelper IAdditionOperators.operator +(BinaryNumberDimHelper left, BinaryNumberDimHelper right) => throw new NotImplementedException(); + static BinaryNumberDimHelper IUnaryNegationOperators.operator -(BinaryNumberDimHelper value) => throw new NotImplementedException(); + static BinaryNumberDimHelper ISubtractionOperators.operator -(BinaryNumberDimHelper left, BinaryNumberDimHelper right) => throw new NotImplementedException(); + static BinaryNumberDimHelper IIncrementOperators.operator ++(BinaryNumberDimHelper value) => throw new NotImplementedException(); + static BinaryNumberDimHelper IDecrementOperators.operator --(BinaryNumberDimHelper value) => throw new NotImplementedException(); + static BinaryNumberDimHelper IMultiplyOperators.operator *(BinaryNumberDimHelper left, BinaryNumberDimHelper right) => throw new NotImplementedException(); + static BinaryNumberDimHelper IDivisionOperators.operator /(BinaryNumberDimHelper left, BinaryNumberDimHelper right) => throw new NotImplementedException(); + static BinaryNumberDimHelper IModulusOperators.operator %(BinaryNumberDimHelper left, BinaryNumberDimHelper right) => throw new NotImplementedException(); + static BinaryNumberDimHelper IBitwiseOperators.operator &(BinaryNumberDimHelper left, BinaryNumberDimHelper right) => throw new NotImplementedException(); + static BinaryNumberDimHelper IBitwiseOperators.operator |(BinaryNumberDimHelper left, BinaryNumberDimHelper right) => throw new NotImplementedException(); + static BinaryNumberDimHelper IBitwiseOperators.operator ^(BinaryNumberDimHelper left, BinaryNumberDimHelper right) => throw new NotImplementedException(); + static bool IEqualityOperators.operator ==(BinaryNumberDimHelper left, BinaryNumberDimHelper right) => throw new NotImplementedException(); + static bool IEqualityOperators.operator !=(BinaryNumberDimHelper left, BinaryNumberDimHelper right) => throw new NotImplementedException(); + static bool IComparisonOperators.operator <(BinaryNumberDimHelper left, BinaryNumberDimHelper right) => throw new NotImplementedException(); + static bool IComparisonOperators.operator >(BinaryNumberDimHelper left, BinaryNumberDimHelper right) => throw new NotImplementedException(); + static bool IComparisonOperators.operator <=(BinaryNumberDimHelper left, BinaryNumberDimHelper right) => throw new NotImplementedException(); + static bool IComparisonOperators.operator >=(BinaryNumberDimHelper left, BinaryNumberDimHelper right) => throw new NotImplementedException(); + } + +} diff --git a/src/libraries/System.Runtime/tests/System/Numerics/IBinaryNumberTests.cs b/src/libraries/System.Runtime/tests/System/Numerics/IBinaryNumberTests.cs new file mode 100644 index 00000000000000..d405522e5e5bae --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/Numerics/IBinaryNumberTests.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.Numerics.Tests +{ + public sealed class IBinaryNumberTests + { + [Fact] + public static void AllBitsSetTest() + { + Assert.Equal(unchecked((int)0xFFFF_FFFF), BinaryNumberHelper.AllBitsSet.Value); + Assert.Equal(0, ~BinaryNumberHelper.AllBitsSet.Value); + } + } +} From 2c4759587a7a9920b6c7ec94ed5ee3756d45173a Mon Sep 17 00:00:00 2001 From: Will Smith Date: Fri, 12 Aug 2022 15:41:54 -0700 Subject: [PATCH 42/56] Disable more ILASM roundtrip tests (#73831) * Disable more ilasm roundtrip tests * Update HugeField1.csproj * Update mainv1.csproj * Update HugeField1.csproj * Update mainv1.csproj --- src/tests/JIT/jit64/opt/cse/HugeField1.csproj | 2 ++ src/tests/readytorun/tests/mainv1.csproj | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/tests/JIT/jit64/opt/cse/HugeField1.csproj b/src/tests/JIT/jit64/opt/cse/HugeField1.csproj index 4457c6382afded..0a681932139e42 100644 --- a/src/tests/JIT/jit64/opt/cse/HugeField1.csproj +++ b/src/tests/JIT/jit64/opt/cse/HugeField1.csproj @@ -3,6 +3,8 @@ Exe true 1 + + true Full diff --git a/src/tests/readytorun/tests/mainv1.csproj b/src/tests/readytorun/tests/mainv1.csproj index e3edd28a48acc1..f045011a690be0 100644 --- a/src/tests/readytorun/tests/mainv1.csproj +++ b/src/tests/readytorun/tests/mainv1.csproj @@ -2,6 +2,8 @@ exe false + + true From cb2dfadda3706518c6b91ba186bc6b68ad48eeb0 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Fri, 12 Aug 2022 16:07:51 -0700 Subject: [PATCH 43/56] Support void* to UIntPtr parameter conversion for Invoked method (#73748) * Add UIntPtr conversion * Use existing method for UIntPtr, update nativeaot version Co-authored-by: Jan Kotas --- .../src/System/InvokeUtils.cs | 14 ++++++------- .../src/System/Reflection/InvokeUtils.cs | 13 ++++++------ .../src/System/Reflection/MethodBase.cs | 2 +- .../tests/System/Reflection/PointerTests.cs | 21 +++++++++++++++++++ src/mono/mono/metadata/object.c | 2 +- 5 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/InvokeUtils.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/InvokeUtils.cs index ba5eb64ab24641..e4921170c2cd2b 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/InvokeUtils.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/InvokeUtils.cs @@ -108,13 +108,13 @@ private static Exception ConvertOrWidenPrimitivesEnumsAndPointersIfPossible(obje if (dstEEType.IsPointer) { - Exception exception = ConvertPointerIfPossible(srcObject, dstEEType, semantics, out IntPtr dstIntPtr); + Exception exception = ConvertPointerIfPossible(srcObject, dstEEType, semantics, out object dstPtr); if (exception != null) { dstObject = null; return exception; } - dstObject = dstIntPtr; + dstObject = dstPtr; return null; } @@ -215,11 +215,11 @@ private static Exception ConvertOrWidenPrimitivesEnumsAndPointersIfPossible(obje return null; } - private static Exception ConvertPointerIfPossible(object srcObject, EETypePtr dstEEType, CheckArgumentSemantics semantics, out IntPtr dstIntPtr) + private static Exception ConvertPointerIfPossible(object srcObject, EETypePtr dstEEType, CheckArgumentSemantics semantics, out object dstPtr) { - if (srcObject is IntPtr srcIntPtr) + if (srcObject is IntPtr or UIntPtr) { - dstIntPtr = srcIntPtr; + dstPtr = srcObject; return null; } @@ -227,12 +227,12 @@ private static Exception ConvertPointerIfPossible(object srcObject, EETypePtr ds { if (dstEEType == typeof(void*).TypeHandle.ToEETypePtr() || RuntimeImports.AreTypesAssignable(pSourceType: srcPointer.GetPointerType().TypeHandle.ToEETypePtr(), pTargetType: dstEEType)) { - dstIntPtr = srcPointer.GetPointerValue(); + dstPtr = srcPointer.GetPointerValue(); return null; } } - dstIntPtr = IntPtr.Zero; + dstPtr = null; return CreateChangeTypeException(srcObject.GetEETypePtr(), dstEEType, semantics); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokeUtils.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokeUtils.cs index c5b26694eebb17..2516a77f289cec 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokeUtils.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokeUtils.cs @@ -4,6 +4,7 @@ using System.Runtime; using System.Runtime.CompilerServices; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace System.Reflection { @@ -16,9 +17,9 @@ public static object ConvertOrWiden(Type srcType, CorElementType srcElementType, if (dstType.IsPointer) { - if (TryConvertPointer(srcObject, out IntPtr dstIntPtr)) + if (TryConvertPointer(srcObject, out object? dstPtr)) { - return dstIntPtr; + return dstPtr; } Debug.Fail($"Unexpected CorElementType: {dstElementType}. Not a valid widening target."); @@ -109,18 +110,18 @@ public static object ConvertOrWiden(Type srcType, CorElementType srcElementType, return dstObject; } - private static bool TryConvertPointer(object srcObject, out IntPtr dstIntPtr) + private static bool TryConvertPointer(object srcObject, [NotNullWhen(true)] out object? dstPtr) { - if (srcObject is IntPtr srcIntPtr) + if (srcObject is IntPtr or UIntPtr) { - dstIntPtr = srcIntPtr; + dstPtr = srcObject; return true; } // The source pointer should already have been converted to an IntPtr. Debug.Assert(srcObject is not Pointer); - dstIntPtr = IntPtr.Zero; + dstPtr = null; return false; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBase.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBase.cs index f05878fb516217..13ff9e34df9201 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBase.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBase.cs @@ -242,7 +242,7 @@ BindingFlags invokeAttr Debug.Assert(arg != null); Debug.Assert( arg.GetType() == sigType || - (sigType.IsPointer && arg.GetType() == typeof(IntPtr)) || + (sigType.IsPointer && (arg.GetType() == typeof(IntPtr) || arg.GetType() == typeof(UIntPtr))) || (sigType.IsByRef && arg.GetType() == RuntimeTypeHandle.GetElementType(sigType)) || ((sigType.IsEnum || arg.GetType().IsEnum) && RuntimeType.GetUnderlyingType((RuntimeType)arg.GetType()) == RuntimeType.GetUnderlyingType(sigType))); #endif diff --git a/src/libraries/System.Runtime/tests/System/Reflection/PointerTests.cs b/src/libraries/System.Runtime/tests/System/Reflection/PointerTests.cs index d99c99872e5aec..890785e24c192f 100644 --- a/src/libraries/System.Runtime/tests/System/Reflection/PointerTests.cs +++ b/src/libraries/System.Runtime/tests/System/Reflection/PointerTests.cs @@ -29,6 +29,8 @@ public object ReturnWithSystemPointer(int expected) { return Pointer.Box((byte*)expected, typeof(byte*)); } + + public static void MethodWithVoidPointer(void* vp) { } } unsafe delegate void MethodDelegate(byte* ptr, int expected); @@ -220,6 +222,25 @@ public void IntPtrMethodParameter(int value) method.Invoke(obj, new object[] { (IntPtr)value, value }); } + public static IEnumerable PointersUInt => + new[] + { + new object[] { UIntPtr.Zero }, + new object[] { 0 }, + new object[] { 1 }, + new object[] { uint.MinValue }, + new object[] { uint.MaxValue }, + }; + + [Theory] + [MemberData(nameof(PointersUInt))] + public void UIntPtrMethodParameter(uint value) + { + var obj = new PointerHolder(); + MethodInfo method = typeof(PointerHolder).GetMethod(nameof(PointerHolder.MethodWithVoidPointer)); + method.Invoke(obj, new object[] { (UIntPtr)value }); + } + [Theory] [MemberData(nameof(Pointers))] public void PointerMethodParameter_InvalidType(int value) diff --git a/src/mono/mono/metadata/object.c b/src/mono/mono/metadata/object.c index a1fb6436bc401e..fde5f8ed8476fc 100644 --- a/src/mono/mono/metadata/object.c +++ b/src/mono/mono/metadata/object.c @@ -4652,7 +4652,7 @@ invoke_span_extract_argument (MonoSpanOfObjects *params_span, int i, MonoType *t if (arg == NULL) { result = NULL; } else { - g_assert (arg->vtable->klass == mono_defaults.int_class); + g_assert (arg->vtable->klass == mono_defaults.int_class || arg->vtable->klass == mono_defaults.uint_class); result = ((MonoIntPtr*)arg)->m_value; } break; From a0993e3be992cc0890597657f55a1b4e75cd96f4 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Fri, 12 Aug 2022 18:36:04 -0500 Subject: [PATCH 44/56] Unblock System.Configuration CI test errors for Android,tvOS,iOS (#73852) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Disables tests to unblock CI. Will investigate further to see if there is an underlying issue. See https://github.com/dotnet/runtime/issues/73792 Co-authored-by: Alexander Köplinger Co-authored-by: Jose Perez Rodriguez --- src/libraries/tests.proj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index afba0d8a60ee23..0b1ca8411cc6da 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -339,6 +339,11 @@ + + + + + From 236915658374ced7d0c1460f8fbd7fc954cd8ea8 Mon Sep 17 00:00:00 2001 From: Vladimir Sadov Date: Fri, 12 Aug 2022 16:37:49 -0700 Subject: [PATCH 45/56] [NativeAOT] Follow up changes for the suspension loop routine. (#73741) * add timing * do not hijack redirected threads * tweak the suspend loop implementation * remove instrumentation --- .../nativeaot/Runtime/StackFrameIterator.cpp | 4 +- src/coreclr/nativeaot/Runtime/thread.cpp | 61 ++++-------- src/coreclr/nativeaot/Runtime/thread.h | 14 ++- src/coreclr/nativeaot/Runtime/threadstore.cpp | 92 +++++++++++++------ 4 files changed, 93 insertions(+), 78 deletions(-) diff --git a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp index fc2370a0759000..2fcceff6d0dd85 100644 --- a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp +++ b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp @@ -95,7 +95,7 @@ GVAL_IMPL_INIT(PTR_VOID, g_RhpRethrow2Addr, PointerToRhpRethrow2); StackFrameIterator::StackFrameIterator(Thread * pThreadToWalk, PInvokeTransitionFrame* pInitialTransitionFrame) { STRESS_LOG0(LF_STACKWALK, LL_INFO10000, "----Init---- [ GC ]\n"); - ASSERT(!pThreadToWalk->DangerousCrossThreadIsHijacked()); + ASSERT(!pThreadToWalk->IsHijacked()); if (pInitialTransitionFrame == INTERRUPTED_THREAD_MARKER) { @@ -1463,7 +1463,7 @@ void StackFrameIterator::NextInternal() else { // if the thread is safe to walk, it better not have a hijack in place. - ASSERT((ThreadStore::GetCurrentThread() == m_pThread) || !m_pThread->DangerousCrossThreadIsHijacked()); + ASSERT(!m_pThread->IsHijacked()); SetControlPC(dac_cast(*(m_RegDisplay.GetAddrOfIP()))); diff --git a/src/coreclr/nativeaot/Runtime/thread.cpp b/src/coreclr/nativeaot/Runtime/thread.cpp index 91199d29a9202a..e3d214a6850cef 100644 --- a/src/coreclr/nativeaot/Runtime/thread.cpp +++ b/src/coreclr/nativeaot/Runtime/thread.cpp @@ -77,6 +77,10 @@ void Thread::WaitForGC(PInvokeTransitionFrame* pTransitionFrame) // set preemptive mode VolatileStoreWithoutBarrier(&m_pTransitionFrame, pTransitionFrame); +#ifdef FEATURE_SUSPEND_REDIRECTION + ClearState(TSF_Redirected); +#endif //FEATURE_SUSPEND_REDIRECTION + RedhawkGCInterface::WaitForGCCompletion(); // must be in cooperative mode when checking the trap flag @@ -587,6 +591,14 @@ void Thread::Hijack() return; } +#ifdef FEATURE_SUSPEND_REDIRECTION + // if the thread is redirected, leave it as-is. + if (IsStateSet(TSF_Redirected)) + { + return; + } +#endif //FEATURE_SUSPEND_REDIRECTION + // PalHijack will call HijackCallback or make the target thread call it. // It may also do nothing if the target thread is in inconvenient state. PalHijack(m_hPalThread, this); @@ -806,6 +818,8 @@ bool Thread::Redirect() if (!PalSetThreadContext(m_hPalThread, redirectionContext)) return false; + // the thread will now inevitably try to suspend + SetState(TSF_Redirected); redirectionContext->SetIp(origIP); STRESS_LOG2(LF_STACKWALK, LL_INFO10000, "InternalRedirect: TgtThread = %llx, IP = %p\n", @@ -887,55 +901,16 @@ void Thread::UnhijackWorker() m_uHijackedReturnValueFlags = 0; } -// @TODO: it would be very, very nice if we did not have to bleed knowledge of hijacking -// and hijack state to other components in the runtime. For now, these are only used -// when getting EH info during exception dispatch. We should find a better way to encapsulate -// this. bool Thread::IsHijacked() { - // Note: this operation is only valid from the current thread. If one thread invokes - // this on another then it may be racing with other changes to the thread's hijack state. - ASSERT(ThreadStore::GetCurrentThread() == this); - - return m_pvHijackedReturnAddress != NULL; -} + ASSERT(((ThreadStore::GetCurrentThread() == this) && IsCurrentThreadInCooperativeMode()) || + ThreadStore::GetCurrentThread()->IsGCSpecial() || + ThreadStore::GetCurrentThread() == ThreadStore::GetSuspendingThread() + ); -// -// WARNING: This method must ONLY be called during stackwalks when we believe that all threads are -// synchronized and there is no other thread racing with us trying to apply hijacks. -// -bool Thread::DangerousCrossThreadIsHijacked() -{ - // If we have a CachedTransitionFrame available, then we're in the proper state. Otherwise, this method - // was called from an improper state. - ASSERT(GetTransitionFrame() != NULL); return m_pvHijackedReturnAddress != NULL; } -void * Thread::GetHijackedReturnAddress() -{ - // Note: this operation is only valid from the current thread. If one thread invokes - // this on another then it may be racing with other changes to the thread's hijack state. - ASSERT(IsHijacked()); - ASSERT(ThreadStore::GetCurrentThread() == this); - - return m_pvHijackedReturnAddress; -} - -void * Thread::GetUnhijackedReturnAddress(void ** ppvReturnAddressLocation) -{ - ASSERT(ThreadStore::GetCurrentThread() == this); - - void * pvReturnAddress; - if (m_ppvHijackedReturnAddressLocation == ppvReturnAddressLocation) - pvReturnAddress = m_pvHijackedReturnAddress; - else - pvReturnAddress = *ppvReturnAddressLocation; - - ASSERT(GetRuntimeInstance()->IsManaged(pvReturnAddress)); - return pvReturnAddress; -} - void Thread::SetState(ThreadStateFlags flags) { PalInterlockedOr(&m_ThreadStateFlags, flags); diff --git a/src/coreclr/nativeaot/Runtime/thread.h b/src/coreclr/nativeaot/Runtime/thread.h index 2c0881acf2fd16..eeebabf05ea8aa 100644 --- a/src/coreclr/nativeaot/Runtime/thread.h +++ b/src/coreclr/nativeaot/Runtime/thread.h @@ -136,6 +136,12 @@ class Thread : private ThreadBuffer #ifdef FEATURE_GC_STRESS TSF_IsRandSeedSet = 0x00000040, // set to indicate the random number generator for GCStress was inited #endif // FEATURE_GC_STRESS + +#ifdef FEATURE_SUSPEND_REDIRECTION + TSF_Redirected = 0x00000080, // Set to indicate the thread is redirected and will inevitably + // suspend once resumed. + // If we see this flag, we skip hijacking as an optimization. +#endif //FEATURE_SUSPEND_REDIRECTION }; private: @@ -197,13 +203,11 @@ class Thread : private ThreadBuffer void Hijack(); void Unhijack(); + bool IsHijacked(); + #ifdef FEATURE_GC_STRESS static void HijackForGcStress(PAL_LIMITED_CONTEXT * pSuspendCtx); #endif // FEATURE_GC_STRESS - bool IsHijacked(); - void * GetHijackedReturnAddress(); - void * GetUnhijackedReturnAddress(void** ppvReturnAddressLocation); - bool DangerousCrossThreadIsHijacked(); bool IsSuppressGcStressSet(); void SetSuppressGcStress(); @@ -230,7 +234,7 @@ class Thread : private ThreadBuffer #endif // DACCESS_COMPILE #ifdef FEATURE_GC_STRESS void SetRandomSeed(uint32_t seed); - uint32_t NextRand(); + uint32_t NextRand(); bool IsRandInited(); #endif // FEATURE_GC_STRESS PTR_ExInfo GetCurExInfo(); diff --git a/src/coreclr/nativeaot/Runtime/threadstore.cpp b/src/coreclr/nativeaot/Runtime/threadstore.cpp index 772787246bc8da..f928e83f4ba785 100644 --- a/src/coreclr/nativeaot/Runtime/threadstore.cpp +++ b/src/coreclr/nativeaot/Runtime/threadstore.cpp @@ -196,6 +196,35 @@ void ThreadStore::UnlockThreadStore() m_Lock.ReleaseReadLock(); } +// exponential spinwait with an approximate time limit for waiting in microsecond range. +// when iteration == -1, only usecLimit is used +void SpinWait(int iteration, int usecLimit) +{ + LARGE_INTEGER li; + PalQueryPerformanceCounter(&li); + int64_t startTicks = li.QuadPart; + + PalQueryPerformanceFrequency(&li); + int64_t ticksPerSecond = li.QuadPart; + int64_t endTicks = startTicks + (usecLimit * ticksPerSecond) / 1000000; + + int l = min((unsigned)iteration, 30); + for (int i = 0; i < l; i++) + { + for (int j = 0; j < (1 << i); j++) + { + System_YieldProcessor(); + } + + PalQueryPerformanceCounter(&li); + int64_t currentTicks = li.QuadPart; + if (currentTicks > endTicks) + { + break; + } + } +} + void ThreadStore::SuspendAllThreads(bool waitForGCEvent) { Thread * pThisThread = GetCurrentThreadIfAvailable(); @@ -216,12 +245,16 @@ void ThreadStore::SuspendAllThreads(bool waitForGCEvent) // reason for this is that we essentially implement Dekker's algorithm, which requires write ordering. PalFlushProcessWriteBuffers(); - bool keepWaiting; - YieldProcessorNormalizationInfo normalizationInfo; - int waitCycles = 1; - do + int retries = 0; + int prevRemaining = 0; + int remaining = 0; + bool observeOnly = false; + + while(true) { - keepWaiting = false; + prevRemaining = remaining; + remaining = 0; + FOREACH_THREAD(pTargetThread) { if (pTargetThread == pThisThread) @@ -229,37 +262,40 @@ void ThreadStore::SuspendAllThreads(bool waitForGCEvent) if (!pTargetThread->CacheTransitionFrameForSuspend()) { - // We drive all threads to preemptive mode by hijacking them with return-address hijack. - keepWaiting = true; - pTargetThread->Hijack(); + remaining++; + if (!observeOnly) + { + pTargetThread->Hijack(); + } } } END_FOREACH_THREAD - if (keepWaiting) + if (!remaining) + break; + + // if we see progress or have just done a hijacking pass + // do not hijack in the next iteration + if (remaining < prevRemaining || !observeOnly) { - if (PalSwitchToThread() == 0 && g_RhNumberOfProcessors > 1) + // 5 usec delay, then check for more progress + SpinWait(-1, 5); + observeOnly = true; + } + else + { + SpinWait(retries++, 100); + observeOnly = false; + + // make sure our spining is not starving other threads, but not too often, + // this can cause a 1-15 msec delay, depending on OS, and that is a lot while + // very rarely needed, since threads are supposed to be releasing their CPUs + if ((retries & 127) == 0) { - // No threads are scheduled on this processor. Perhaps we're waiting for a thread - // that's scheduled on another processor. If so, let's give it a little time - // to make forward progress. - // Note that we do not call Sleep, because the minimum granularity of Sleep is much - // too long (we probably don't need a 15ms wait here). Instead, we'll just burn some - // cycles. - // @TODO: need tuning for spin - // @TODO: need tuning for this whole loop as well. - // we are likley too aggressive with interruptions which may result in longer pauses. - YieldProcessorNormalizedForPreSkylakeCount(normalizationInfo, waitCycles); - - // simplistic linear backoff for now - // we could be catching threads in restartable sequences such as LL/SC style interlocked on ARM64 - // and forcing them to restart. - // if interrupt mechanism is fast, eagerness could be hurting our overall progress. - waitCycles += 10000; + PalSwitchToThread(); } } - - } while (keepWaiting); + } #if defined(TARGET_ARM) || defined(TARGET_ARM64) // Flush the store buffers on all CPUs, to ensure that all changes made so far are seen From 49a6080d875ffc42780c91b33065249d71bfcb55 Mon Sep 17 00:00:00 2001 From: Theodore Tsirpanis Date: Sat, 13 Aug 2022 02:48:47 +0300 Subject: [PATCH 46/56] Optimize `AssemblyLoadContext.LoadFromStream` and fix partial reads. (#72783) --- .../Reflection/Augments/ReflectionAugments.cs | 2 +- .../Reflection/Core/AssemblyBinder.cs | 2 +- .../Reflection/Runtime/General/Dispensers.cs | 2 +- .../ReflectionCoreCallbacksImplementation.cs | 4 +- .../Loader/AssemblyLoadContext.NativeAot.cs | 2 +- .../ReflectionCoreCallbacksImplementation.cs | 2 +- .../Execution/AssemblyBinderImplementation.cs | 6 +-- .../Runtime/Loader/AssemblyLoadContext.cs | 52 +++++++++++++------ .../Loader/AssemblyLoadContext.Mono.cs | 5 +- 9 files changed, 48 insertions(+), 29 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs index 1ad76958f36489..af76bd703e5eba 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs @@ -121,7 +121,7 @@ internal static ReflectionCoreCallbacks ReflectionCoreCallbacks public abstract class ReflectionCoreCallbacks { public abstract Assembly Load(AssemblyName refName, bool throwOnFileNotFound); - public abstract Assembly Load(byte[] rawAssembly, byte[] pdbSymbolStore); + public abstract Assembly Load(ReadOnlySpan rawAssembly, ReadOnlySpan pdbSymbolStore); public abstract Assembly Load(string assemblyPath); public abstract MethodBase GetMethodFromHandle(RuntimeMethodHandle runtimeMethodHandle); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/AssemblyBinder.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/AssemblyBinder.cs index f68d8407b2781b..690fcdafa6722e 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/AssemblyBinder.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/AssemblyBinder.cs @@ -36,7 +36,7 @@ public abstract class AssemblyBinder public abstract bool Bind(RuntimeAssemblyName refName, bool cacheMissedLookups, out AssemblyBindResult result, out Exception exception); - public abstract bool Bind(byte[] rawAssembly, byte[] rawSymbolStore, out AssemblyBindResult result, out Exception exception); + public abstract bool Bind(ReadOnlySpan rawAssembly, ReadOnlySpan rawSymbolStore, out AssemblyBindResult result, out Exception exception); public abstract bool Bind(string assemblyPath, out AssemblyBindResult bindResult, out Exception exception); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/Dispensers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/Dispensers.cs index 730215bdbab6a6..ed40267a963fc2 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/Dispensers.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/Dispensers.cs @@ -42,7 +42,7 @@ internal static RuntimeAssembly GetRuntimeAssembly(RuntimeAssemblyName assemblyR /// /// Returns non-null or throws. /// - internal static RuntimeAssembly GetRuntimeAssemblyFromByteArray(byte[] rawAssembly, byte[] pdbSymbolStore) + internal static RuntimeAssembly GetRuntimeAssemblyFromByteArray(ReadOnlySpan rawAssembly, ReadOnlySpan pdbSymbolStore) { AssemblyBinder binder = ReflectionCoreExecution.ExecutionDomain.ReflectionDomainSetup.AssemblyBinder; if (!binder.Bind(rawAssembly, pdbSymbolStore, out AssemblyBindResult bindResult, out Exception exception)) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/ReflectionCoreCallbacksImplementation.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/ReflectionCoreCallbacksImplementation.cs index 9de2c480274e37..17455b1f5ec565 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/ReflectionCoreCallbacksImplementation.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/ReflectionCoreCallbacksImplementation.cs @@ -40,9 +40,9 @@ public sealed override Assembly Load(AssemblyName assemblyRef, bool throwOnFileN return RuntimeAssemblyInfo.GetRuntimeAssemblyIfExists(assemblyRef.ToRuntimeAssemblyName()); } - public sealed override Assembly Load(byte[] rawAssembly, byte[] pdbSymbolStore) + public sealed override Assembly Load(ReadOnlySpan rawAssembly, ReadOnlySpan pdbSymbolStore) { - if (rawAssembly == null) + if (rawAssembly.IsEmpty) throw new ArgumentNullException(nameof(rawAssembly)); return RuntimeAssemblyInfo.GetRuntimeAssemblyFromByteArray(rawAssembly, pdbSymbolStore); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.NativeAot.cs index b14bc6e0ffe03c..d2477f75b10f95 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.NativeAot.cs @@ -48,7 +48,7 @@ private static Assembly InternalLoadFromPath(string? assemblyPath, string? nativ } #pragma warning disable CA1822 - internal Assembly InternalLoad(byte[] arrAssembly, byte[] arrSymbols) + internal Assembly InternalLoad(ReadOnlySpan arrAssembly, ReadOnlySpan arrSymbols) { return ReflectionAugments.ReflectionCoreCallbacks.Load(arrAssembly, arrSymbols); } diff --git a/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Reflection/ReflectionCoreCallbacksImplementation.cs b/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Reflection/ReflectionCoreCallbacksImplementation.cs index 474db052488311..60de9603f48b26 100644 --- a/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Reflection/ReflectionCoreCallbacksImplementation.cs +++ b/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Reflection/ReflectionCoreCallbacksImplementation.cs @@ -47,7 +47,7 @@ public override object ActivatorCreateInstance( public override Type GetTypeFromCLSID(Guid clsid, string server, bool throwOnError) => throw new NotSupportedException(SR.Reflection_Disabled); #endif public override Assembly Load(AssemblyName refName, bool throwOnFileNotFound) => throw new NotSupportedException(SR.Reflection_Disabled); - public override Assembly Load(byte[] rawAssembly, byte[] pdbSymbolStore) => throw new NotSupportedException(SR.Reflection_Disabled); + public override Assembly Load(ReadOnlySpan rawAssembly, ReadOnlySpan pdbSymbolStore) => throw new NotSupportedException(SR.Reflection_Disabled); public override Assembly Load(string assemblyPath) => throw new NotSupportedException(SR.Reflection_Disabled); public override void MakeTypedReference(object target, FieldInfo[] flds, out Type type, out int offset) => throw new NotSupportedException(SR.Reflection_Disabled); public override void RunModuleConstructor(Module module) => throw new NotSupportedException(SR.Reflection_Disabled); diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.cs index 7808d1695c8e0c..9ea91a368b3d70 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.cs @@ -34,7 +34,7 @@ private AssemblyBinderImplementation() public static AssemblyBinderImplementation Instance { get; } = new AssemblyBinderImplementation(); partial void BindEcmaFilePath(string assemblyPath, ref AssemblyBindResult bindResult, ref Exception exception, ref bool? result); - partial void BindEcmaByteArray(byte[] rawAssembly, byte[] rawSymbolStore, ref AssemblyBindResult bindResult, ref Exception exception, ref bool? result); + partial void BindEcmaBytes(ReadOnlySpan rawAssembly, ReadOnlySpan rawSymbolStore, ref AssemblyBindResult bindResult, ref Exception exception, ref bool? result); partial void BindEcmaAssemblyName(RuntimeAssemblyName refName, bool cacheMissedLookups, ref AssemblyBindResult result, ref Exception exception, ref Exception preferredException, ref bool resultBoolean); partial void InsertEcmaLoadedAssemblies(List loadedAssemblies); @@ -53,13 +53,13 @@ public sealed override bool Bind(string assemblyPath, out AssemblyBindResult bin return result.Value; } - public sealed override bool Bind(byte[] rawAssembly, byte[] rawSymbolStore, out AssemblyBindResult bindResult, out Exception exception) + public sealed override bool Bind(ReadOnlySpan rawAssembly, ReadOnlySpan rawSymbolStore, out AssemblyBindResult bindResult, out Exception exception) { bool? result = null; exception = null; bindResult = default(AssemblyBindResult); - BindEcmaByteArray(rawAssembly, rawSymbolStore, ref bindResult, ref exception, ref result); + BindEcmaBytes(rawAssembly, rawSymbolStore, ref bindResult, ref exception, ref result); // If the Ecma assembly binder isn't linked in, simply throw PlatformNotSupportedException if (!result.HasValue) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs index d5423e5c6ec5a7..6679b94f731d51 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs @@ -374,34 +374,54 @@ public Assembly LoadFromStream(Stream assembly, Stream? assemblySymbols) { ArgumentNullException.ThrowIfNull(assembly); - int iAssemblyStreamLength = (int)assembly.Length; - - if (iAssemblyStreamLength <= 0) + ReadOnlySpan spanAssembly = ReadAllBytes(assembly); + if (spanAssembly.IsEmpty) { throw new BadImageFormatException(SR.BadImageFormat_BadILFormat); } - // Allocate the byte[] to hold the assembly - byte[] arrAssembly = new byte[iAssemblyStreamLength]; - - // Copy the assembly to the byte array - assembly.Read(arrAssembly, 0, iAssemblyStreamLength); - - // Get the symbol stream in byte[] if provided - byte[]? arrSymbols = null; + // Read the symbol stream if provided + ReadOnlySpan spanSymbols = default; if (assemblySymbols != null) { - int iSymbolLength = (int)assemblySymbols.Length; - arrSymbols = new byte[iSymbolLength]; - - assemblySymbols.Read(arrSymbols, 0, iSymbolLength); + spanSymbols = ReadAllBytes(assemblySymbols); } lock (_unloadLock) { VerifyIsAlive(); - return InternalLoad(arrAssembly, arrSymbols); + return InternalLoad(spanAssembly, spanSymbols); + } + + static ReadOnlySpan ReadAllBytes(Stream stream) + { + if (stream.GetType() == typeof(MemoryStream) && ((MemoryStream)stream).TryGetBuffer(out ArraySegment memoryStreamBuffer)) + { + int position = (int)stream.Position; + // Simulate that we read the stream to its end. + stream.Seek(0, SeekOrigin.End); + return memoryStreamBuffer.AsSpan(position); + } + + long length = stream.Length - stream.Position; + + if (length == 0) + { + return ReadOnlySpan.Empty; + } + + if (((ulong)length) > (ulong)Array.MaxLength) + { + throw new BadImageFormatException(SR.BadImageFormat_BadILFormat); + } + + byte[] bytes = GC.AllocateUninitializedArray((int)length); + + // Copy the stream to the byte array + stream.ReadExactly(bytes); + + return bytes; } } diff --git a/src/mono/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.Mono.cs index 30cad197e913ef..2de6d8e69f6c9c 100644 --- a/src/mono/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.Mono.cs @@ -47,15 +47,14 @@ private Assembly InternalLoadFromPath(string? assemblyPath, string? nativeImageP #pragma warning restore IDE0060 [RequiresUnreferencedCode("Types and members the loaded assembly depends on might be removed")] - internal Assembly InternalLoad(byte[] arrAssembly, byte[]? arrSymbols) + internal Assembly InternalLoad(ReadOnlySpan arrAssembly, ReadOnlySpan arrSymbols) { unsafe { - int symbolsLength = arrSymbols?.Length ?? 0; fixed (byte* ptrAssembly = arrAssembly, ptrSymbols = arrSymbols) { return InternalLoadFromStream(NativeALC, new IntPtr(ptrAssembly), arrAssembly.Length, - new IntPtr(ptrSymbols), symbolsLength); + new IntPtr(ptrSymbols), arrSymbols.Length); } } } From b8f864500738f9b18c2a062e0ebc52086ae8cd62 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 12 Aug 2022 17:04:08 -0700 Subject: [PATCH 47/56] Fix ASAN-detected alloc-dealloc mismatch (#73820) * Fix the alloc/dealloc mismatch by using a custom parameterized new operator. * Use custom struct wrapper type to avoid errors about placement new --- src/coreclr/vm/crossloaderallocatorhash.h | 11 +++++++++-- src/coreclr/vm/crossloaderallocatorhash.inl | 10 +++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/coreclr/vm/crossloaderallocatorhash.h b/src/coreclr/vm/crossloaderallocatorhash.h index 9f16cc07f21c6c..d23ddc0db225d9 100644 --- a/src/coreclr/vm/crossloaderallocatorhash.h +++ b/src/coreclr/vm/crossloaderallocatorhash.h @@ -23,11 +23,11 @@ class NoRemoveDefaultCrossLoaderAllocatorHashTraits // CrossLoaderAllocatorHash requires that a particular null value exist, which represents an empty value slot static bool IsNullValue(const TValue &value) { return value == NULL; } static TValue NullValue() { return NULL; } - + static BOOL KeyEquals(const TKey &k1, const TKey &k2) { return k1 == k2; } static BOOL ValueEquals(const TValue &v1, const TValue &v2) { return v1 == v2; } static TCount Hash(const TKey &k) { return (TCount)(size_t)k; } - + static LoaderAllocator *GetLoaderAllocator(const TKey &k) { return k->GetLoaderAllocator(); } }; @@ -192,6 +192,13 @@ class CrossLoaderAllocatorHash private: KeyValueStore(TCount capacity, const TKey &key) : _capacity(capacity), _key(key) {} + struct CountWrapper + { + TCount value; + }; + static void* operator new(size_t) = delete; + static void* operator new(size_t baseSize, CountWrapper capacity); + public: static KeyValueStore *Create(TCount capacity, const TKey &key); diff --git a/src/coreclr/vm/crossloaderallocatorhash.inl b/src/coreclr/vm/crossloaderallocatorhash.inl index 9037da4f36a4a9..1fe80787a87e77 100644 --- a/src/coreclr/vm/crossloaderallocatorhash.inl +++ b/src/coreclr/vm/crossloaderallocatorhash.inl @@ -82,6 +82,12 @@ template } } +template +/*static*/ void* CrossLoaderAllocatorHash::KeyValueStore::operator new(size_t baseSize, CountWrapper capacity) +{ + return ::operator new(baseSize + capacity.value * sizeof(TValue)); +} + template /*static*/ typename CrossLoaderAllocatorHash::KeyValueStore *CrossLoaderAllocatorHash::KeyValueStore::Create( TCount capacity, @@ -95,9 +101,7 @@ template } CONTRACTL_END; - NewArrayHolder bufferHolder = new BYTE[sizeof(KeyValueStore) + capacity * sizeof(TValue)]; - KeyValueStore *keyValueStore = new(bufferHolder) KeyValueStore(capacity, key); - bufferHolder.SuppressRelease(); + KeyValueStore *keyValueStore = new({capacity}) KeyValueStore(capacity, key); for (TCount i = 0; i < capacity; i++) { keyValueStore->GetValues()[i] = TRAITS::NullValue(); From e94f7ceb73244d9c4bf7a0d38fb59d01e300ef00 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 12 Aug 2022 20:49:10 -0400 Subject: [PATCH 48/56] Update dependencies from https://github.com/dotnet/linker build 20220812.3 (#73865) Microsoft.NET.ILLink.Tasks From Version 7.0.100-1.22411.2 -> To Version 7.0.100-1.22412.3 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index da25ebce1d5a98..b098c598b6292a 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -234,9 +234,9 @@ https://github.com/dotnet/runtime 0c5ca95fec9b6e2cc5e757ad36d0b094f81a59a6 - + https://github.com/dotnet/linker - 81ffbb5af38a45ff60648999df8f35a79061ae43 + 1ef0acf496369c15911dd5fe78e2d100659fa389 https://github.com/dotnet/xharness diff --git a/eng/Versions.props b/eng/Versions.props index 3659964a3d011f..4406b25e7d29b3 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -171,7 +171,7 @@ 7.0.0-preview-20220721.1 - 7.0.100-1.22411.2 + 7.0.100-1.22412.3 $(MicrosoftNETILLinkTasksVersion) 7.0.0-rc.1.22408.1 From 94015cd98a5128930dd20f7fea94da2ae30268c6 Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Fri, 12 Aug 2022 23:38:46 -0300 Subject: [PATCH 49/56] [wasm][debugger] Breakpoint set in a invalid line, set it to the next available line (#73189) * Adding more tests, including async cases. Some odd ones are broken at this time Co-authored-by: Ankit Jain --- .../debugger/BrowserDebugProxy/DebugStore.cs | 58 +++++++- .../debugger/BrowserDebugProxy/MonoProxy.cs | 15 +- .../DebuggerTestSuite/SteppingTests.cs | 46 ++++-- .../debugger-test/debugger-async-test.cs | 134 ++++++++++++++++++ .../tests/debugger-test/debugger-test.cs | 18 ++- 5 files changed, 251 insertions(+), 20 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs index fa7bd1142ec64f..5bc2aaf564451f 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs @@ -1596,7 +1596,7 @@ private static bool Match(SequencePoint sp, int line, int column) return true; } - public IEnumerable FindBreakpointLocations(BreakpointRequest request) + public IEnumerable FindBreakpointLocations(BreakpointRequest request, bool ifNoneFoundThenFindNext = false) { request.TryResolve(this); @@ -1606,16 +1606,64 @@ public IEnumerable FindBreakpointLocations(BreakpointRequest req if (sourceFile == null) yield break; - foreach (MethodInfo method in sourceFile.Methods) + List methodList = FindMethodsContainingLine(sourceFile, request.Line); + if (methodList.Count == 0) + yield break; + + List locations = new List(); + foreach (var method in methodList) + { + foreach (SequencePoint sequencePoint in method.DebugInformation.GetSequencePoints()) + { + if (!sequencePoint.IsHidden && + Match(sequencePoint, request.Line, request.Column) && + sequencePoint.StartLine - 1 == request.Line && + (request.Column == 0 || sequencePoint.StartColumn - 1 == request.Column)) + { + // Found an exact match + locations.Add(new SourceLocation(method, sequencePoint)); + } + } + } + if (locations.Count == 0 && ifNoneFoundThenFindNext) { - if (!method.DebugInformation.SequencePointsBlob.IsNil) + (MethodInfo method, SequencePoint seqPoint)? closest = null; + foreach (var method in methodList) { foreach (SequencePoint sequencePoint in method.DebugInformation.GetSequencePoints()) { - if (!sequencePoint.IsHidden && Match(sequencePoint, request.Line, request.Column)) - yield return new SourceLocation(method, sequencePoint); + if (!sequencePoint.IsHidden && + sequencePoint.StartLine > request.Line && + (closest is null || closest.Value.seqPoint.StartLine > sequencePoint.StartLine)) + { + // sequence points in a method are ordered, + // and we found the one right after request.Line + closest = (method, sequencePoint); + // .. and now we can look for it in other methods + break; + } } } + + if (closest is not null) + locations.Add(new SourceLocation(closest.Value.method, closest.Value.seqPoint)); + } + + foreach (SourceLocation loc in locations) + yield return loc; + + static List FindMethodsContainingLine(SourceFile sourceFile, int line) + { + List ret = new(); + foreach (MethodInfo method in sourceFile.Methods) + { + if (method.DebugInformation.SequencePointsBlob.IsNil) + continue; + if (!(method.StartLocation.Line <= line && line <= method.EndLocation.Line)) + continue; + ret.Add(method); + } + return ret; } } diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs index f94d666441db97..2c3acb5fa2f080 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs @@ -1553,18 +1553,22 @@ protected async Task RuntimeReady(SessionId sessionId, CancellationT return store; } - private static IEnumerable> GetBPReqLocations(DebugStore store, BreakpointRequest req) + private static IEnumerable> GetBPReqLocations(DebugStore store, BreakpointRequest req, bool ifNoneFoundThenFindNext = false) { var comparer = new SourceLocation.LocationComparer(); // if column is specified the frontend wants the exact matches // and will clear the bp if it isn't close enoug - IEnumerable> locations = store.FindBreakpointLocations(req) - .Distinct(comparer) - .Where(l => l.Line == req.Line && (req.Column == 0 || l.Column == req.Column)) + var bpLocations = store.FindBreakpointLocations(req, ifNoneFoundThenFindNext); + IEnumerable> locations = bpLocations.Distinct(comparer) .OrderBy(l => l.Column) .GroupBy(l => l.Id); + if (ifNoneFoundThenFindNext && !locations.Any()) + { + locations = bpLocations.GroupBy(l => l.Id); + } return locations; } + private async Task ResetBreakpoint(SessionId msg_id, DebugStore store, MethodInfo method, CancellationToken token) { ExecutionContext context = GetContext(msg_id); @@ -1622,12 +1626,11 @@ protected async Task SetBreakpoint(SessionId sessionId, DebugStore store, Breakp return; } - var locations = GetBPReqLocations(store, req); + var locations = GetBPReqLocations(store, req, true); logger.LogDebug("BP request for '{Req}' runtime ready {Context.RuntimeReady}", req, context.IsRuntimeReady); var breakpoints = new List(); - foreach (IGrouping sourceId in locations) { SourceLocation loc = sourceId.First(); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs index a8861bf154f725..77a967f1f96f35 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs @@ -839,7 +839,7 @@ await EvaluateAndCheck( } [ConditionalFact(nameof(RunningOnChrome))] - async Task StepOverHiddenLinesInMethodWithNoNextAvailableLineShouldResumeAtCallSite() + public async Task StepOverHiddenLinesInMethodWithNoNextAvailableLineShouldResumeAtCallSite() { string source_loc = "dotnet://debugger-test.dll/debugger-test.cs"; await SetBreakpoint(source_loc, 552, 8); @@ -852,15 +852,45 @@ await EvaluateAndCheck( await StepAndCheck(StepKind.Over, source_loc, 544, 4, "HiddenSequencePointTest.StepOverHiddenSP"); } - // [ConditionalFact(nameof(RunningOnChrome))] - // Issue: https://github.com/dotnet/runtime/issues/42704 - async Task BreakpointOnHiddenLineShouldStopAtEarliestNextAvailableLine() + [ConditionalTheory(nameof(RunningOnChrome))] + [InlineData(539, 8, 542, 8, "StepOverHiddenSP", "HiddenSequencePointTest.StepOverHiddenSP")] + [InlineData(1272, 8, 1266, 8, "StepOverHiddenSP3", "HiddenSequencePointTest.StepOverHiddenSP3")] + public async Task BreakpointOnHiddenLineShouldStopAtEarliestNextAvailableLine(int line_bp, int column_bp, int line_pause, int column_pause, string method_to_call, string method_name) { - await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 539, 8); + Console.WriteLine(await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", line_bp, column_bp)); await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method ('[debugger-test] HiddenSequencePointTest:StepOverHiddenSP'); }, 1);", - "dotnet://debugger-test.dll/debugger-test.cs", 546, 4, - "StepOverHiddenSP2"); + "window.setTimeout(function() { invoke_static_method ('[debugger-test] HiddenSequencePointTest:" + method_to_call + "'); }, 1);", + "dotnet://debugger-test.dll/debugger-test.cs", line_pause, column_pause, + method_name); + } + + // [ConditionalTheory(nameof(RunningOnChrome))] + //[ActiveIssue("https://github.com/dotnet/runtime/issues/73867")] + [InlineData(184, 20, 161, 8, "HiddenLinesContainingStartOfAnAsyncBlock")] + [InlineData(206, 20, 201, 8, "HiddenLinesAtTheEndOfANestedAsyncBlockWithWithLineDefaultOutsideTheMethod")] + [InlineData(224, 20, 220, 8, "HiddenLinesAtTheEndOfANestedAsyncBlockWithWithLineDefaultOutsideTheMethod2")] + public async Task BreakpointOnHiddenLineShouldStopAtEarliestNextAvailableLineAsync_PauseEarlier(int line_bp, int column_bp, int line_pause, int column_pause, string method_name) + { + await SetBreakpoint("dotnet://debugger-test.dll/debugger-async-test.cs", line_bp, column_bp); + await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method('[debugger-test] DebuggerTests.AsyncTests.ContinueWithTests:RunAsyncWithLineHidden'); }, 1);", + "dotnet://debugger-test.dll/debugger-async-test.cs", line_pause, column_pause, + $"DebuggerTests.AsyncTests.ContinueWithTests.{method_name}"); + } + + [ConditionalTheory(nameof(RunningOnChrome))] + [InlineData(112, 16, 114, 16, "HiddenLinesInAnAsyncBlock")] + [InlineData(130, 16, 133, 16, "HiddenLinesJustBeforeANestedAsyncBlock")] + [InlineData(153, 20, 155, 16, "HiddenLinesAtTheEndOfANestedAsyncBlockWithNoLinesAtEndOfTheMethod.AnonymousMethod__1")] + [InlineData(154, 20, 155, 16, "HiddenLinesAtTheEndOfANestedAsyncBlockWithNoLinesAtEndOfTheMethod.AnonymousMethod__1")] + [InlineData(170, 20, 172, 16, "HiddenLinesAtTheEndOfANestedAsyncBlockWithBreakableLineAtEndOfTheMethod.AnonymousMethod__1")] + public async Task BreakpointOnHiddenLineShouldStopAtEarliestNextAvailableLineAsync(int line_bp, int column_bp, int line_pause, int column_pause, string method_name) + { + await SetBreakpoint("dotnet://debugger-test.dll/debugger-async-test.cs", line_bp, column_bp); + await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method('[debugger-test] DebuggerTests.AsyncTests.ContinueWithTests:RunAsyncWithLineHidden'); }, 1);", + "dotnet://debugger-test.dll/debugger-async-test.cs", line_pause, column_pause, + $"DebuggerTests.AsyncTests.ContinueWithTests.{method_name}"); } [ConditionalFact(nameof(RunningOnChrome))] diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-async-test.cs b/src/mono/wasm/debugger/tests/debugger-test/debugger-async-test.cs index 087e56201c2a4c..da30a3132967ed 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-async-test.cs +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-async-test.cs @@ -94,6 +94,140 @@ await Task.Delay(300).ContinueWith(t2 => Console.WriteLine ($"done with this method"); } + public static async Task RunAsyncWithLineHidden() + { + await HiddenLinesInAnAsyncBlock("foobar"); + await HiddenLinesJustBeforeANestedAsyncBlock("foobar"); + await HiddenLinesAtTheEndOfANestedAsyncBlockWithNoLinesAtEndOfTheMethod("foobar"); + await HiddenLinesAtTheEndOfANestedAsyncBlockWithBreakableLineAtEndOfTheMethod("foobar"); + await HiddenLinesContainingStartOfAnAsyncBlock("foobar"); + await HiddenLinesAtTheEndOfANestedAsyncBlockWithWithLineDefaultOutsideTheMethod("foobar"); + await HiddenLinesAtTheEndOfANestedAsyncBlockWithWithLineDefaultOutsideTheMethod2("foobar"); + System.Diagnostics.Debugger.Break(); + } + public static async Task HiddenLinesInAnAsyncBlock(string str) + { + await Task.Delay(500).ContinueWith(async t => + { +#line hidden + var code = t.Status; +#line default + var ncs_dt0 = new DateTime(3412, 4, 6, 8, 0, 2); + Console.WriteLine ($"First continueWith: {code}, {ncs_dt0}"); // t, code, str, dt0 + await Task.Delay(300).ContinueWith(t2 => + { + var ncs_dt1 = new DateTime(4513, 4, 5, 6, 7, 8); + Console.WriteLine ($"t2: {t2.Status}, str: {str}, {ncs_dt1}, {ncs_dt0}");//t2, dt1, str, dt0 + }); + }); + Console.WriteLine ($"done with this method"); + } + static async Task HiddenLinesJustBeforeANestedAsyncBlock(string str) + { + await Task.Delay(500).ContinueWith(async t => + { + Console.WriteLine($"First continueWith"); + #line hidden + var code = t.Status; // Next line will be in the next async block? hidden line just before async block + Console.WriteLine("another line of code"); + #line default + await Task.Delay(300).ContinueWith(t2 => + { + var ncs_dt1 = new DateTime(4513, 4, 5, 6, 7, 8); + Console.WriteLine($"t2: {t2.Status}, str: {str}, {ncs_dt1}");//t2, dt1, str, dt0 + }); + }); + Console.WriteLine($"done with this method"); + } + + static async Task HiddenLinesAtTheEndOfANestedAsyncBlockWithNoLinesAtEndOfTheMethod(string str) + { + await Task.Delay(500).ContinueWith(async t => + { + var code = t.Status; + Console.WriteLine($"First continueWith"); + await Task.Delay(300).ContinueWith(t2 => + { + var ncs_dt1 = new DateTime(4513, 4, 5, 6, 7, 8); + Console.WriteLine($"t2: {t2.Status}, str: {str}, {ncs_dt1}");//t2, dt1, str, dt0 + #line hidden + Console.WriteLine("something else"); // Next line will be in the next async block? hidden line at end of async block + #line default + }); + }); + } + + static async Task HiddenLinesAtTheEndOfANestedAsyncBlockWithBreakableLineAtEndOfTheMethod(string str) + { + await Task.Delay(500).ContinueWith(async t => + { + var code = t.Status; + Console.WriteLine($"First continueWith"); + await Task.Delay(300).ContinueWith(t2 => + { + var ncs_dt1 = new DateTime(4513, 4, 5, 6, 7, 8); + Console.WriteLine($"t2: {t2.Status}, str: {str}, {ncs_dt1}");//t2, dt1, str, dt0 + #line hidden + Console.WriteLine("something else"); // Next line will be in the next async block? hidden line at end of async block + #line default + }); + }); + Console.WriteLine ($"Last line.."); + } + + static async Task HiddenLinesContainingStartOfAnAsyncBlock(string str) + { + await Task.Delay(500).ContinueWith(async t => + { + var code = t.Status; + Console.WriteLine($"First continueWith"); + #line hidden + await Task.Delay(300).ContinueWith(t2 => + #line default + { + var ncs_dt1 = new DateTime(4513, 4, 5, 6, 7, 8); + Console.WriteLine($"t2: {t2.Status}, str: {str}, {ncs_dt1}");//t2, dt1, str, dt0 + Console.WriteLine("something else"); // Next line will be in the next async block? hidden line at end of async block + }); + }); + Console.WriteLine($"done with this method"); + } + + static async Task HiddenLinesAtTheEndOfANestedAsyncBlockWithWithLineDefaultOutsideTheMethod(string str) + { + await Task.Delay(500).ContinueWith(async t => + { + var code = t.Status; + Console.WriteLine($"First continueWith"); + await Task.Delay(300).ContinueWith(t2 => + { + var ncs_dt1 = new DateTime(4513, 4, 5, 6, 7, 8); + Console.WriteLine($"t2: {t2.Status}, str: {str}, {ncs_dt1}");//t2, dt1, str, dt0 + #line hidden + Console.WriteLine("somethind else"); // Next line will be in the next async block? hidden line at end of async block + }); + }); + #line default + Console.WriteLine($"done with this method"); + } + + static async Task HiddenLinesAtTheEndOfANestedAsyncBlockWithWithLineDefaultOutsideTheMethod2(string str) + { + await Task.Delay(500).ContinueWith(async t => + { + var code = t.Status; + Console.WriteLine($"First continueWith"); + await Task.Delay(300).ContinueWith(t2 => + { + var ncs_dt1 = new DateTime(4513, 4, 5, 6, 7, 8); + Console.WriteLine($"t2: {t2.Status}, str: {str}, {ncs_dt1}");//t2, dt1, str, dt0 + #line hidden + Console.WriteLine("somethind else"); // Next line will be in the next async block? hidden line at end of async block + }); + #line default + }); + Console.WriteLine($"done with this method"); + } } } diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs index fc9902754a7483..6424ca3d5cdd93 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs @@ -532,7 +532,7 @@ public static void LoadLazyAssembly(string asm_base64, string pdb_base64) } } -public class HiddenSequencePointTest { +public partial class HiddenSequencePointTest { public static void StepOverHiddenSP() { Console.WriteLine("first line"); @@ -1259,3 +1259,19 @@ public static void Run() System.Diagnostics.Debugger.Break(); } } + +public partial class HiddenSequencePointTest { + public static void StepOverHiddenSP3() + { + MethodWithHiddenLinesAtTheEnd3(); + System.Diagnostics.Debugger.Break(); + } + public static void MethodWithHiddenLinesAtTheEnd3() + { + Console.WriteLine ($"MethodWithHiddenLinesAtTheEnd"); +#line hidden + Console.WriteLine ($"debugger shouldn't be able to step here"); + } +#line default +} + From 339a6bcc082c1ab229e40522d7bc84f9feca02ca Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Fri, 12 Aug 2022 19:44:10 -0700 Subject: [PATCH 50/56] Update for recent compiler changes for ref fields (#73466) * Update for recent compiler changes for ref fields --- .../Globalization/FormatProvider.Number.cs | 20 ++-- .../System/Net/Sockets/SocketPal.Windows.cs | 2 +- .../src/System/Globalization/DateTimeParse.cs | 106 +++++++++--------- .../src/System/Number.Formatting.cs | 2 +- .../src/System/Number.Parsing.cs | 2 +- .../System/Reflection/AssemblyNameParser.cs | 2 +- .../Reader/Utf8JsonReader.MultiSegment.cs | 20 ++-- .../System/Text/Json/Reader/Utf8JsonReader.cs | 14 +-- .../Converters/CastingConverter.cs | 2 +- .../Converters/Collection/ArrayConverter.cs | 2 +- .../IAsyncEnumerableOfTConverter.cs | 4 +- .../Collection/ICollectionOfTConverter.cs | 2 +- .../Collection/IDictionaryConverter.cs | 2 +- .../IDictionaryOfTKeyTValueConverter.cs | 2 +- .../Collection/IEnumerableConverter.cs | 2 +- .../Collection/IEnumerableOfTConverter.cs | 2 +- .../Converters/Collection/IListConverter.cs | 2 +- .../Collection/IListOfTConverter.cs | 2 +- ...ReadOnlyDictionaryOfTKeyTValueConverter.cs | 2 +- .../Converters/Collection/ISetOfTConverter.cs | 2 +- ...mmutableDictionaryOfTKeyTValueConverter.cs | 2 +- .../ImmutableEnumerableOfTConverter.cs | 2 +- .../Collection/JsonCollectionConverter.cs | 4 +- .../Collection/JsonDictionaryConverter.cs | 6 +- .../Converters/Collection/ListOfTConverter.cs | 2 +- .../Collection/QueueOfTConverter.cs | 2 +- .../Collection/StackOfTConverter.cs | 2 +- .../Collection/StackOrQueueConverter.cs | 2 +- .../Converters/FSharp/FSharpListConverter.cs | 2 +- .../Converters/FSharp/FSharpMapConverter.cs | 2 +- .../FSharp/FSharpOptionConverter.cs | 2 +- .../Converters/FSharp/FSharpSetConverter.cs | 2 +- .../FSharp/FSharpValueOptionConverter.cs | 2 +- .../JsonMetadataServicesConverter.cs | 2 +- .../Converters/Node/JsonObjectConverter.cs | 2 +- .../Converters/Object/ObjectConverter.cs | 2 +- .../Object/ObjectDefaultConverter.cs | 6 +- ...ParameterizedConstructorConverter.Large.cs | 2 +- ...ParameterizedConstructorConverter.Small.cs | 4 +- ...ctWithParameterizedConstructorConverter.cs | 16 +-- .../Converters/Value/NullableConverter.cs | 2 +- .../Serialization/JsonConverter.ReadAhead.cs | 4 +- .../Text/Json/Serialization/JsonConverter.cs | 8 +- .../Serialization/JsonConverterFactory.cs | 6 +- .../JsonConverterOfT.ReadCore.cs | 4 +- .../Json/Serialization/JsonConverterOfT.cs | 8 +- .../JsonSerializer.Read.HandleMetadata.cs | 10 +- .../JsonSerializer.Read.HandlePropertyName.cs | 2 +- .../JsonSerializer.Read.Helpers.cs | 2 +- .../JsonSerializer.Read.Utf8JsonReader.cs | 2 +- .../Metadata/JsonPropertyInfo.cs | 8 +- .../Metadata/JsonPropertyInfoOfT.cs | 4 +- .../Text/Json/ThrowHelper.Serialization.cs | 30 ++--- 53 files changed, 175 insertions(+), 175 deletions(-) diff --git a/src/libraries/Common/src/System/Globalization/FormatProvider.Number.cs b/src/libraries/Common/src/System/Globalization/FormatProvider.Number.cs index 688e587609dee2..a630552a064e3d 100644 --- a/src/libraries/Common/src/System/Globalization/FormatProvider.Number.cs +++ b/src/libraries/Common/src/System/Globalization/FormatProvider.Number.cs @@ -356,7 +356,7 @@ private static unsafe bool AllowHyphenDuringParsing(NumberFormatInfo info) return ret; } - private static unsafe bool ParseNumber(ref char* str, char* strEnd, NumberStyles options, ref NumberBuffer number, StringBuilder? sb, NumberFormatInfo numfmt, bool parseDecimal) + private static unsafe bool ParseNumber(ref char* str, char* strEnd, NumberStyles options, scoped ref NumberBuffer number, StringBuilder? sb, NumberFormatInfo numfmt, bool parseDecimal) { Debug.Assert(str != null); Debug.Assert(strEnd != null); @@ -598,7 +598,7 @@ private static bool TrailingZeros(ReadOnlySpan s, int index) => // For compatibility, we need to allow trailing zeros at the end of a number string s.Slice(index).IndexOfAnyExcept('\0') < 0; - internal static unsafe bool TryStringToNumber(ReadOnlySpan str, NumberStyles options, ref NumberBuffer number, StringBuilder sb, NumberFormatInfo numfmt, bool parseDecimal) + internal static unsafe bool TryStringToNumber(ReadOnlySpan str, NumberStyles options, scoped ref NumberBuffer number, StringBuilder sb, NumberFormatInfo numfmt, bool parseDecimal) { Debug.Assert(numfmt != null); @@ -741,7 +741,7 @@ internal static char ParseFormatSpecifier(ReadOnlySpan format, out int dig '\0'; } - internal static unsafe void NumberToString(ref ValueStringBuilder sb, ref NumberBuffer number, char format, int nMaxDigits, NumberFormatInfo info, bool isDecimal) + internal static unsafe void NumberToString(ref ValueStringBuilder sb, scoped ref NumberBuffer number, char format, int nMaxDigits, NumberFormatInfo info, bool isDecimal) { int nMinDigits = -1; @@ -901,7 +901,7 @@ internal static unsafe void NumberToString(ref ValueStringBuilder sb, ref Number } } - private static void FormatCurrency(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info) + private static void FormatCurrency(ref ValueStringBuilder sb, scoped ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info) { string fmt = number.sign ? s_negCurrencyFormats[info.CurrencyNegativePattern] : @@ -927,7 +927,7 @@ private static void FormatCurrency(ref ValueStringBuilder sb, ref NumberBuffer n } } - private static unsafe void FormatFixed(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, int[]? groupDigits, string sDecimal, string? sGroup) + private static unsafe void FormatFixed(ref ValueStringBuilder sb, scoped ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, int[]? groupDigits, string sDecimal, string? sGroup) { Debug.Assert(sGroup != null || groupDigits == null); @@ -1048,7 +1048,7 @@ private static unsafe void FormatFixed(ref ValueStringBuilder sb, ref NumberBuff } } - private static void FormatNumber(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info) + private static void FormatNumber(ref ValueStringBuilder sb, scoped ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info) { string fmt = number.sign ? s_negNumberFormats[info.NumberNegativePattern] : @@ -1071,7 +1071,7 @@ private static void FormatNumber(ref ValueStringBuilder sb, ref NumberBuffer num } } - private static unsafe void FormatScientific(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, char expChar) + private static unsafe void FormatScientific(ref ValueStringBuilder sb, scoped ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, char expChar) { char* dig = number.digits; @@ -1118,7 +1118,7 @@ private static unsafe void FormatExponent(ref ValueStringBuilder sb, NumberForma } } - private static unsafe void FormatGeneral(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, char expChar, bool bSuppressScientific) + private static unsafe void FormatGeneral(ref ValueStringBuilder sb, scoped ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, char expChar, bool bSuppressScientific) { int digPos = number.scale; bool scientific = false; @@ -1169,7 +1169,7 @@ private static unsafe void FormatGeneral(ref ValueStringBuilder sb, ref NumberBu } } - private static void FormatPercent(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info) + private static void FormatPercent(ref ValueStringBuilder sb, scoped ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info) { string fmt = number.sign ? s_negPercentFormats[info.PercentNegativePattern] : @@ -1289,7 +1289,7 @@ private static unsafe int FindSection(ReadOnlySpan format, int section) } } - internal static unsafe void NumberToStringFormat(ref ValueStringBuilder sb, ref NumberBuffer number, ReadOnlySpan format, NumberFormatInfo info) + internal static unsafe void NumberToStringFormat(ref ValueStringBuilder sb, scoped ref NumberBuffer number, ReadOnlySpan format, NumberFormatInfo info) { int digitCount; int decimalPos; diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs index b18ca115412753..872e9ee128424d 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs @@ -883,7 +883,7 @@ public static unsafe SocketError Poll(SafeSocketHandle handle, int microseconds, public static unsafe SocketError Select(IList? checkRead, IList? checkWrite, IList? checkError, int microseconds) { const int StackThreshold = 64; // arbitrary limit to avoid too much space on stack - static bool ShouldStackAlloc(IList? list, ref IntPtr[]? lease, out Span span) + static bool ShouldStackAlloc(IList? list, scoped ref IntPtr[]? lease, out Span span) { int count; if (list == null || (count = list.Count) == 0) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs index 6455793addd00b..28ba7c96a9c5d2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs @@ -79,7 +79,7 @@ internal static bool TryParseExact(ReadOnlySpan s, ReadOnlySpan form return false; } - internal static bool TryParseExact(ReadOnlySpan s, ReadOnlySpan format, DateTimeFormatInfo dtfi, DateTimeStyles style, ref DateTimeResult result) + internal static bool TryParseExact(ReadOnlySpan s, ReadOnlySpan format, DateTimeFormatInfo dtfi, DateTimeStyles style, scoped ref DateTimeResult result) { if (s.Length == 0) { @@ -166,7 +166,7 @@ internal static bool TryParseExactMultiple(ReadOnlySpan s, string?[]? form } internal static bool TryParseExactMultiple(ReadOnlySpan s, string?[]? formats, - DateTimeFormatInfo dtfi, DateTimeStyles style, ref DateTimeResult result) + DateTimeFormatInfo dtfi, DateTimeStyles style, scoped ref DateTimeResult result) { if (formats == null) { @@ -491,7 +491,7 @@ private static bool ParseFraction(ref __DTString str, out double result) ** FormatException if invalid timezone format is found. ============================================================================*/ - private static bool ParseTimeZone(ref __DTString str, ref TimeSpan result) + private static bool ParseTimeZone(ref __DTString str, scoped ref TimeSpan result) { // The hour/minute offset for timezone. int hourOffset; @@ -565,7 +565,7 @@ private static bool ParseTimeZone(ref __DTString str, ref TimeSpan result) } // This is the helper function to handle timezone in string in the format like +/-0800 - private static bool HandleTimeZone(ref __DTString str, ref DateTimeResult result) + private static bool HandleTimeZone(ref __DTString str, scoped ref DateTimeResult result) { if (str.Index < str.Length - 1) { @@ -601,7 +601,7 @@ private static bool HandleTimeZone(ref __DTString str, ref DateTimeResult result // This is the lexer. Check the character at the current index, and put the found token in dtok and // some raw date/time information in raw. // - private static bool Lex(DS dps, ref __DTString str, ref DateTimeToken dtok, ref DateTimeRawInfo raw, ref DateTimeResult result, ref DateTimeFormatInfo dtfi, DateTimeStyles styles) + private static bool Lex(DS dps, ref __DTString str, scoped ref DateTimeToken dtok, scoped ref DateTimeRawInfo raw, scoped ref DateTimeResult result, scoped ref DateTimeFormatInfo dtfi, DateTimeStyles styles) { int indexBeforeSeparator; char charBeforeSeparator; @@ -1539,14 +1539,14 @@ private static bool SetDateYDM(ref DateTimeResult result, int year, int day, int return SetDateYMD(ref result, year, month, day); } - private static void GetDefaultYear(ref DateTimeResult result, ref DateTimeStyles styles) + private static void GetDefaultYear(ref DateTimeResult result, scoped ref DateTimeStyles styles) { result.Year = result.calendar.GetYear(GetDateTimeNow(ref result, ref styles)); result.flags |= ParseFlags.YearDefault; } // Processing teriminal case: DS.DX_NN - private static bool GetDayOfNN(ref DateTimeResult result, ref DateTimeStyles styles, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) + private static bool GetDayOfNN(ref DateTimeResult result, scoped ref DateTimeStyles styles, scoped ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) { if ((result.flags & ParseFlags.HaveDate) != 0) { @@ -1588,7 +1588,7 @@ private static bool GetDayOfNN(ref DateTimeResult result, ref DateTimeStyles sty } // Processing teriminal case: DS.DX_NNN - private static bool GetDayOfNNN(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) + private static bool GetDayOfNNN(ref DateTimeResult result, scoped ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) { if ((result.flags & ParseFlags.HaveDate) != 0) { @@ -1644,7 +1644,7 @@ private static bool GetDayOfNNN(ref DateTimeResult result, ref DateTimeRawInfo r return false; } - private static bool GetDayOfMN(ref DateTimeResult result, ref DateTimeStyles styles, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) + private static bool GetDayOfMN(ref DateTimeResult result, scoped ref DateTimeStyles styles, scoped ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) { if ((result.flags & ParseFlags.HaveDate) != 0) { @@ -1703,7 +1703,7 @@ private static bool GetDayOfMN(ref DateTimeResult result, ref DateTimeStyles sty // //////////////////////////////////////////////////////////////////////// - private static bool GetHebrewDayOfNM(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) + private static bool GetHebrewDayOfNM(ref DateTimeResult result, scoped ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) { if (!GetMonthDayOrder(dtfi.MonthDayPattern, out int monthDayOrder)) { @@ -1723,7 +1723,7 @@ private static bool GetHebrewDayOfNM(ref DateTimeResult result, ref DateTimeRawI return false; } - private static bool GetDayOfNM(ref DateTimeResult result, ref DateTimeStyles styles, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) + private static bool GetDayOfNM(ref DateTimeResult result, scoped ref DateTimeStyles styles, scoped ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) { if ((result.flags & ParseFlags.HaveDate) != 0) { @@ -1776,7 +1776,7 @@ private static bool GetDayOfNM(ref DateTimeResult result, ref DateTimeStyles sty return true; } - private static bool GetDayOfMNN(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) + private static bool GetDayOfMNN(ref DateTimeResult result, scoped ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) { if ((result.flags & ParseFlags.HaveDate) != 0) { @@ -1845,7 +1845,7 @@ private static bool GetDayOfMNN(ref DateTimeResult result, ref DateTimeRawInfo r return false; } - private static bool GetDayOfYNN(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) + private static bool GetDayOfYNN(ref DateTimeResult result, scoped ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) { if ((result.flags & ParseFlags.HaveDate) != 0) { @@ -1879,7 +1879,7 @@ private static bool GetDayOfYNN(ref DateTimeResult result, ref DateTimeRawInfo r return false; } - private static bool GetDayOfNNY(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) + private static bool GetDayOfNNY(ref DateTimeResult result, scoped ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) { if ((result.flags & ParseFlags.HaveDate) != 0) { @@ -1917,7 +1917,7 @@ private static bool GetDayOfNNY(ref DateTimeResult result, ref DateTimeRawInfo r return false; } - private static bool GetDayOfYMN(ref DateTimeResult result, ref DateTimeRawInfo raw) + private static bool GetDayOfYMN(ref DateTimeResult result, scoped ref DateTimeRawInfo raw) { if ((result.flags & ParseFlags.HaveDate) != 0) { @@ -1935,7 +1935,7 @@ private static bool GetDayOfYMN(ref DateTimeResult result, ref DateTimeRawInfo r return false; } - private static bool GetDayOfYN(ref DateTimeResult result, ref DateTimeRawInfo raw) + private static bool GetDayOfYN(ref DateTimeResult result, scoped ref DateTimeRawInfo raw) { if ((result.flags & ParseFlags.HaveDate) != 0) { @@ -1953,7 +1953,7 @@ private static bool GetDayOfYN(ref DateTimeResult result, ref DateTimeRawInfo ra return false; } - private static bool GetDayOfYM(ref DateTimeResult result, ref DateTimeRawInfo raw) + private static bool GetDayOfYM(ref DateTimeResult result, scoped ref DateTimeRawInfo raw) { if ((result.flags & ParseFlags.HaveDate) != 0) { @@ -1971,7 +1971,7 @@ private static bool GetDayOfYM(ref DateTimeResult result, ref DateTimeRawInfo ra return false; } - private static void AdjustTimeMark(DateTimeFormatInfo dtfi, ref DateTimeRawInfo raw) + private static void AdjustTimeMark(DateTimeFormatInfo dtfi, scoped ref DateTimeRawInfo raw) { // Specail case for culture which uses AM as empty string. // E.g. af-ZA (0x0436) @@ -2025,7 +2025,7 @@ private static bool AdjustHour(ref int hour, TM timeMark) return true; } - private static bool GetTimeOfN(ref DateTimeResult result, ref DateTimeRawInfo raw) + private static bool GetTimeOfN(ref DateTimeResult result, scoped ref DateTimeRawInfo raw) { if ((result.flags & ParseFlags.HaveTime) != 0) { @@ -2046,7 +2046,7 @@ private static bool GetTimeOfN(ref DateTimeResult result, ref DateTimeRawInfo ra return true; } - private static bool GetTimeOfNN(ref DateTimeResult result, ref DateTimeRawInfo raw) + private static bool GetTimeOfNN(ref DateTimeResult result, scoped ref DateTimeRawInfo raw) { Debug.Assert(raw.numCount >= 2, "raw.numCount >= 2"); if ((result.flags & ParseFlags.HaveTime) != 0) @@ -2062,7 +2062,7 @@ private static bool GetTimeOfNN(ref DateTimeResult result, ref DateTimeRawInfo r return true; } - private static bool GetTimeOfNNN(ref DateTimeResult result, ref DateTimeRawInfo raw) + private static bool GetTimeOfNNN(ref DateTimeResult result, scoped ref DateTimeRawInfo raw) { if ((result.flags & ParseFlags.HaveTime) != 0) { @@ -2081,7 +2081,7 @@ private static bool GetTimeOfNNN(ref DateTimeResult result, ref DateTimeRawInfo // // Processing terminal state: A Date suffix followed by one number. // - private static bool GetDateOfDSN(ref DateTimeResult result, ref DateTimeRawInfo raw) + private static bool GetDateOfDSN(ref DateTimeResult result, scoped ref DateTimeRawInfo raw) { if (raw.numCount != 1 || result.Day != -1) { @@ -2092,7 +2092,7 @@ private static bool GetDateOfDSN(ref DateTimeResult result, ref DateTimeRawInfo return true; } - private static bool GetDateOfNDS(ref DateTimeResult result, ref DateTimeRawInfo raw) + private static bool GetDateOfNDS(ref DateTimeResult result, scoped ref DateTimeRawInfo raw) { if (result.Month == -1) { @@ -2116,7 +2116,7 @@ private static bool GetDateOfNDS(ref DateTimeResult result, ref DateTimeRawInfo return true; } - private static bool GetDateOfNNDS(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) + private static bool GetDateOfNNDS(ref DateTimeResult result, scoped ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) { // For partial CJK Dates, the only valid formats are with a specified year, followed by two numbers, which // will be the Month and Day, and with a specified Month, when the numbers are either the year and day or @@ -2165,7 +2165,7 @@ private static bool GetDateOfNNDS(ref DateTimeResult result, ref DateTimeRawInfo // // A date suffix is found, use this method to put the number into the result. // - private static bool ProcessDateTimeSuffix(ref DateTimeResult result, ref DateTimeRawInfo raw, ref DateTimeToken dtok) + private static bool ProcessDateTimeSuffix(ref DateTimeResult result, scoped ref DateTimeRawInfo raw, scoped ref DateTimeToken dtok) { switch (dtok.suffix) { @@ -2229,7 +2229,7 @@ private static bool ProcessDateTimeSuffix(ref DateTimeResult result, ref DateTim // //////////////////////////////////////////////////////////////////////// - internal static bool ProcessHebrewTerminalState(DS dps, ref DateTimeResult result, ref DateTimeStyles styles, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) + internal static bool ProcessHebrewTerminalState(DS dps, scoped ref DateTimeResult result, scoped ref DateTimeStyles styles, scoped ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) { // The following are accepted terminal state for Hebrew date. switch (dps) @@ -2339,7 +2339,7 @@ internal static bool ProcessHebrewTerminalState(DS dps, ref DateTimeResult resul // A terminal state has been reached, call the appropriate function to fill in the parsing result. // Return true if the state is a terminal state. // - internal static bool ProcessTerminalState(DS dps, ref DateTimeResult result, ref DateTimeStyles styles, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) + internal static bool ProcessTerminalState(DS dps, scoped ref DateTimeResult result, scoped ref DateTimeStyles styles, scoped ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) { bool passed = true; switch (dps) @@ -2484,7 +2484,7 @@ internal static bool TryParse(ReadOnlySpan s, DateTimeFormatInfo dtfi, Dat // // This is the real method to do the parsing work. // - internal static bool TryParse(ReadOnlySpan s, DateTimeFormatInfo dtfi, DateTimeStyles styles, ref DateTimeResult result) + internal static bool TryParse(ReadOnlySpan s, DateTimeFormatInfo dtfi, DateTimeStyles styles, scoped ref DateTimeResult result) { if (s.Length == 0) { @@ -2951,7 +2951,7 @@ private static bool AdjustTimeZoneToLocal(ref DateTimeResult result, bool bTimeO // Parse the ISO8601 format string found during Parse(); // // - private static bool ParseISO8601(ref DateTimeRawInfo raw, ref __DTString str, DateTimeStyles styles, ref DateTimeResult result) + private static bool ParseISO8601(scoped ref DateTimeRawInfo raw, ref __DTString str, DateTimeStyles styles, scoped ref DateTimeResult result) { str.Index--; int second = 0; @@ -3158,7 +3158,7 @@ internal static bool ParseDigits(ref __DTString str, int minDigitLen, int maxDig **Exceptions: FormatException if error in parsing number. ==============================================================================*/ - private static bool ParseFractionExact(ref __DTString str, int maxDigitLen, ref double result) + private static bool ParseFractionExact(ref __DTString str, int maxDigitLen, scoped ref double result) { if (!str.GetNextDigit()) { @@ -3191,7 +3191,7 @@ private static bool ParseFractionExact(ref __DTString str, int maxDigitLen, ref ** symbol is not found. ==============================================================================*/ - private static bool ParseSign(ref __DTString str, ref bool result) + private static bool ParseSign(ref __DTString str, scoped ref bool result) { if (!str.GetNext()) { @@ -3222,7 +3222,7 @@ private static bool ParseSign(ref __DTString str, ref bool result) **Exceptions: FormatException if errors in parsing. ==============================================================================*/ - private static bool ParseTimeZoneOffset(ref __DTString str, int len, ref TimeSpan result) + private static bool ParseTimeZoneOffset(ref __DTString str, int len, scoped ref TimeSpan result) { bool isPositive = true; int hourOffset; @@ -3293,7 +3293,7 @@ private static bool ParseTimeZoneOffset(ref __DTString str, int len, ref TimeSpa **Exceptions: FormatException if an abbreviated month name can not be found. ==============================================================================*/ - private static bool MatchAbbreviatedMonthName(ref __DTString str, DateTimeFormatInfo dtfi, ref int result) + private static bool MatchAbbreviatedMonthName(ref __DTString str, DateTimeFormatInfo dtfi, scoped ref int result) { int maxMatchStrLen = 0; result = -1; @@ -3364,7 +3364,7 @@ private static bool MatchAbbreviatedMonthName(ref __DTString str, DateTimeFormat **Exceptions: FormatException if a month name can not be found. ==============================================================================*/ - private static bool MatchMonthName(ref __DTString str, DateTimeFormatInfo dtfi, ref int result) + private static bool MatchMonthName(ref __DTString str, DateTimeFormatInfo dtfi, scoped ref int result) { int maxMatchStrLen = 0; result = -1; @@ -3436,7 +3436,7 @@ private static bool MatchMonthName(ref __DTString str, DateTimeFormatInfo dtfi, **Exceptions: FormatException if a abbreviated day of week name can not be found. ==============================================================================*/ - private static bool MatchAbbreviatedDayName(ref __DTString str, DateTimeFormatInfo dtfi, ref int result) + private static bool MatchAbbreviatedDayName(ref __DTString str, DateTimeFormatInfo dtfi, scoped ref int result) { int maxMatchStrLen = 0; result = -1; @@ -3474,7 +3474,7 @@ private static bool MatchAbbreviatedDayName(ref __DTString str, DateTimeFormatIn **Exceptions: FormatException if a day of week name can not be found. ==============================================================================*/ - private static bool MatchDayName(ref __DTString str, DateTimeFormatInfo dtfi, ref int result) + private static bool MatchDayName(ref __DTString str, DateTimeFormatInfo dtfi, scoped ref int result) { // Turkish (tr-TR) got day names with the same prefix. int maxMatchStrLen = 0; @@ -3513,7 +3513,7 @@ private static bool MatchDayName(ref __DTString str, DateTimeFormatInfo dtfi, re **Exceptions: FormatException if an era name can not be found. ==============================================================================*/ - private static bool MatchEraName(ref __DTString str, DateTimeFormatInfo dtfi, ref int result) + private static bool MatchEraName(ref __DTString str, DateTimeFormatInfo dtfi, scoped ref int result) { if (str.GetNext()) { @@ -3551,7 +3551,7 @@ private static bool MatchEraName(ref __DTString str, DateTimeFormatInfo dtfi, re **Exceptions: FormatException if a time mark can not be found. ==============================================================================*/ - private static bool MatchTimeMark(ref __DTString str, DateTimeFormatInfo dtfi, ref TM result) + private static bool MatchTimeMark(ref __DTString str, DateTimeFormatInfo dtfi, scoped ref TM result) { result = TM.NotSet; // In some cultures have empty strings in AM/PM mark. E.g. af-ZA (0x0436), the AM mark is "", and PM mark is "nm". @@ -3606,7 +3606,7 @@ private static bool MatchTimeMark(ref __DTString str, DateTimeFormatInfo dtfi, r **Exceptions: FormatException if a abbreviated time mark can not be found. ==============================================================================*/ - private static bool MatchAbbreviatedTimeMark(ref __DTString str, DateTimeFormatInfo dtfi, ref TM result) + private static bool MatchAbbreviatedTimeMark(ref __DTString str, DateTimeFormatInfo dtfi, scoped ref TM result) { // NOTENOTE : the assumption here is that abbreviated time mark is the first // character of the AM/PM designator. If this invariant changes, we have to @@ -3640,7 +3640,7 @@ private static bool MatchAbbreviatedTimeMark(ref __DTString str, DateTimeFormatI **Exceptions: ==============================================================================*/ - private static bool CheckNewValue(ref int currentValue, int newValue, char patternChar, ref DateTimeResult result) + private static bool CheckNewValue(scoped ref int currentValue, int newValue, char patternChar, scoped ref DateTimeResult result) { if (currentValue == -1) { @@ -3658,7 +3658,7 @@ private static bool CheckNewValue(ref int currentValue, int newValue, char patte return true; } - private static DateTime GetDateTimeNow(ref DateTimeResult result, ref DateTimeStyles styles) + private static DateTime GetDateTimeNow(scoped ref DateTimeResult result, scoped ref DateTimeStyles styles) { if ((result.flags & ParseFlags.CaptureOffset) != 0) { @@ -3678,7 +3678,7 @@ private static DateTime GetDateTimeNow(ref DateTimeResult result, ref DateTimeSt return DateTime.Now; } - private static bool CheckDefaultDateTime(ref DateTimeResult result, ref Calendar cal, DateTimeStyles styles) + private static bool CheckDefaultDateTime(scoped ref DateTimeResult result, scoped ref Calendar cal, DateTimeStyles styles) { if ((result.flags & ParseFlags.CaptureOffset) != 0) { @@ -3779,7 +3779,7 @@ X X X Parsed year Parsed month Parsed day // This method also set the dtfi according/parseInfo to some special pre-defined // formats. // - private static string ExpandPredefinedFormat(ReadOnlySpan format, ref DateTimeFormatInfo dtfi, ref ParsingInfo parseInfo, ref DateTimeResult result) + private static string ExpandPredefinedFormat(ReadOnlySpan format, scoped ref DateTimeFormatInfo dtfi, scoped ref ParsingInfo parseInfo, scoped ref DateTimeResult result) { // // Check the format to see if we need to override the dtfi to be InvariantInfo, @@ -3842,7 +3842,7 @@ private static bool ParseJapaneseEraStart(ref __DTString str, DateTimeFormatInfo return true; } - private static void ConfigureFormatR(ref DateTimeFormatInfo dtfi, ref ParsingInfo parseInfo, ref DateTimeResult result) + private static void ConfigureFormatR(scoped ref DateTimeFormatInfo dtfi, scoped ref ParsingInfo parseInfo, scoped ref DateTimeResult result) { parseInfo.calendar = GregorianCalendar.GetDefaultInstance(); dtfi = DateTimeFormatInfo.InvariantInfo; @@ -3852,7 +3852,7 @@ private static void ConfigureFormatR(ref DateTimeFormatInfo dtfi, ref ParsingInf } } - private static void ConfigureFormatOS(ref DateTimeFormatInfo dtfi, ref ParsingInfo parseInfo) + private static void ConfigureFormatOS(scoped ref DateTimeFormatInfo dtfi, scoped ref ParsingInfo parseInfo) { parseInfo.calendar = GregorianCalendar.GetDefaultInstance(); dtfi = DateTimeFormatInfo.InvariantInfo; @@ -3863,9 +3863,9 @@ private static void ConfigureFormatOS(ref DateTimeFormatInfo dtfi, ref ParsingIn private static bool ParseByFormat( ref __DTString str, ref __DTString format, - ref ParsingInfo parseInfo, + scoped ref ParsingInfo parseInfo, DateTimeFormatInfo dtfi, - ref DateTimeResult result) + scoped ref DateTimeResult result) { int tokenLen; int tempYear = 0, tempMonth = 0, tempDay = 0, tempDayOfWeek = 0, tempHour = 0, tempMinute = 0, tempSecond = 0; @@ -4451,7 +4451,7 @@ private static bool DoStrictParse( ReadOnlySpan formatParam, DateTimeStyles styles, DateTimeFormatInfo dtfi, - ref DateTimeResult result) + scoped ref DateTimeResult result) { ParsingInfo parseInfo = default; parseInfo.Init(); @@ -4657,7 +4657,7 @@ private static bool DoStrictParse( return DetermineTimeZoneAdjustments(ref result, styles, bTimeOnly); } - private static bool ParseFormatR(ReadOnlySpan source, ref ParsingInfo parseInfo, ref DateTimeResult result) + private static bool ParseFormatR(ReadOnlySpan source, scoped ref ParsingInfo parseInfo, scoped ref DateTimeResult result) { // Example: // Tue, 03 Jan 2017 08:08:05 GMT @@ -4852,7 +4852,7 @@ private static bool ParseFormatR(ReadOnlySpan source, ref ParsingInfo pars return true; } - private static bool ParseFormatO(ReadOnlySpan source, ref DateTimeResult result) + private static bool ParseFormatO(ReadOnlySpan source, scoped ref DateTimeResult result) { // Examples: // 2017-06-12T05:30:45.7680000 (interpreted as local time wrt to current time zone) @@ -5066,7 +5066,7 @@ private static bool ParseFormatO(ReadOnlySpan source, ref DateTimeResult r return DetermineTimeZoneAdjustments(ref result, DateTimeStyles.None, bTimeOnly: false); } - private static Exception GetDateTimeParseException(ref DateTimeResult result) + private static Exception GetDateTimeParseException(scoped ref DateTimeResult result) { switch (result.failure) { @@ -5438,7 +5438,7 @@ internal bool MatchSpecifiedWord(string target) => Index + target.Length <= Length && m_info.Compare(Value.Slice(Index, target.Length), target, CompareOptions.IgnoreCase) == 0; - internal bool MatchSpecifiedWords(string target, bool checkWordBoundary, ref int matchLength) + internal bool MatchSpecifiedWords(string target, bool checkWordBoundary, scoped ref int matchLength) { int valueRemaining = Value.Length - Index; matchLength = target.Length; @@ -5584,7 +5584,7 @@ internal bool Match(char ch) // maxMatchStrLen [in/out] the initialized maximum length. This parameter can be used to // find the longest match in two string arrays. // - internal int MatchLongestWords(string[] words, ref int maxMatchStrLen) + internal int MatchLongestWords(string[] words, scoped ref int maxMatchStrLen) { int result = -1; for (int i = 0; i < words.Length; i++) diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs index f81c6ac437aba2..9268f341d7dac5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs @@ -349,7 +349,7 @@ public static unsafe bool TryFormatDecimal(decimal value, ReadOnlySpan for return sb.TryCopyTo(destination, out charsWritten); } - internal static unsafe void DecimalToNumber(ref decimal d, ref NumberBuffer number) + internal static unsafe void DecimalToNumber(scoped ref decimal d, ref NumberBuffer number) { byte* buffer = number.GetDigitsPointer(); number.DigitsCount = DecimalPrecision; diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs index 65c1c8745eb656..a9bb8ec61bd68d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs @@ -342,7 +342,7 @@ internal static UInt128 ParseUInt128(ReadOnlySpan value, NumberStyles styl return result; } - private static unsafe bool TryParseNumber(ref char* str, char* strEnd, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info) + private static unsafe bool TryParseNumber(scoped ref char* str, char* strEnd, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info) { Debug.Assert(str != null); Debug.Assert(strEnd != null); diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyNameParser.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyNameParser.cs index ab6dce32c2b953..8ab6b644581f71 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyNameParser.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyNameParser.cs @@ -73,7 +73,7 @@ public static AssemblyNameParts Parse(ReadOnlySpan name) return new AssemblyNameParser(name).Parse(); } - private void RecordNewSeenOrThrow(ref AttributeKind seenAttributes, AttributeKind newAttribute) + private void RecordNewSeenOrThrow(scoped ref AttributeKind seenAttributes, AttributeKind newAttribute) { if ((seenAttributes & newAttribute) != 0) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.MultiSegment.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.MultiSegment.cs index 9bdc3a1cad1517..8635031292e32c 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.MultiSegment.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.MultiSegment.cs @@ -1102,7 +1102,7 @@ private bool ConsumeStringAndValidateMultiSegment(ReadOnlySpan data, int i return true; } - private void RollBackState(in PartialStateForRollback state, bool isError = false) + private void RollBackState(scoped in PartialStateForRollback state, bool isError = false) { _totalConsumed = state._prevTotalConsumed; @@ -1251,7 +1251,7 @@ private bool TryGetNumberMultiSegment(ReadOnlySpan data, out int consumed) return true; } - private ConsumeNumberResult ConsumeNegativeSignMultiSegment(ref ReadOnlySpan data, ref int i, in PartialStateForRollback rollBackState) + private ConsumeNumberResult ConsumeNegativeSignMultiSegment(ref ReadOnlySpan data, scoped ref int i, scoped in PartialStateForRollback rollBackState) { Debug.Assert(i == 0); byte nextByte = data[i]; @@ -1293,7 +1293,7 @@ private ConsumeNumberResult ConsumeNegativeSignMultiSegment(ref ReadOnlySpan data, ref int i, in PartialStateForRollback rollBackState) + private ConsumeNumberResult ConsumeZeroMultiSegment(ref ReadOnlySpan data, scoped ref int i, scoped in PartialStateForRollback rollBackState) { Debug.Assert(data[i] == (byte)'0'); Debug.Assert(i == 0 || i == 1); @@ -1349,7 +1349,7 @@ private ConsumeNumberResult ConsumeZeroMultiSegment(ref ReadOnlySpan data, return ConsumeNumberResult.OperationIncomplete; } - private ConsumeNumberResult ConsumeIntegerDigitsMultiSegment(ref ReadOnlySpan data, ref int i) + private ConsumeNumberResult ConsumeIntegerDigitsMultiSegment(ref ReadOnlySpan data, scoped ref int i) { byte nextByte = default; int counter = 0; @@ -1427,7 +1427,7 @@ private ConsumeNumberResult ConsumeIntegerDigitsMultiSegment(ref ReadOnlySpan data, ref int i, in PartialStateForRollback rollBackState) + private ConsumeNumberResult ConsumeDecimalDigitsMultiSegment(ref ReadOnlySpan data, scoped ref int i, scoped in PartialStateForRollback rollBackState) { if (i >= data.Length) { @@ -1461,7 +1461,7 @@ private ConsumeNumberResult ConsumeDecimalDigitsMultiSegment(ref ReadOnlySpan data, ref int i, in PartialStateForRollback rollBackState) + private ConsumeNumberResult ConsumeSignMultiSegment(ref ReadOnlySpan data, scoped ref int i, scoped in PartialStateForRollback rollBackState) { if (i >= data.Length) { @@ -1926,7 +1926,7 @@ private ConsumeTokenResult ConsumeNextTokenFromLastNonCommentTokenMultiSegment() return ConsumeTokenResult.NotEnoughDataRollBackState; } - private bool SkipAllCommentsMultiSegment(ref byte marker) + private bool SkipAllCommentsMultiSegment(scoped ref byte marker) { while (marker == JsonConstants.Slash) { @@ -1961,7 +1961,7 @@ private bool SkipAllCommentsMultiSegment(ref byte marker) return false; } - private bool SkipAllCommentsMultiSegment(ref byte marker, ExceptionResource resource) + private bool SkipAllCommentsMultiSegment(scoped ref byte marker, ExceptionResource resource) { while (marker == JsonConstants.Slash) { @@ -2394,7 +2394,7 @@ private bool SkipSingleLineCommentMultiSegment(ReadOnlySpan localBuffer, o return true; } - private int FindLineSeparatorMultiSegment(ReadOnlySpan localBuffer, ref int dangerousLineSeparatorBytesConsumed) + private int FindLineSeparatorMultiSegment(ReadOnlySpan localBuffer, scoped ref int dangerousLineSeparatorBytesConsumed) { Debug.Assert(dangerousLineSeparatorBytesConsumed >= 0 && dangerousLineSeparatorBytesConsumed <= 2); @@ -2444,7 +2444,7 @@ private int FindLineSeparatorMultiSegment(ReadOnlySpan localBuffer, ref in } // assumes first byte (JsonConstants.UnexpectedEndOfLineSeparator) is already read - private void ThrowOnDangerousLineSeparatorMultiSegment(ReadOnlySpan localBuffer, ref int dangerousLineSeparatorBytesConsumed) + private void ThrowOnDangerousLineSeparatorMultiSegment(ReadOnlySpan localBuffer, scoped ref int dangerousLineSeparatorBytesConsumed) { Debug.Assert(dangerousLineSeparatorBytesConsumed == 1 || dangerousLineSeparatorBytesConsumed == 2); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.cs index 699ccb6fb5a71e..eb8518376681c6 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.cs @@ -1524,7 +1524,7 @@ private bool TryGetNumber(ReadOnlySpan data, out int consumed) return true; } - private ConsumeNumberResult ConsumeNegativeSign(ref ReadOnlySpan data, ref int i) + private ConsumeNumberResult ConsumeNegativeSign(ref ReadOnlySpan data, scoped ref int i) { byte nextByte = data[i]; @@ -1551,7 +1551,7 @@ private ConsumeNumberResult ConsumeNegativeSign(ref ReadOnlySpan data, ref return ConsumeNumberResult.OperationIncomplete; } - private ConsumeNumberResult ConsumeZero(ref ReadOnlySpan data, ref int i) + private ConsumeNumberResult ConsumeZero(ref ReadOnlySpan data, scoped ref int i) { Debug.Assert(data[i] == (byte)'0'); i++; @@ -1590,7 +1590,7 @@ private ConsumeNumberResult ConsumeZero(ref ReadOnlySpan data, ref int i) return ConsumeNumberResult.OperationIncomplete; } - private ConsumeNumberResult ConsumeIntegerDigits(ref ReadOnlySpan data, ref int i) + private ConsumeNumberResult ConsumeIntegerDigits(ref ReadOnlySpan data, scoped ref int i) { byte nextByte = default; for (; i < data.Length; i++) @@ -1623,7 +1623,7 @@ private ConsumeNumberResult ConsumeIntegerDigits(ref ReadOnlySpan data, re return ConsumeNumberResult.OperationIncomplete; } - private ConsumeNumberResult ConsumeDecimalDigits(ref ReadOnlySpan data, ref int i) + private ConsumeNumberResult ConsumeDecimalDigits(ref ReadOnlySpan data, scoped ref int i) { if (i >= data.Length) { @@ -1645,7 +1645,7 @@ private ConsumeNumberResult ConsumeDecimalDigits(ref ReadOnlySpan data, re return ConsumeIntegerDigits(ref data, ref i); } - private ConsumeNumberResult ConsumeSign(ref ReadOnlySpan data, ref int i) + private ConsumeNumberResult ConsumeSign(ref ReadOnlySpan data, scoped ref int i) { if (i >= data.Length) { @@ -2059,7 +2059,7 @@ private ConsumeTokenResult ConsumeNextTokenFromLastNonCommentToken() return ConsumeTokenResult.NotEnoughDataRollBackState; } - private bool SkipAllComments(ref byte marker) + private bool SkipAllComments(scoped ref byte marker) { while (marker == JsonConstants.Slash) { @@ -2094,7 +2094,7 @@ private bool SkipAllComments(ref byte marker) return false; } - private bool SkipAllComments(ref byte marker, ExceptionResource resource) + private bool SkipAllComments(scoped ref byte marker, ExceptionResource resource) { while (marker == JsonConstants.Slash) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/CastingConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/CastingConverter.cs index 090480c83fb908..d33924a4dcdce0 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/CastingConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/CastingConverter.cs @@ -43,7 +43,7 @@ internal CastingConverter(JsonConverter sourceConverter) : base(initial public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) => _sourceConverter.Write(writer, CastOnWrite(value), options); - internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, out T? value) + internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, scoped ref ReadStack state, out T? value) { bool result = _sourceConverter.OnTryRead(ref reader, typeToConvert, options, ref state, out TSource? sourceValue); value = CastOnRead(sourceValue); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ArrayConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ArrayConverter.cs index 3aa53102d60e12..3c189d499275be 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ArrayConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ArrayConverter.cs @@ -19,7 +19,7 @@ protected override void Add(in TElement value, ref ReadStack state) } internal override bool SupportsCreateObjectDelegate => false; - protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) + protected override void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state, JsonSerializerOptions options) { state.Current.ReturnValue = new List(); } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableOfTConverter.cs index 8dd3924f661897..d518eb1596e1df 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableOfTConverter.cs @@ -12,7 +12,7 @@ internal sealed class IAsyncEnumerableOfTConverter : JsonCollectionConverter where TAsyncEnumerable : IAsyncEnumerable { - internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, out TAsyncEnumerable value) + internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, scoped ref ReadStack state, out TAsyncEnumerable value) { if (!typeToConvert.IsAssignableFrom(typeof(IAsyncEnumerable))) { @@ -28,7 +28,7 @@ protected override void Add(in TElement value, ref ReadStack state) } internal override bool SupportsCreateObjectDelegate => false; - protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) + protected override void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state, JsonSerializerOptions options) { state.Current.ReturnValue = new BufferedAsyncEnumerable(); } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ICollectionOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ICollectionOfTConverter.cs index b975961d21c57d..47bcf0b9d554af 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ICollectionOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ICollectionOfTConverter.cs @@ -24,7 +24,7 @@ protected override void Add(in TElement value, ref ReadStack state) }; } - protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) + protected override void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state, JsonSerializerOptions options) { base.CreateCollection(ref reader, ref state, options); TCollection returnValue = (TCollection)state.Current.ReturnValue!; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryConverter.cs index 3d861866599766..8a7017f5352026 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryConverter.cs @@ -26,7 +26,7 @@ protected override void Add(string key, in object? value, JsonSerializerOptions } } - protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state) + protected override void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state) { base.CreateCollection(ref reader, ref state); TDictionary returnValue = (TDictionary)state.Current.ReturnValue!; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryOfTKeyTValueConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryOfTKeyTValueConverter.cs index 4102404974c1c0..9dd55564ed67f5 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryOfTKeyTValueConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryOfTKeyTValueConverter.cs @@ -26,7 +26,7 @@ protected override void Add(TKey key, in TValue value, JsonSerializerOptions opt }; } - protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state) + protected override void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state) { base.CreateCollection(ref reader, ref state); TDictionary returnValue = (TDictionary)state.Current.ReturnValue!; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverter.cs index 9c98de517695b8..33c6379542491d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverter.cs @@ -23,7 +23,7 @@ protected override void Add(in object? value, ref ReadStack state) } internal override bool SupportsCreateObjectDelegate => false; - protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) + protected override void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state, JsonSerializerOptions options) { if (!_isDeserializable) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableOfTConverter.cs index ed1ba61a2132b8..905e7211ae2145 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableOfTConverter.cs @@ -21,7 +21,7 @@ protected override void Add(in TElement value, ref ReadStack state) } internal override bool SupportsCreateObjectDelegate => false; - protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) + protected override void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state, JsonSerializerOptions options) { if (!_isDeserializable) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListConverter.cs index 7c54b27ad93b5f..208d1c1fcc7e7d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListConverter.cs @@ -23,7 +23,7 @@ protected override void Add(in object? value, ref ReadStack state) } } - protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) + protected override void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state, JsonSerializerOptions options) { base.CreateCollection(ref reader, ref state, options); TCollection returnValue = (TCollection)state.Current.ReturnValue!; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListOfTConverter.cs index 4de171768d7938..36ee248dab4e1a 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListOfTConverter.cs @@ -24,7 +24,7 @@ protected override void Add(in TElement value, ref ReadStack state) }; } - protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) + protected override void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state, JsonSerializerOptions options) { base.CreateCollection(ref reader, ref state, options); TCollection returnValue = (TCollection)state.Current.ReturnValue!; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IReadOnlyDictionaryOfTKeyTValueConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IReadOnlyDictionaryOfTKeyTValueConverter.cs index 9841403ec7acc8..e42ecfe496b493 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IReadOnlyDictionaryOfTKeyTValueConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IReadOnlyDictionaryOfTKeyTValueConverter.cs @@ -19,7 +19,7 @@ protected override void Add(TKey key, in TValue value, JsonSerializerOptions opt } internal override bool SupportsCreateObjectDelegate => false; - protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state) + protected override void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state) { if (!_isDeserializable) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ISetOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ISetOfTConverter.cs index 2c1fd6b756f3ef..390ed6097dda74 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ISetOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ISetOfTConverter.cs @@ -21,7 +21,7 @@ protected override void Add(in TElement value, ref ReadStack state) }; } - protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) + protected override void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state, JsonSerializerOptions options) { base.CreateCollection(ref reader, ref state, options); TCollection returnValue = (TCollection)state.Current.ReturnValue!; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverter.cs index de10ec730bf0f1..db6b62a8de82b0 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverter.cs @@ -20,7 +20,7 @@ protected sealed override void Add(TKey key, in TValue value, JsonSerializerOpti internal sealed override bool CanHaveMetadata => false; internal override bool SupportsCreateObjectDelegate => false; - protected sealed override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state) + protected sealed override void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state) { state.Current.ReturnValue = new Dictionary(); } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverter.cs index 46d7f2cc11b8b3..541f5cbf57a706 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverter.cs @@ -19,7 +19,7 @@ protected sealed override void Add(in TElement value, ref ReadStack state) internal sealed override bool CanHaveMetadata => false; internal override bool SupportsCreateObjectDelegate => false; - protected sealed override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) + protected sealed override void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state, JsonSerializerOptions options) { state.Current.ReturnValue = new List(); } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonCollectionConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonCollectionConverter.cs index 01160029874551..ae28e2dc658105 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonCollectionConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonCollectionConverter.cs @@ -23,7 +23,7 @@ internal abstract class JsonCollectionConverter : JsonRes /// /// When overridden, create the collection. It may be a temporary collection or the final collection. /// - protected virtual void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) + protected virtual void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state, JsonSerializerOptions options) { JsonTypeInfo typeInfo = state.Current.JsonTypeInfo; @@ -63,7 +63,7 @@ internal override bool OnTryRead( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, - ref ReadStack state, + scoped ref ReadStack state, [MaybeNullWhen(false)] out TCollection value) { JsonTypeInfo elementTypeInfo = state.Current.JsonTypeInfo.ElementTypeInfo!; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonDictionaryConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonDictionaryConverter.cs index b255d679b3446c..2f326688984345 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonDictionaryConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonDictionaryConverter.cs @@ -38,7 +38,7 @@ protected virtual void ConvertCollection(ref ReadStack state, JsonSerializerOpti /// /// When overridden, create the collection. It may be a temporary collection or the final collection. /// - protected virtual void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state) + protected virtual void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state) { JsonTypeInfo typeInfo = state.Current.JsonTypeInfo; @@ -78,7 +78,7 @@ internal sealed override bool OnTryRead( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, - ref ReadStack state, + scoped ref ReadStack state, [MaybeNullWhen(false)] out TDictionary value) { JsonTypeInfo keyTypeInfo = state.Current.JsonTypeInfo.KeyTypeInfo!; @@ -301,7 +301,7 @@ internal sealed override bool OnTryRead( value = (TDictionary)state.Current.ReturnValue!; return true; - static TKey ReadDictionaryKey(JsonConverter keyConverter, ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) + static TKey ReadDictionaryKey(JsonConverter keyConverter, ref Utf8JsonReader reader, scoped ref ReadStack state, JsonSerializerOptions options) { TKey key; string unescapedPropertyNameAsString = reader.GetString()!; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ListOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ListOfTConverter.cs index 35e398fe5869fb..0ab52bfc4939b0 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ListOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ListOfTConverter.cs @@ -15,7 +15,7 @@ protected override void Add(in TElement value, ref ReadStack state) ((TCollection)state.Current.ReturnValue!).Add(value); } - protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) + protected override void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state, JsonSerializerOptions options) { if (state.Current.JsonTypeInfo.CreateObject == null) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/QueueOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/QueueOfTConverter.cs index 6e28f4ef2458bd..5c02525afa980a 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/QueueOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/QueueOfTConverter.cs @@ -14,7 +14,7 @@ protected override void Add(in TElement value, ref ReadStack state) ((TCollection)state.Current.ReturnValue!).Enqueue(value); } - protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) + protected override void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state, JsonSerializerOptions options) { if (state.Current.JsonTypeInfo.CreateObject == null) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOfTConverter.cs index 565754d6a24b86..87e7369cee5b40 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOfTConverter.cs @@ -15,7 +15,7 @@ protected override void Add(in TElement value, ref ReadStack state) ((TCollection)state.Current.ReturnValue!).Push(value); } - protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) + protected override void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state, JsonSerializerOptions options) { if (state.Current.JsonTypeInfo.CreateObject == null) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverter.cs index 59f3f3c336ec9a..f291a263f3b9e9 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverter.cs @@ -18,7 +18,7 @@ protected sealed override void Add(in object? value, ref ReadStack state) addMethodDelegate((TCollection)state.Current.ReturnValue!, value); } - protected sealed override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) + protected sealed override void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state, JsonSerializerOptions options) { JsonTypeInfo typeInfo = state.Current.JsonTypeInfo; Func? constructorDelegate = typeInfo.CreateObject; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpListConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpListConverter.cs index fa228e000f360e..0cd7ccb1a30202 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpListConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpListConverter.cs @@ -26,7 +26,7 @@ protected override void Add(in TElement value, ref ReadStack state) } internal override bool SupportsCreateObjectDelegate => false; - protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) + protected override void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state, JsonSerializerOptions options) { state.Current.ReturnValue = new List(); } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpMapConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpMapConverter.cs index c7cebda077438c..112da6da2c8d6a 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpMapConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpMapConverter.cs @@ -29,7 +29,7 @@ protected override void Add(TKey key, in TValue value, JsonSerializerOptions opt internal override bool CanHaveMetadata => false; internal override bool SupportsCreateObjectDelegate => false; - protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state) + protected override void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state) { state.Current.ReturnValue = new List>(); } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpOptionConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpOptionConverter.cs index c0ba55e63d2a49..8130f829471c7a 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpOptionConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpOptionConverter.cs @@ -37,7 +37,7 @@ public FSharpOptionConverter(JsonConverter elementConverter) RequiresReadAhead = elementConverter.RequiresReadAhead; } - internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, out TOption? value) + internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, scoped ref ReadStack state, out TOption? value) { // `null` values deserialize as `None` if (!state.IsContinuation && reader.TokenType == JsonTokenType.Null) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpSetConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpSetConverter.cs index 73230b273c99db..167534d8de3f73 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpSetConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpSetConverter.cs @@ -26,7 +26,7 @@ protected override void Add(in TElement value, ref ReadStack state) } internal override bool SupportsCreateObjectDelegate => false; - protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) + protected override void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state, JsonSerializerOptions options) { state.Current.ReturnValue = new List(); } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpValueOptionConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpValueOptionConverter.cs index 1c147c4b640696..7d960e17b04585 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpValueOptionConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpValueOptionConverter.cs @@ -37,7 +37,7 @@ public FSharpValueOptionConverter(JsonConverter elementConverter) RequiresReadAhead = elementConverter.RequiresReadAhead; } - internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, out TValueOption value) + internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, scoped ref ReadStack state, out TValueOption value) { // `null` values deserialize as `ValueNone` if (!state.IsContinuation && reader.TokenType == JsonTokenType.Null) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonMetadataServicesConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonMetadataServicesConverter.cs index 45f8a66ce57c57..e029760aa91527 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonMetadataServicesConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonMetadataServicesConverter.cs @@ -60,7 +60,7 @@ public JsonMetadataServicesConverter(JsonConverter converter) _converterStrategy = converter.ConverterStrategy; } - internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, out T? value) + internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, scoped ref ReadStack state, out T? value) => Converter.OnTryRead(ref reader, typeToConvert, options, ref state, out value); internal override bool OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, ref WriteStack state) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonObjectConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonObjectConverter.cs index b3279a01936dda..b8e2d7eea528bf 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonObjectConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonObjectConverter.cs @@ -19,7 +19,7 @@ internal override void ReadElementAndSetProperty( string propertyName, ref Utf8JsonReader reader, JsonSerializerOptions options, - ref ReadStack state) + scoped ref ReadStack state) { bool success = JsonNodeConverter.Instance.TryRead(ref reader, typeof(JsonNode), options, ref state, out JsonNode? value); Debug.Assert(success); // Node converters are not resumable. diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectConverter.cs index 6c0a095061fac9..62835f45b94061 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectConverter.cs @@ -35,7 +35,7 @@ public override void Write(Utf8JsonWriter writer, object? value, JsonSerializerO writer.WriteEndObject(); } - internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, out object? value) + internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, scoped ref ReadStack state, out object? value) { object? referenceValue; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs index f12f1991b56258..4a2090f659a99e 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs @@ -17,7 +17,7 @@ internal class ObjectDefaultConverter : JsonObjectConverter where T : notn internal override bool CanHaveMetadata => true; internal override bool SupportsCreateObjectDelegate => true; - internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, [MaybeNullWhen(false)] out T value) + internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, scoped ref ReadStack state, [MaybeNullWhen(false)] out T value) { JsonTypeInfo jsonTypeInfo = state.Current.JsonTypeInfo; @@ -413,7 +413,7 @@ internal sealed override bool OnTryWrite( [MethodImpl(MethodImplOptions.AggressiveInlining)] protected static void ReadPropertyValue( object obj, - ref ReadStack state, + scoped ref ReadStack state, ref Utf8JsonReader reader, JsonPropertyInfo jsonPropertyInfo, bool useExtensionProperty) @@ -442,7 +442,7 @@ protected static void ReadPropertyValue( state.Current.EndProperty(); } - protected static bool ReadAheadPropertyValue(ref ReadStack state, ref Utf8JsonReader reader, JsonPropertyInfo jsonPropertyInfo) + protected static bool ReadAheadPropertyValue(scoped ref ReadStack state, ref Utf8JsonReader reader, JsonPropertyInfo jsonPropertyInfo) { // Returning false below will cause the read-ahead functionality to finish the read. state.Current.PropertyState = StackFramePropertyState.ReadValue; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Large.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Large.cs index eabe8e6a62fec6..938d2a9605699f 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Large.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Large.cs @@ -14,7 +14,7 @@ namespace System.Text.Json.Serialization.Converters /// internal class LargeObjectWithParameterizedConstructorConverter : ObjectWithParameterizedConstructorConverter where T : notnull { - protected sealed override bool ReadAndCacheConstructorArgument(ref ReadStack state, ref Utf8JsonReader reader, JsonParameterInfo jsonParameterInfo) + protected sealed override bool ReadAndCacheConstructorArgument(scoped ref ReadStack state, ref Utf8JsonReader reader, JsonParameterInfo jsonParameterInfo) { Debug.Assert(jsonParameterInfo.ShouldDeserialize); Debug.Assert(jsonParameterInfo.Options != null); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Small.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Small.cs index 1482a32144ec4f..3c7fd1a7c27f64 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Small.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Small.cs @@ -22,7 +22,7 @@ protected override object CreateObject(ref ReadStackFrame frame) } protected override bool ReadAndCacheConstructorArgument( - ref ReadStack state, + scoped ref ReadStack state, ref Utf8JsonReader reader, JsonParameterInfo jsonParameterInfo) { @@ -54,7 +54,7 @@ protected override bool ReadAndCacheConstructorArgument( } private static bool TryRead( - ref ReadStack state, + scoped ref ReadStack state, ref Utf8JsonReader reader, JsonParameterInfo jsonParameterInfo, out TArg arg) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.cs index aeba2b9fc95d2f..74137c578c7e8f 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.cs @@ -21,7 +21,7 @@ internal abstract partial class ObjectWithParameterizedConstructorConverter : { internal sealed override bool ConstructorIsParameterized => true; - internal sealed override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, [MaybeNullWhen(false)] out T value) + internal sealed override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, scoped ref ReadStack state, [MaybeNullWhen(false)] out T value) { JsonTypeInfo jsonTypeInfo = state.Current.JsonTypeInfo; @@ -248,7 +248,7 @@ internal sealed override bool OnTryRead(ref Utf8JsonReader reader, Type typeToCo protected abstract void InitializeConstructorArgumentCaches(ref ReadStack state, JsonSerializerOptions options); - protected abstract bool ReadAndCacheConstructorArgument(ref ReadStack state, ref Utf8JsonReader reader, JsonParameterInfo jsonParameterInfo); + protected abstract bool ReadAndCacheConstructorArgument(scoped ref ReadStack state, ref Utf8JsonReader reader, JsonParameterInfo jsonParameterInfo); protected abstract object CreateObject(ref ReadStackFrame frame); @@ -256,7 +256,7 @@ internal sealed override bool OnTryRead(ref Utf8JsonReader reader, Type typeToCo /// Performs a full first pass of the JSON input and deserializes the ctor args. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReadConstructorArguments(ref ReadStack state, ref Utf8JsonReader reader, JsonSerializerOptions options) + private void ReadConstructorArguments(scoped ref ReadStack state, ref Utf8JsonReader reader, JsonSerializerOptions options) { BeginRead(ref state, ref reader, options); @@ -342,7 +342,7 @@ private void ReadConstructorArguments(ref ReadStack state, ref Utf8JsonReader re } } - private bool ReadConstructorArgumentsWithContinuation(ref ReadStack state, ref Utf8JsonReader reader, JsonSerializerOptions options) + private bool ReadConstructorArgumentsWithContinuation(scoped ref ReadStack state, ref Utf8JsonReader reader, JsonSerializerOptions options) { // Process all properties. while (true) @@ -425,7 +425,7 @@ private bool ReadConstructorArgumentsWithContinuation(ref ReadStack state, ref U [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool HandleConstructorArgumentWithContinuation( - ref ReadStack state, + scoped ref ReadStack state, ref Utf8JsonReader reader, JsonParameterInfo jsonParameterInfo) { @@ -462,7 +462,7 @@ private bool HandleConstructorArgumentWithContinuation( [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool HandlePropertyWithContinuation( - ref ReadStack state, + scoped ref ReadStack state, ref Utf8JsonReader reader, JsonPropertyInfo jsonPropertyInfo) { @@ -537,7 +537,7 @@ private static bool HandlePropertyWithContinuation( } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void BeginRead(ref ReadStack state, ref Utf8JsonReader reader, JsonSerializerOptions options) + private void BeginRead(scoped ref ReadStack state, ref Utf8JsonReader reader, JsonSerializerOptions options) { JsonTypeInfo jsonTypeInfo = state.Current.JsonTypeInfo; @@ -562,7 +562,7 @@ private void BeginRead(ref ReadStack state, ref Utf8JsonReader reader, JsonSeria /// Lookup the constructor parameter given its name in the reader. /// protected virtual bool TryLookupConstructorParameter( - ref ReadStack state, + scoped ref ReadStack state, ref Utf8JsonReader reader, JsonSerializerOptions options, out JsonParameterInfo? jsonParameterInfo) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/NullableConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/NullableConverter.cs index 292d70e42b646f..1e31cba7043eab 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/NullableConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/NullableConverter.cs @@ -25,7 +25,7 @@ public NullableConverter(JsonConverter elementConverter) RequiresReadAhead = elementConverter.RequiresReadAhead; } - internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, out T? value) + internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, scoped ref ReadStack state, out T? value) { if (!state.IsContinuation && reader.TokenType == JsonTokenType.Null) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.ReadAhead.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.ReadAhead.cs index 50987aa96cb653..b15deac8c486ad 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.ReadAhead.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.ReadAhead.cs @@ -17,7 +17,7 @@ public abstract partial class JsonConverter // AggressiveInlining used since this method is on a hot path and short. The optionally called // method DoSingleValueReadWithReadAhead is not inlined. [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool SingleValueReadWithReadAhead(bool requiresReadAhead, ref Utf8JsonReader reader, ref ReadStack state) + internal static bool SingleValueReadWithReadAhead(bool requiresReadAhead, ref Utf8JsonReader reader, scoped ref ReadStack state) { bool readAhead = requiresReadAhead && state.ReadAhead; if (!readAhead) @@ -28,7 +28,7 @@ internal static bool SingleValueReadWithReadAhead(bool requiresReadAhead, ref Ut return DoSingleValueReadWithReadAhead(ref reader, ref state); } - internal static bool DoSingleValueReadWithReadAhead(ref Utf8JsonReader reader, ref ReadStack state) + internal static bool DoSingleValueReadWithReadAhead(ref Utf8JsonReader reader, scoped ref ReadStack state) { // When we're reading ahead we always have to save the state as we don't know if the next token // is an opening object or an array brace. diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs index bfdb8479a4e3f8..ee97c178405ae3 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs @@ -62,7 +62,7 @@ internal virtual void ReadElementAndSetProperty( string propertyName, ref Utf8JsonReader reader, JsonSerializerOptions options, - ref ReadStack state) + scoped ref ReadStack state) { Debug.Fail("Should not be reachable."); @@ -111,7 +111,7 @@ internal virtual JsonTypeInfo CreateCustomJsonTypeInfo(JsonSerializerOptions opt /// /// Loosely-typed ReadCore() that forwards to strongly-typed ReadCore(). /// - internal abstract object? ReadCoreAsObject(ref Utf8JsonReader reader, JsonSerializerOptions options, ref ReadStack state); + internal abstract object? ReadCoreAsObject(ref Utf8JsonReader reader, JsonSerializerOptions options, scoped ref ReadStack state); internal static bool ShouldFlush(Utf8JsonWriter writer, ref WriteStack state) @@ -123,8 +123,8 @@ internal static bool ShouldFlush(Utf8JsonWriter writer, ref WriteStack state) // This is used internally to quickly determine the type being converted for JsonConverter. internal abstract Type TypeToConvert { get; } - internal abstract bool OnTryReadAsObject(ref Utf8JsonReader reader, JsonSerializerOptions options, ref ReadStack state, out object? value); - internal abstract bool TryReadAsObject(ref Utf8JsonReader reader, JsonSerializerOptions options, ref ReadStack state, out object? value); + internal abstract bool OnTryReadAsObject(ref Utf8JsonReader reader, JsonSerializerOptions options, scoped ref ReadStack state, out object? value); + internal abstract bool TryReadAsObject(ref Utf8JsonReader reader, JsonSerializerOptions options, scoped ref ReadStack state, out object? value); internal abstract bool TryWriteAsObject(Utf8JsonWriter writer, object? value, JsonSerializerOptions options, ref WriteStack state); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterFactory.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterFactory.cs index fbc407b4a347f8..ecd84de71c0664 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterFactory.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterFactory.cs @@ -64,7 +64,7 @@ internal JsonConverter GetConverterInternal(Type typeToConvert, JsonSerializerOp internal sealed override object ReadCoreAsObject( ref Utf8JsonReader reader, JsonSerializerOptions options, - ref ReadStack state) + scoped ref ReadStack state) { Debug.Fail("We should never get here."); @@ -74,7 +74,7 @@ internal sealed override object ReadCoreAsObject( internal sealed override bool OnTryReadAsObject( ref Utf8JsonReader reader, JsonSerializerOptions options, - ref ReadStack state, + scoped ref ReadStack state, out object? value) { Debug.Fail("We should never get here."); @@ -85,7 +85,7 @@ internal sealed override bool OnTryReadAsObject( internal sealed override bool TryReadAsObject( ref Utf8JsonReader reader, JsonSerializerOptions options, - ref ReadStack state, + scoped ref ReadStack state, out object? value) { Debug.Fail("We should never get here."); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.ReadCore.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.ReadCore.cs index 129416323bc4c8..df4963e3b6ef23 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.ReadCore.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.ReadCore.cs @@ -10,7 +10,7 @@ public partial class JsonConverter internal sealed override object? ReadCoreAsObject( ref Utf8JsonReader reader, JsonSerializerOptions options, - ref ReadStack state) + scoped ref ReadStack state) { return ReadCore(ref reader, options, ref state); } @@ -18,7 +18,7 @@ public partial class JsonConverter internal T? ReadCore( ref Utf8JsonReader reader, JsonSerializerOptions options, - ref ReadStack state) + scoped ref ReadStack state) { try { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs index 0a9f00a67db7dc..c69ba39daf4dd8 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs @@ -160,7 +160,7 @@ internal virtual bool OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializerO } // Provide a default implementation for value converters. - internal virtual bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, out T? value) + internal virtual bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, scoped ref ReadStack state, out T? value) { value = Read(ref reader, typeToConvert, options); return true; @@ -179,7 +179,7 @@ internal virtual bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, J /// Note that the value of determines if the converter handles null JSON tokens. public abstract T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options); - internal bool TryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, out T? value) + internal bool TryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, scoped ref ReadStack state, out T? value) { // For perf and converter simplicity, handle null here instead of forwarding to the converter. if (reader.TokenType == JsonTokenType.Null && !HandleNullOnRead && !state.IsContinuation) @@ -301,14 +301,14 @@ internal bool TryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSeriali return success; } - internal sealed override bool OnTryReadAsObject(ref Utf8JsonReader reader, JsonSerializerOptions options, ref ReadStack state, out object? value) + internal sealed override bool OnTryReadAsObject(ref Utf8JsonReader reader, JsonSerializerOptions options, scoped ref ReadStack state, out object? value) { bool success = OnTryRead(ref reader, TypeToConvert, options, ref state, out T? typedValue); value = typedValue; return success; } - internal sealed override bool TryReadAsObject(ref Utf8JsonReader reader, JsonSerializerOptions options, ref ReadStack state, out object? value) + internal sealed override bool TryReadAsObject(ref Utf8JsonReader reader, JsonSerializerOptions options, scoped ref ReadStack state, out object? value) { bool success = TryRead(ref reader, TypeToConvert, options, ref state, out T? typedValue); value = typedValue; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleMetadata.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleMetadata.cs index 029c2b43fc38cf..73cf9844ce3928 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleMetadata.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleMetadata.cs @@ -21,7 +21,7 @@ public static partial class JsonSerializer internal static readonly byte[] s_typePropertyName = Encoding.UTF8.GetBytes(TypePropertyName); internal static readonly byte[] s_valuesPropertyName = Encoding.UTF8.GetBytes(ValuesPropertyName); - internal static bool TryReadMetadata(JsonConverter converter, JsonTypeInfo jsonTypeInfo, ref Utf8JsonReader reader, ref ReadStack state) + internal static bool TryReadMetadata(JsonConverter converter, JsonTypeInfo jsonTypeInfo, ref Utf8JsonReader reader, scoped ref ReadStack state) { Debug.Assert(state.Current.ObjectState == StackFrameObjectState.StartToken); Debug.Assert(state.Current.CanContainMetadata); @@ -285,7 +285,7 @@ internal static MetadataPropertyName GetMetadataPropertyName(ReadOnlySpan internal static bool TryHandleReferenceFromJsonElement( ref Utf8JsonReader reader, - ref ReadStack state, + scoped ref ReadStack state, JsonElement element, [NotNullWhen(true)] out object? referenceValue) { @@ -349,7 +349,7 @@ internal static bool TryHandleReferenceFromJsonElement( internal static bool TryHandleReferenceFromJsonNode( ref Utf8JsonReader reader, - ref ReadStack state, + scoped ref ReadStack state, JsonNode jsonNode, [NotNullWhen(true)] out object? referenceValue) { @@ -425,7 +425,7 @@ static string ReadAsStringMetadataValue(JsonNode? jsonNode) return refMetadataFound; } - internal static void ValidateMetadataForObjectConverter(JsonConverter converter, ref Utf8JsonReader reader, ref ReadStack state) + internal static void ValidateMetadataForObjectConverter(JsonConverter converter, ref Utf8JsonReader reader, scoped ref ReadStack state) { if (state.Current.MetadataPropertyNames.HasFlag(MetadataPropertyName.Values)) { @@ -434,7 +434,7 @@ internal static void ValidateMetadataForObjectConverter(JsonConverter converter, } } - internal static void ValidateMetadataForArrayConverter(JsonConverter converter, ref Utf8JsonReader reader, ref ReadStack state) + internal static void ValidateMetadataForArrayConverter(JsonConverter converter, ref Utf8JsonReader reader, scoped ref ReadStack state) { switch (reader.TokenType) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs index f374fc70daf79c..e88a52dd52ba7e 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs @@ -71,7 +71,7 @@ internal static JsonPropertyInfo LookupProperty( [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static ReadOnlySpan GetPropertyName( - ref ReadStack state, + scoped ref ReadStack state, ref Utf8JsonReader reader, JsonSerializerOptions options) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Helpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Helpers.cs index f3cb5b762fa59a..318e25da2bc7a5 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Helpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Helpers.cs @@ -9,7 +9,7 @@ namespace System.Text.Json { public static partial class JsonSerializer { - private static TValue? ReadCore(ref Utf8JsonReader reader, JsonTypeInfo jsonTypeInfo, ref ReadStack state) + private static TValue? ReadCore(ref Utf8JsonReader reader, JsonTypeInfo jsonTypeInfo, scoped ref ReadStack state) { if (jsonTypeInfo is JsonTypeInfo typedInfo) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Utf8JsonReader.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Utf8JsonReader.cs index 7eaadb22a99d23..fe0a696109f31c 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Utf8JsonReader.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Utf8JsonReader.cs @@ -262,7 +262,7 @@ public static partial class JsonSerializer } } - private static Utf8JsonReader GetReaderScopedToNextValue(ref Utf8JsonReader reader, ref ReadStack state) + private static Utf8JsonReader GetReaderScopedToNextValue(ref Utf8JsonReader reader, scoped ref ReadStack state) { // Advances the provided reader, validating that it is pointing to a complete JSON value. // If successful, returns a new Utf8JsonReader that is scoped to the next value, reusing existing buffers. diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfo.cs index 137a3b238bacee..dee80c211cbc92 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfo.cs @@ -700,7 +700,7 @@ public int Order internal bool ReadJsonAndAddExtensionProperty( object obj, - ref ReadStack state, + scoped ref ReadStack state, ref Utf8JsonReader reader) { object propValue = GetValueAsObject(obj)!; @@ -748,11 +748,11 @@ JsonConverter GetDictionaryValueConverter() } } - internal abstract bool ReadJsonAndSetMember(object obj, ref ReadStack state, ref Utf8JsonReader reader); + internal abstract bool ReadJsonAndSetMember(object obj, scoped ref ReadStack state, ref Utf8JsonReader reader); - internal abstract bool ReadJsonAsObject(ref ReadStack state, ref Utf8JsonReader reader, out object? value); + internal abstract bool ReadJsonAsObject(scoped ref ReadStack state, ref Utf8JsonReader reader, out object? value); - internal bool ReadJsonExtensionDataValue(ref ReadStack state, ref Utf8JsonReader reader, out object? value) + internal bool ReadJsonExtensionDataValue(scoped ref ReadStack state, ref Utf8JsonReader reader, out object? value) { Debug.Assert(this == state.Current.JsonTypeInfo.ExtensionDataProperty); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs index f75d1da274f300..8c86d9462246c4 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs @@ -334,7 +334,7 @@ internal override bool GetMemberAndWriteJsonExtensionData(object obj, ref WriteS return success; } - internal override bool ReadJsonAndSetMember(object obj, ref ReadStack state, ref Utf8JsonReader reader) + internal override bool ReadJsonAndSetMember(object obj, scoped ref ReadStack state, ref Utf8JsonReader reader) { bool success; @@ -387,7 +387,7 @@ internal override bool ReadJsonAndSetMember(object obj, ref ReadStack state, ref return success; } - internal override bool ReadJsonAsObject(ref ReadStack state, ref Utf8JsonReader reader, out object? value) + internal override bool ReadJsonAsObject(scoped ref ReadStack state, ref Utf8JsonReader reader, out object? value) { bool success; bool isNullToken = reader.TokenType == JsonTokenType.Null; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs index c0d750799106e4..b8ce2bacfd3f8c 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs @@ -334,7 +334,7 @@ public static void ThrowInvalidOperationException_ConverterCanConvertMultipleTyp public static void ThrowNotSupportedException_ObjectWithParameterizedCtorRefMetadataNotSupported( ReadOnlySpan propertyName, ref Utf8JsonReader reader, - ref ReadStack state) + scoped ref ReadStack state) { JsonTypeInfo jsonTypeInfo = state.GetTopJsonTypeInfoWithParameterizedConstructor(); state.Current.JsonPropertyName = propertyName.ToArray(); @@ -357,7 +357,7 @@ public static void ThrowInvalidOperationException_CreateObjectConverterNotCompat } [DoesNotReturn] - public static void ReThrowWithPath(ref ReadStack state, JsonReaderException ex) + public static void ReThrowWithPath(scoped ref ReadStack state, JsonReaderException ex) { Debug.Assert(ex.Path == null); @@ -383,14 +383,14 @@ public static void ReThrowWithPath(ref ReadStack state, JsonReaderException ex) } [DoesNotReturn] - public static void ReThrowWithPath(ref ReadStack state, in Utf8JsonReader reader, Exception ex) + public static void ReThrowWithPath(scoped ref ReadStack state, in Utf8JsonReader reader, Exception ex) { JsonException jsonException = new JsonException(null, ex); AddJsonExceptionInformation(ref state, reader, jsonException); throw jsonException; } - public static void AddJsonExceptionInformation(ref ReadStack state, in Utf8JsonReader reader, JsonException ex) + public static void AddJsonExceptionInformation(scoped ref ReadStack state, in Utf8JsonReader reader, JsonException ex) { Debug.Assert(ex.Path is null); // do not overwrite existing path information @@ -482,7 +482,7 @@ public static void ThrowInvalidOperationException_NodeJsonObjectCustomConverterN } [DoesNotReturn] - public static void ThrowNotSupportedException(ref ReadStack state, in Utf8JsonReader reader, NotSupportedException ex) + public static void ThrowNotSupportedException(scoped ref ReadStack state, in Utf8JsonReader reader, NotSupportedException ex) { string message = ex.Message; @@ -536,7 +536,7 @@ public static void ThrowNotSupportedException(ref WriteStack state, NotSupported } [DoesNotReturn] - public static void ThrowNotSupportedException_DeserializeNoConstructor(Type type, ref Utf8JsonReader reader, ref ReadStack state) + public static void ThrowNotSupportedException_DeserializeNoConstructor(Type type, ref Utf8JsonReader reader, scoped ref ReadStack state) { string message; @@ -553,7 +553,7 @@ public static void ThrowNotSupportedException_DeserializeNoConstructor(Type type } [DoesNotReturn] - public static void ThrowNotSupportedException_CannotPopulateCollection(Type type, ref Utf8JsonReader reader, ref ReadStack state) + public static void ThrowNotSupportedException_CannotPopulateCollection(Type type, ref Utf8JsonReader reader, scoped ref ReadStack state) { ThrowNotSupportedException(ref state, reader, new NotSupportedException(SR.Format(SR.CannotPopulateCollection, type))); } @@ -583,14 +583,14 @@ public static void ThrowJsonException_MetadataValueWasNotString(JsonValueKind va } [DoesNotReturn] - public static void ThrowJsonException_MetadataReferenceObjectCannotContainOtherProperties(ReadOnlySpan propertyName, ref ReadStack state) + public static void ThrowJsonException_MetadataReferenceObjectCannotContainOtherProperties(ReadOnlySpan propertyName, scoped ref ReadStack state) { state.Current.JsonPropertyName = propertyName.ToArray(); ThrowJsonException_MetadataReferenceObjectCannotContainOtherProperties(); } [DoesNotReturn] - public static void ThrowJsonException_MetadataUnexpectedProperty(ReadOnlySpan propertyName, ref ReadStack state) + public static void ThrowJsonException_MetadataUnexpectedProperty(ReadOnlySpan propertyName, scoped ref ReadStack state) { state.Current.JsonPropertyName = propertyName.ToArray(); ThrowJsonException(SR.Format(SR.MetadataUnexpectedProperty)); @@ -603,21 +603,21 @@ public static void ThrowJsonException_MetadataReferenceObjectCannotContainOtherP } [DoesNotReturn] - public static void ThrowJsonException_MetadataIdIsNotFirstProperty(ReadOnlySpan propertyName, ref ReadStack state) + public static void ThrowJsonException_MetadataIdIsNotFirstProperty(ReadOnlySpan propertyName, scoped ref ReadStack state) { state.Current.JsonPropertyName = propertyName.ToArray(); ThrowJsonException(SR.MetadataIdIsNotFirstProperty); } [DoesNotReturn] - public static void ThrowJsonException_MetadataStandaloneValuesProperty(ref ReadStack state, ReadOnlySpan propertyName) + public static void ThrowJsonException_MetadataStandaloneValuesProperty(scoped ref ReadStack state, ReadOnlySpan propertyName) { state.Current.JsonPropertyName = propertyName.ToArray(); ThrowJsonException(SR.MetadataStandaloneValuesProperty); } [DoesNotReturn] - public static void ThrowJsonException_MetadataInvalidPropertyWithLeadingDollarSign(ReadOnlySpan propertyName, ref ReadStack state, in Utf8JsonReader reader) + public static void ThrowJsonException_MetadataInvalidPropertyWithLeadingDollarSign(ReadOnlySpan propertyName, scoped ref ReadStack state, in Utf8JsonReader reader) { // Set PropertyInfo or KeyName to write down the conflicting property name in JsonException.Path if (state.Current.IsProcessingDictionary()) @@ -651,7 +651,7 @@ public static void ThrowJsonException_MetadataInvalidReferenceToValueType(Type p } [DoesNotReturn] - public static void ThrowJsonException_MetadataInvalidPropertyInArrayMetadata(ref ReadStack state, Type propertyType, in Utf8JsonReader reader) + public static void ThrowJsonException_MetadataInvalidPropertyInArrayMetadata(scoped ref ReadStack state, Type propertyType, in Utf8JsonReader reader) { state.Current.JsonPropertyName = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan.ToArray(); string propertyNameAsString = reader.GetString()!; @@ -662,7 +662,7 @@ public static void ThrowJsonException_MetadataInvalidPropertyInArrayMetadata(ref } [DoesNotReturn] - public static void ThrowJsonException_MetadataPreservedArrayValuesNotFound(ref ReadStack state, Type propertyType) + public static void ThrowJsonException_MetadataPreservedArrayValuesNotFound(scoped ref ReadStack state, Type propertyType) { // Missing $values, JSON path should point to the property's object. state.Current.JsonPropertyName = null; @@ -695,7 +695,7 @@ public static void ThrowInvalidOperationException_JsonPropertyInfoIsBoundToDiffe internal static void ThrowUnexpectedMetadataException( ReadOnlySpan propertyName, ref Utf8JsonReader reader, - ref ReadStack state) + scoped ref ReadStack state) { MetadataPropertyName name = JsonSerializer.GetMetadataPropertyName(propertyName, state.Current.BaseJsonTypeInfo.PolymorphicTypeResolver); From bff7c3d522cfd6845d8452544ef2d4b81d1e5fb4 Mon Sep 17 00:00:00 2001 From: Steve Molloy Date: Fri, 12 Aug 2022 19:59:44 -0700 Subject: [PATCH 51/56] Revert "Add annotations to System.Private.DataContractSerialization (#73337)" (#73878) This reverts commit c4ea2caaa63322da54e289f490ba3bb4820e9303. --- ...m.Private.DataContractSerialization.csproj | 3 +- .../Runtime/Serialization/AccessorBuilder.cs | 5 -- .../Serialization/ClassDataContract.cs | 27 -------- .../Runtime/Serialization/CodeGenerator.cs | 2 - .../Serialization/CollectionDataContract.cs | 44 +----------- .../Runtime/Serialization/DataContract.cs | 51 -------------- .../Serialization/DataContractResolver.cs | 2 - .../Serialization/DataContractSerializer.cs | 28 -------- .../Runtime/Serialization/DataContractSet.cs | 12 ---- .../DataContractSurrogateCaller.cs | 3 - .../Runtime/Serialization/DataMember.cs | 4 -- .../Runtime/Serialization/EnumDataContract.cs | 4 -- .../GenericParameterDataContract.cs | 2 - .../System/Runtime/Serialization/Globals.cs | 2 - .../Json/DataContractJsonSerializer.cs | 38 ----------- .../Json/JsonByteArrayDataContract.cs | 2 - .../Json/JsonClassDataContract.cs | 6 -- .../Json/JsonCollectionDataContract.cs | 7 -- .../Serialization/Json/JsonDataContract.cs | 10 --- .../Json/JsonEnumDataContract.cs | 4 -- .../Json/JsonFormatReaderGenerator.cs | 19 ------ .../Json/JsonFormatWriterGenerator.cs | 13 ---- .../Json/JsonObjectDataContract.cs | 3 - .../Json/JsonQNameDataContract.cs | 2 - .../Json/JsonStringDataContract.cs | 2 - .../Serialization/Json/JsonUriDataContract.cs | 2 - .../Serialization/Json/JsonXmlDataContract.cs | 3 - .../Json/ReflectionJsonFormatReader.cs | 7 -- .../Json/ReflectionJsonFormatWriter.cs | 4 -- ...lObjectSerializerReadContextComplexJson.cs | 6 -- ...ObjectSerializerWriteContextComplexJson.cs | 14 ---- .../KnownTypeDataContractResolver.cs | 2 - .../Serialization/PrimitiveDataContract.cs | 68 ------------------- .../Serialization/ReflectionClassWriter.cs | 5 -- .../Runtime/Serialization/ReflectionReader.cs | 19 ------ .../ReflectionXmlFormatReader.cs | 7 +- .../ReflectionXmlFormatWriter.cs | 6 +- .../Runtime/Serialization/SchemaExporter.cs | 9 --- .../Serialization/SurrogateDataContract.cs | 4 -- .../Serialization/XPathQueryGenerator.cs | 4 -- .../Runtime/Serialization/XmlDataContract.cs | 8 --- .../XmlFormatGeneratorStatics.cs | 2 - .../Serialization/XmlFormatReaderGenerator.cs | 20 ------ .../Serialization/XmlFormatWriterGenerator.cs | 14 ---- .../Serialization/XmlObjectSerializer.cs | 34 ---------- .../XmlObjectSerializerContext.cs | 17 ----- .../XmlObjectSerializerReadContext.cs | 22 ------ .../XmlObjectSerializerReadContextComplex.cs | 9 --- .../XmlObjectSerializerWriteContext.cs | 27 -------- .../XmlObjectSerializerWriteContextComplex.cs | 8 --- .../Serialization/XsdDataContractExporter.cs | 13 ---- .../ref/System.Runtime.Serialization.Json.cs | 23 ------- .../ref/System.Runtime.Serialization.Xml.cs | 43 ------------ 53 files changed, 7 insertions(+), 688 deletions(-) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System.Private.DataContractSerialization.csproj b/src/libraries/System.Private.DataContractSerialization/src/System.Private.DataContractSerialization.csproj index b5fd2b74823358..f8df7d3599de5e 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System.Private.DataContractSerialization.csproj +++ b/src/libraries/System.Private.DataContractSerialization/src/System.Private.DataContractSerialization.csproj @@ -1,11 +1,10 @@ - + $(NetCoreAppCurrent) $(NoWarn);1634;1691;649 true false - true diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/AccessorBuilder.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/AccessorBuilder.cs index df00abecc6eed4..e6af588406a6df 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/AccessorBuilder.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/AccessorBuilder.cs @@ -27,7 +27,6 @@ internal static class FastInvokerBuilder [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2060:MakeGenericMethod", Justification = "The call to MakeGenericMethod is safe due to the fact that we are preserving the constructors of type which is what Make() is doing.")] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static Func GetMakeNewInstanceFunc( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type) @@ -67,9 +66,7 @@ public static Getter CreateGetter(MemberInfo memberInfo) // Only JIT if dynamic code is supported. if (RuntimeFeature.IsDynamicCodeSupported || (!declaringType.IsValueType && !propertyType.IsValueType)) { -#pragma warning disable IL3050 // AOT compiling should recognize that this call is gated by RuntimeFeature.IsDynamicCodeSupported var createGetterGeneric = s_createGetterInternal.MakeGenericMethod(declaringType, propertyType).CreateDelegate>(); -#pragma warning restore IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling. return createGetterGeneric(propInfo); } else @@ -124,9 +121,7 @@ public static Setter CreateSetter(MemberInfo memberInfo) // Only JIT if dynamic code is supported. if (RuntimeFeature.IsDynamicCodeSupported || (!declaringType.IsValueType && !propertyType.IsValueType)) { -#pragma warning disable IL3050 // AOT compiling should recognize that this call is gated by RuntimeFeature.IsDynamicCodeSupported var createSetterGeneric = s_createSetterInternal.MakeGenericMethod(propInfo.DeclaringType!, propInfo.PropertyType).CreateDelegate>(); -#pragma warning restore IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling. return createSetterGeneric(propInfo); } else diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ClassDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ClassDataContract.cs index f412ee7557ed19..1200e4ee07097c 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ClassDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ClassDataContract.cs @@ -41,14 +41,12 @@ internal sealed class ClassDataContract : DataContract DynamicallyAccessedMemberTypes.PublicProperties; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal ClassDataContract(Type type) : base(new ClassDataContractCriticalHelper(type)) { InitClassDataContract(); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private ClassDataContract(Type type, XmlDictionaryString ns, string[] memberNames) : base(new ClassDataContractCriticalHelper(type, ns, memberNames)) { InitClassDataContract(); @@ -77,7 +75,6 @@ internal List? Members public XmlDictionaryString?[]? ChildElementNamespaces { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_childElementNamespaces == null) @@ -136,7 +133,6 @@ internal MethodInfo? ExtensionDataSetMethod public override DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { return _helper.KnownDataContracts; } } @@ -208,8 +204,6 @@ internal MethodInfo? GetKeyValuePairMethodInfo } private Func? _makeNewInstance; - [UnconditionalSuppressMessage("AOT Analysis", "IL3050:RequiresDynamicCode", - Justification = "Cannot annotate a field, annotating the usage instead")] private Func MakeNewInstance => _makeNewInstance ??= FastInvokerBuilder.GetMakeNewInstanceFunc(UnderlyingType); internal bool CreateNewInstanceViaDefaultConstructor([NotNullWhen(true)] out object? obj) @@ -235,7 +229,6 @@ internal bool CreateNewInstanceViaDefaultConstructor([NotNullWhen(true)] out obj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private XmlFormatClassWriterDelegate CreateXmlFormatWriterDelegate() { return new XmlFormatWriterGenerator().GenerateClassWriter(this); @@ -244,7 +237,6 @@ private XmlFormatClassWriterDelegate CreateXmlFormatWriterDelegate() internal XmlFormatClassWriterDelegate XmlFormatWriterDelegate { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_helper.XmlFormatWriterDelegate == null) @@ -267,7 +259,6 @@ internal XmlFormatClassWriterDelegate XmlFormatWriterDelegate } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private XmlFormatClassReaderDelegate CreateXmlFormatReaderDelegate() { return new XmlFormatReaderGenerator().GenerateClassReader(this); @@ -276,7 +267,6 @@ private XmlFormatClassReaderDelegate CreateXmlFormatReaderDelegate() internal XmlFormatClassReaderDelegate XmlFormatReaderDelegate { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_helper.XmlFormatReaderDelegate == null) @@ -299,7 +289,6 @@ internal XmlFormatClassReaderDelegate XmlFormatReaderDelegate } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static ClassDataContract CreateClassDataContractForKeyValue(Type type, XmlDictionaryString ns, string[] memberNames) { ClassDataContract? cdc = (ClassDataContract?)DataContract.GetDataContractFromGeneratedAssembly(type); @@ -334,7 +323,6 @@ internal static void CheckAndAddMember(List members, DataMember memb } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static XmlDictionaryString? GetChildNamespaceToDeclare(DataContract dataContract, Type childType, XmlDictionary dictionary) { childType = DataContract.UnwrapNullableType(childType); @@ -458,7 +446,6 @@ internal static bool IsNonSerializedMember(Type type, string memberName) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private XmlDictionaryString?[]? CreateChildElementNamespaces() { if (Members == null) @@ -487,7 +474,6 @@ private void EnsureMethodsImported() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { Debug.Assert(context != null); @@ -500,7 +486,6 @@ public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, Xml } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) { if (_isScriptObject) @@ -708,8 +693,6 @@ private sealed class ClassDataContractCriticalHelper : DataContract.DataContract { private static Type[]? s_serInfoCtorArgs; private static readonly MethodInfo s_getKeyValuePairMethod = typeof(KeyValuePairAdapter<,>).GetMethod("GetKeyValuePair", Globals.ScanAllMembers)!; - [UnconditionalSuppressMessage("AOT Analysis", "IL3050:RequiresDynamicCode", - Justification = "Cannot annotate a field, annotating the method that uses the field instead")] private static readonly ConstructorInfo s_ctorGenericMethod = typeof(KeyValuePairAdapter<,>).GetConstructor(Globals.ScanAllMembers, new Type[] { typeof(KeyValuePair<,>).MakeGenericType(typeof(KeyValuePairAdapter<,>).GetGenericArguments()) })!; private ClassDataContract? _baseContract; @@ -742,7 +725,6 @@ private sealed class ClassDataContractCriticalHelper : DataContract.DataContract public XmlDictionaryString[]? MemberNamespaces; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal ClassDataContractCriticalHelper([DynamicallyAccessedMembers(DataContractPreserveMemberTypes)] Type type) : base(type) { @@ -857,7 +839,6 @@ internal ClassDataContractCriticalHelper([DynamicallyAccessedMembers(DataContrac } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal ClassDataContractCriticalHelper( [DynamicallyAccessedMembers(DataContractPreserveMemberTypes)] Type type, XmlDictionaryString ns, string[] memberNames) : base(type) @@ -934,7 +915,6 @@ private void EnsureIsReferenceImported(Type type) [MemberNotNull(nameof(_members))] [MemberNotNull(nameof(Members))] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ImportDataMembers() { Type type = this.UnderlyingType; @@ -1113,7 +1093,6 @@ private static bool CanSerializeMember(FieldInfo? field) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static bool SetIfGetOnlyCollection(DataMember memberContract) { //OK to call IsCollection here since the use of surrogated collection types is not supported in get-only scenarios @@ -1188,7 +1167,6 @@ private void SetIfMembersHaveConflict(List members) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private XmlQualifiedName GetStableNameAndSetHasDataContract(Type type) { return DataContract.GetStableName(type, out _hasDataContract); @@ -1359,7 +1337,6 @@ internal override DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_knownDataContracts != null) @@ -1409,7 +1386,6 @@ internal bool IsNonAttributedType } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void SetKeyValuePairAdapterFlags( [DynamicallyAccessedMembers(DataContractPreserveMemberTypes)] Type type) @@ -1534,7 +1510,6 @@ public int Compare(Member x, Member y) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal ClassDataContractCriticalHelper Clone() { ClassDataContractCriticalHelper clonedHelper = new ClassDataContractCriticalHelper(this.UnderlyingType); @@ -1597,7 +1572,6 @@ internal Type ObjectType } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal ClassDataContract Clone() { ClassDataContract clonedDc = new ClassDataContract(this.UnderlyingType); @@ -1612,7 +1586,6 @@ internal ClassDataContract Clone() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void UpdateNamespaceAndMembers(Type type, XmlDictionaryString ns, string[] memberNames) { this.StableName = new XmlQualifiedName(GetStableName(type).Name, ns.Value); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs index f7d61607657414..b39d1af3d439ff 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs @@ -121,7 +121,6 @@ internal void BeginMethod(DynamicMethod dynamicMethod, Type delegateType, string InitILGeneration(methodName, argTypes); } - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void BeginMethod(string methodName, Type delegateType, bool allowPrivateMemberAccess) { MethodInfo signature = JsonFormatWriterGenerator.GetInvokeMethod(delegateType); @@ -133,7 +132,6 @@ internal void BeginMethod(string methodName, Type delegateType, bool allowPrivat _delegateType = delegateType; } - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void BeginMethod(Type returnType, string methodName, Type[] argTypes, bool allowPrivateMemberAccess) { _dynamicMethod = new DynamicMethod(methodName, returnType, argTypes, SerializationModule, allowPrivateMemberAccess); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CollectionDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CollectionDataContract.cs index fde7dd4a8ef8d8..df57f68205cf2e 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CollectionDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CollectionDataContract.cs @@ -134,14 +134,12 @@ internal sealed class CollectionDataContract : DataContract private CollectionDataContractCriticalHelper _helper; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal CollectionDataContract(Type type) : base(new CollectionDataContractCriticalHelper(type)) { InitCollectionDataContract(this); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private CollectionDataContract(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, string? deserializationExceptionMessage) : base(new CollectionDataContractCriticalHelper(type, kind, itemType, getEnumeratorMethod, deserializationExceptionMessage)) { @@ -149,7 +147,6 @@ private CollectionDataContract(Type type, CollectionKind kind, Type itemType, Me } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private CollectionDataContract(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, MethodInfo? addMethod, ConstructorInfo? constructor) : base(new CollectionDataContractCriticalHelper(type, kind, itemType, getEnumeratorMethod, addMethod, constructor)) { @@ -157,7 +154,6 @@ private CollectionDataContract(Type type, CollectionKind kind, Type itemType, Me } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private CollectionDataContract(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, MethodInfo? addMethod, ConstructorInfo? constructor, bool isConstructorCheckRequired) : base(new CollectionDataContractCriticalHelper(type, kind, itemType, getEnumeratorMethod, addMethod, constructor, isConstructorCheckRequired)) { @@ -165,7 +161,6 @@ private CollectionDataContract(Type type, CollectionKind kind, Type itemType, Me } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private CollectionDataContract(Type type, string invalidCollectionInSharedContractMessage) : base(new CollectionDataContractCriticalHelper(type, invalidCollectionInSharedContractMessage)) { InitCollectionDataContract(GetSharedTypeContract(type)); @@ -174,7 +169,6 @@ private CollectionDataContract(Type type, string invalidCollectionInSharedContra [MemberNotNull(nameof(_helper))] [MemberNotNull(nameof(_collectionItemName))] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void InitCollectionDataContract(DataContract? sharedTypeContract) { _helper = (base.Helper as CollectionDataContractCriticalHelper)!; @@ -208,7 +202,6 @@ public Type ItemType public DataContract ItemContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { return _itemContract ?? _helper.ItemContract; @@ -268,7 +261,6 @@ internal bool IsDictionary public XmlDictionaryString? ChildElementNamespace { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_childElementNamespace == null) @@ -327,7 +319,6 @@ internal ConstructorInfo? Constructor public override DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { return _helper.KnownDataContracts; } @@ -352,7 +343,6 @@ internal bool IsReadOnlyContract } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private XmlFormatCollectionWriterDelegate CreateXmlFormatWriterDelegate() { return new XmlFormatWriterGenerator().GenerateCollectionWriter(this); @@ -361,7 +351,6 @@ private XmlFormatCollectionWriterDelegate CreateXmlFormatWriterDelegate() internal XmlFormatCollectionWriterDelegate XmlFormatWriterDelegate { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_helper.XmlFormatWriterDelegate == null) @@ -384,7 +373,6 @@ internal XmlFormatCollectionWriterDelegate XmlFormatWriterDelegate } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private XmlFormatCollectionReaderDelegate CreateXmlFormatReaderDelegate() { return new XmlFormatReaderGenerator().GenerateCollectionReader(this); @@ -393,7 +381,6 @@ private XmlFormatCollectionReaderDelegate CreateXmlFormatReaderDelegate() internal XmlFormatCollectionReaderDelegate XmlFormatReaderDelegate { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_helper.XmlFormatReaderDelegate == null) @@ -421,7 +408,6 @@ internal XmlFormatCollectionReaderDelegate XmlFormatReaderDelegate } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private XmlFormatGetOnlyCollectionReaderDelegate CreateXmlFormatGetOnlyCollectionReaderDelegate() { return new XmlFormatReaderGenerator().GenerateGetOnlyCollectionReader(this); @@ -431,7 +417,6 @@ private XmlFormatGetOnlyCollectionReaderDelegate CreateXmlFormatGetOnlyCollectio internal XmlFormatGetOnlyCollectionReaderDelegate XmlFormatGetOnlyCollectionReaderDelegate { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_helper.XmlFormatGetOnlyCollectionReaderDelegate == null) @@ -468,20 +453,17 @@ internal XmlFormatGetOnlyCollectionReaderDelegate XmlFormatGetOnlyCollectionRead } } - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void IncrementCollectionCount(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context) { _helper.IncrementCollectionCount(xmlWriter, obj, context); } - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal IEnumerator GetEnumeratorForCollection(object obj) { return _helper.GetEnumeratorForCollection(obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal Type GetCollectionElementType() { return _helper.GetCollectionElementType(); @@ -529,7 +511,6 @@ private sealed class CollectionDataContractCriticalHelper : DataContract.DataCon }; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void Init(CollectionKind kind, Type? itemType, CollectionDataContractAttribute? collectionContractAttribute) { _kind = kind; @@ -586,7 +567,6 @@ private void Init(CollectionKind kind, Type? itemType, CollectionDataContractAtt // array [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal CollectionDataContractCriticalHelper( [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] Type type) : base(type) @@ -601,7 +581,6 @@ internal CollectionDataContractCriticalHelper( // read-only collection [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal CollectionDataContractCriticalHelper( [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] Type type, @@ -623,7 +602,6 @@ internal CollectionDataContractCriticalHelper( // collection [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal CollectionDataContractCriticalHelper( [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] Type type, @@ -647,7 +625,6 @@ internal CollectionDataContractCriticalHelper( // collection [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal CollectionDataContractCriticalHelper(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, MethodInfo? addMethod, ConstructorInfo? constructor, bool isConstructorCheckRequired) : this(type, kind, itemType, getEnumeratorMethod, addMethod, constructor) { @@ -655,7 +632,6 @@ internal CollectionDataContractCriticalHelper(Type type, CollectionKind kind, Ty } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal CollectionDataContractCriticalHelper( [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] Type type, @@ -679,7 +655,6 @@ internal Type ItemType internal DataContract ItemContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_itemContract == null) @@ -773,7 +748,6 @@ internal bool IsItemTypeNullable internal override DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (!_isKnownTypeAttributeChecked && UnderlyingType != null) @@ -823,7 +797,6 @@ internal XmlFormatGetOnlyCollectionReaderDelegate? XmlFormatGetOnlyCollectionRea private static void DummyIncrementCollectionCount(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context) { } - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void IncrementCollectionCount(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context) { if (_incrementCollectionCountDelegate == null) @@ -869,7 +842,6 @@ internal void IncrementCollectionCount(XmlWriterDelegator xmlWriter, object obj, [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2060:MakeGenericMethod", Justification = "The call to MakeGenericMethod is safe due to the fact that CollectionDataContractCriticalHelper.BuildIncrementCollectionCountDelegate is not annotated.")] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static MethodInfo GetBuildIncrementCollectionCountGenericDelegate(Type type) => BuildIncrementCollectionCountDelegateMethod.MakeGenericMethod(type); private static MethodInfo? s_buildIncrementCollectionCountDelegateMethod; @@ -889,7 +861,6 @@ private static IncrementCollectionCountDelegate BuildIncrementCollectionCountDel private CreateGenericDictionaryEnumeratorDelegate? _createGenericDictionaryEnumeratorDelegate; - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal IEnumerator GetEnumeratorForCollection(object obj) { IEnumerator enumerator = ((IEnumerable)obj).GetEnumerator(); @@ -912,12 +883,11 @@ internal IEnumerator GetEnumeratorForCollection(object obj) return enumerator; [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2060:MakeGenericMethod", - Justification = "The call to MakeGenericMethod is safe due to the fact that CollectionDataContractCriticalHelper.BuildCreateGenericDictionaryEnumerator is not annotated.")] + Justification = "The call to MakeGenericMethod is safe due to the fact that CollectionDataContractCriticalHelper.BuildCreateGenericDictionaryEnumerator is not annotated.")] static MethodInfo GetBuildCreateGenericDictionaryEnumeratorGenericMethod(Type[] keyValueTypes) => GetBuildCreateGenericDictionaryEnumeratorMethodInfo.MakeGenericMethod(keyValueTypes[0], keyValueTypes[1]); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal Type GetCollectionElementType() { Debug.Assert(Kind != CollectionKind.Array, "GetCollectionElementType should not be called on Arrays"); @@ -986,7 +956,6 @@ private static CreateGenericDictionaryEnumeratorDelegate BuildCreateGenericDicti } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private DataContract? GetSharedTypeContract(Type type) { if (type.IsDefined(Globals.TypeOfCollectionDataContractAttribute, false)) @@ -1008,28 +977,24 @@ internal static bool IsCollectionInterface(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static bool IsCollection(Type type) { return IsCollection(type, out _); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static bool IsCollection(Type type, [NotNullWhen(true)] out Type? itemType) { return IsCollectionHelper(type, out itemType, true /*constructorRequired*/); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static bool IsCollection(Type type, bool constructorRequired) { return IsCollectionHelper(type, out _, constructorRequired); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static bool IsCollectionHelper(Type type, [NotNullWhen(true)] out Type? itemType, bool constructorRequired) { if (type.IsArray && DataContract.GetBuiltInDataContract(type) == null) @@ -1041,14 +1006,12 @@ private static bool IsCollectionHelper(Type type, [NotNullWhen(true)] out Type? } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static bool TryCreate(Type type, [NotNullWhen(true)] out DataContract? dataContract) { return IsCollectionOrTryCreate(type, tryCreate: true, out dataContract!, out _, constructorRequired: true); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static bool CreateGetOnlyCollectionDataContract(Type type, [NotNullWhen(true)] out DataContract? dataContract) { if (type.IsArray) @@ -1063,7 +1026,6 @@ internal static bool CreateGetOnlyCollectionDataContract(Type type, [NotNullWhen } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static bool TryCreateGetOnlyCollectionDataContract(Type type, [NotNullWhen(true)] out DataContract? dataContract) { dataContract = DataContract.GetDataContractFromGeneratedAssembly(type); @@ -1112,7 +1074,6 @@ private static bool IsArraySegment(Type t) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static bool IsCollectionOrTryCreate(Type type, bool tryCreate, out DataContract? dataContract, out Type itemType, bool constructorRequired) { dataContract = null; @@ -1351,7 +1312,6 @@ internal static bool IsCollectionDataContract(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static bool HandleIfInvalidCollection(Type type, bool tryCreate, bool hasCollectionDataContract, bool createContractWithException, string message, string? param, ref DataContract? dataContract) { if (hasCollectionDataContract) @@ -1605,7 +1565,6 @@ internal bool RequiresMemberAccessForWrite(SecurityException? securityException) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { Debug.Assert(context != null); @@ -1616,7 +1575,6 @@ public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, Xml } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) { Debug.Assert(context != null); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs index b7bf190cb74c8e..f859f2f518ceff 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs @@ -30,7 +30,6 @@ internal abstract class DataContract internal const string SerializerTrimmerWarning = "Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the " + "required types are preserved."; - internal const string SerializerAOTWarning = "Data Contract Serialization and Deserialization might require types that cannot be statically analyzed."; public static Dictionary GetDataContracts() { @@ -71,21 +70,18 @@ internal MethodInfo? ParseMethod } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetDataContract(Type type) { return GetDataContract(type.TypeHandle, type); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type type) { return GetDataContract(typeHandle, type, SerializationMode.SharedContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type? type, SerializationMode mode) { int id = GetId(typeHandle); @@ -94,7 +90,6 @@ internal static DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type? } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetDataContract(int id, RuntimeTypeHandle typeHandle, SerializationMode mode) { DataContract dataContract = GetDataContractSkipValidation(id, typeHandle, null); @@ -102,14 +97,12 @@ internal static DataContract GetDataContract(int id, RuntimeTypeHandle typeHandl } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetDataContractSkipValidation(int id, RuntimeTypeHandle typeHandle, Type? type) { return DataContractCriticalHelper.GetDataContractSkipValidation(id, typeHandle, type); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetGetOnlyCollectionDataContract(int id, RuntimeTypeHandle typeHandle, Type? type, SerializationMode mode) { DataContract dataContract = GetGetOnlyCollectionDataContractSkipValidation(id, typeHandle, type); @@ -122,7 +115,6 @@ internal static DataContract GetGetOnlyCollectionDataContract(int id, RuntimeTyp } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetGetOnlyCollectionDataContractSkipValidation(int id, RuntimeTypeHandle typeHandle, Type? type) { return DataContractCriticalHelper.GetGetOnlyCollectionDataContractSkipValidation(id, typeHandle, type); @@ -144,21 +136,18 @@ internal static int GetId(RuntimeTypeHandle typeHandle) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static DataContract? GetBuiltInDataContract(Type type) { return DataContractCriticalHelper.GetBuiltInDataContract(type); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static DataContract? GetBuiltInDataContract(string name, string ns) { return DataContractCriticalHelper.GetBuiltInDataContract(name, ns); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static DataContract? GetBuiltInDataContract(string typeName) { return DataContractCriticalHelper.GetBuiltInDataContract(typeName); @@ -214,21 +203,18 @@ internal Type TypeForInitialization } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(this.GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) { throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(this.GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(this.GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); @@ -269,7 +255,6 @@ public XmlQualifiedName StableName public virtual DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { return _helper.KnownDataContracts; } @@ -394,7 +379,6 @@ internal class DataContractCriticalHelper private Type _typeForInitialization; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetDataContractSkipValidation(int id, RuntimeTypeHandle typeHandle, Type? type) { DataContract dataContract = s_dataContractCache[id]; @@ -411,7 +395,6 @@ internal static DataContract GetDataContractSkipValidation(int id, RuntimeTypeHa } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetGetOnlyCollectionDataContractSkipValidation(int id, RuntimeTypeHandle typeHandle, Type? type) { DataContract dataContract = s_dataContractCache[id]; @@ -492,7 +475,6 @@ internal static int GetId(RuntimeTypeHandle typeHandle) // check whether a corresponding update is required in ClassDataContract.IsNonAttributedTypeValidForSerialization [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static DataContract CreateDataContract(int id, RuntimeTypeHandle typeHandle, Type? type) { DataContract? dataContract = s_dataContractCache[id]; @@ -522,7 +504,6 @@ private static DataContract CreateDataContract(int id, RuntimeTypeHandle typeHan } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static DataContract CreateDataContract(Type type) { type = UnwrapNullableType(type); @@ -578,7 +559,6 @@ private static void AssignDataContractToId(DataContract dataContract, int id) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static DataContract CreateGetOnlyCollectionDataContract(int id, RuntimeTypeHandle typeHandle, Type? type) { DataContract? dataContract = null; @@ -601,7 +581,6 @@ private static DataContract CreateGetOnlyCollectionDataContract(int id, RuntimeT // This method returns adapter types used at runtime to create DataContract. [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static Type GetDataContractAdapterType(Type type) { // Replace the DataTimeOffset ISerializable type passed in with the internal DateTimeOffsetAdapter DataContract type. @@ -650,7 +629,6 @@ private static RuntimeTypeHandle GetDataContractAdapterTypeHandle(RuntimeTypeHan } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static DataContract? GetBuiltInDataContract(Type type) { if (type.IsInterface && !CollectionDataContract.IsCollectionInterface(type)) @@ -671,7 +649,6 @@ private static RuntimeTypeHandle GetDataContractAdapterTypeHandle(RuntimeTypeHan } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static DataContract? GetBuiltInDataContract(string name, string ns) { lock (s_initBuiltInContractsLock) @@ -690,7 +667,6 @@ private static RuntimeTypeHandle GetDataContractAdapterTypeHandle(RuntimeTypeHan } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static DataContract? GetBuiltInDataContract(string typeName) { if (!typeName.StartsWith("System.", StringComparison.Ordinal)) @@ -769,7 +745,6 @@ private static RuntimeTypeHandle GetDataContractAdapterTypeHandle(RuntimeTypeHan } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static bool TryCreateBuiltInDataContract(Type type, [NotNullWhen(true)] out DataContract? dataContract) { if (type.IsEnum) // Type.GetTypeCode will report Enums as TypeCode.IntXX @@ -852,7 +827,6 @@ public static bool TryCreateBuiltInDataContract(Type type, [NotNullWhen(true)] o } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static bool TryCreateBuiltInDataContract(string name, string ns, [NotNullWhen(true)] out DataContract? dataContract) { dataContract = null; @@ -1120,7 +1094,6 @@ internal XmlQualifiedName StableName internal virtual DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { return null; } set { /* do nothing */ } } @@ -1221,7 +1194,6 @@ internal void ThrowInvalidDataContractException(string message) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static bool IsTypeSerializable(Type type, HashSet? previousCollectionTypes = null) { Type? itemType; @@ -1325,14 +1297,12 @@ internal static bool IsValidNCName(string name) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static XmlQualifiedName GetStableName(Type type) { return GetStableName(type, out _); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static XmlQualifiedName GetStableName(Type type, out bool hasDataContract) { type = UnwrapRedundantNullableType(type); @@ -1360,7 +1330,6 @@ internal static XmlQualifiedName GetStableName(Type type, out bool hasDataContra } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static XmlQualifiedName GetDCTypeStableName(Type type, DataContractAttribute dataContractAttribute) { string? name, ns; @@ -1390,7 +1359,6 @@ private static XmlQualifiedName GetDCTypeStableName(Type type, DataContractAttri } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static XmlQualifiedName GetNonDCTypeStableName(Type type) { string? name, ns; @@ -1413,7 +1381,6 @@ private static XmlQualifiedName GetNonDCTypeStableName(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static bool TryGetBuiltInXmlAndArrayTypeStableName(Type type, [NotNullWhen(true)] out XmlQualifiedName? stableName) { stableName = null; @@ -1454,7 +1421,6 @@ internal static bool TryGetDCAttribute(Type type, [NotNullWhen(true)] out DataCo } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static XmlQualifiedName GetCollectionStableName(Type type, Type itemType, out CollectionDataContractAttribute? collectionContractAttribute) { string? name, ns; @@ -1500,7 +1466,6 @@ internal static XmlQualifiedName GetCollectionStableName(Type type, Type itemTyp } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static string GetArrayPrefix(ref Type itemType) { string arrayOfPrefix = string.Empty; @@ -1520,14 +1485,12 @@ internal static string GetCollectionNamespace(string elementNs) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static XmlQualifiedName GetDefaultStableName(Type type) { return CreateQualifiedName(GetDefaultStableLocalName(type), GetDefaultStableNamespace(type)); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static string GetDefaultStableLocalName(Type type) { if (type.IsGenericParameter) @@ -1659,7 +1622,6 @@ internal static string GetDefaultStableNamespace(string? clrNs) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static void GetDefaultStableName(string fullTypeName, out string localName, out string ns) { CodeTypeReference typeReference = new CodeTypeReference(fullTypeName); @@ -1667,7 +1629,6 @@ internal static void GetDefaultStableName(string fullTypeName, out string localN } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static void GetDefaultStableName(CodeTypeReference typeReference, out string localName, out string ns) { string fullTypeName = typeReference.BaseType; @@ -1932,7 +1893,6 @@ private static byte[] ComputeHash(byte[] namespaces) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static string ExpandGenericParameters(string format, Type type) { GenericNameProvider genericNameProviderForType = new GenericNameProvider(type); @@ -1940,7 +1900,6 @@ private static string ExpandGenericParameters(string format, Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static string ExpandGenericParameters(string format, IGenericNameProvider genericNameProvider) { string? digest = null; @@ -1994,7 +1953,6 @@ internal static bool IsTypeNullable(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContractDictionary? ImportKnownTypeAttributes(Type type) { DataContractDictionary? knownDataContracts = null; @@ -2004,7 +1962,6 @@ internal static bool IsTypeNullable(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static void ImportKnownTypeAttributes(Type? type, Dictionary typesChecked, ref DataContractDictionary? knownDataContracts) { while (type != null && DataContract.IsTypeSerializable(type)) @@ -2099,7 +2056,6 @@ private static void ImportKnownTypeAttributes(Type? type, Dictionary } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static void CheckAndAdd(Type type, Dictionary typesChecked, [NotNullIfNotNull(nameof(nameToDataContractTable))] ref DataContractDictionary? nameToDataContractTable) { type = DataContract.UnwrapNullableType(type); @@ -2241,16 +2197,13 @@ internal interface IGenericNameProvider int GetParameterCount(); IList GetNestedParameterCounts(); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] string GetParameterName(int paramIndex); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] string GetNamespaces(); string GetGenericTypeName(); bool ParametersFromBuiltInNamespaces { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get; } } @@ -2287,14 +2240,12 @@ public IList GetNestedParameterCounts() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public string GetParameterName(int paramIndex) { return GetStableName(paramIndex).Name; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public string GetNamespaces() { StringBuilder namespaces = new StringBuilder(); @@ -2311,7 +2262,6 @@ public string GetGenericTypeName() public bool ParametersFromBuiltInNamespaces { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { bool parametersFromBuiltInNamespaces = true; @@ -2327,7 +2277,6 @@ public bool ParametersFromBuiltInNamespaces } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private XmlQualifiedName GetStableName(int i) { object o = _genericParams[i]; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractResolver.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractResolver.cs index c1426d4b474cf9..2b9345dc2c5f23 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractResolver.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractResolver.cs @@ -10,10 +10,8 @@ namespace System.Runtime.Serialization public abstract class DataContractResolver { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public abstract bool TryResolveType(Type type, Type? declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString? typeName, out XmlDictionaryString? typeNamespace); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public abstract Type? ResolveName(string typeName, string? typeNamespace, Type? declaredType, DataContractResolver knownTypeResolver); } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs index f798ef38e082d9..cd8b099284e332 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs @@ -170,7 +170,6 @@ public ReadOnlyCollection KnownTypes internal override DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (this.knownDataContracts == null && this.knownTypeList != null) @@ -219,7 +218,6 @@ public bool SerializeReadOnlyTypes private DataContract RootContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_rootContract == null) @@ -232,14 +230,12 @@ private DataContract RootContract } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void InternalWriteObject(XmlWriterDelegator writer, object? graph) { InternalWriteObject(writer, graph, null); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void InternalWriteObject(XmlWriterDelegator writer, object? graph, DataContractResolver? dataContractResolver) { InternalWriteStartObject(writer, graph); @@ -248,119 +244,102 @@ internal override void InternalWriteObject(XmlWriterDelegator writer, object? gr } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteObject(XmlWriter writer, object? graph) { WriteObjectHandleExceptions(new XmlWriterDelegator(writer), graph); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteStartObject(XmlWriter writer, object? graph) { WriteStartObjectHandleExceptions(new XmlWriterDelegator(writer), graph); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteObjectContent(XmlWriter writer, object? graph) { WriteObjectContentHandleExceptions(new XmlWriterDelegator(writer), graph); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteEndObject(XmlWriter writer) { WriteEndObjectHandleExceptions(new XmlWriterDelegator(writer)); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteStartObject(XmlDictionaryWriter writer, object? graph) { WriteStartObjectHandleExceptions(new XmlWriterDelegator(writer), graph); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteObjectContent(XmlDictionaryWriter writer, object? graph) { WriteObjectContentHandleExceptions(new XmlWriterDelegator(writer), graph); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteEndObject(XmlDictionaryWriter writer) { WriteEndObjectHandleExceptions(new XmlWriterDelegator(writer)); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public void WriteObject(XmlDictionaryWriter writer, object? graph, DataContractResolver? dataContractResolver) { WriteObjectHandleExceptions(new XmlWriterDelegator(writer), graph, dataContractResolver); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadObject(XmlReader reader) { return ReadObjectHandleExceptions(new XmlReaderDelegator(reader), true /*verifyObjectName*/); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadObject(XmlReader reader, bool verifyObjectName) { return ReadObjectHandleExceptions(new XmlReaderDelegator(reader), verifyObjectName); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override bool IsStartObject(XmlReader reader) { return IsStartObjectHandleExceptions(new XmlReaderDelegator(reader)); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadObject(XmlDictionaryReader reader, bool verifyObjectName) { return ReadObjectHandleExceptions(new XmlReaderDelegator(reader), verifyObjectName); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override bool IsStartObject(XmlDictionaryReader reader) { return IsStartObjectHandleExceptions(new XmlReaderDelegator(reader)); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public object? ReadObject(XmlDictionaryReader reader, bool verifyObjectName, DataContractResolver? dataContractResolver) { return ReadObjectHandleExceptions(new XmlReaderDelegator(reader), verifyObjectName, dataContractResolver); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void InternalWriteStartObject(XmlWriterDelegator writer, object? graph) { WriteRootElement(writer, RootContract, _rootName, _rootNamespace, _needsContractNsAtRoot); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void InternalWriteObjectContent(XmlWriterDelegator writer, object? graph) { InternalWriteObjectContent(writer, graph, null); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void InternalWriteObjectContent(XmlWriterDelegator writer, object? graph, DataContractResolver? dataContractResolver) { if (MaxItemsInObjectGraph == 0) @@ -417,7 +396,6 @@ internal void InternalWriteObjectContent(XmlWriterDelegator writer, object? grap } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetDataContract(DataContract declaredTypeContract, Type declaredType, Type objectType) { if (declaredType.IsInterface && CollectionDataContract.IsCollectionInterface(declaredType)) @@ -435,7 +413,6 @@ internal static DataContract GetDataContract(DataContract declaredTypeContract, } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void InternalWriteEndObject(XmlWriterDelegator writer) { if (!IsRootXmlAny(_rootName, RootContract)) @@ -445,14 +422,12 @@ internal override void InternalWriteEndObject(XmlWriterDelegator writer) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override object? InternalReadObject(XmlReaderDelegator xmlReader, bool verifyObjectName) { return InternalReadObject(xmlReader, verifyObjectName, null); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override object? InternalReadObject(XmlReaderDelegator xmlReader, bool verifyObjectName, DataContractResolver? dataContractResolver) { if (MaxItemsInObjectGraph == 0) @@ -501,7 +476,6 @@ internal override void InternalWriteEndObject(XmlWriterDelegator writer) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override bool InternalIsStartObject(XmlReaderDelegator reader) { return IsRootElement(reader, RootContract, _rootName, _rootNamespace); @@ -519,7 +493,6 @@ internal override bool InternalIsStartObject(XmlReaderDelegator reader) [return: NotNullIfNotNull(nameof(oldObj))] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static object? SurrogateToDataContractType(ISerializationSurrogateProvider serializationSurrogateProvider, object? oldObj, Type surrogatedDeclaredType, ref Type objType) { object? obj = DataContractSurrogateCaller.GetObjectToSerialize(serializationSurrogateProvider, oldObj, objType, surrogatedDeclaredType); @@ -531,7 +504,6 @@ internal override bool InternalIsStartObject(XmlReaderDelegator reader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static Type GetSurrogatedType(ISerializationSurrogateProvider serializationSurrogateProvider, Type type) { return DataContractSurrogateCaller.GetDataContractType(serializationSurrogateProvider, DataContract.UnwrapNullableType(type)); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSet.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSet.cs index 08b6a90a16e756..41bae413f816aa 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSet.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSet.cs @@ -32,7 +32,6 @@ internal DataContractSet(IDataContractSurrogate dataContractSurrogate, ICollecti #endif [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal DataContractSet(DataContractSet dataContractSet) { ArgumentNullException.ThrowIfNull(dataContractSet); @@ -66,7 +65,6 @@ internal DataContractSet(DataContractSet dataContractSet) #endif [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void Add(Type type) { DataContract dataContract = GetDataContract(type); @@ -81,14 +79,12 @@ internal static void EnsureTypeNotGeneric(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void Add(DataContract dataContract) { Add(dataContract.StableName, dataContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public void Add(XmlQualifiedName name, DataContract dataContract) { if (dataContract.IsBuiltInDataContract) @@ -97,7 +93,6 @@ public void Add(XmlQualifiedName name, DataContract dataContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void InternalAdd(XmlQualifiedName name, DataContract dataContract) { DataContract? dataContractInSet; @@ -134,7 +129,6 @@ internal void InternalAdd(XmlQualifiedName name, DataContract dataContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void AddClassDataContract(ClassDataContract classDataContract) { if (classDataContract.BaseContract != null) @@ -168,7 +162,6 @@ private void AddClassDataContract(ClassDataContract classDataContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void AddCollectionDataContract(CollectionDataContract collectionDataContract) { if (collectionDataContract.IsDictionary) @@ -186,14 +179,12 @@ private void AddCollectionDataContract(CollectionDataContract collectionDataCont } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void AddXmlDataContract(XmlDataContract xmlDataContract) { AddKnownDataContracts(xmlDataContract.KnownDataContracts); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void AddKnownDataContracts(DataContractDictionary? knownDataContracts) { if (knownDataContracts != null) @@ -206,7 +197,6 @@ private void AddKnownDataContracts(DataContractDictionary? knownDataContracts) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetDataContract(Type clrType) { #if SUPPORT_SURROGATE @@ -237,7 +227,6 @@ internal static DataContract GetDataContract(Type clrType) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetMemberTypeDataContract(DataMember dataMember) { Type dataMemberType = dataMember.MemberType; @@ -263,7 +252,6 @@ internal static DataContract GetMemberTypeDataContract(DataMember dataMember) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetItemTypeDataContract(CollectionDataContract collectionContract) { if (collectionContract.ItemType != null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSurrogateCaller.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSurrogateCaller.cs index f31813e05acf68..03d145ba53b884 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSurrogateCaller.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSurrogateCaller.cs @@ -12,7 +12,6 @@ namespace System.Runtime.Serialization internal static class DataContractSurrogateCaller { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static Type GetDataContractType(ISerializationSurrogateProvider surrogateProvider, Type type) { if (DataContract.GetBuiltInDataContract(type) != null) @@ -22,7 +21,6 @@ internal static Type GetDataContractType(ISerializationSurrogateProvider surroga [return: NotNullIfNotNull(nameof(obj))] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static object? GetObjectToSerialize(ISerializationSurrogateProvider surrogateProvider, object? obj, Type objType, Type membertype) { if (obj == null) @@ -34,7 +32,6 @@ internal static Type GetDataContractType(ISerializationSurrogateProvider surroga [return: NotNullIfNotNull(nameof(obj))] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static object? GetDeserializedObject(ISerializationSurrogateProvider surrogateProvider, object? obj, Type objType, Type memberType) { if (obj == null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataMember.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataMember.cs index ad63d673006aa8..251e896e864880 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataMember.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataMember.cs @@ -90,7 +90,6 @@ internal Type MemberType internal DataContract MemberTypeContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { return _helper.MemberTypeContract; } } @@ -98,7 +97,6 @@ internal DataContract MemberTypeContract internal PrimitiveDataContract? MemberPrimitiveContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { return _helper.MemberPrimitiveContract; @@ -212,7 +210,6 @@ internal Type MemberType internal DataContract MemberTypeContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_memberTypeContract == null) @@ -252,7 +249,6 @@ internal DataMember? ConflictingMember internal PrimitiveDataContract? MemberPrimitiveContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_memberPrimitiveContract == PrimitiveDataContract.NullContract) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/EnumDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/EnumDataContract.cs index 0d696455e1d135..9014a71b47d401 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/EnumDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/EnumDataContract.cs @@ -22,7 +22,6 @@ internal sealed class EnumDataContract : DataContract public XmlQualifiedName? BaseContractName { get; set; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal EnumDataContract(Type type) : base(new EnumDataContractCriticalHelper(type)) { _helper = (base.Helper as EnumDataContractCriticalHelper)!; @@ -99,7 +98,6 @@ internal static void Add(Type type, string localName) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal EnumDataContractCriticalHelper( [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] Type type) : base(type) @@ -362,14 +360,12 @@ internal long GetEnumValueFromString(string value) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { WriteEnumValue(xmlWriter, obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) { object obj = ReadEnumValue(xmlReader); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/GenericParameterDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/GenericParameterDataContract.cs index ddcc9e1182a15f..8f5bd20805c9de 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/GenericParameterDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/GenericParameterDataContract.cs @@ -12,7 +12,6 @@ internal sealed class GenericParameterDataContract : DataContract private readonly GenericParameterDataContractCriticalHelper _helper; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal GenericParameterDataContract(Type type) : base(new GenericParameterDataContractCriticalHelper(type)) { @@ -38,7 +37,6 @@ private sealed class GenericParameterDataContractCriticalHelper : DataContract.D private readonly int _parameterPosition; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal GenericParameterDataContractCriticalHelper( [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] Type type) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Globals.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Globals.cs index 8ef893fdf3e856..48167f42d04f67 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Globals.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Globals.cs @@ -299,7 +299,6 @@ internal static partial class Globals internal static Type TypeOfHashtable { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get => s_typeOfHashtable ??= TypeOfDictionaryGeneric.MakeGenericType(TypeOfObject, TypeOfObject); } @@ -322,7 +321,6 @@ internal static Type TypeOfHashtable private static readonly Type? s_typeOfScriptObject; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static ClassDataContract CreateScriptObjectClassDataContract() { Debug.Assert(s_typeOfScriptObject != null); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs index ed81d44d7b4f2a..4ab7ae4006ca95 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs @@ -35,49 +35,42 @@ public sealed class DataContractJsonSerializer : XmlObjectSerializer [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public DataContractJsonSerializer(Type type) : this(type, (IEnumerable?)null) { } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public DataContractJsonSerializer(Type type, string? rootName) : this(type, rootName, null) { } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public DataContractJsonSerializer(Type type, XmlDictionaryString? rootName) : this(type, rootName, null) { } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public DataContractJsonSerializer(Type type, IEnumerable? knownTypes) : this(type, null, knownTypes, int.MaxValue, false, false) { } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public DataContractJsonSerializer(Type type, string? rootName, IEnumerable? knownTypes) : this(type, new DataContractJsonSerializerSettings() { RootName = rootName, KnownTypes = knownTypes }) { } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public DataContractJsonSerializer(Type type, XmlDictionaryString? rootName, IEnumerable? knownTypes) : this(type, rootName, knownTypes, int.MaxValue, false, false) { } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public DataContractJsonSerializer(Type type, DataContractJsonSerializerSettings? settings) { settings ??= new DataContractJsonSerializerSettings(); @@ -88,7 +81,6 @@ public DataContractJsonSerializer(Type type, DataContractJsonSerializerSettings? } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal DataContractJsonSerializer(Type type, XmlDictionaryString? rootName, IEnumerable? knownTypes, @@ -127,7 +119,6 @@ public ReadOnlyCollection KnownTypes internal override DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (this.knownDataContracts == null && this.knownTypeList != null) @@ -183,7 +174,6 @@ public bool UseSimpleDictionaryFormat private DataContract RootContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_rootContract == null) @@ -204,7 +194,6 @@ private XmlDictionaryString RootName } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override bool IsStartObject(XmlReader reader) { // No need to pass in DateTimeFormat to JsonReaderDelegator: no DateTimes will be read in IsStartObject @@ -212,7 +201,6 @@ public override bool IsStartObject(XmlReader reader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override bool IsStartObject(XmlDictionaryReader reader) { // No need to pass in DateTimeFormat to JsonReaderDelegator: no DateTimes will be read in IsStartObject @@ -220,7 +208,6 @@ public override bool IsStartObject(XmlDictionaryReader reader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadObject(Stream stream) { ArgumentNullException.ThrowIfNull(stream); @@ -229,35 +216,30 @@ public override bool IsStartObject(XmlDictionaryReader reader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadObject(XmlReader reader) { return ReadObjectHandleExceptions(new JsonReaderDelegator(reader, this.DateTimeFormat), true); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadObject(XmlReader reader, bool verifyObjectName) { return ReadObjectHandleExceptions(new JsonReaderDelegator(reader, this.DateTimeFormat), verifyObjectName); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadObject(XmlDictionaryReader reader) { return ReadObjectHandleExceptions(new JsonReaderDelegator(reader, this.DateTimeFormat), true); // verifyObjectName } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadObject(XmlDictionaryReader reader, bool verifyObjectName) { return ReadObjectHandleExceptions(new JsonReaderDelegator(reader, this.DateTimeFormat), verifyObjectName); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteEndObject(XmlWriter writer) { // No need to pass in DateTimeFormat to JsonWriterDelegator: no DateTimes will be written in end object @@ -265,7 +247,6 @@ public override void WriteEndObject(XmlWriter writer) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteEndObject(XmlDictionaryWriter writer) { // No need to pass in DateTimeFormat to JsonWriterDelegator: no DateTimes will be written in end object @@ -273,7 +254,6 @@ public override void WriteEndObject(XmlDictionaryWriter writer) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteObject(Stream stream, object? graph) { ArgumentNullException.ThrowIfNull(stream); @@ -284,35 +264,30 @@ public override void WriteObject(Stream stream, object? graph) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteObject(XmlWriter writer, object? graph) { WriteObjectHandleExceptions(new JsonWriterDelegator(writer, this.DateTimeFormat), graph); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteObject(XmlDictionaryWriter writer, object? graph) { WriteObjectHandleExceptions(new JsonWriterDelegator(writer, this.DateTimeFormat), graph); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteObjectContent(XmlWriter writer, object? graph) { WriteObjectContentHandleExceptions(new JsonWriterDelegator(writer, this.DateTimeFormat), graph); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteObjectContent(XmlDictionaryWriter writer, object? graph) { WriteObjectContentHandleExceptions(new JsonWriterDelegator(writer, this.DateTimeFormat), graph); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteStartObject(XmlWriter writer, object? graph) { // No need to pass in DateTimeFormat to JsonWriterDelegator: no DateTimes will be written in start object @@ -320,7 +295,6 @@ public override void WriteStartObject(XmlWriter writer, object? graph) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteStartObject(XmlDictionaryWriter writer, object? graph) { // No need to pass in DateTimeFormat to JsonWriterDelegator: no DateTimes will be written in start object @@ -384,7 +358,6 @@ internal static bool IsJsonLocalName(XmlReaderDelegator reader, string elementNa } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static object? ReadJsonValue(DataContract contract, XmlReaderDelegator reader, XmlObjectSerializerReadContextComplexJson? context) { return JsonDataContract.GetJsonDataContract(contract).ReadJsonValue(reader, context); @@ -396,7 +369,6 @@ internal static void WriteJsonNull(XmlWriterDelegator writer) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static void WriteJsonValue(JsonDataContract contract, XmlWriterDelegator writer, object graph, XmlObjectSerializerWriteContextComplexJson? context, RuntimeTypeHandle declaredTypeHandle) { contract.WriteJsonValue(writer, graph, context, declaredTypeHandle); @@ -413,7 +385,6 @@ internal static void WriteJsonValue(JsonDataContract contract, XmlWriterDelegato } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override bool InternalIsStartObject(XmlReaderDelegator reader) { if (IsRootElement(reader, RootContract, RootName, XmlDictionaryString.Empty)) @@ -425,7 +396,6 @@ internal override bool InternalIsStartObject(XmlReaderDelegator reader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override object? InternalReadObject(XmlReaderDelegator xmlReader, bool verifyObjectName) { if (MaxItemsInObjectGraph == 0) @@ -456,14 +426,12 @@ internal override bool InternalIsStartObject(XmlReaderDelegator reader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void InternalWriteEndObject(XmlWriterDelegator writer) { writer.WriteEndElement(); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void InternalWriteObject(XmlWriterDelegator writer, object? graph) { InternalWriteStartObject(writer, graph); @@ -472,7 +440,6 @@ internal override void InternalWriteObject(XmlWriterDelegator writer, object? gr } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void InternalWriteObjectContent(XmlWriterDelegator writer, object? graph) { if (MaxItemsInObjectGraph == 0) @@ -521,7 +488,6 @@ internal override void InternalWriteObjectContent(XmlWriterDelegator writer, obj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void InternalWriteStartObject(XmlWriterDelegator writer, object? graph) { if (_rootNameRequiresMapping) @@ -536,7 +502,6 @@ internal override void InternalWriteStartObject(XmlWriterDelegator writer, objec } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void AddCollectionItemTypeToKnownTypes(Type knownType) { Type? itemType; @@ -554,7 +519,6 @@ private void AddCollectionItemTypeToKnownTypes(Type knownType) [MemberNotNull(nameof(_rootType))] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void Initialize(Type type, IEnumerable? knownTypes, int maxItemsInObjectGraph, @@ -595,7 +559,6 @@ private void Initialize(Type type, [MemberNotNull(nameof(_rootType))] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void Initialize(Type type, XmlDictionaryString? rootName, IEnumerable? knownTypes, @@ -624,7 +587,6 @@ internal static void CheckIfTypeIsReference(DataContract dataContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract GetDataContract(DataContract declaredTypeContract, Type declaredType, Type objectType) { DataContract contract = DataContractSerializer.GetDataContract(declaredTypeContract, declaredType, objectType); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonByteArrayDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonByteArrayDataContract.cs index dfd95ac53e3ed6..5518442c8649da 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonByteArrayDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonByteArrayDataContract.cs @@ -13,14 +13,12 @@ namespace System.Runtime.Serialization.Json internal sealed class JsonByteArrayDataContract : JsonDataContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonByteArrayDataContract(ByteArrayDataContract traditionalByteArrayDataContract) : base(traditionalByteArrayDataContract) { } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { if (context == null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonClassDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonClassDataContract.cs index be8c47db186099..6ff59d9eaa7f2b 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonClassDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonClassDataContract.cs @@ -15,7 +15,6 @@ internal sealed class JsonClassDataContract : JsonDataContract private readonly JsonClassDataContractCriticalHelper _helper; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonClassDataContract(ClassDataContract traditionalDataContract) : base(new JsonClassDataContractCriticalHelper(traditionalDataContract)) { @@ -25,7 +24,6 @@ public JsonClassDataContract(ClassDataContract traditionalDataContract) internal JsonFormatClassReaderDelegate JsonFormatReaderDelegate { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_helper.JsonFormatReaderDelegate == null) @@ -56,7 +54,6 @@ internal JsonFormatClassReaderDelegate JsonFormatReaderDelegate internal JsonFormatClassWriterDelegate JsonFormatWriterDelegate { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_helper.JsonFormatWriterDelegate == null) @@ -91,7 +88,6 @@ internal JsonFormatClassWriterDelegate JsonFormatWriterDelegate private ClassDataContract TraditionalClassDataContract => _helper.TraditionalClassDataContract; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { jsonReader.Read(); @@ -101,7 +97,6 @@ internal JsonFormatClassWriterDelegate JsonFormatWriterDelegate } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteJsonValueCore(XmlWriterDelegator jsonWriter, object obj, XmlObjectSerializerWriteContextComplexJson? context, RuntimeTypeHandle declaredTypeHandle) { Debug.Assert(context != null); @@ -118,7 +113,6 @@ private sealed class JsonClassDataContractCriticalHelper : JsonDataContractCriti private readonly string _typeName; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonClassDataContractCriticalHelper(ClassDataContract traditionalDataContract) : base(traditionalDataContract) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonCollectionDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonCollectionDataContract.cs index 74cf2f047883e5..9d0a40cc5d1c9a 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonCollectionDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonCollectionDataContract.cs @@ -15,7 +15,6 @@ internal sealed class JsonCollectionDataContract : JsonDataContract private readonly JsonCollectionDataContractCriticalHelper _helper; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonCollectionDataContract(CollectionDataContract traditionalDataContract) : base(new JsonCollectionDataContractCriticalHelper(traditionalDataContract)) { @@ -25,7 +24,6 @@ public JsonCollectionDataContract(CollectionDataContract traditionalDataContract internal JsonFormatCollectionReaderDelegate JsonFormatReaderDelegate { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_helper.JsonFormatReaderDelegate == null) @@ -56,7 +54,6 @@ internal JsonFormatCollectionReaderDelegate JsonFormatReaderDelegate internal JsonFormatGetOnlyCollectionReaderDelegate JsonFormatGetOnlyReaderDelegate { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_helper.JsonFormatGetOnlyReaderDelegate == null) @@ -93,7 +90,6 @@ internal JsonFormatGetOnlyCollectionReaderDelegate JsonFormatGetOnlyReaderDelega internal JsonFormatCollectionWriterDelegate JsonFormatWriterDelegate { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (_helper.JsonFormatWriterDelegate == null) @@ -124,7 +120,6 @@ internal JsonFormatCollectionWriterDelegate JsonFormatWriterDelegate private CollectionDataContract TraditionalCollectionDataContract => _helper.TraditionalCollectionDataContract; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { Debug.Assert(context != null); @@ -146,7 +141,6 @@ internal JsonFormatCollectionWriterDelegate JsonFormatWriterDelegate } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteJsonValueCore(XmlWriterDelegator jsonWriter, object obj, XmlObjectSerializerWriteContextComplexJson? context, RuntimeTypeHandle declaredTypeHandle) { Debug.Assert(context != null); @@ -163,7 +157,6 @@ private sealed class JsonCollectionDataContractCriticalHelper : JsonDataContract private readonly CollectionDataContract _traditionalCollectionDataContract; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonCollectionDataContractCriticalHelper(CollectionDataContract traditionalDataContract) : base(traditionalDataContract) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonDataContract.cs index a9783115ccd084..4330703dc3ab89 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonDataContract.cs @@ -16,7 +16,6 @@ internal class JsonDataContract private readonly JsonDataContractCriticalHelper _helper; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected JsonDataContract(DataContract traditionalDataContract) { _helper = new JsonDataContractCriticalHelper(traditionalDataContract); @@ -64,14 +63,12 @@ internal static JsonReadWriteDelegates GetReadWriteDelegatesFromGeneratedAssembl } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static JsonDataContract GetJsonDataContract(DataContract traditionalDataContract) { return JsonDataContractCriticalHelper.GetJsonDataContract(traditionalDataContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public object? ReadJsonValue(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { PushKnownDataContracts(context); @@ -81,14 +78,12 @@ public static JsonDataContract GetJsonDataContract(DataContract traditionalDataC } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { return TraditionalDataContract.ReadXmlValue(jsonReader, context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public void WriteJsonValue(XmlWriterDelegator jsonWriter, object obj, XmlObjectSerializerWriteContextComplexJson? context, RuntimeTypeHandle declaredTypeHandle) { PushKnownDataContracts(context); @@ -97,7 +92,6 @@ public void WriteJsonValue(XmlWriterDelegator jsonWriter, object obj, XmlObjectS } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual void WriteJsonValueCore(XmlWriterDelegator jsonWriter, object obj, XmlObjectSerializerWriteContextComplexJson? context, RuntimeTypeHandle declaredTypeHandle) { TraditionalDataContract.WriteXmlValue(jsonWriter, obj, context); @@ -155,7 +149,6 @@ internal class JsonDataContractCriticalHelper private readonly string _typeName; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal JsonDataContractCriticalHelper(DataContract traditionalDataContract) { _traditionalDataContract = traditionalDataContract; @@ -170,7 +163,6 @@ internal JsonDataContractCriticalHelper(DataContract traditionalDataContract) internal virtual string TypeName => _typeName; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static JsonDataContract GetJsonDataContract(DataContract traditionalDataContract) { int id = JsonDataContractCriticalHelper.GetId(traditionalDataContract.UnderlyingType.TypeHandle); @@ -217,7 +209,6 @@ internal static int GetId(RuntimeTypeHandle typeHandle) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static JsonDataContract CreateJsonDataContract(int id, DataContract traditionalDataContract) { lock (s_createDataContractLock) @@ -282,7 +273,6 @@ private static JsonDataContract CreateJsonDataContract(int id, DataContract trad } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void AddCollectionItemContractsToKnownDataContracts() { if (_traditionalDataContract.KnownDataContracts != null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEnumDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEnumDataContract.cs index 5303f259d84a3b..76f774cde91f09 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEnumDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEnumDataContract.cs @@ -10,7 +10,6 @@ internal sealed class JsonEnumDataContract : JsonDataContract private readonly JsonEnumDataContractCriticalHelper _helper; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonEnumDataContract(EnumDataContract traditionalDataContract) : base(new JsonEnumDataContractCriticalHelper(traditionalDataContract)) { @@ -20,7 +19,6 @@ public JsonEnumDataContract(EnumDataContract traditionalDataContract) public bool IsULong => _helper.IsULong; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { object enumValue; @@ -38,7 +36,6 @@ public JsonEnumDataContract(EnumDataContract traditionalDataContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteJsonValueCore(XmlWriterDelegator jsonWriter, object obj, XmlObjectSerializerWriteContextComplexJson? context, RuntimeTypeHandle declaredTypeHandle) { if (IsULong) @@ -56,7 +53,6 @@ private sealed class JsonEnumDataContractCriticalHelper : JsonDataContractCritic private readonly bool _isULong; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonEnumDataContractCriticalHelper(EnumDataContract traditionalEnumDataContract) : base(traditionalEnumDataContract) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatReaderGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatReaderGenerator.cs index 8af5ffd850ce98..ceee570b7475ef 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatReaderGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatReaderGenerator.cs @@ -27,21 +27,18 @@ public JsonFormatReaderGenerator() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonFormatClassReaderDelegate GenerateClassReader(ClassDataContract classContract) { return _helper.GenerateClassReader(classContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonFormatCollectionReaderDelegate GenerateCollectionReader(CollectionDataContract collectionContract) { return _helper.GenerateCollectionReader(collectionContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader(CollectionDataContract collectionContract) { return _helper.GenerateGetOnlyCollectionReader(collectionContract); @@ -59,7 +56,6 @@ private sealed class CriticalHelper private ArgBuilder _emptyDictionaryStringArg = null!; // initialized in InitArgs [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonFormatClassReaderDelegate GenerateClassReader(ClassDataContract classContract) { _ilg = new CodeGenerator(); @@ -128,7 +124,6 @@ public JsonFormatClassReaderDelegate GenerateClassReader(ClassDataContract class } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonFormatCollectionReaderDelegate GenerateCollectionReader(CollectionDataContract collectionContract) { _ilg = GenerateCollectionReaderHelper(collectionContract, false /*isGetOnlyCollection*/); @@ -139,7 +134,6 @@ public JsonFormatCollectionReaderDelegate GenerateCollectionReader(CollectionDat } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader(CollectionDataContract collectionContract) { _ilg = GenerateCollectionReaderHelper(collectionContract, true /*isGetOnlyCollection*/); @@ -147,7 +141,6 @@ public JsonFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader return (JsonFormatGetOnlyCollectionReaderDelegate)_ilg.EndMethod(); } - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private CodeGenerator GenerateCollectionReaderHelper(CollectionDataContract collectionContract, bool isGetOnlyCollection) { _ilg = new CodeGenerator(); @@ -179,7 +172,6 @@ private CodeGenerator GenerateCollectionReaderHelper(CollectionDataContract coll return _ilg; } - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static void BeginMethod(CodeGenerator ilg, string methodName, Type delegateType, bool allowPrivateMemberAccess) { MethodInfo signature = JsonFormatWriterGenerator.GetInvokeMethod(delegateType); @@ -284,7 +276,6 @@ private bool InvokeFactoryMethod(ClassDataContract classContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ReadClass(ClassDataContract classContract) { if (classContract.HasExtensionData) @@ -310,7 +301,6 @@ private void ReadClass(ClassDataContract classContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ReadMembers(ClassDataContract classContract, LocalBuilder? extensionDataLocal) { int memberCount = classContract.MemberNames!.Length; @@ -359,7 +349,6 @@ private void ReadMembers(ClassDataContract classContract, LocalBuilder? extensio } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private int ReadMembers(ClassDataContract classContract, BitFlagsGenerator expectedElements, Label[] memberLabels, Label throwDuplicateMemberLabel, LocalBuilder memberIndexLocal) { @@ -468,7 +457,6 @@ private void ReadISerializable(ClassDataContract classContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private LocalBuilder ReadValue(Type type, string name) { LocalBuilder value = _ilg.DeclareLocal(type, "valueRead"); @@ -584,7 +572,6 @@ private void InternalDeserialize(LocalBuilder value, Type type, string name) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WrapNullableObject(LocalBuilder innerValue, LocalBuilder outerValue, int nullables) { Type innerType = innerValue.LocalType, outerType = outerValue.LocalType; @@ -600,7 +587,6 @@ private void WrapNullableObject(LocalBuilder innerValue, LocalBuilder outerValue } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ReadCollection(CollectionDataContract collectionContract) { Type type = collectionContract.UnderlyingType; @@ -731,7 +717,6 @@ private void ReadCollection(CollectionDataContract collectionContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ReadSimpleDictionary(CollectionDataContract collectionContract, Type keyValueType) { Type[] keyValueTypes = keyValueType.GetGenericArguments(); @@ -825,7 +810,6 @@ private void ReadSimpleDictionary(CollectionDataContract collectionContract, Typ } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ReadGetOnlyCollection(CollectionDataContract collectionContract) { Type type = collectionContract.UnderlyingType; @@ -915,7 +899,6 @@ private void ReadGetOnlyCollection(CollectionDataContract collectionContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private bool TryReadPrimitiveArray(Type itemType) { PrimitiveDataContract? primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(itemType); @@ -967,7 +950,6 @@ private bool TryReadPrimitiveArray(Type itemType) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private LocalBuilder ReadCollectionItem(CollectionDataContract collectionContract, Type itemType) { if (collectionContract.Kind == CollectionKind.Dictionary || collectionContract.Kind == CollectionKind.GenericDictionary) @@ -991,7 +973,6 @@ private LocalBuilder ReadCollectionItem(CollectionDataContract collectionContrac } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void StoreCollectionValue(LocalBuilder collection, LocalBuilder value, CollectionDataContract collectionContract) { if (collectionContract.Kind == CollectionKind.GenericDictionary || collectionContract.Kind == CollectionKind.Dictionary) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs index 2cef510cefa9c3..afe02b062b6a7b 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs @@ -25,14 +25,12 @@ public JsonFormatWriterGenerator() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal JsonFormatClassWriterDelegate GenerateClassWriter(ClassDataContract classContract) { return _helper.GenerateClassWriter(classContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal JsonFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionDataContract collectionContract) { return _helper.GenerateCollectionWriter(collectionContract); @@ -60,7 +58,6 @@ private sealed class CriticalHelper private int _childElementIndex; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal JsonFormatClassWriterDelegate GenerateClassWriter(ClassDataContract classContract) { _ilg = new CodeGenerator(); @@ -87,7 +84,6 @@ internal JsonFormatClassWriterDelegate GenerateClassWriter(ClassDataContract cla } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal JsonFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionDataContract collectionContract) { _ilg = new CodeGenerator(); @@ -116,7 +112,6 @@ internal JsonFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionD return (JsonFormatCollectionWriterDelegate)_ilg.EndMethod(); } - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static void BeginMethod(CodeGenerator ilg, string methodName, Type delegateType, bool allowPrivateMemberAccess) { MethodInfo signature = GetInvokeMethod(delegateType); @@ -130,7 +125,6 @@ private static void BeginMethod(CodeGenerator ilg, string methodName, Type deleg } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void InitArgs(Type objType) { _xmlWriterArg = _ilg.GetArg(0); @@ -213,7 +207,6 @@ private void InvokeOnSerialized(ClassDataContract classContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WriteClass(ClassDataContract classContract) { InvokeOnSerializing(classContract); @@ -243,7 +236,6 @@ private void WriteClass(ClassDataContract classContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private int WriteMembers(ClassDataContract classContract, LocalBuilder? extensionDataLocal, ClassDataContract derivedMostClassContract) { int memberCount = (classContract.BaseContract == null) ? 0 : @@ -319,7 +311,6 @@ private LocalBuilder LoadMemberValue(DataMember member) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WriteCollection(CollectionDataContract collectionContract) { LocalBuilder itemName = _ilg.DeclareLocal(typeof(XmlDictionaryString), "itemName"); @@ -523,7 +514,6 @@ private void WriteCollection(CollectionDataContract collectionContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private bool TryWritePrimitive(Type type, LocalBuilder? value, MemberInfo? memberInfo, LocalBuilder? arrayItemIndex, LocalBuilder? name, int nameIndex) { PrimitiveDataContract? primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(type); @@ -571,7 +561,6 @@ private bool TryWritePrimitive(Type type, LocalBuilder? value, MemberInfo? membe } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private bool TryWritePrimitiveArray(Type type, Type itemType, LocalBuilder value, LocalBuilder itemName) { PrimitiveDataContract? primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(itemType); @@ -638,7 +627,6 @@ private void WriteObjectAttribute() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WriteValue(LocalBuilder memberValue) { Type memberType = memberValue.LocalType; @@ -734,7 +722,6 @@ private void InternalSerialize(MethodInfo methodInfo, LocalBuilder memberValue, } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private LocalBuilder UnwrapNullableObject(LocalBuilder memberValue)// Leaves !HasValue on stack { Type memberType = memberValue.LocalType; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs index 7a08e94154d9b1..078c4c51793393 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs @@ -11,14 +11,12 @@ namespace System.Runtime.Serialization.Json internal sealed class JsonObjectDataContract : JsonDataContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonObjectDataContract(DataContract traditionalDataContract) : base(traditionalDataContract) { } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { object? obj; @@ -56,7 +54,6 @@ public JsonObjectDataContract(DataContract traditionalDataContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteJsonValueCore(XmlWriterDelegator jsonWriter, object obj, XmlObjectSerializerWriteContextComplexJson? context, RuntimeTypeHandle declaredTypeHandle) { jsonWriter.WriteAttributeString(null, JsonGlobals.typeString, null, JsonGlobals.objectString); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonQNameDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonQNameDataContract.cs index c00625e3590030..8bc4a2d1f50424 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonQNameDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonQNameDataContract.cs @@ -13,14 +13,12 @@ namespace System.Runtime.Serialization.Json internal sealed class JsonQNameDataContract : JsonDataContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonQNameDataContract(QNameDataContract traditionalQNameDataContract) : base(traditionalQNameDataContract) { } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { if (context == null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonStringDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonStringDataContract.cs index 3e6e8f24d623e7..e65c58c9caed53 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonStringDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonStringDataContract.cs @@ -13,14 +13,12 @@ namespace System.Runtime.Serialization.Json internal sealed class JsonStringDataContract : JsonDataContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonStringDataContract(StringDataContract traditionalStringDataContract) : base(traditionalStringDataContract) { } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { if (context == null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonUriDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonUriDataContract.cs index 56540cca5bc335..bd4b68daf4103a 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonUriDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonUriDataContract.cs @@ -8,14 +8,12 @@ namespace System.Runtime.Serialization.Json internal sealed class JsonUriDataContract : JsonDataContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonUriDataContract(UriDataContract traditionalUriDataContract) : base(traditionalUriDataContract) { } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { if (context == null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonXmlDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonXmlDataContract.cs index e085fb5cf74f94..99640f43461ac6 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonXmlDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonXmlDataContract.cs @@ -12,14 +12,12 @@ namespace System.Runtime.Serialization.Json internal sealed class JsonXmlDataContract : JsonDataContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public JsonXmlDataContract(XmlDataContract traditionalXmlDataContract) : base(traditionalXmlDataContract) { } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { string xmlContent = jsonReader.ReadElementContentAsString(); @@ -43,7 +41,6 @@ public JsonXmlDataContract(XmlDataContract traditionalXmlDataContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteJsonValueCore(XmlWriterDelegator jsonWriter, object obj, XmlObjectSerializerWriteContextComplexJson? context, RuntimeTypeHandle declaredTypeHandle) { DataContractSerializer dataContractSerializer = new DataContractSerializer(Type.GetTypeFromHandle(declaredTypeHandle)!, diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatReader.cs index 971d808646e5f5..4161f53a29aece 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatReader.cs @@ -27,7 +27,6 @@ public ReflectionJsonClassReader(ClassDataContract classDataContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public object ReflectionReadClass(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContextComplexJson? context, XmlDictionaryString emptyDictionaryString, XmlDictionaryString[]? memberNames) { Debug.Assert(_classContract != null); @@ -40,14 +39,12 @@ internal sealed class ReflectionJsonCollectionReader private readonly ReflectionReader _reflectionReader = new ReflectionJsonReader(); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public object ReflectionReadCollection(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContextComplexJson context, XmlDictionaryString emptyDictionaryString, XmlDictionaryString itemName, CollectionDataContract collectionContract) { return _reflectionReader.ReflectionReadCollection(xmlReader, context, itemName, emptyDictionaryString/*itemNamespace*/, collectionContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public void ReflectionReadGetOnlyCollection(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContextComplexJson context, XmlDictionaryString emptyDictionaryString, XmlDictionaryString itemName, CollectionDataContract collectionContract) { _reflectionReader.ReflectionReadGetOnlyCollection(xmlReader, context, itemName, emptyDictionaryString/*itemNamespace*/, collectionContract); @@ -57,7 +54,6 @@ public void ReflectionReadGetOnlyCollection(XmlReaderDelegator xmlReader, XmlObj internal sealed class ReflectionJsonReader : ReflectionReader { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override void ReflectionReadMembers(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString[] memberNames, XmlDictionaryString[]? memberNamespaces, ClassDataContract classContract, ref object obj) { var jsonContext = context as XmlObjectSerializerReadContextComplexJson; @@ -112,7 +108,6 @@ protected override string GetCollectionContractNamespace(CollectionDataContract } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override object? ReflectionReadDictionaryItem(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, CollectionDataContract collectionContract) { var jsonContext = context as XmlObjectSerializerReadContextComplexJson; @@ -125,7 +120,6 @@ protected override string GetCollectionContractNamespace(CollectionDataContract } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override bool ReflectionReadSpecialCollection(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, CollectionDataContract collectionContract, object? resultCollection) { var jsonContext = context as XmlObjectSerializerReadContextComplexJson; @@ -142,7 +136,6 @@ protected override bool ReflectionReadSpecialCollection(XmlReaderDelegator xmlRe } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ReadSimpleDictionary(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, CollectionDataContract collectionContract, Type keyValueType, object? dictionary) { Type[] keyValueTypes = keyValueType.GetGenericArguments(); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatWriter.cs index 6d34048f6f213b..74fbefe525979e 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatWriter.cs @@ -19,14 +19,12 @@ internal sealed class ReflectionJsonFormatWriter private readonly ReflectionJsonClassWriter _reflectionClassWriter = new ReflectionJsonClassWriter(); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public void ReflectionWriteClass(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContextComplexJson context, ClassDataContract classContract, XmlDictionaryString[]? memberNames) { _reflectionClassWriter.ReflectionWriteClass(xmlWriter, obj, context, classContract, memberNames); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static void ReflectionWriteCollection(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContextComplexJson context, CollectionDataContract collectionContract) { JsonWriterDelegator? jsonWriter = xmlWriter as JsonWriterDelegator; @@ -140,7 +138,6 @@ private static void ReflectionWriteObjectAttribute(XmlWriterDelegator xmlWriter) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static bool ReflectionTryWritePrimitiveArray(JsonWriterDelegator jsonWriter, object obj, Type underlyingType, Type itemType, XmlDictionaryString collectionItemName) { PrimitiveDataContract? primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(itemType); @@ -198,7 +195,6 @@ private static void ReflectionWriteArrayAttribute(XmlWriterDelegator xmlWriter) internal sealed class ReflectionJsonClassWriter : ReflectionClassWriter { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override int ReflectionWriteMembers(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context, ClassDataContract classContract, ClassDataContract derivedMostClassContract, int childElementIndex, XmlDictionaryString[]? memberNames) { Debug.Assert(memberNames != null); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerReadContextComplexJson.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerReadContextComplexJson.cs index 0a89fda737fc38..9c61887a150b4e 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerReadContextComplexJson.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerReadContextComplexJson.cs @@ -32,14 +32,12 @@ internal static XmlObjectSerializerReadContextComplexJson CreateContext(DataCont } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override object? ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader) { return DataContractJsonSerializer.ReadJsonValue(dataContract, reader, this); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public int GetJsonMemberIndex(XmlReaderDelegator xmlReader, XmlDictionaryString[] memberNames, int memberIndex, ExtensionDataObject? extensionData) { int length = memberNames.Length; @@ -258,7 +256,6 @@ protected override XmlReaderDelegator CreateReaderDelegatorForReader(XmlReader x } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type? type) { DataContract dataContract = base.GetDataContract(typeHandle, type); @@ -267,7 +264,6 @@ internal override DataContract GetDataContract(RuntimeTypeHandle typeHandle, Typ } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override DataContract GetDataContractSkipValidation(int typeId, RuntimeTypeHandle typeHandle, Type? type) { DataContract dataContract = base.GetDataContractSkipValidation(typeId, typeHandle, type); @@ -276,7 +272,6 @@ internal override DataContract GetDataContractSkipValidation(int typeId, Runtime } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override DataContract GetDataContract(int id, RuntimeTypeHandle typeHandle) { DataContract dataContract = base.GetDataContract(id, typeHandle); @@ -347,7 +342,6 @@ private static bool IsBitSet(byte[] bytes, int bitIndex) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override DataContract? ResolveDataContractFromRootDataContract(XmlQualifiedName typeQName) { return XmlObjectSerializerWriteContextComplexJson.ResolveJsonDataContractFromRootDataContract(this, typeQName, rootTypeDataContract!); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerWriteContextComplexJson.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerWriteContextComplexJson.cs index d43d2563909e12..8a644b13bec9e4 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerWriteContextComplexJson.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerWriteContextComplexJson.cs @@ -91,7 +91,6 @@ internal static string TruncateDefaultDataContractNamespace(string dataContractN } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override bool WriteTypeInfo(XmlWriterDelegator writer, DataContract contract, DataContract declaredContract) { if (!((object.ReferenceEquals(contract.Name, declaredContract.Name) && @@ -143,7 +142,6 @@ private static void WriteTypeInfo(XmlWriterDelegator writer, string typeInformat } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override void WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle declaredTypeHandle) { JsonDataContract jsonDataContract = JsonDataContract.GetJsonDataContract(dataContract); @@ -166,7 +164,6 @@ internal static XmlDictionaryString CollectionItemName } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override void SerializeWithXsiType(XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle objectTypeHandle, Type? objectType, int declaredTypeID, RuntimeTypeHandle declaredTypeHandle, Type declaredType) { DataContract dataContract; @@ -199,7 +196,6 @@ protected override void SerializeWithXsiType(XmlWriterDelegator xmlWriter, objec } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void HandleCollectionAssignedToObject(Type declaredType, ref DataContract dataContract, ref object obj, ref bool verifyKnownType) { if ((declaredType != dataContract.UnderlyingType) && (dataContract is CollectionDataContract)) @@ -235,7 +231,6 @@ private void HandleCollectionAssignedToObject(Type declaredType, ref DataContrac } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void SerializeWithXsiTypeAtTopLevel(DataContract dataContract, XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle originalDeclaredTypeHandle, Type graphType) { bool verifyKnownType = false; @@ -257,7 +252,6 @@ internal override void SerializeWithXsiTypeAtTopLevel(DataContract dataContract, } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void VerifyType(DataContract dataContract, Type declaredType) { bool knownTypesAddedInCurrentScope = false; @@ -285,7 +279,6 @@ internal static void WriteJsonNameWithMapping(XmlWriterDelegator xmlWriter, XmlD } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void WriteExtensionDataTypeInfo(XmlWriterDelegator xmlWriter, IDataNode dataNode) { Type dataType = dataNode.DataType; @@ -329,7 +322,6 @@ internal static void VerifyObjectCompatibilityWithInterface(DataContract contrac } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void WriteJsonISerializable(XmlWriterDelegator xmlWriter, ISerializable obj) { Type objType = obj.GetType(); @@ -347,7 +339,6 @@ internal void WriteJsonISerializable(XmlWriterDelegator xmlWriter, ISerializable [return: NotNullIfNotNull(nameof(oldItemContract))] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract? GetRevisedItemContract(DataContract oldItemContract) { if ((oldItemContract != null) && @@ -360,7 +351,6 @@ internal void WriteJsonISerializable(XmlWriterDelegator xmlWriter, ISerializable } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type? type) { DataContract dataContract = base.GetDataContract(typeHandle, type); @@ -369,7 +359,6 @@ internal override DataContract GetDataContract(RuntimeTypeHandle typeHandle, Typ } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override DataContract GetDataContractSkipValidation(int typeId, RuntimeTypeHandle typeHandle, Type? type) { DataContract dataContract = base.GetDataContractSkipValidation(typeId, typeHandle, type); @@ -378,7 +367,6 @@ internal override DataContract GetDataContractSkipValidation(int typeId, Runtime } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override DataContract GetDataContract(int id, RuntimeTypeHandle typeHandle) { DataContract dataContract = base.GetDataContract(id, typeHandle); @@ -387,14 +375,12 @@ internal override DataContract GetDataContract(int id, RuntimeTypeHandle typeHan } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override DataContract? ResolveDataContractFromRootDataContract(XmlQualifiedName typeQName) { return XmlObjectSerializerWriteContextComplexJson.ResolveJsonDataContractFromRootDataContract(this, typeQName, rootTypeDataContract!); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContract? ResolveJsonDataContractFromRootDataContract(XmlObjectSerializerContext context, XmlQualifiedName typeQName, DataContract rootTypeDataContract) { if (rootTypeDataContract.StableName == typeQName) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/KnownTypeDataContractResolver.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/KnownTypeDataContractResolver.cs index 391417d733f22c..ab1d030913b578 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/KnownTypeDataContractResolver.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/KnownTypeDataContractResolver.cs @@ -18,7 +18,6 @@ internal KnownTypeDataContractResolver(XmlObjectSerializerContext context) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override bool TryResolveType(Type type, Type? declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString? typeName, out XmlDictionaryString? typeNamespace) { if (type == null) @@ -50,7 +49,6 @@ public override bool TryResolveType(Type type, Type? declaredType, DataContractR } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override Type? ResolveName(string typeName, string? typeNamespace, Type? declaredType, DataContractResolver knownTypeResolver) { if (typeName == null || typeNamespace == null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/PrimitiveDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/PrimitiveDataContract.cs index 538d8b43c1f69b..8cd388f9601b03 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/PrimitiveDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/PrimitiveDataContract.cs @@ -25,14 +25,12 @@ protected PrimitiveDataContract( } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static PrimitiveDataContract? GetPrimitiveDataContract(Type type) { return DataContract.GetBuiltInDataContract(type) as PrimitiveDataContract; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static PrimitiveDataContract? GetPrimitiveDataContract(string name, string ns) { return DataContract.GetBuiltInDataContract(name, ns) as PrimitiveDataContract; @@ -92,7 +90,6 @@ internal MethodInfo XmlFormatContentWriterMethod _helper.XmlFormatReaderMethod ??= typeof(XmlReaderDelegator).GetMethod(ReadMethodName, Globals.ScanAllMembers)!; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { xmlWriter.WriteAnyType(obj); @@ -166,14 +163,12 @@ internal CharDataContract(XmlDictionaryString name, XmlDictionaryString ns) : ba internal override string ReadMethodName { get { return "ReadElementContentAsChar"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteChar((char)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsChar() @@ -181,7 +176,6 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteChar((char)obj!, name, ns); @@ -203,14 +197,12 @@ public BooleanDataContract() : base(typeof(bool), DictionaryGlobals.BooleanLocal internal override string ReadMethodName { get { return "ReadElementContentAsBoolean"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteBoolean((bool)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsBoolean() @@ -218,7 +210,6 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteBoolean((bool)obj!, name, ns); @@ -235,14 +226,12 @@ public SignedByteDataContract() : base(typeof(sbyte), DictionaryGlobals.SignedBy internal override string ReadMethodName { get { return "ReadElementContentAsSignedByte"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteSignedByte((sbyte)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsSignedByte() @@ -250,7 +239,6 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteSignedByte((sbyte)obj!, name, ns); @@ -267,14 +255,12 @@ public UnsignedByteDataContract() : base(typeof(byte), DictionaryGlobals.Unsigne internal override string ReadMethodName { get { return "ReadElementContentAsUnsignedByte"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteUnsignedByte((byte)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsUnsignedByte() @@ -282,7 +268,6 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteUnsignedByte((byte)obj!, name, ns); @@ -299,14 +284,12 @@ public ShortDataContract() : base(typeof(short), DictionaryGlobals.ShortLocalNam internal override string ReadMethodName { get { return "ReadElementContentAsShort"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteShort((short)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsShort() @@ -314,7 +297,6 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteShort((short)obj!, name, ns); @@ -331,14 +313,12 @@ public UnsignedShortDataContract() : base(typeof(ushort), DictionaryGlobals.Unsi internal override string ReadMethodName { get { return "ReadElementContentAsUnsignedShort"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteUnsignedShort((ushort)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsUnsignedShort() @@ -346,7 +326,6 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteUnsignedShort((ushort)obj!, name, ns); @@ -385,21 +364,18 @@ internal override string WriteMethodName } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { throw new NotImplementedException(); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { throw new NotImplementedException(); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { throw new NotImplementedException(); @@ -416,14 +392,12 @@ public IntDataContract() : base(typeof(int), DictionaryGlobals.IntLocalName, Dic internal override string ReadMethodName { get { return "ReadElementContentAsInt"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteInt((int)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsInt() @@ -431,7 +405,6 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteInt((int)obj!, name, ns); @@ -448,14 +421,12 @@ public UnsignedIntDataContract() : base(typeof(uint), DictionaryGlobals.Unsigned internal override string ReadMethodName { get { return "ReadElementContentAsUnsignedInt"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteUnsignedInt((uint)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsUnsignedInt() @@ -463,7 +434,6 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteUnsignedInt((uint)obj!, name, ns); @@ -484,14 +454,12 @@ internal LongDataContract(XmlDictionaryString name, XmlDictionaryString ns) : ba internal override string ReadMethodName { get { return "ReadElementContentAsLong"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteLong((long)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsLong() @@ -499,7 +467,6 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteLong((long)obj!, name, ns); @@ -541,14 +508,12 @@ public UnsignedLongDataContract() : base(typeof(ulong), DictionaryGlobals.Unsign internal override string ReadMethodName { get { return "ReadElementContentAsUnsignedLong"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteUnsignedLong((ulong)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsUnsignedLong() @@ -556,7 +521,6 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteUnsignedLong((ulong)obj!, name, ns); @@ -573,14 +537,12 @@ public FloatDataContract() : base(typeof(float), DictionaryGlobals.FloatLocalNam internal override string ReadMethodName { get { return "ReadElementContentAsFloat"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteFloat((float)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsFloat() @@ -588,7 +550,6 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteFloat((float)obj!, name, ns); @@ -605,14 +566,12 @@ public DoubleDataContract() : base(typeof(double), DictionaryGlobals.DoubleLocal internal override string ReadMethodName { get { return "ReadElementContentAsDouble"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteDouble((double)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsDouble() @@ -620,7 +579,6 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteDouble((double)obj!, name, ns); @@ -637,14 +595,12 @@ public DecimalDataContract() : base(typeof(decimal), DictionaryGlobals.DecimalLo internal override string ReadMethodName { get { return "ReadElementContentAsDecimal"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteDecimal((decimal)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsDecimal() @@ -652,7 +608,6 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteDecimal((decimal)obj!, name, ns); @@ -673,14 +628,12 @@ public DateTimeDataContract() : base(typeof(DateTime), DictionaryGlobals.DateTim internal override string ReadMethodName { get { return "ReadElementContentAsDateTime"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteDateTime((DateTime)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsDateTime() @@ -688,7 +641,6 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteDateTime((DateTime)obj!, name, ns); @@ -709,14 +661,12 @@ internal StringDataContract(XmlDictionaryString name, XmlDictionaryString ns) : internal override string ReadMethodName { get { return "ReadElementContentAsString"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteString((string)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { if (context == null) @@ -730,7 +680,6 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { context.WriteString(xmlWriter, (string?)obj, name, ns); @@ -847,14 +796,12 @@ public ByteArrayDataContract() : base(typeof(byte[]), DictionaryGlobals.ByteArra internal override string ReadMethodName { get { return "ReadElementContentAsBase64"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteBase64((byte[])obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { if (context == null) @@ -868,7 +815,6 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteStartElement(name, ns); @@ -887,14 +833,12 @@ public ObjectDataContract() : base(typeof(object), DictionaryGlobals.ObjectLocal internal override string ReadMethodName { get { return "ReadElementContentAsAnyType"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { // write nothing } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { object obj; @@ -946,14 +890,12 @@ internal TimeSpanDataContract(XmlDictionaryString name, XmlDictionaryString ns) internal override string ReadMethodName { get { return "ReadElementContentAsTimeSpan"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteTimeSpan((TimeSpan)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsTimeSpan() @@ -961,7 +903,6 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator writer, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { writer.WriteTimeSpan((TimeSpan)obj!, name, ns); @@ -987,14 +928,12 @@ internal GuidDataContract(XmlDictionaryString name, XmlDictionaryString ns) : ba internal override string ReadMethodName { get { return "ReadElementContentAsGuid"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteGuid((Guid)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsGuid() @@ -1002,7 +941,6 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteGuid((Guid)obj!, name, ns); @@ -1024,14 +962,12 @@ public UriDataContract() : base(typeof(Uri), DictionaryGlobals.UriLocalName, Dic internal override string ReadMethodName { get { return "ReadElementContentAsUri"; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteUri((Uri)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { if (context == null) @@ -1045,7 +981,6 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator writer, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { writer.WriteUri((Uri?)obj, name, ns); @@ -1067,14 +1002,12 @@ internal override bool IsPrimitive } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteQName((XmlQualifiedName)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { if (context == null) @@ -1088,7 +1021,6 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlElement(XmlWriterDelegator writer, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { context.WriteQName(writer, (XmlQualifiedName?)obj, name, ns); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionClassWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionClassWriter.cs index 120986ebb66fba..3cc3c97a7261e0 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionClassWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionClassWriter.cs @@ -18,7 +18,6 @@ namespace System.Runtime.Serialization internal abstract class ReflectionClassWriter { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public void ReflectionWriteClass(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context, ClassDataContract classContract, XmlDictionaryString[]? memberNames) { InvokeOnSerializing(obj, context, classContract); @@ -41,7 +40,6 @@ public void ReflectionWriteClass(XmlWriterDelegator xmlWriter, object obj, XmlOb } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static void ReflectionWriteValue(XmlWriterDelegator xmlWriter, XmlObjectSerializerWriteContext context, Type type, object? value, bool writeXsiType, PrimitiveDataContract? primitiveContractForParamType) { Type memberType = type; @@ -106,7 +104,6 @@ public static void ReflectionWriteValue(XmlWriterDelegator xmlWriter, XmlObjectS } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected abstract int ReflectionWriteMembers(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context, ClassDataContract classContract, ClassDataContract derivedMostClassContract, int childElementIndex, XmlDictionaryString[]? memberNames); protected static object? ReflectionGetMemberValue(object obj, DataMember dataMember) @@ -115,7 +112,6 @@ public static void ReflectionWriteValue(XmlWriterDelegator xmlWriter, XmlObjectS } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected static bool ReflectionTryWritePrimitive(XmlWriterDelegator xmlWriter, XmlObjectSerializerWriteContext context, Type type, object? value, XmlDictionaryString name, XmlDictionaryString? ns, PrimitiveDataContract? primitiveContract) { if (primitiveContract == null || primitiveContract.UnderlyingType == Globals.TypeOfObject) @@ -168,7 +164,6 @@ private static object ResolveAdapterType(object obj, ClassDataContract classCont } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static void ReflectionInternalSerialize(XmlWriterDelegator xmlWriter, XmlObjectSerializerWriteContext context, object obj, bool isDeclaredType, bool writeXsiType, Type memberType, bool isNullableOfT = false) { if (isNullableOfT) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionReader.cs index b559ba071bc8b4..b28f41784478c8 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionReader.cs @@ -30,7 +30,6 @@ internal abstract class ReflectionReader s_getCollectionSetItemDelegateMethod ??= typeof(ReflectionReader).GetMethod(nameof(GetCollectionSetItemDelegate), BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static)!; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public object ReflectionReadClass(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context, XmlDictionaryString[]? memberNames, XmlDictionaryString[]? memberNamespaces, ClassDataContract classContract) { Debug.Assert(context != null); @@ -62,7 +61,6 @@ public object ReflectionReadClass(XmlReaderDelegator xmlReader, XmlObjectSeriali } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public void ReflectionReadGetOnlyCollection(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString collectionItemName, XmlDictionaryString collectionItemNamespace, CollectionDataContract collectionContract) { object? resultCollection = context.GetCollectionMember(); @@ -85,14 +83,12 @@ public void ReflectionReadGetOnlyCollection(XmlReaderDelegator xmlReader, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public object ReflectionReadCollection(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString collectionItemName, XmlDictionaryString collectionItemNamespace, CollectionDataContract collectionContract) { return ReflectionReadCollectionCore(xmlReader, context, collectionItemName, collectionItemNamespace, collectionContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private object ReflectionReadCollectionCore(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString collectionItemName, XmlDictionaryString collectionItemNamespace, CollectionDataContract collectionContract) { bool isArray = (collectionContract.Kind == CollectionKind.Array); @@ -118,7 +114,6 @@ private object ReflectionReadCollectionCore(XmlReaderDelegator xmlReader, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private CollectionReadItemDelegate GetCollectionReadItemDelegate(CollectionDataContract collectionContract) { CollectionReadItemDelegate collectionReadItemDelegate; @@ -141,7 +136,6 @@ private CollectionReadItemDelegate GetCollectionReadItemDelegate(CollectionDataC } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private object ReadCollectionItems(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString collectionItemName, XmlDictionaryString collectionItemNamespace, CollectionDataContract collectionContract, object resultCollection, bool isReadOnlyCollection) { string itemName = GetCollectionContractItemName(collectionContract); @@ -188,17 +182,14 @@ private object ReadCollectionItems(XmlReaderDelegator xmlReader, XmlObjectSerial } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected abstract void ReflectionReadMembers(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString[] memberNames, XmlDictionaryString[]? memberNamespaces, ClassDataContract classContract, ref object obj); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected abstract object? ReflectionReadDictionaryItem(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, CollectionDataContract collectionContract); protected abstract string GetCollectionContractItemName(CollectionDataContract collectionContract); protected abstract string GetCollectionContractNamespace(CollectionDataContract collectionContract); protected abstract string GetClassContractNamespace(ClassDataContract classContract); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected virtual bool ReflectionReadSpecialCollection(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, CollectionDataContract collectionContract, object? resultCollection) { return false; @@ -217,7 +208,6 @@ protected int ReflectionGetMembers(ClassDataContract classContract, DataMember[] } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected void ReflectionReadMember(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, ClassDataContract classContract, ref object obj, int memberIndex, DataMember[] members) { DataMember dataMember = members[memberIndex]; @@ -241,7 +231,6 @@ protected void ReflectionReadMember(XmlReaderDelegator xmlReader, XmlObjectSeria } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected object? ReflectionReadValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, Type type, string name, string ns, PrimitiveDataContract? primitiveContractForOriginalType = null) { object? value; @@ -269,7 +258,6 @@ protected void ReflectionReadMember(XmlReaderDelegator xmlReader, XmlObjectSeria } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private object? ReadItemOfPrimitiveType(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, Type type, string name, string ns, PrimitiveDataContract? primitiveContract, int nullables) { object? value; @@ -324,7 +312,6 @@ protected void ReflectionReadMember(XmlReaderDelegator xmlReader, XmlObjectSeria } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static object ReadISerializable(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, ClassDataContract classContract) { object obj; @@ -337,7 +324,6 @@ private static object ReadISerializable(XmlReaderDelegator xmlReader, XmlObjectS // This method is a perf optimization for collections. The original method is ReflectionReadValue. [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private CollectionReadItemDelegate GetReflectionReadValueDelegate(Type type) { int nullables = 0; @@ -375,7 +361,6 @@ private static void ReflectionSetMemberValue(ref object obj, object? memberValue } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private object? ReflectionReadValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, DataMember dataMember, string ns) { Type type = dataMember.MemberType; @@ -385,7 +370,6 @@ private static void ReflectionSetMemberValue(ref object obj, object? memberValue } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private object? ReflectionInternalDeserialize(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, CollectionDataContract? collectionContract, Type type, string name, string ns) { return context.InternalDeserialize(xmlReader, DataContract.GetId(type.TypeHandle), type.TypeHandle, name, ns); @@ -474,7 +458,6 @@ private static bool IsArrayLikeCollection(CollectionDataContract collectionContr } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static object ReflectionCreateCollection(CollectionDataContract collectionContract) { if (IsArrayLikeCollection(collectionContract)) @@ -522,7 +505,6 @@ private static object ReflectionCreateCollection(CollectionDataContract collecti return ((KeyValue)o).Value; } - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static CollectionSetItemDelegate GetCollectionSetItemDelegate(CollectionDataContract collectionContract, object resultCollectionObject, bool isReadOnlyCollection) { if (isReadOnlyCollection && collectionContract.Kind == CollectionKind.Array) @@ -622,7 +604,6 @@ private static CollectionSetItemDelegate GetCollectionSetItemDelegate(Collect } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static bool ReflectionTryReadPrimitiveArray(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString collectionItemName, XmlDictionaryString collectionItemNamespace, Type type, Type itemType, int arraySize, [NotNullWhen(true)] out object? resultArray) { resultArray = null; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatReader.cs index 2915cf01b42ef0..98e3c1ea9a4ff0 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatReader.cs @@ -27,24 +27,23 @@ public ReflectionXmlClassReader(ClassDataContract classDataContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public object ReflectionReadClass(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context, XmlDictionaryString[]? memberNames, XmlDictionaryString[]? memberNamespaces) { return _reflectionReader.ReflectionReadClass(xmlReader, context, memberNames, memberNamespaces, _classContract); } } - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal sealed class ReflectionXmlCollectionReader { private readonly ReflectionReader _reflectionReader = new ReflectionXmlReader(); + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public object ReflectionReadCollection(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString itemName, XmlDictionaryString itemNamespace, CollectionDataContract collectionContract) { return _reflectionReader.ReflectionReadCollection(xmlReader, context, itemName, itemNamespace/*itemNamespace*/, collectionContract); } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public void ReflectionReadGetOnlyCollection(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString itemName, XmlDictionaryString itemNs, CollectionDataContract collectionContract) { _reflectionReader.ReflectionReadGetOnlyCollection(xmlReader, context, itemName, itemNs, collectionContract); @@ -54,7 +53,6 @@ public void ReflectionReadGetOnlyCollection(XmlReaderDelegator xmlReader, XmlObj internal sealed class ReflectionXmlReader : ReflectionReader { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override void ReflectionReadMembers(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString[] memberNames, XmlDictionaryString[]? memberNamespaces, ClassDataContract classContract, ref object obj) { Debug.Assert(memberNamespaces != null); @@ -117,7 +115,6 @@ protected override string GetCollectionContractNamespace(CollectionDataContract } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override object? ReflectionReadDictionaryItem(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, CollectionDataContract collectionContract) { Debug.Assert(collectionContract.Kind == CollectionKind.Dictionary || collectionContract.Kind == CollectionKind.GenericDictionary); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatWriter.cs index e2a2694060fd3c..dc70a9cb4ae833 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatWriter.cs @@ -11,17 +11,17 @@ namespace System.Runtime.Serialization { - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal sealed class ReflectionXmlFormatWriter { private readonly ReflectionXmlClassWriter _reflectionClassWriter = new ReflectionXmlClassWriter(); + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public void ReflectionWriteClass(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context, ClassDataContract classContract) { _reflectionClassWriter.ReflectionWriteClass(xmlWriter, obj, context, classContract, null/*memberNames*/); } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public static void ReflectionWriteCollection(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context, CollectionDataContract collectionDataContract) { XmlDictionaryString ns = collectionDataContract.Namespace; @@ -88,6 +88,7 @@ public static void ReflectionWriteCollection(XmlWriterDelegator xmlWriter, objec } } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private static bool ReflectionTryWritePrimitiveArray(XmlWriterDelegator xmlWriter, object obj, Type type, Type itemType, XmlDictionaryString collectionItemName, XmlDictionaryString itemNamespace) { PrimitiveDataContract? primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(itemType); @@ -128,7 +129,6 @@ private static bool ReflectionTryWritePrimitiveArray(XmlWriterDelegator xmlWrite internal sealed class ReflectionXmlClassWriter : ReflectionClassWriter { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override int ReflectionWriteMembers(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context, ClassDataContract classContract, ClassDataContract derivedMostClassContract, int childElementIndex, XmlDictionaryString[]? emptyStringArray) { int memberCount = (classContract.BaseContract == null) ? 0 : diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaExporter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaExporter.cs index 52a40d34927904..1c76e5760047ef 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaExporter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaExporter.cs @@ -36,7 +36,6 @@ private XmlSchemaSet Schemas private XmlDocument XmlDoc => _xmlDoc ??= new XmlDocument(); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void Export() { try @@ -73,7 +72,6 @@ private void ExportSerializationSchema() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ExportDataContract(DataContract dataContract) { if (dataContract.IsBuiltInDataContract) @@ -114,7 +112,6 @@ private XmlSchemaElement ExportTopLevelElement(DataContract dataContract, XmlSch } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ExportClassDataContract(ClassDataContract classDataContract, XmlSchema schema) { XmlSchemaComplexType type = new XmlSchemaComplexType(); @@ -246,7 +243,6 @@ private static XmlElement ExportActualType(XmlQualifiedName typeName, XmlDocumen } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private XmlElement ExportGenericInfo(Type clrType, string elementName, string elementNs) { Type? itemType; @@ -340,7 +336,6 @@ private XmlElement ExportGenericInfo(Type clrType, string elementName, string el } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ExportCollectionDataContract(CollectionDataContract collectionDataContract, XmlSchema schema) { XmlSchemaComplexType type = new XmlSchemaComplexType(); @@ -443,7 +438,6 @@ internal static long GetDefaultEnumValue(bool isFlags, int index) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ExportISerializableDataContract(ClassDataContract dataContract, XmlSchema schema) { XmlSchemaComplexType type = new XmlSchemaComplexType(); @@ -484,7 +478,6 @@ private static XmlSchemaComplexContentExtension CreateTypeContent(XmlSchemaCompl } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ExportXmlDataContract(XmlDataContract dataContract) { XmlQualifiedName? typeQName; @@ -572,7 +565,6 @@ private static void ReprocessAll(XmlSchemaSet schemas)// and remove duplicate it } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static void GetXmlTypeInfo(Type type, out XmlQualifiedName stableName, out XmlSchemaType? xsdType, out bool hasRoot) { if (IsSpecialXmlType(type, out stableName!, out xsdType, out hasRoot)) @@ -585,7 +577,6 @@ internal static void GetXmlTypeInfo(Type type, out XmlQualifiedName stableName, } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static bool InvokeSchemaProviderMethod(Type clrType, XmlSchemaSet schemas, out XmlQualifiedName stableName, out XmlSchemaType? xsdType, out bool hasRoot) { xsdType = null; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SurrogateDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SurrogateDataContract.cs index df47c081184ae0..02b82aaee0d804 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SurrogateDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SurrogateDataContract.cs @@ -14,7 +14,6 @@ internal sealed class SurrogateDataContract : DataContract private readonly SurrogateDataContractCriticalHelper _helper; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal SurrogateDataContract(Type type, ISerializationSurrogate serializationSurrogate) : base(new SurrogateDataContractCriticalHelper(type, serializationSurrogate)) { @@ -27,7 +26,6 @@ internal ISerializationSurrogate SerializationSurrogate } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { Debug.Assert(context != null); @@ -64,7 +62,6 @@ private void SerializationSurrogateGetObjectData(object obj, SerializationInfo s } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) { Debug.Assert(context != null); @@ -90,7 +87,6 @@ private sealed class SurrogateDataContractCriticalHelper : DataContract.DataCont private readonly ISerializationSurrogate serializationSurrogate; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal SurrogateDataContractCriticalHelper( [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] Type type, diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XPathQueryGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XPathQueryGenerator.cs index f1e6ced3e84aef..364038c247a3e2 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XPathQueryGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XPathQueryGenerator.cs @@ -19,7 +19,6 @@ public static class XPathQueryGenerator private const string NsSeparator = ":"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static string CreateFromDataContractSerializer(Type type, MemberInfo[] pathToMember, out XmlNamespaceManager namespaces) { return CreateFromDataContractSerializer(type, pathToMember, null, out namespaces); @@ -27,7 +26,6 @@ public static string CreateFromDataContractSerializer(Type type, MemberInfo[] pa // Here you can provide your own root element Xpath which will replace the Xpath of the top level element [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public static string CreateFromDataContractSerializer(Type type, MemberInfo[] pathToMember, StringBuilder? rootElementXpath, out XmlNamespaceManager namespaces) { ArgumentNullException.ThrowIfNull(type); @@ -56,7 +54,6 @@ public static string CreateFromDataContractSerializer(Type type, MemberInfo[] pa } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static DataContract ProcessDataContract(DataContract contract, ExportContext context, MemberInfo memberNode) { if (contract is ClassDataContract) @@ -67,7 +64,6 @@ private static DataContract ProcessDataContract(DataContract contract, ExportCon } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static DataContract ProcessClassDataContract(ClassDataContract contract, ExportContext context, MemberInfo memberNode) { string prefix = context.SetNamespace(contract.Namespace!.Value); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlDataContract.cs index 5ab510e1def2ba..9ca8757798163f 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlDataContract.cs @@ -22,7 +22,6 @@ internal sealed class XmlDataContract : DataContract private readonly XmlDataContractCriticalHelper _helper; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal XmlDataContract(Type type) : base(new XmlDataContractCriticalHelper(type)) { _helper = (base.Helper as XmlDataContractCriticalHelper)!; @@ -31,7 +30,6 @@ internal XmlDataContract(Type type) : base(new XmlDataContractCriticalHelper(typ public override DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { return _helper.KnownDataContracts; } @@ -88,7 +86,6 @@ internal bool IsTopLevelElementNullable internal CreateXmlSerializableDelegate CreateXmlSerializableDelegate { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { // We create XmlSerializableDelegate via CodeGen when CodeGen is enabled; @@ -130,7 +127,6 @@ private sealed class XmlDataContractCriticalHelper : DataContract.DataContractCr private XmlSchemaType? _xsdType; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal XmlDataContractCriticalHelper( [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] Type type) : base(type) @@ -178,7 +174,6 @@ internal XmlDataContractCriticalHelper( internal override DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (!_isKnownTypeAttributeChecked && UnderlyingType != null) @@ -259,7 +254,6 @@ internal CreateXmlSerializableDelegate? CreateXmlSerializableDelegate } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal CreateXmlSerializableDelegate GenerateCreateXmlSerializableDelegate() { Type type = this.UnderlyingType; @@ -377,7 +371,6 @@ internal IXmlSerializable ReflectionCreateXmlSerializable(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { if (context == null) @@ -387,7 +380,6 @@ public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, Xml } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) { object? o; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatGeneratorStatics.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatGeneratorStatics.cs index 0f861c09d2c879..6791ee9e45f586 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatGeneratorStatics.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatGeneratorStatics.cs @@ -206,7 +206,6 @@ internal static PropertyInfo NodeTypeProperty internal static ConstructorInfo HashtableCtor { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { if (s_hashtableCtor == null) @@ -760,7 +759,6 @@ internal static MethodInfo GetDefaultValueMethod [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2060:MakeGenericMethod", Justification = "The call to MakeGenericMethod is safe due to the fact that XmlObjectSerializerWriteContext.GetDefaultValue is not annotated.")] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static object? GetDefaultValue(Type type) { return GetDefaultValueMethod.MakeGenericMethod(type).Invoke(null, Array.Empty()); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatReaderGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatReaderGenerator.cs index ed132bfa10589f..50ed91a7377f49 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatReaderGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatReaderGenerator.cs @@ -31,21 +31,18 @@ public XmlFormatReaderGenerator() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public XmlFormatClassReaderDelegate GenerateClassReader(ClassDataContract classContract) { return _helper.GenerateClassReader(classContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public XmlFormatCollectionReaderDelegate GenerateCollectionReader(CollectionDataContract collectionContract) { return _helper.GenerateCollectionReader(collectionContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public XmlFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader(CollectionDataContract collectionContract) { return _helper.GenerateGetOnlyCollectionReader(collectionContract); @@ -68,14 +65,12 @@ private sealed class CriticalHelper private ArgBuilder? _collectionContractArg; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static XmlFormatClassReaderDelegate CreateReflectionXmlClassReader(ClassDataContract classContract) { return new ReflectionXmlClassReader(classContract).ReflectionReadClass; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public XmlFormatClassReaderDelegate GenerateClassReader(ClassDataContract classContract) { if (DataContractSerializer.Option == SerializationOption.ReflectionOnly) @@ -166,14 +161,12 @@ public XmlFormatClassReaderDelegate GenerateClassReader(ClassDataContract classC } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static XmlFormatCollectionReaderDelegate CreateReflectionXmlCollectionReader() { return new ReflectionXmlCollectionReader().ReflectionReadCollection; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public XmlFormatCollectionReaderDelegate GenerateCollectionReader(CollectionDataContract collectionContract) { if (DataContractSerializer.Option == SerializationOption.ReflectionOnly) @@ -191,14 +184,12 @@ public XmlFormatCollectionReaderDelegate GenerateCollectionReader(CollectionData } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static XmlFormatGetOnlyCollectionReaderDelegate CreateReflectionReadGetOnlyCollectionReader() { return new ReflectionXmlCollectionReader().ReflectionReadGetOnlyCollection; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public XmlFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader(CollectionDataContract collectionContract) { if (DataContractSerializer.Option == SerializationOption.ReflectionOnly) @@ -213,7 +204,6 @@ public XmlFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader( } } - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private CodeGenerator GenerateCollectionReaderHelper(CollectionDataContract collectionContract, bool isGetOnlyCollection) { _ilg = new CodeGenerator(); @@ -350,7 +340,6 @@ private bool InvokeFactoryMethod(ClassDataContract classContract, LocalBuilder? } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ReadClass(ClassDataContract classContract) { if (classContract.HasExtensionData) @@ -376,7 +365,6 @@ private void ReadClass(ClassDataContract classContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ReadMembers(ClassDataContract classContract, LocalBuilder? extensionDataLocal) { int memberCount = classContract.MemberNames!.Length; @@ -409,7 +397,6 @@ private void ReadMembers(ClassDataContract classContract, LocalBuilder? extensio } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private int ReadMembers(ClassDataContract classContract, bool[] requiredMembers, Label[] memberLabels, LocalBuilder memberIndexLocal, LocalBuilder? requiredIndexLocal) { Debug.Assert(_objectLocal != null); @@ -498,7 +485,6 @@ private void ReadISerializable(ClassDataContract classContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private LocalBuilder ReadValue(Type type, string name, string ns) { LocalBuilder value = _ilg.DeclareLocal(type, "valueRead"); @@ -606,7 +592,6 @@ private void InternalDeserialize(LocalBuilder value, Type type, string name, str } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WrapNullableObject(LocalBuilder innerValue, LocalBuilder outerValue, int nullables) { Type innerType = innerValue.LocalType, outerType = outerValue.LocalType; @@ -623,7 +608,6 @@ private void WrapNullableObject(LocalBuilder innerValue, LocalBuilder outerValue [MemberNotNull(nameof(_objectLocal))] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ReadCollection(CollectionDataContract collectionContract) { Type type = collectionContract.UnderlyingType; @@ -765,7 +749,6 @@ private void ReadCollection(CollectionDataContract collectionContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void ReadGetOnlyCollection(CollectionDataContract collectionContract) { Type type = collectionContract.UnderlyingType; @@ -829,7 +812,6 @@ private void ReadGetOnlyCollection(CollectionDataContract collectionContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private bool TryReadPrimitiveArray(Type type, Type itemType, LocalBuilder size) { Debug.Assert(_objectLocal != null); @@ -880,7 +862,6 @@ private bool TryReadPrimitiveArray(Type type, Type itemType, LocalBuilder size) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private LocalBuilder ReadCollectionItem(CollectionDataContract collectionContract, Type itemType, string itemName, string itemNs) { if (collectionContract.Kind == CollectionKind.Dictionary || collectionContract.Kind == CollectionKind.GenericDictionary) @@ -903,7 +884,6 @@ private LocalBuilder ReadCollectionItem(CollectionDataContract collectionContrac } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void StoreCollectionValue(LocalBuilder collection, LocalBuilder value, CollectionDataContract collectionContract) { Debug.Assert(collectionContract.AddMethod != null); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatWriterGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatWriterGenerator.cs index af674cb274d862..b510882ffec99d 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatWriterGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatWriterGenerator.cs @@ -28,14 +28,12 @@ public XmlFormatWriterGenerator() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal XmlFormatClassWriterDelegate GenerateClassWriter(ClassDataContract classContract) { return _helper.GenerateClassWriter(classContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal XmlFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionDataContract collectionContract) { return _helper.GenerateCollectionWriter(collectionContract); @@ -62,14 +60,12 @@ private sealed class CriticalHelper private int _childElementIndex; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static XmlFormatClassWriterDelegate CreateReflectionXmlFormatClassWriterDelegate() { return new ReflectionXmlFormatWriter().ReflectionWriteClass; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal XmlFormatClassWriterDelegate GenerateClassWriter(ClassDataContract classContract) { if (DataContractSerializer.Option == SerializationOption.ReflectionOnly) @@ -102,14 +98,12 @@ internal XmlFormatClassWriterDelegate GenerateClassWriter(ClassDataContract clas } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static XmlFormatCollectionWriterDelegate CreateReflectionXmlFormatCollectionWriterDelegate() { return ReflectionXmlFormatWriter.ReflectionWriteCollection; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal XmlFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionDataContract collectionContract) { if (DataContractSerializer.Option == SerializationOption.ReflectionOnly) @@ -142,7 +136,6 @@ internal XmlFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionDa } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void InitArgs(Type objType) { _xmlWriterArg = _ilg.GetArg(0); @@ -209,7 +202,6 @@ private void InvokeOnSerialized(ClassDataContract classContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WriteClass(ClassDataContract classContract) { InvokeOnSerializing(classContract); @@ -263,7 +255,6 @@ private void WriteClass(ClassDataContract classContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private int WriteMembers(ClassDataContract classContract, LocalBuilder? extensionDataLocal, ClassDataContract derivedMostClassContract) { int memberCount = (classContract.BaseContract == null) ? 0 : @@ -348,7 +339,6 @@ private LocalBuilder LoadMemberValue(DataMember member) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WriteCollection(CollectionDataContract collectionContract) { LocalBuilder itemNamespace = _ilg.DeclareLocal(typeof(XmlDictionaryString), "itemNamespace"); @@ -515,7 +505,6 @@ private void WriteCollection(CollectionDataContract collectionContract) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private bool TryWritePrimitive(Type type, LocalBuilder? value, MemberInfo? memberInfo, LocalBuilder? arrayItemIndex, LocalBuilder ns, LocalBuilder? name, int nameIndex) { PrimitiveDataContract? primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(type); @@ -563,7 +552,6 @@ private bool TryWritePrimitive(Type type, LocalBuilder? value, MemberInfo? membe } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private bool TryWritePrimitiveArray(Type type, Type itemType, LocalBuilder value, LocalBuilder itemName, LocalBuilder itemNamespace) { PrimitiveDataContract? primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(itemType); @@ -610,7 +598,6 @@ private bool TryWritePrimitiveArray(Type type, Type itemType, LocalBuilder value } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WriteValue(LocalBuilder memberValue, bool writeXsiType) { Type memberType = memberValue.LocalType; @@ -695,7 +682,6 @@ private void InternalSerialize(MethodInfo methodInfo, LocalBuilder memberValue, [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private LocalBuilder UnwrapNullableObject(LocalBuilder memberValue)// Leaves !HasValue on stack { Type memberType = memberValue.LocalType; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializer.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializer.cs index 2311de112f5f6e..8ba00fd7041759 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializer.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializer.cs @@ -17,17 +17,13 @@ namespace System.Runtime.Serialization public abstract class XmlObjectSerializer { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public abstract void WriteStartObject(XmlDictionaryWriter writer, object? graph); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public abstract void WriteObjectContent(XmlDictionaryWriter writer, object? graph); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public abstract void WriteEndObject(XmlDictionaryWriter writer); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual void WriteObject(Stream stream, object? graph) { ArgumentNullException.ThrowIfNull(stream); @@ -38,7 +34,6 @@ public virtual void WriteObject(Stream stream, object? graph) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual void WriteObject(XmlWriter writer, object? graph) { ArgumentNullException.ThrowIfNull(writer); @@ -47,7 +42,6 @@ public virtual void WriteObject(XmlWriter writer, object? graph) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual void WriteStartObject(XmlWriter writer, object? graph) { ArgumentNullException.ThrowIfNull(writer); @@ -56,7 +50,6 @@ public virtual void WriteStartObject(XmlWriter writer, object? graph) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual void WriteObjectContent(XmlWriter writer, object? graph) { ArgumentNullException.ThrowIfNull(writer); @@ -65,7 +58,6 @@ public virtual void WriteObjectContent(XmlWriter writer, object? graph) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual void WriteEndObject(XmlWriter writer) { ArgumentNullException.ThrowIfNull(writer); @@ -74,21 +66,18 @@ public virtual void WriteEndObject(XmlWriter writer) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual void WriteObject(XmlDictionaryWriter writer, object? graph) { WriteObjectHandleExceptions(new XmlWriterDelegator(writer), graph); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void WriteObjectHandleExceptions(XmlWriterDelegator writer, object? graph) { WriteObjectHandleExceptions(writer, graph, null); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void WriteObjectHandleExceptions(XmlWriterDelegator writer, object? graph, DataContractResolver? dataContractResolver) { ArgumentNullException.ThrowIfNull(writer); @@ -110,7 +99,6 @@ internal void WriteObjectHandleExceptions(XmlWriterDelegator writer, object? gra internal virtual DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { return null; @@ -118,7 +106,6 @@ internal virtual DataContractDictionary? KnownDataContracts } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void InternalWriteObject(XmlWriterDelegator writer, object? graph) { WriteStartObject(writer.Writer, graph); @@ -127,28 +114,24 @@ internal virtual void InternalWriteObject(XmlWriterDelegator writer, object? gra } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void InternalWriteObject(XmlWriterDelegator writer, object? graph, DataContractResolver? dataContractResolver) { InternalWriteObject(writer, graph); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void InternalWriteStartObject(XmlWriterDelegator writer, object? graph) { DiagnosticUtility.DebugAssert("XmlObjectSerializer.InternalWriteStartObject should never get called"); throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException()); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void InternalWriteObjectContent(XmlWriterDelegator writer, object? graph) { DiagnosticUtility.DebugAssert("XmlObjectSerializer.InternalWriteObjectContent should never get called"); throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException()); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void InternalWriteEndObject(XmlWriterDelegator writer) { DiagnosticUtility.DebugAssert("XmlObjectSerializer.InternalWriteEndObject should never get called"); @@ -156,7 +139,6 @@ internal virtual void InternalWriteEndObject(XmlWriterDelegator writer) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void WriteStartObjectHandleExceptions(XmlWriterDelegator writer, object? graph) { ArgumentNullException.ThrowIfNull(writer); @@ -176,7 +158,6 @@ internal void WriteStartObjectHandleExceptions(XmlWriterDelegator writer, object } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void WriteObjectContentHandleExceptions(XmlWriterDelegator writer, object? graph) { ArgumentNullException.ThrowIfNull(writer); @@ -198,7 +179,6 @@ internal void WriteObjectContentHandleExceptions(XmlWriterDelegator writer, obje } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void WriteEndObjectHandleExceptions(XmlWriterDelegator writer) { ArgumentNullException.ThrowIfNull(writer); @@ -264,7 +244,6 @@ internal static bool IsContractDeclared(DataContract contract, DataContract decl } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual object? ReadObject(Stream stream) { ArgumentNullException.ThrowIfNull(stream); @@ -273,7 +252,6 @@ internal static bool IsContractDeclared(DataContract contract, DataContract decl } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual object? ReadObject(XmlReader reader) { ArgumentNullException.ThrowIfNull(reader); @@ -282,14 +260,12 @@ internal static bool IsContractDeclared(DataContract contract, DataContract decl } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual object? ReadObject(XmlDictionaryReader reader) { return ReadObjectHandleExceptions(new XmlReaderDelegator(reader), true /*verifyObjectName*/); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual object? ReadObject(XmlReader reader, bool verifyObjectName) { ArgumentNullException.ThrowIfNull(reader); @@ -298,11 +274,9 @@ internal static bool IsContractDeclared(DataContract contract, DataContract decl } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public abstract object? ReadObject(XmlDictionaryReader reader, bool verifyObjectName); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public virtual bool IsStartObject(XmlReader reader) { ArgumentNullException.ThrowIfNull(reader); @@ -311,25 +285,21 @@ public virtual bool IsStartObject(XmlReader reader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public abstract bool IsStartObject(XmlDictionaryReader reader); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual object? InternalReadObject(XmlReaderDelegator reader, bool verifyObjectName) { return ReadObject(reader.UnderlyingReader, verifyObjectName); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual object? InternalReadObject(XmlReaderDelegator reader, bool verifyObjectName, DataContractResolver? dataContractResolver) { return InternalReadObject(reader, verifyObjectName); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual bool InternalIsStartObject(XmlReaderDelegator reader) { DiagnosticUtility.DebugAssert("XmlObjectSerializer.InternalIsStartObject should never get called"); @@ -337,14 +307,12 @@ internal virtual bool InternalIsStartObject(XmlReaderDelegator reader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal object? ReadObjectHandleExceptions(XmlReaderDelegator reader, bool verifyObjectName) { return ReadObjectHandleExceptions(reader, verifyObjectName, null); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal object? ReadObjectHandleExceptions(XmlReaderDelegator reader, bool verifyObjectName, DataContractResolver? dataContractResolver) { ArgumentNullException.ThrowIfNull(reader); @@ -364,7 +332,6 @@ internal virtual bool InternalIsStartObject(XmlReaderDelegator reader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal bool IsStartObjectHandleExceptions(XmlReaderDelegator reader) { ArgumentNullException.ThrowIfNull(reader); @@ -394,7 +361,6 @@ internal static bool IsStartElement(XmlReaderDelegator reader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static bool IsRootElement(XmlReaderDelegator reader, DataContract contract, XmlDictionaryString? name, XmlDictionaryString? ns) { reader.MoveToElement(); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerContext.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerContext.cs index 5c4fb25fac5b9b..66ce7c6f8f8c06 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerContext.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerContext.cs @@ -99,14 +99,12 @@ protected DataContractResolver? DataContractResolver _knownTypeResolver ??= new KnownTypeDataContractResolver(this); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal DataContract GetDataContract(Type type) { return GetDataContract(type.TypeHandle, type); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type? type) { if (IsGetOnlyCollection) @@ -120,7 +118,6 @@ internal virtual DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual DataContract GetDataContractSkipValidation(int typeId, RuntimeTypeHandle typeHandle, Type? type) { if (IsGetOnlyCollection) @@ -134,7 +131,6 @@ internal virtual DataContract GetDataContractSkipValidation(int typeId, RuntimeT } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual DataContract GetDataContract(int id, RuntimeTypeHandle typeHandle) { if (IsGetOnlyCollection) @@ -148,7 +144,6 @@ internal virtual DataContract GetDataContract(int id, RuntimeTypeHandle typeHand } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void CheckIfTypeSerializable(Type memberType, bool isMemberTypeSerializable) { if (!isMemberTypeSerializable) @@ -156,7 +151,6 @@ internal virtual void CheckIfTypeSerializable(Type memberType, bool isMemberType } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual Type GetSurrogatedType(Type type) { return type; @@ -164,7 +158,6 @@ internal virtual Type GetSurrogatedType(Type type) internal virtual DataContractDictionary? SerializerKnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] get { // This field must be initialized during construction by serializers using data contracts. @@ -178,7 +171,6 @@ internal virtual DataContractDictionary? SerializerKnownDataContracts } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private DataContract? GetDataContractFromSerializerKnownTypes(XmlQualifiedName qname) { DataContractDictionary? serializerKnownDataContracts = this.SerializerKnownDataContracts; @@ -189,7 +181,6 @@ internal virtual DataContractDictionary? SerializerKnownDataContracts } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static DataContractDictionary? GetDataContractsForKnownTypes(IList knownTypeList) { if (knownTypeList == null) return null; @@ -207,7 +198,6 @@ internal virtual DataContractDictionary? SerializerKnownDataContracts } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal bool IsKnownType(DataContract dataContract, DataContractDictionary? knownDataContracts, Type? declaredType) { bool knownTypesAddedInCurrentScope = false; @@ -227,7 +217,6 @@ internal bool IsKnownType(DataContract dataContract, DataContractDictionary? kno } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal bool IsKnownType(DataContract dataContract, Type? declaredType) { DataContract? knownContract = ResolveDataContractFromKnownTypes(dataContract.StableName.Name, dataContract.StableName.Namespace, null /*memberTypeContract*/, declaredType); @@ -235,7 +224,6 @@ internal bool IsKnownType(DataContract dataContract, Type? declaredType) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal Type? ResolveNameFromKnownTypes(XmlQualifiedName typeName) { DataContract? dataContract = ResolveDataContractFromKnownTypes(typeName); @@ -243,14 +231,12 @@ internal bool IsKnownType(DataContract dataContract, Type? declaredType) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private DataContract? ResolveDataContractFromKnownTypes(XmlQualifiedName typeName) => PrimitiveDataContract.GetPrimitiveDataContract(typeName.Name, typeName.Namespace) ?? scopedKnownTypes.GetDataContract(typeName) ?? GetDataContractFromSerializerKnownTypes(typeName); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected DataContract? ResolveDataContractFromKnownTypes(string typeName, string? typeNs, DataContract? memberTypeContract, Type? declaredType) { XmlQualifiedName qname = new XmlQualifiedName(typeName, typeNs); @@ -284,7 +270,6 @@ internal bool IsKnownType(DataContract dataContract, Type? declaredType) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected virtual DataContract? ResolveDataContractFromRootDataContract(XmlQualifiedName typeQName) { CollectionDataContract? collectionContract = rootTypeDataContract as CollectionDataContract; @@ -301,7 +286,6 @@ internal bool IsKnownType(DataContract dataContract, Type? declaredType) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void PushKnownTypes(DataContract dc) { if (dc != null && dc.KnownDataContracts != null) @@ -311,7 +295,6 @@ internal void PushKnownTypes(DataContract dc) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void PopKnownTypes(DataContract dc) { if (dc != null && dc.KnownDataContracts != null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContext.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContext.cs index 0a4a686e3d7efa..09fb6e269353de 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContext.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContext.cs @@ -82,7 +82,6 @@ internal XmlObjectSerializerReadContext(DataContractSerializer serializer, DataC } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual object? InternalDeserialize(XmlReaderDelegator xmlReader, int id, RuntimeTypeHandle declaredTypeHandle, string name, string ns) { DataContract dataContract = GetDataContract(id, declaredTypeHandle); @@ -90,7 +89,6 @@ internal XmlObjectSerializerReadContext(DataContractSerializer serializer, DataC } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual object? InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, string name, string ns) { DataContract dataContract = GetDataContract(declaredType); @@ -98,7 +96,6 @@ internal XmlObjectSerializerReadContext(DataContractSerializer serializer, DataC } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual object? InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract? dataContract, string? name, string? ns) { dataContract ??= GetDataContract(declaredType); @@ -131,7 +128,6 @@ protected bool TryHandleNullOrRef(XmlReaderDelegator reader, Type declaredType, } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected object? InternalDeserialize(XmlReaderDelegator reader, string? name, string? ns, Type declaredType, ref DataContract dataContract) { object? retObj = null; @@ -212,7 +208,6 @@ internal static bool MoveToNextElement(XmlReaderDelegator xmlReader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal int GetMemberIndex(XmlReaderDelegator xmlReader, XmlDictionaryString[] memberNames, XmlDictionaryString[] memberNamespaces, int memberIndex, ExtensionDataObject? extensionData) { for (int i = memberIndex + 1; i < memberNames.Length; i++) @@ -225,7 +220,6 @@ internal int GetMemberIndex(XmlReaderDelegator xmlReader, XmlDictionaryString[] } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal int GetMemberIndexWithRequiredMembers(XmlReaderDelegator xmlReader, XmlDictionaryString[] memberNames, XmlDictionaryString[] memberNamespaces, int memberIndex, int requiredIndex, ExtensionDataObject? extensionData) { for (int i = memberIndex + 1; i < memberNames.Length; i++) @@ -257,7 +251,6 @@ internal static void ThrowRequiredMemberMissingException(XmlReaderDelegator xmlR } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected void HandleMemberNotFound(XmlReaderDelegator xmlReader, ExtensionDataObject? extensionData, int memberIndex) { xmlReader.MoveToContent(); @@ -271,7 +264,6 @@ protected void HandleMemberNotFound(XmlReaderDelegator xmlReader, ExtensionDataO } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void HandleUnknownElement(XmlReaderDelegator xmlReader, ExtensionDataObject extensionData, int memberIndex) { extensionData.Members ??= new List(); @@ -285,7 +277,6 @@ internal void SkipUnknownElement(XmlReaderDelegator xmlReader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal string ReadIfNullOrRef(XmlReaderDelegator xmlReader, Type memberType, bool isMemberTypeSerializable) { Debug.Assert(attributes != null); @@ -462,7 +453,6 @@ internal void CheckEndOfArray(XmlReaderDelegator xmlReader, int arraySize, XmlDi } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal object? ReadIXmlSerializable(XmlReaderDelegator xmlReader, XmlDataContract xmlDataContract, bool isMemberType) { _xmlSerializableReader ??= new XmlSerializableReader(); @@ -470,14 +460,12 @@ internal void CheckEndOfArray(XmlReaderDelegator xmlReader, int arraySize, XmlDi } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static object? ReadRootIXmlSerializable(XmlReaderDelegator xmlReader, XmlDataContract xmlDataContract, bool isMemberType) { return ReadIXmlSerializable(new XmlSerializableReader(), xmlReader, xmlDataContract, isMemberType); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal static object? ReadIXmlSerializable(XmlSerializableReader xmlSerializableReader, XmlReaderDelegator xmlReader, XmlDataContract xmlDataContract, bool isMemberType) { object? obj = null; @@ -509,7 +497,6 @@ internal void CheckEndOfArray(XmlReaderDelegator xmlReader, int arraySize, XmlDi } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public SerializationInfo ReadSerializationInfo(XmlReaderDelegator xmlReader, Type type) { var serInfo = new SerializationInfo(type, XmlObjectSerializer.FormatterConverter); @@ -554,7 +541,6 @@ public SerializationInfo ReadSerializationInfo(XmlReaderDelegator xmlReader, Typ } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected virtual DataContract? ResolveDataContractFromTypeName() { Debug.Assert(attributes != null); @@ -563,7 +549,6 @@ public SerializationInfo ReadSerializationInfo(XmlReaderDelegator xmlReader, Typ } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private ExtensionDataMember ReadExtensionDataMember(XmlReaderDelegator xmlReader, int memberIndex) { var member = new ExtensionDataMember(xmlReader.LocalName, xmlReader.NamespaceURI) @@ -576,7 +561,6 @@ private ExtensionDataMember ReadExtensionDataMember(XmlReaderDelegator xmlReader } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public IDataNode? ReadExtensionDataValue(XmlReaderDelegator xmlReader) { ReadAttributes(xmlReader); @@ -675,7 +659,6 @@ protected virtual void StartReadExtensionDataValue(XmlReaderDelegator xmlReader) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private IDataNode ReadExtensionDataValue(XmlReaderDelegator xmlReader, string? dataContractName, string? dataContractNamespace) { Debug.Assert(attributes != null); @@ -750,7 +733,6 @@ private IDataNode ReadUnknownPrimitiveData(XmlReaderDelegator xmlReader, Type ty } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private ClassDataNode ReadUnknownClassData(XmlReaderDelegator xmlReader, string? dataContractName, string? dataContractNamespace) { var dataNode = new ClassDataNode(); @@ -771,7 +753,6 @@ private ClassDataNode ReadUnknownClassData(XmlReaderDelegator xmlReader, string? } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private CollectionDataNode ReadUnknownCollectionData(XmlReaderDelegator xmlReader, string? dataContractName, string? dataContractNamespace) { Debug.Assert(attributes != null); @@ -828,7 +809,6 @@ private CollectionDataNode ReadUnknownCollectionData(XmlReaderDelegator xmlReade } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private ISerializableDataNode ReadUnknownISerializableData(XmlReaderDelegator xmlReader, string? dataContractName, string? dataContractNamespace) { Debug.Assert(attributes != null); @@ -907,7 +887,6 @@ private IDataNode ReadUnknownXmlData(XmlReaderDelegator xmlReader, string? dataC // items be unqualified. If the XML only contains elements (no attributes or other nodes) is recognized as a // class/class hierarchy. Otherwise it is deserialized as XML. [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private IDataNode ReadAndResolveUnknownXmlData(XmlReaderDelegator xmlReader, IDictionary? namespaces, string? dataContractName, string? dataContractNamespace) { @@ -1054,7 +1033,6 @@ internal static Exception CreateSerializationException(string message) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected virtual object? ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader) { return dataContract.ReadXmlValue(reader, this); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContextComplex.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContextComplex.cs index 8867f198f62234..53f4021549ec10 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContextComplex.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContextComplex.cs @@ -32,7 +32,6 @@ internal override SerializationMode Mode } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override object? InternalDeserialize(XmlReaderDelegator xmlReader, int declaredTypeID, RuntimeTypeHandle declaredTypeHandle, string name, string ns) { if (_mode == SerializationMode.SharedContract) @@ -49,7 +48,6 @@ internal override SerializationMode Mode } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override object? InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, string name, string ns) { if (_mode == SerializationMode.SharedContract) @@ -66,7 +64,6 @@ internal override SerializationMode Mode } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override object? InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract? dataContract, string? name, string? ns) { if (_mode == SerializationMode.SharedContract) @@ -83,7 +80,6 @@ internal override SerializationMode Mode } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private object? InternalDeserializeInSharedTypeMode(XmlReaderDelegator xmlReader, int declaredTypeID, Type declaredType, string? name, string? ns) { Debug.Assert(attributes != null); @@ -126,7 +122,6 @@ internal override SerializationMode Mode } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private object? InternalDeserializeWithSurrogate(XmlReaderDelegator xmlReader, Type declaredType, DataContract? surrogateDataContract, string? name, string? ns) { Debug.Assert(_serializationSurrogateProvider != null); @@ -154,7 +149,6 @@ private static Type ResolveDataContractTypeInSharedTypeMode(string assemblyName, } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private DataContract? ResolveDataContractInSharedTypeMode(string assemblyName, string typeName, out Assembly assembly, out Type type) { type = ResolveDataContractTypeInSharedTypeMode(assemblyName, typeName, out assembly); @@ -167,7 +161,6 @@ private static Type ResolveDataContractTypeInSharedTypeMode(string assemblyName, } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected override DataContract? ResolveDataContractFromTypeName() { Debug.Assert(attributes != null); @@ -187,7 +180,6 @@ private static Type ResolveDataContractTypeInSharedTypeMode(string assemblyName, } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void CheckIfTypeSerializable(Type memberType, bool isMemberTypeSerializable) { if (_serializationSurrogateProvider != null) @@ -204,7 +196,6 @@ internal override void CheckIfTypeSerializable(Type memberType, bool isMemberTyp } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override Type GetSurrogatedType(Type type) { if (_serializationSurrogateProvider == null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContext.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContext.cs index 727d65a5238389..00abd388dc0920 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContext.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContext.cs @@ -80,7 +80,6 @@ internal void ResetIsGetOnlyCollection() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void InternalSerializeReference(XmlWriterDelegator xmlWriter, object obj, bool isDeclaredType, bool writeXsiType, int declaredTypeID, RuntimeTypeHandle declaredTypeHandle) { if (!OnHandleReference(xmlWriter, obj, true /*canContainCyclicReference*/)) @@ -89,7 +88,6 @@ internal void InternalSerializeReference(XmlWriterDelegator xmlWriter, object ob } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void InternalSerialize(XmlWriterDelegator xmlWriter, object obj, bool isDeclaredType, bool writeXsiType, int declaredTypeID, RuntimeTypeHandle declaredTypeHandle) { if (writeXsiType) @@ -120,7 +118,6 @@ internal virtual void InternalSerialize(XmlWriterDelegator xmlWriter, object obj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle declaredTypeHandle) { if (OnHandleIsReference(xmlWriter, dataContract, obj)) @@ -138,7 +135,6 @@ internal void SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelega } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void SerializeWithXsiTypeAtTopLevel(DataContract dataContract, XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle originalDeclaredTypeHandle, Type graphType) { Debug.Assert(rootTypeDataContract != null); @@ -161,7 +157,6 @@ internal virtual void SerializeWithXsiTypeAtTopLevel(DataContract dataContract, } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected virtual void SerializeWithXsiType(XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle objectTypeHandle, Type? objectType, int declaredTypeID, RuntimeTypeHandle declaredTypeHandle, Type declaredType) { bool verifyKnownType = false; @@ -226,7 +221,6 @@ internal bool OnHandleIsReference(XmlWriterDelegator xmlWriter, DataContract con } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected void SerializeAndVerifyType(DataContract dataContract, XmlWriterDelegator xmlWriter, object obj, bool verifyKnownType, RuntimeTypeHandle declaredTypeHandle, Type declaredType) { bool knownTypesAddedInCurrentScope = false; @@ -287,7 +281,6 @@ internal virtual void WriteString(XmlWriterDelegator xmlWriter, string value) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void WriteString(XmlWriterDelegator xmlWriter, string? value, XmlDictionaryString name, XmlDictionaryString? ns) { if (value == null) @@ -306,7 +299,6 @@ internal virtual void WriteBase64(XmlWriterDelegator xmlWriter, byte[] value) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void WriteBase64(XmlWriterDelegator xmlWriter, byte[] value, XmlDictionaryString name, XmlDictionaryString ns) { if (value == null) @@ -325,7 +317,6 @@ internal virtual void WriteUri(XmlWriterDelegator xmlWriter, Uri value) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void WriteUri(XmlWriterDelegator xmlWriter, Uri value, XmlDictionaryString name, XmlDictionaryString ns) { if (value == null) @@ -344,7 +335,6 @@ internal virtual void WriteQName(XmlWriterDelegator xmlWriter, XmlQualifiedName } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void WriteQName(XmlWriterDelegator xmlWriter, XmlQualifiedName? value, XmlDictionaryString name, XmlDictionaryString? ns) { if (value == null) @@ -395,7 +385,6 @@ internal virtual void OnEndHandleReference(XmlWriterDelegator xmlWriter, object } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void WriteNull(XmlWriterDelegator xmlWriter, Type memberType, bool isMemberTypeSerializable) { CheckIfTypeSerializable(memberType, isMemberTypeSerializable); @@ -403,7 +392,6 @@ internal void WriteNull(XmlWriterDelegator xmlWriter, Type memberType, bool isMe } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void WriteNull(XmlWriterDelegator xmlWriter, Type memberType, bool isMemberTypeSerializable, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteStartElement(name, ns); @@ -507,7 +495,6 @@ internal static void GetObjectData(ISerializable obj, SerializationInfo serInfo, } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public void WriteISerializable(XmlWriterDelegator xmlWriter, ISerializable obj) { Type objType = obj.GetType(); @@ -524,7 +511,6 @@ public void WriteISerializable(XmlWriterDelegator xmlWriter, ISerializable obj) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void WriteSerializationInfo(XmlWriterDelegator xmlWriter, Type objType, SerializationInfo serInfo) { if (DataContract.GetClrTypeFullName(objType) != serInfo.FullTypeName) @@ -566,7 +552,6 @@ internal void WriteSerializationInfo(XmlWriterDelegator xmlWriter, Type objType, } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected virtual void WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle declaredTypeHandle) { dataContract.WriteXmlValue(xmlWriter, obj, this); @@ -578,7 +563,6 @@ protected virtual void WriteNull(XmlWriterDelegator xmlWriter) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WriteResolvedTypeInfo(XmlWriterDelegator writer, Type objectType, Type declaredType) { XmlDictionaryString? typeName, typeNamespace; @@ -589,7 +573,6 @@ private void WriteResolvedTypeInfo(XmlWriterDelegator writer, Type objectType, T } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private bool ResolveType(Type objectType, Type declaredType, [NotNullWhen(true)] out XmlDictionaryString? typeName, [NotNullWhen(true)] out XmlDictionaryString? typeNamespace) { Debug.Assert(DataContractResolver != null); @@ -617,7 +600,6 @@ private bool ResolveType(Type objectType, Type declaredType, [NotNullWhen(true)] } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] protected virtual bool WriteTypeInfo(XmlWriterDelegator writer, DataContract contract, DataContract declaredContract) { if (!XmlObjectSerializer.IsContractDeclared(contract, declaredContract)) @@ -647,7 +629,6 @@ protected virtual void WriteTypeInfo(XmlWriterDelegator writer, XmlDictionaryStr } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public void WriteExtensionData(XmlWriterDelegator xmlWriter, ExtensionDataObject? extensionData, int memberIndex) { if (IgnoreExtensionDataObject || extensionData == null) @@ -668,7 +649,6 @@ public void WriteExtensionData(XmlWriterDelegator xmlWriter, ExtensionDataObject } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WriteExtensionDataMember(XmlWriterDelegator xmlWriter, ExtensionDataMember member) { xmlWriter.WriteStartElement(member.Name, member.Namespace); @@ -678,7 +658,6 @@ private void WriteExtensionDataMember(XmlWriterDelegator xmlWriter, ExtensionDat } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal virtual void WriteExtensionDataTypeInfo(XmlWriterDelegator xmlWriter, IDataNode dataNode) { if (dataNode.DataContractName != null) @@ -688,7 +667,6 @@ internal virtual void WriteExtensionDataTypeInfo(XmlWriterDelegator xmlWriter, I } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal void WriteExtensionDataValue(XmlWriterDelegator xmlWriter, IDataNode? dataNode) { IncrementItemCount(1); @@ -731,7 +709,6 @@ internal void WriteExtensionDataValue(XmlWriterDelegator xmlWriter, IDataNode? d } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal bool TryWriteDeserializedExtensionData(XmlWriterDelegator xmlWriter, IDataNode dataNode) { object? o = dataNode.Value; @@ -744,7 +721,6 @@ internal bool TryWriteDeserializedExtensionData(XmlWriterDelegator xmlWriter, ID } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WriteExtensionClassData(XmlWriterDelegator xmlWriter, ClassDataNode dataNode) { if (!TryWriteDeserializedExtensionData(xmlWriter, dataNode)) @@ -763,7 +739,6 @@ private void WriteExtensionClassData(XmlWriterDelegator xmlWriter, ClassDataNode } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WriteExtensionCollectionData(XmlWriterDelegator xmlWriter, CollectionDataNode dataNode) { if (!TryWriteDeserializedExtensionData(xmlWriter, dataNode)) @@ -786,7 +761,6 @@ private void WriteExtensionCollectionData(XmlWriterDelegator xmlWriter, Collecti } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WriteExtensionISerializableData(XmlWriterDelegator xmlWriter, ISerializableDataNode dataNode) { if (!TryWriteDeserializedExtensionData(xmlWriter, dataNode)) @@ -811,7 +785,6 @@ private void WriteExtensionISerializableData(XmlWriterDelegator xmlWriter, ISeri } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void WriteExtensionXmlData(XmlWriterDelegator xmlWriter, XmlDataNode dataNode) { if (!TryWriteDeserializedExtensionData(xmlWriter, dataNode)) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContextComplex.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContextComplex.cs index 687013b8e8dd57..b1abf32a8ee00f 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContextComplex.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContextComplex.cs @@ -62,7 +62,6 @@ internal override void WriteString(XmlWriterDelegator xmlWriter, string value) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void WriteString(XmlWriterDelegator xmlWriter, string? value, XmlDictionaryString name, XmlDictionaryString? ns) { if (value == null) @@ -83,7 +82,6 @@ internal override void WriteBase64(XmlWriterDelegator xmlWriter, byte[] value) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void WriteBase64(XmlWriterDelegator xmlWriter, byte[] value, XmlDictionaryString name, XmlDictionaryString ns) { if (value == null) @@ -104,7 +102,6 @@ internal override void WriteUri(XmlWriterDelegator xmlWriter, Uri value) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void WriteUri(XmlWriterDelegator xmlWriter, Uri value, XmlDictionaryString name, XmlDictionaryString ns) { if (value == null) @@ -125,7 +122,6 @@ internal override void WriteQName(XmlWriterDelegator xmlWriter, XmlQualifiedName } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void WriteQName(XmlWriterDelegator xmlWriter, XmlQualifiedName? value, XmlDictionaryString name, XmlDictionaryString? ns) { if (value == null) @@ -143,7 +139,6 @@ internal override void WriteQName(XmlWriterDelegator xmlWriter, XmlQualifiedName } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void InternalSerialize(XmlWriterDelegator xmlWriter, object obj, bool isDeclaredType, bool writeXsiType, int declaredTypeID, RuntimeTypeHandle declaredTypeHandle) { if (_serializationSurrogateProvider == null) @@ -182,7 +177,6 @@ internal override void OnEndHandleReference(XmlWriterDelegator xmlWriter, object } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override void CheckIfTypeSerializable(Type memberType, bool isMemberTypeSerializable) { if (_serializationSurrogateProvider != null) @@ -199,7 +193,6 @@ internal override void CheckIfTypeSerializable(Type memberType, bool isMemberTyp } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] internal override Type GetSurrogatedType(Type type) { if (_serializationSurrogateProvider == null) @@ -223,7 +216,6 @@ internal override Type GetSurrogatedType(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void InternalSerializeWithSurrogate(XmlWriterDelegator xmlWriter, object obj, bool isDeclaredType, bool writeXsiType, int declaredTypeID, RuntimeTypeHandle declaredTypeHandle) { RuntimeTypeHandle objTypeHandle = isDeclaredType ? declaredTypeHandle : obj.GetType().TypeHandle; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XsdDataContractExporter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XsdDataContractExporter.cs index de8b5373ed0655..ad147ba1af8fd7 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XsdDataContractExporter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XsdDataContractExporter.cs @@ -62,7 +62,6 @@ private static DataContractSet DataContractSet } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public void Export(ICollection assemblies) { ArgumentNullException.ThrowIfNull(assemblies); @@ -90,7 +89,6 @@ public void Export(ICollection assemblies) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public void Export(ICollection types) { ArgumentNullException.ThrowIfNull(types); @@ -115,7 +113,6 @@ public void Export(ICollection types) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public void Export(Type type) { ArgumentNullException.ThrowIfNull(type); @@ -134,7 +131,6 @@ public void Export(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public XmlQualifiedName GetSchemaTypeName(Type type) { ArgumentNullException.ThrowIfNull(type); @@ -149,7 +145,6 @@ public XmlQualifiedName GetSchemaTypeName(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public XmlSchemaType? GetSchemaType(Type type) { ArgumentNullException.ThrowIfNull(type); @@ -164,7 +159,6 @@ public XmlQualifiedName GetSchemaTypeName(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public XmlQualifiedName? GetRootElementName(Type type) { ArgumentNullException.ThrowIfNull(type); @@ -193,7 +187,6 @@ private static Type GetSurrogatedType(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static void CheckAndAddType(Type type) { type = GetSurrogatedType(type); @@ -202,14 +195,12 @@ private static void CheckAndAddType(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private static void AddType(Type type) { DataContractSet.Add(type); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void Export() { AddKnownTypes(); @@ -218,7 +209,6 @@ private void Export() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] private void AddKnownTypes() { if (Options != null) @@ -239,7 +229,6 @@ private void AddKnownTypes() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public bool CanExport(ICollection assemblies) { ArgumentNullException.ThrowIfNull(assemblies); @@ -272,7 +261,6 @@ public bool CanExport(ICollection assemblies) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public bool CanExport(ICollection types) { ArgumentNullException.ThrowIfNull(types); @@ -302,7 +290,6 @@ public bool CanExport(ICollection types) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - [RequiresDynamicCode(DataContract.SerializerAOTWarning)] public bool CanExport(Type type) { ArgumentNullException.ThrowIfNull(type); diff --git a/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.cs b/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.cs index ff1b2a25d75d2a..014c498f6eace0 100644 --- a/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.cs +++ b/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.cs @@ -26,25 +26,18 @@ namespace System.Runtime.Serialization.Json public sealed partial class DataContractJsonSerializer : System.Runtime.Serialization.XmlObjectSerializer { [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public DataContractJsonSerializer(System.Type type) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public DataContractJsonSerializer(System.Type type, System.Collections.Generic.IEnumerable? knownTypes) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public DataContractJsonSerializer(System.Type type, System.Runtime.Serialization.Json.DataContractJsonSerializerSettings? settings) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public DataContractJsonSerializer(System.Type type, string? rootName) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public DataContractJsonSerializer(System.Type type, string? rootName, System.Collections.Generic.IEnumerable? knownTypes) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public DataContractJsonSerializer(System.Type type, System.Xml.XmlDictionaryString? rootName) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public DataContractJsonSerializer(System.Type type, System.Xml.XmlDictionaryString? rootName, System.Collections.Generic.IEnumerable? knownTypes) { } public System.Runtime.Serialization.DateTimeFormat? DateTimeFormat { get { throw null; } } public System.Runtime.Serialization.EmitTypeInformation EmitTypeInformation { get { throw null; } } @@ -54,52 +47,36 @@ public DataContractJsonSerializer(System.Type type, System.Xml.XmlDictionaryStri public bool SerializeReadOnlyTypes { get { throw null; } } public bool UseSimpleDictionaryFormat { get { throw null; } } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override bool IsStartObject(System.Xml.XmlDictionaryReader reader) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override bool IsStartObject(System.Xml.XmlReader reader) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override object? ReadObject(System.IO.Stream stream) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override object? ReadObject(System.Xml.XmlDictionaryReader reader) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override object? ReadObject(System.Xml.XmlDictionaryReader reader, bool verifyObjectName) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override object? ReadObject(System.Xml.XmlReader reader) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override object? ReadObject(System.Xml.XmlReader reader, bool verifyObjectName) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteEndObject(System.Xml.XmlDictionaryWriter writer) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteEndObject(System.Xml.XmlWriter writer) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteObject(System.IO.Stream stream, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteObject(System.Xml.XmlDictionaryWriter writer, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteObject(System.Xml.XmlWriter writer, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteObjectContent(System.Xml.XmlDictionaryWriter writer, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteObjectContent(System.Xml.XmlWriter writer, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteStartObject(System.Xml.XmlDictionaryWriter writer, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteStartObject(System.Xml.XmlWriter writer, object? graph) { } } public partial class DataContractJsonSerializerSettings diff --git a/src/libraries/System.Runtime.Serialization.Xml/ref/System.Runtime.Serialization.Xml.cs b/src/libraries/System.Runtime.Serialization.Xml/ref/System.Runtime.Serialization.Xml.cs index 94cc20e75fb6ec..6a3d406d810269 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/ref/System.Runtime.Serialization.Xml.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/ref/System.Runtime.Serialization.Xml.cs @@ -10,10 +10,8 @@ public abstract partial class DataContractResolver { protected DataContractResolver() { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public abstract System.Type? ResolveName(string typeName, string? typeNamespace, System.Type? declaredType, System.Runtime.Serialization.DataContractResolver knownTypeResolver); [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public abstract bool TryResolveType(System.Type type, System.Type? declaredType, System.Runtime.Serialization.DataContractResolver knownTypeResolver, out System.Xml.XmlDictionaryString? typeName, out System.Xml.XmlDictionaryString? typeNamespace); } public sealed partial class DataContractSerializer : System.Runtime.Serialization.XmlObjectSerializer @@ -32,46 +30,32 @@ public DataContractSerializer(System.Type type, System.Xml.XmlDictionaryString r public bool PreserveObjectReferences { get { throw null; } } public bool SerializeReadOnlyTypes { get { throw null; } } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override bool IsStartObject(System.Xml.XmlDictionaryReader reader) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override bool IsStartObject(System.Xml.XmlReader reader) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override object? ReadObject(System.Xml.XmlDictionaryReader reader, bool verifyObjectName) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public object? ReadObject(System.Xml.XmlDictionaryReader reader, bool verifyObjectName, System.Runtime.Serialization.DataContractResolver? dataContractResolver) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override object? ReadObject(System.Xml.XmlReader reader) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override object? ReadObject(System.Xml.XmlReader reader, bool verifyObjectName) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteEndObject(System.Xml.XmlDictionaryWriter writer) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteEndObject(System.Xml.XmlWriter writer) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public void WriteObject(System.Xml.XmlDictionaryWriter writer, object? graph, System.Runtime.Serialization.DataContractResolver? dataContractResolver) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteObject(System.Xml.XmlWriter writer, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteObjectContent(System.Xml.XmlDictionaryWriter writer, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteObjectContent(System.Xml.XmlWriter writer, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteStartObject(System.Xml.XmlDictionaryWriter writer, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public override void WriteStartObject(System.Xml.XmlWriter writer, object? graph) { } } public static partial class DataContractSerializerExtensions @@ -108,52 +92,36 @@ public abstract partial class XmlObjectSerializer { protected XmlObjectSerializer() { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public abstract bool IsStartObject(System.Xml.XmlDictionaryReader reader); [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public virtual bool IsStartObject(System.Xml.XmlReader reader) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public virtual object? ReadObject(System.IO.Stream stream) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public virtual object? ReadObject(System.Xml.XmlDictionaryReader reader) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public abstract object? ReadObject(System.Xml.XmlDictionaryReader reader, bool verifyObjectName); [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public virtual object? ReadObject(System.Xml.XmlReader reader) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public virtual object? ReadObject(System.Xml.XmlReader reader, bool verifyObjectName) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public abstract void WriteEndObject(System.Xml.XmlDictionaryWriter writer); [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public virtual void WriteEndObject(System.Xml.XmlWriter writer) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public virtual void WriteObject(System.IO.Stream stream, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public virtual void WriteObject(System.Xml.XmlDictionaryWriter writer, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public virtual void WriteObject(System.Xml.XmlWriter writer, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public abstract void WriteObjectContent(System.Xml.XmlDictionaryWriter writer, object? graph); [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public virtual void WriteObjectContent(System.Xml.XmlWriter writer, object? graph) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public abstract void WriteStartObject(System.Xml.XmlDictionaryWriter writer, object? graph); [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public virtual void WriteStartObject(System.Xml.XmlWriter writer, object? graph) { } } public static partial class XmlSerializableServices @@ -165,10 +133,8 @@ public static void WriteNodes(System.Xml.XmlWriter xmlWriter, System.Xml.XmlNode public static partial class XPathQueryGenerator { [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public static string CreateFromDataContractSerializer(System.Type type, System.Reflection.MemberInfo[] pathToMember, System.Text.StringBuilder? rootElementXpath, out System.Xml.XmlNamespaceManager namespaces) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public static string CreateFromDataContractSerializer(System.Type type, System.Reflection.MemberInfo[] pathToMember, out System.Xml.XmlNamespaceManager namespaces) { throw null; } } public partial class XsdDataContractExporter @@ -178,31 +144,22 @@ public XsdDataContractExporter(System.Xml.Schema.XmlSchemaSet? schemas) { } public System.Runtime.Serialization.ExportOptions? Options { get { throw null; } set { } } public System.Xml.Schema.XmlSchemaSet Schemas { get { throw null; } } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public bool CanExport(System.Collections.Generic.ICollection assemblies) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public bool CanExport(System.Collections.Generic.ICollection types) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public bool CanExport(System.Type type) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public void Export(System.Collections.Generic.ICollection assemblies) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public void Export(System.Collections.Generic.ICollection types) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public void Export(System.Type type) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public System.Xml.XmlQualifiedName? GetRootElementName(System.Type type) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public System.Xml.Schema.XmlSchemaType? GetSchemaType(System.Type type) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed.")] public System.Xml.XmlQualifiedName GetSchemaTypeName(System.Type type) { throw null; } } } From 802bbbdb3232912c6343db7a31e50ed63a59dc46 Mon Sep 17 00:00:00 2001 From: Steve Molloy Date: Sat, 13 Aug 2022 00:07:25 -0700 Subject: [PATCH 52/56] DCS alignment to 4.8 with schema support (#71752) * Fix Private.Xml solution broken by https://github.com/dotnet/runtime/pull/70706. * Align DCS with 4.8 implementation - minus schema import/export. * External DCS Schema support groundwork. Before DC-tree work and other public APIs. * Added public APIs, but not the DataContract tree yet. * All APIs in. * Cleanup. * Addressed some nits and PR feedback on API. * API nit, more PR feedback. * Fix HasRoot hiding issue. * Add basic schema tests. Fix DataMember bug. * Add /// comments for new schema project. * Fixing bad format in xml comment. * Skip import test on Wasm. * Obsolete old half-coded schema exporter. * Add obsolete attribute in ref project as well. * Removing obsolete for now, since it appears Syndication depends on old class. * Fix the DCJS ref project. * API cleanup. * Project file cleanup * Fix mono issue with reflection access to non-public private fields. * Addressing feedback from API review. * Port NetFx Export/Import test suites to Serializer test projects. * Fix bugs found by newly ported tests. * Account for different newline sizes on different platforms. * Skip flaky test on wasm for now. * Non-draft PR feedback. * Change DC.Members API. Drop KVP shennanigans. * More PR feedback. Co-authored-by: Viktor Hofer --- .../src/Resources/Strings.resx | 242 +- ...m.Private.DataContractSerialization.csproj | 100 +- .../Runtime/Serialization/AccessorBuilder.cs | 1 + .../Serialization/ClassDataContract.cs | 848 +++---- .../Runtime/Serialization/CodeGenerator.cs | 292 +-- .../Serialization/CollectionDataContract.cs | 525 ++--- .../Runtime/Serialization/DataContract.cs | 1007 ++++---- .../Serialization/DataContractResolver.cs | 1 + .../Serialization/DataContractSerializer.cs | 62 +- .../Runtime/Serialization/DataContractSet.cs | 569 ++++- .../DataContractSurrogateCaller.cs | 28 +- .../Runtime/Serialization/DataMember.cs | 190 +- .../Serialization/DateTimeOffsetAdapter.cs | 2 +- .../Serialization/DiagnosticUtility.cs | 46 + .../Runtime/Serialization/EnumDataContract.cs | 199 +- .../Runtime/Serialization/ExportOptions.cs | 28 +- .../Serialization/ExtensionDataObject.cs | 2 +- .../Serialization/ExtensionDataReader.cs | 328 ++- .../GenericParameterDataContract.cs | 26 +- .../System/Runtime/Serialization/Globals.cs | 42 +- .../Json/DataContractJsonSerializer.cs | 30 +- .../DataContractJsonSerializerExtensions.cs | 18 + .../Json/JsonByteArrayDataContract.cs | 5 +- .../Json/JsonClassDataContract.cs | 9 +- .../Json/JsonCollectionDataContract.cs | 1 + .../Serialization/Json/JsonDataContract.cs | 24 +- .../Json/JsonEnumDataContract.cs | 1 + .../Json/JsonFormatGeneratorStatics.cs | 1 + .../Json/JsonFormatReaderGenerator.cs | 35 +- .../Json/JsonFormatWriterGenerator.cs | 34 +- .../Json/JsonObjectDataContract.cs | 7 +- .../Json/JsonQNameDataContract.cs | 5 +- .../Json/JsonStringDataContract.cs | 5 +- .../Serialization/Json/JsonUriDataContract.cs | 1 + .../Serialization/Json/JsonXmlDataContract.cs | 13 +- .../Json/ReflectionJsonFormatReader.cs | 1 + .../Json/ReflectionJsonFormatWriter.cs | 10 +- ...lObjectSerializerReadContextComplexJson.cs | 7 +- ...ObjectSerializerWriteContextComplexJson.cs | 17 +- .../KnownTypeDataContractResolver.cs | 4 +- .../Serialization/PrimitiveDataContract.cs | 263 +-- .../Serialization/ReflectionClassWriter.cs | 14 +- .../Serialization/ReflectionFeature.cs | 10 - .../Runtime/Serialization/ReflectionReader.cs | 20 +- .../ReflectionXmlFormatReader.cs | 17 +- .../ReflectionXmlFormatWriter.cs | 13 +- .../Runtime/Serialization/SchemaExporter.cs | 109 +- .../Runtime/Serialization/SchemaHelper.cs | 111 +- .../Runtime/Serialization/SchemaImporter.cs | 1459 ++++++++++++ .../Runtime/Serialization/ScopedKnownTypes.cs | 4 +- .../Serialization/SerializationMode.cs | 10 - .../Serialization/SpecialTypeDataContract.cs | 2 +- .../Serialization/SurrogateDataContract.cs | 18 +- .../System/Runtime/Serialization/TypeCode.cs | 80 - .../Serialization/XPathQueryGenerator.cs | 13 +- .../Runtime/Serialization/XmlDataContract.cs | 160 +- .../XmlFormatGeneratorStatics.cs | 63 +- .../Serialization/XmlFormatReaderGenerator.cs | 86 +- .../Serialization/XmlFormatWriterGenerator.cs | 94 +- .../Serialization/XmlObjectSerializer.cs | 10 +- .../XmlObjectSerializerContext.cs | 53 +- .../XmlObjectSerializerReadContext.cs | 59 +- .../XmlObjectSerializerReadContextComplex.cs | 130 +- .../XmlObjectSerializerWriteContext.cs | 33 +- .../XmlObjectSerializerWriteContextComplex.cs | 16 +- .../Serialization/XmlReaderDelegator.cs | 37 +- .../Serialization/XmlSerializableReader.cs | 39 +- .../Serialization/XmlSerializableWriter.cs | 1 + .../Serialization/XmlWriterDelegator.cs | 13 +- .../Serialization/XsdDataContractExporter.cs | 165 +- .../src/System/Xml/ArrayHelper.cs | 5 + .../src/System/Xml/EncodingStreamWrapper.cs | 8 +- .../src/System/Xml/StringHandle.cs | 21 +- .../src/System/Xml/ValueHandle.cs | 2 + .../src/System/Xml/XmlBaseReader.cs | 108 +- .../src/System/Xml/XmlBaseWriter.cs | 209 +- .../src/System/Xml/XmlBinaryReader.cs | 25 +- .../src/System/Xml/XmlBinaryWriter.cs | 132 +- .../src/System/Xml/XmlBufferReader.cs | 53 +- .../src/System/Xml/XmlCanonicalWriter.cs | 3 +- .../src/System/Xml/XmlConverter.cs | 31 +- .../src/System/Xml/XmlDictionaryReader.cs | 38 +- .../src/System/Xml/XmlDictionaryWriter.cs | 5 +- .../src/System/Xml/XmlExceptionHelper.cs | 18 +- .../src/System/Xml/XmlSigningNodeWriter.cs | 2 +- .../src/System/Xml/XmlStreamNodeWriter.cs | 54 +- .../src/System/Xml/XmlUTF8TextWriter.cs | 21 +- .../ref/System.Runtime.Serialization.Json.cs | 2 + .../System.Runtime.Serialization.Json.csproj | 8 +- ...System.Runtime.Serialization.Primitives.cs | 7 + ...em.Runtime.Serialization.Primitives.csproj | 1 + .../ISerializationSurrogateProvider.cs | 29 + .../ISerializationSurrogateProvider2.cs | 48 + .../Directory.Build.props | 8 + .../System.Runtime.Serialization.Schema.sln | 51 + .../System.Runtime.Serialization.Schema.cs | 56 + ...System.Runtime.Serialization.Schema.csproj | 13 + .../src/Resources/Strings.resx | 181 ++ ...System.Runtime.Serialization.Schema.csproj | 28 + .../Serialization/Schema/CodeExporter.cs | 2033 +++++++++++++++++ .../Schema/ContractCodeDomInfo.cs | 64 + .../Serialization/Schema/DiagnosticUtility.cs | 100 + .../ISerializationCodeDomSurrogateProvider.cs | 24 + .../Serialization/Schema/ImportGlobals.cs | 156 ++ .../Serialization/Schema/ImportOptions.cs | 72 + .../Schema/SchemaImportHelper.cs | 127 + .../Schema/XsdDataContractImporter.cs | 406 ++++ ....Runtime.Serialization.Schema.Tests.csproj | 19 + .../Serialization/Schema/DataContracts.cs | 160 ++ .../Schema/Import/ImportOptionsTests.cs | 160 ++ .../Schema/Import/ImporterTests.cs | 417 ++++ .../Schema/Import/SurrogateTests.cs | 220 ++ .../Serialization/Schema/RoundTripTest.cs | 349 +++ .../Serialization/Schema/SchemaUtils.cs | 370 +++ .../ref/System.Runtime.Serialization.Xml.cs | 85 + .../tests/DataContractSerializer.cs | 149 +- ...tem.Runtime.Serialization.Xml.Tests.csproj | 21 +- .../ExportOptionsTests.cs | 58 + .../ExporterApiTests.cs | 336 +++ .../ExporterTypesTests.cs | 843 +++++++ .../SchemaUtils.cs | 54 + .../SerializationTypes/ArrayTypes.cs | 102 + .../SerializationTypes/ConfigTypes.cs | 52 + .../ConflictingNameTypes.cs | 72 + .../DataContractSurrogate.cs | 98 + .../SerializationTypes/DataContractTypes.cs | 607 +++++ .../SerializationTypes/Enums.cs | 138 ++ .../SerializationTypes/GenericTypes.cs | 97 + .../SerializationTypes/ISerializableTypes.cs | 113 + .../IXmlSerializableTypes.cs | 399 ++++ .../SerializationTypes/LegacyTypes.cs | 42 + .../SerializationTypes/NullableTypes.cs | 66 + .../SerializationTypes/PartialTrust.cs | 110 + .../SerializationTypes/SerializableTypes.cs | 67 + .../SerializationTypes.csproj | 21 + .../SurrogateTests.cs | 525 +++++ .../XsdDataContractExporterTests/Types.cs | 138 ++ .../ref/System.Xml.ReaderWriter.csproj | 8 +- src/libraries/oob.proj | 2 +- src/libraries/tests.proj | 1 + 140 files changed, 15028 insertions(+), 2888 deletions(-) create mode 100644 src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializerExtensions.cs delete mode 100644 src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionFeature.cs create mode 100644 src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaImporter.cs delete mode 100644 src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SerializationMode.cs delete mode 100644 src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/TypeCode.cs create mode 100644 src/libraries/System.Runtime.Serialization.Primitives/src/System/Runtime/Serialization/ISerializationSurrogateProvider2.cs create mode 100644 src/libraries/System.Runtime.Serialization.Schema/Directory.Build.props create mode 100644 src/libraries/System.Runtime.Serialization.Schema/System.Runtime.Serialization.Schema.sln create mode 100644 src/libraries/System.Runtime.Serialization.Schema/ref/System.Runtime.Serialization.Schema.cs create mode 100644 src/libraries/System.Runtime.Serialization.Schema/ref/System.Runtime.Serialization.Schema.csproj create mode 100644 src/libraries/System.Runtime.Serialization.Schema/src/Resources/Strings.resx create mode 100644 src/libraries/System.Runtime.Serialization.Schema/src/System.Runtime.Serialization.Schema.csproj create mode 100644 src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/CodeExporter.cs create mode 100644 src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ContractCodeDomInfo.cs create mode 100644 src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/DiagnosticUtility.cs create mode 100644 src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ISerializationCodeDomSurrogateProvider.cs create mode 100644 src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ImportGlobals.cs create mode 100644 src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ImportOptions.cs create mode 100644 src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/SchemaImportHelper.cs create mode 100644 src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/XsdDataContractImporter.cs create mode 100644 src/libraries/System.Runtime.Serialization.Schema/tests/System.Runtime.Serialization.Schema.Tests.csproj create mode 100644 src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/DataContracts.cs create mode 100644 src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/Import/ImportOptionsTests.cs create mode 100644 src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/Import/ImporterTests.cs create mode 100644 src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/Import/SurrogateTests.cs create mode 100644 src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/RoundTripTest.cs create mode 100644 src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/SchemaUtils.cs create mode 100644 src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/ExportOptionsTests.cs create mode 100644 src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/ExporterApiTests.cs create mode 100644 src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/ExporterTypesTests.cs create mode 100644 src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SchemaUtils.cs create mode 100644 src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ArrayTypes.cs create mode 100644 src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ConfigTypes.cs create mode 100644 src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ConflictingNameTypes.cs create mode 100644 src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/DataContractSurrogate.cs create mode 100644 src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/DataContractTypes.cs create mode 100644 src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/Enums.cs create mode 100644 src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/GenericTypes.cs create mode 100644 src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ISerializableTypes.cs create mode 100644 src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/IXmlSerializableTypes.cs create mode 100644 src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/LegacyTypes.cs create mode 100644 src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/NullableTypes.cs create mode 100644 src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/PartialTrust.cs create mode 100644 src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/SerializableTypes.cs create mode 100644 src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/SerializationTypes.csproj create mode 100644 src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SurrogateTests.cs create mode 100644 src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/Types.cs diff --git a/src/libraries/System.Private.DataContractSerialization/src/Resources/Strings.resx b/src/libraries/System.Private.DataContractSerialization/src/Resources/Strings.resx index f772c16d39646f..4cee84fe5e6392 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/Resources/Strings.resx +++ b/src/libraries/System.Private.DataContractSerialization/src/Resources/Strings.resx @@ -312,6 +312,12 @@ Member '{0}' in type '{1}' cannot have DataMemberAttribute attribute Name set to null or empty string. + + '{0}' is an invalid data node when deserializing extension data. + + + Type '{0}' in namespace '{1}' is not a valid base type for enum '{2}' in namespace '{3}'. + '{0}' in type '{1}' cannot have EnumMemberAttribute attribute Value set to null or empty string. @@ -624,6 +630,9 @@ Unrecognized Byte Order Mark. + + The maximum nametable character count quota ({0}) has been exceeded while reading XML data. The nametable is a data structure used to store strings encountered during XML processing - long XML documents with non-repeating element names, attribute names and attribute values may trigger this quota. This quota may be increased by changing the MaxNameTableCharCount property on the XmlDictionaryReaderQuotas object used when creating the XML reader. + Base64 encoded data expected. Found {0}. @@ -786,6 +795,9 @@ The maximum array length quota ({0}) has been exceeded while reading XML data. This quota may be increased by changing the MaxArrayLength property on the XmlDictionaryReaderQuotas object used when creating the XML reader. + + The maximum array length quota ({0}) or the maximum items in object graph quota has been exceeded while reading XML data. These quotas may be increased by changing the MaxArrayLength property on XmlDictionaryReaderQuotas or the MaxItemsInObjectGraph setting. + The 'maximum bytes per Read operation' quota ({0}) has been exceeded while reading XML data. Long element start tags (consisting of the element name, attribute names and attribute values) may trigger this quota. This quota may be increased by changing the MaxBytesPerRead property on the XmlDictionaryReaderQuotas object used when creating the XML reader. @@ -1059,6 +1071,9 @@ DataContract for type '{0}' cannot be added to DataContractSet since type '{1}' with the same data contract name '{2}' in namespace '{3}' is already present and the contracts are not equivalent. + + DataContract name '{0}' from namespace '{1}' does not match the generic name '{2}' from namespace '{3}'. + Type '{0}' cannot be exported as a schema type because it is an open generic type. You can only export a generic type if all its generic parameter types are actual types. @@ -1155,10 +1170,235 @@ '{0}' must be greater than 0. + + {0} The class cannot be deserialized. + Collection type '{0}' cannot be deserialized since it Unknown Type for null value - \ No newline at end of file + + List of referenced types contains more than one type with same data contract name. Need to exclude all but one of the following types. Only matching types can be valid references: {0} + + + List of referenced types contains more than one type with data contract name '{0}' in namespace '{1}'. Need to exclude all but one of the following types. Only matching types can be valid references: {2} + + + List of referenced collection types contains more than one type with same data contract name. Include only one of the following types. Only matching types can be valid references: {0} + + + List of referenced collection types contains more than one type with data contract name '{0}' in namespace '{1}'. Include only one of the following types. Only matching types can be valid references: {2} + + + ReferencedCollectionTypes specified via ImportOptions must contain valid types. Cannot contain null. + + + (matching) + + + (not matching) + + + ReferencedTypes specified via ImportOptions must contain valid types. Cannot contain null. + + + Using surrogates with get-only collection properties is not supported. Consider removing the surrogate associated with '{0}' or adding a setter to '{1}.{2}'. + + + The element cannot have 'abstract' set to 'true'. + + + The type cannot have 'abstract' set to 'true'. + + + Invalid '{0}' annotation in type '{1}' from namespace '{2}'. Attribute '{3}' not present. + + + Anonymous type in element '{0}' from namespace '{1}' is not supported. + + + 'anyAttribute' is not supported. + + + Form for element '{0}' must be qualified. + + + Array type '{0}' in namespace '{1}' cannot be imported. {2} + + + One of its base types, '{0}' from namespace '{1}' is not ISerializable. + + + A unique name cannot be computed for '{0}' because there are already Int32.MaxValue types of with the same name. + + + The type contains two attributes with the same name '{0}'. Multiple attributes with the same name in one type are not supported. + + + The type contains two elements with the same name '{0}'. Multiple elements with the same name in one type are not supported because members marked with DataMemberAttribute attribute must have unique names. + + + Cannot import invalid schemas. Compilation on the XmlSchemaSet failed. + + + Cannot import type for null XmlQualifiedName specified via parameter. + + + Cannot import null XmlSchema contained in XmlSchemaSet specified via parameter. + + + An internal error has occurred. Could not load serialization schema. Consider providing schema with namespace '{0}'. + + + Default value on element '{0}' is not supported. + + + It is not ISerializable but its base type '{0}' in namespace '{1}' is ISerializable. + + + 'maxOccurs' on element '{0}' must be 1. + + + 'minOccurs' on element '{0}' must be 0 or 1. + + + Ref to element '{0}' in '{1}' namespace is not supported. + + + Enumeration facets without 'value' are not supported. + + + Anonymous type with <list> cannot be used to create Flags enumeration because it is not a valid enum type. + + + Simple type list must contain an anonymous type specifying enumeration facets. + + + Facets other than enumeration facets are not supported. + + + Anonymous type with <restriction> cannot be used to create Flags enumeration because it is not a valid enum type. + + + Enum type '{0}' in namespace '{1}' cannot be imported. {2} + + + Anonymous type with <union>. cannot be used to create Flags enumeration because it is not a valid enum type. + + + Fixed value on element '{0}' is not supported. + + + Form on element '{0}' must be qualified. + + + Annotation for generic type '{0}' did not have attribute '{1}'. + + + Nested level on annotation elements '{0}' from namespace '{1}' for generic type '{2}' must be in increasing order. + + + Annotation element '{0}' from namespace '{1}' for generic type '{2}' has an invalid value '{3}' for attribute '{4}'. Expecting value to be of type '{5}'. + + + Annotation for generic type '{2}' has an invalid element '{0}' from namespace '{1}'. + + + Annotation '{0}' from namespace '{1}' has an invalid element '{2}' from namespace '{3}'. Expecting text. + + + Type '{0}' in namespace '{1}' cannot be used as the base type of a data contract type, because it itself does not have a data contract. Consider marking type '{0}' with the DataContractAttribute attribute. + + + Annotation for element {0} in type {1} from namespace {2} specifies EmitDefaultValue as 'true'. This requires the element to be either nillable or the element's type must be a value type. + + + Cannot import type '{0}' in namespace '{1}' as its base type because derived type is ISerializable but the base type is not ISerializable. + + + It is an invalid dictionary type. Element '{0}' must reference a complex type containing a sequence with two required elements. Either fix the schema or remove the IsDictionary annotation. + + + It is an invalid dictionary type since element '{0}' references a type from a different namespace '{1}'. Either fix the schema or remove the IsDictionary annotation. + + + '{0}' is an invalid value for IsDictionary annotation. {1} + + + '{0}' is an invalid value for IsValueType annotation. {1} + + + Its root sequence contains more than one particle. + + + Derived ISerializable types cannot contain any particles. + + + It does not contain root sequence with a wildcard element <any>. + + + It does not reference attribute '{0}' from namespace '{1}'. + + + ISerializable type '{0}' in namespace '{1}' cannot be imported. '{2}' + + + 'maxOccurs' on the wildcard element must be '{0}'. + + + 'minOccurs' on the wildcard element must be '{0}'. + + + Namespace on the wildcard element must be '{0}'. + + + ProcessContents on the wildcard element must be '{0}'. + + + Complex type with mixed content is not supported. + + + The root sequence must contain only local elements. Group ref, choice, any and nested sequences are not supported. + + + Redefine is not supported. + + + The root particle must be a sequence. + + + 'maxOccurs' on the root sequence must be 1. + + + 'minOccurs' on the root sequence must be 1. + + + Complex types with simple content extension are not supported. + + + Simple type restriction must specify a base type. + + + Simple types with <union> content are not supported. + + + Invalid type specified. Type with name '{0}' not found in schema with namespace '{1}'. + + + Substitution group on element '{0}' is not supported. + + + The global element found in the schema with same name references a different type '{0}' in namespace '{1}'. Data contract types must have the same name as their root element name. Consider removing the global element or changing its type. + + + Type '{0}' in namespace '{1}' cannot be imported. {2} + + + {0} Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer. + + + Attributes must be optional and from namespace '{0}'. + + diff --git a/src/libraries/System.Private.DataContractSerialization/src/System.Private.DataContractSerialization.csproj b/src/libraries/System.Private.DataContractSerialization/src/System.Private.DataContractSerialization.csproj index f8df7d3599de5e..f81bacc216dca3 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System.Private.DataContractSerialization.csproj +++ b/src/libraries/System.Private.DataContractSerialization/src/System.Private.DataContractSerialization.csproj @@ -1,4 +1,4 @@ - + $(NetCoreAppCurrent) $(NoWarn);1634;1691;649 @@ -15,11 +15,11 @@ - - + + + + @@ -29,44 +29,56 @@ + + + + - + + + + + + + + + + + + + - + + + + - - - - - - - - - - + + + @@ -81,6 +93,7 @@ + @@ -89,6 +102,7 @@ + @@ -96,59 +110,42 @@ + + + + + + + + - + + + + + - - - - - - + + - - - - - - - - + + - - - - - - - - - - - - - - - - - - - @@ -168,6 +165,7 @@ + diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/AccessorBuilder.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/AccessorBuilder.cs index e6af588406a6df..23f1ea8aeeb707 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/AccessorBuilder.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/AccessorBuilder.cs @@ -8,6 +8,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Serialization; +using System.Runtime.Serialization.DataContracts; using System.Text; using System.Threading.Tasks; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ClassDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ClassDataContract.cs index 1200e4ee07097c..a395b82d6f99fa 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ClassDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ClassDataContract.cs @@ -2,44 +2,35 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.IO; using System.Linq; using System.Reflection; -using System.Runtime.CompilerServices; using System.Security; using System.Threading; using System.Xml; -using DataContractDictionary = System.Collections.Generic.Dictionary; -namespace System.Runtime.Serialization +using DataContractDictionary = System.Collections.Generic.Dictionary; + +namespace System.Runtime.Serialization.DataContracts { internal sealed class ClassDataContract : DataContract { + internal const string ContractTypeString = nameof(ClassDataContract); + public override string? ContractType => ContractTypeString; + public XmlDictionaryString[]? ContractNamespaces; public XmlDictionaryString[]? MemberNames; - public XmlDictionaryString[]? MemberNamespaces; + internal XmlDictionaryString[]? MemberNamespaces; private XmlDictionaryString?[]? _childElementNamespaces; private ClassDataContractCriticalHelper _helper; - private bool _isScriptObject; - - internal const DynamicallyAccessedMemberTypes DataContractPreserveMemberTypes = - DynamicallyAccessedMemberTypes.PublicMethods | - DynamicallyAccessedMemberTypes.NonPublicMethods | - DynamicallyAccessedMemberTypes.PublicConstructors | - DynamicallyAccessedMemberTypes.NonPublicConstructors | - DynamicallyAccessedMemberTypes.PublicFields | - DynamicallyAccessedMemberTypes.PublicProperties; - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal ClassDataContract(Type type) : base(new ClassDataContractCriticalHelper(type)) { @@ -56,23 +47,32 @@ private ClassDataContract(Type type, XmlDictionaryString ns, string[] memberName private void InitClassDataContract() { _helper = (base.Helper as ClassDataContractCriticalHelper)!; - this.ContractNamespaces = _helper.ContractNamespaces; - this.MemberNames = _helper.MemberNames; - this.MemberNamespaces = _helper.MemberNamespaces; - _isScriptObject = _helper.IsScriptObject; + ContractNamespaces = _helper.ContractNamespaces; + MemberNames = _helper.MemberNames; + MemberNamespaces = _helper.MemberNamespaces; + } + + public override DataContract? BaseContract + { + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + get => BaseClassContract; } - internal ClassDataContract? BaseContract + internal ClassDataContract? BaseClassContract { - get { return _helper.BaseContract; } + get => _helper.BaseClassContract; + set => _helper.BaseClassContract = value; } internal List? Members { - get { return _helper.Members; } + get => _helper.Members; + set => _helper.Members = value; } - public XmlDictionaryString?[]? ChildElementNamespaces + public override ReadOnlyCollection DataMembers => (Members == null) ? DataContract.s_emptyDataMemberList : Members.AsReadOnly(); + + internal XmlDictionaryString?[]? ChildElementNamespaces { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] get @@ -95,95 +95,39 @@ public XmlDictionaryString?[]? ChildElementNamespaces } return _childElementNamespaces; } - set - { - _childElementNamespaces = value; - } } - internal MethodInfo? OnSerializing - { - get - { return _helper.OnSerializing; } - } + internal MethodInfo? OnSerializing => _helper.OnSerializing; - internal MethodInfo? OnSerialized - { - get - { return _helper.OnSerialized; } - } + internal MethodInfo? OnSerialized => _helper.OnSerialized; - internal MethodInfo? OnDeserializing - { - get - { return _helper.OnDeserializing; } - } + internal MethodInfo? OnDeserializing => _helper.OnDeserializing; - internal MethodInfo? OnDeserialized - { - get - { return _helper.OnDeserialized; } - } + internal MethodInfo? OnDeserialized => _helper.OnDeserialized; - internal MethodInfo? ExtensionDataSetMethod - { - get { return _helper.ExtensionDataSetMethod; } - } + internal MethodInfo? ExtensionDataSetMethod => _helper.ExtensionDataSetMethod; public override DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - get - { return _helper.KnownDataContracts; } + get => _helper.KnownDataContracts; + internal set => _helper.KnownDataContracts = value; } public override bool IsISerializable { - get { return _helper.IsISerializable; } - set { _helper.IsISerializable = value; } - } - - internal bool IsNonAttributedType - { - get - { return _helper.IsNonAttributedType; } - } - - public bool HasExtensionData - { - get - { return _helper.HasExtensionData; } - set { _helper.HasExtensionData = value; } + get => _helper.IsISerializable; + internal set => _helper.IsISerializable = value; } - [MemberNotNullWhen(true, nameof(KeyValuePairGenericArguments))] - [MemberNotNullWhen(true, nameof(KeyValuePairAdapterConstructorInfo))] - [MemberNotNullWhen(true, nameof(GetKeyValuePairMethodInfo))] - internal bool IsKeyValuePairAdapter - { - get -#pragma warning disable CS8775 // Member must have a non-null value when exiting in some condition. - { return _helper.IsKeyValuePairAdapter; } -#pragma warning restore CS8775 // Member must have a non-null value when exiting in some condition. - } + internal bool IsNonAttributedType => _helper.IsNonAttributedType; - internal Type[]? KeyValuePairGenericArguments - { - get - { return _helper.KeyValuePairGenericArguments; } - } + internal bool HasExtensionData => _helper.HasExtensionData; - internal ConstructorInfo? KeyValuePairAdapterConstructorInfo - { - get - { return _helper.KeyValuePairAdapterConstructorInfo; } - } + internal string? SerializationExceptionMessage => _helper.SerializationExceptionMessage; + internal string? DeserializationExceptionMessage => _helper.DeserializationExceptionMessage; - internal MethodInfo? GetKeyValuePairMethodInfo - { - get - { return _helper.GetKeyValuePairMethodInfo; } - } + internal bool IsReadOnlyContract => DeserializationExceptionMessage != null; internal ConstructorInfo? GetISerializableConstructor() { @@ -209,7 +153,7 @@ internal MethodInfo? GetKeyValuePairMethodInfo internal bool CreateNewInstanceViaDefaultConstructor([NotNullWhen(true)] out object? obj) { ConstructorInfo? ci = GetNonAttributedTypeConstructor(); - if (ci == null) + if (ci == null || UnderlyingType == Globals.TypeOfSchemaDefinedType) { obj = null; return false; @@ -231,6 +175,7 @@ internal bool CreateNewInstanceViaDefaultConstructor([NotNullWhen(true)] out obj [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private XmlFormatClassWriterDelegate CreateXmlFormatWriterDelegate() { + Debug.Assert(UnderlyingType != Globals.TypeOfSchemaDefinedType); return new XmlFormatWriterGenerator().GenerateClassWriter(this); } @@ -253,14 +198,12 @@ internal XmlFormatClassWriterDelegate XmlFormatWriterDelegate } return _helper.XmlFormatWriterDelegate; } - set - { - } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private XmlFormatClassReaderDelegate CreateXmlFormatReaderDelegate() { + Debug.Assert(UnderlyingType != Globals.TypeOfSchemaDefinedType); return new XmlFormatReaderGenerator().GenerateClassReader(this); } @@ -275,6 +218,10 @@ internal XmlFormatClassReaderDelegate XmlFormatReaderDelegate { if (_helper.XmlFormatReaderDelegate == null) { + if (IsReadOnlyContract) + { + ThrowInvalidDataContractException(DeserializationExceptionMessage, type: null); + } XmlFormatClassReaderDelegate tempDelegate = CreateXmlFormatReaderDelegate(); Interlocked.MemoryBarrier(); _helper.XmlFormatReaderDelegate = tempDelegate; @@ -283,31 +230,17 @@ internal XmlFormatClassReaderDelegate XmlFormatReaderDelegate } return _helper.XmlFormatReaderDelegate; } - set - { - } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal static ClassDataContract CreateClassDataContractForKeyValue(Type type, XmlDictionaryString ns, string[] memberNames) { - ClassDataContract? cdc = (ClassDataContract?)DataContract.GetDataContractFromGeneratedAssembly(type); - if (cdc == null) - { - return new ClassDataContract(type, ns, memberNames); - } - else - { - ClassDataContract cloned = cdc.Clone(); - cloned.UpdateNamespaceAndMembers(type, ns, memberNames); - return cloned; - } + return new ClassDataContract(type, ns, memberNames); } internal static void CheckAndAddMember(List members, DataMember memberContract, Dictionary memberNamesTable) { - DataMember? existingMemberContract; - if (memberNamesTable.TryGetValue(memberContract.Name, out existingMemberContract)) + if (memberNamesTable.TryGetValue(memberContract.Name, out DataMember? existingMemberContract)) { Type declaringType = memberContract.MemberInfo.DeclaringType!; DataContract.ThrowInvalidDataContractException( @@ -329,7 +262,7 @@ internal static void CheckAndAddMember(List members, DataMember memb if (!childType.IsEnum && !Globals.TypeOfIXmlSerializable.IsAssignableFrom(childType) && DataContract.GetBuiltInDataContract(childType) == null && childType != Globals.TypeOfDBNull) { - string ns = DataContract.GetStableName(childType).Namespace; + string ns = DataContract.GetXmlName(childType).Namespace; if (ns.Length > 0 && ns != dataContract.Namespace.Value) return dictionary.Add(ns); } @@ -370,11 +303,9 @@ internal static bool IsNonAttributedTypeValidForSerialization( if (type.IsDefined(Globals.TypeOfCollectionDataContractAttribute, false)) return false; - Type[] interfaceTypes = type.GetInterfaces(); - if (!IsArraySegment(type)) { - foreach (Type interfaceType in interfaceTypes) + foreach (Type interfaceType in type.GetInterfaces()) { if (CollectionDataContract.IsCollectionInterface(interfaceType)) return false; @@ -389,60 +320,15 @@ internal static bool IsNonAttributedTypeValidForSerialization( if (type.IsDefined(Globals.TypeOfDataContractAttribute, false)) return false; - if (type.IsValueType) - { - return type.IsVisible; - } - else - { - return (type.IsVisible && - type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, Type.EmptyTypes) != null); - } - } - private static readonly Dictionary s_knownSerializableTypeInfos = new Dictionary { - { "System.Collections.Generic.KeyValuePair`2", Array.Empty() }, - { "System.Collections.Generic.Queue`1", new[] { "_syncRoot" } }, - { "System.Collections.Generic.Stack`1", new[] {"_syncRoot" } }, - { "System.Collections.ObjectModel.ReadOnlyCollection`1", new[] {"_syncRoot" } }, - { "System.Collections.ObjectModel.ReadOnlyDictionary`2", new[] {"_syncRoot", "_keys", "_values" } }, - { "System.Tuple`1", Array.Empty() }, - { "System.Tuple`2", Array.Empty() }, - { "System.Tuple`3", Array.Empty() }, - { "System.Tuple`4", Array.Empty() }, - { "System.Tuple`5", Array.Empty() }, - { "System.Tuple`6", Array.Empty() }, - { "System.Tuple`7", Array.Empty() }, - { "System.Tuple`8", Array.Empty() }, - { "System.Collections.Queue", new[] {"_syncRoot" } }, - { "System.Collections.Stack", new[] {"_syncRoot" } }, - { "System.Globalization.CultureInfo", Array.Empty() }, - { "System.Version", Array.Empty() }, - }; - - private static string GetGeneralTypeName(Type type) - { - return type.IsGenericType && !type.IsGenericParameter - ? type.GetGenericTypeDefinition().FullName! - : type.FullName!; - } - - internal static bool IsKnownSerializableType(Type type) - { - // Applies to known types that DCS understands how to serialize/deserialize - // - string typeFullName = GetGeneralTypeName(type); - - return s_knownSerializableTypeInfos.ContainsKey(typeFullName); - } + if (type == Globals.TypeOfExtensionDataObject) + return false; - internal static bool IsNonSerializedMember(Type type, string memberName) - { - string typeFullName = GetGeneralTypeName(type); + if (type.IsValueType) + return type.IsVisible; - string[]? members; - return s_knownSerializableTypeInfos.TryGetValue(typeFullName, out members) - && members.Contains(memberName); + return (type.IsVisible && + type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, Type.EmptyTypes) != null); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -452,17 +338,17 @@ internal static bool IsNonSerializedMember(Type type, string memberName) return null; XmlDictionaryString?[]? baseChildElementNamespaces = null; - if (this.BaseContract != null) - baseChildElementNamespaces = this.BaseContract.ChildElementNamespaces; + if (BaseClassContract != null) + baseChildElementNamespaces = BaseClassContract.ChildElementNamespaces; int baseChildElementNamespaceCount = (baseChildElementNamespaces != null) ? baseChildElementNamespaces.Length : 0; XmlDictionaryString?[] childElementNamespaces = new XmlDictionaryString?[Members.Count + baseChildElementNamespaceCount]; if (baseChildElementNamespaceCount > 0) Array.Copy(baseChildElementNamespaces!, childElementNamespaces, baseChildElementNamespaces!.Length); XmlDictionary dictionary = new XmlDictionary(); - for (int i = 0; i < this.Members.Count; i++) + for (int i = 0; i < Members.Count; i++) { - childElementNamespaces[i + baseChildElementNamespaceCount] = GetChildNamespaceToDeclare(this, this.Members[i].MemberType, dictionary); + childElementNamespaces[i + baseChildElementNamespaceCount] = GetChildNamespaceToDeclare(this, Members[i].MemberType, dictionary); } return childElementNamespaces; @@ -474,24 +360,15 @@ private void EnsureMethodsImported() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { Debug.Assert(context != null); - - if (_isScriptObject) - { - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(this.GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); - } XmlFormatWriterDelegate(xmlWriter, obj, context, this); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) { - if (_isScriptObject) - { - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(this.GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); - } xmlReader.Read(); object? o = XmlFormatReaderDelegate(xmlReader, context, MemberNames, MemberNamespaces); xmlReader.ReadEndElement(); @@ -518,15 +395,24 @@ internal bool RequiresMemberAccessForRead(SecurityException? securityException) } return true; } - if (this.BaseContract != null && this.BaseContract.RequiresMemberAccessForRead(securityException)) + if (BaseClassContract != null && BaseClassContract.RequiresMemberAccessForRead(securityException)) return true; - if (ConstructorRequiresMemberAccess(GetNonAttributedTypeConstructor())) + if (ConstructorRequiresMemberAccess(GetISerializableConstructor())) { - if (Globals.TypeOfScriptObject_IsAssignableFrom(UnderlyingType)) + if (securityException != null) { - return true; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new SecurityException(SR.Format( + SR.PartialTrustIXmlSerialzableNoPublicConstructor, + DataContract.GetClrTypeFullName(UnderlyingType)), + securityException)); } + return true; + } + + if (ConstructorRequiresMemberAccess(GetNonAttributedTypeConstructor())) + { if (securityException != null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( @@ -538,7 +424,7 @@ internal bool RequiresMemberAccessForRead(SecurityException? securityException) return true; } - if (MethodRequiresMemberAccess(this.OnDeserializing)) + if (MethodRequiresMemberAccess(OnDeserializing)) { if (securityException != null) { @@ -546,13 +432,13 @@ internal bool RequiresMemberAccessForRead(SecurityException? securityException) new SecurityException(SR.Format( SR.PartialTrustDataContractOnDeserializingNotPublic, DataContract.GetClrTypeFullName(UnderlyingType), - this.OnDeserializing!.Name), + OnDeserializing!.Name), securityException)); } return true; } - if (MethodRequiresMemberAccess(this.OnDeserialized)) + if (MethodRequiresMemberAccess(OnDeserialized)) { if (securityException != null) { @@ -560,27 +446,27 @@ internal bool RequiresMemberAccessForRead(SecurityException? securityException) new SecurityException(SR.Format( SR.PartialTrustDataContractOnDeserializedNotPublic, DataContract.GetClrTypeFullName(UnderlyingType), - this.OnDeserialized!.Name), + OnDeserialized!.Name), securityException)); } return true; } - if (this.Members != null) + if (Members != null) { - for (int i = 0; i < this.Members.Count; i++) + for (int i = 0; i < Members.Count; i++) { - if (this.Members[i].RequiresMemberAccessForSet()) + if (Members[i].RequiresMemberAccessForSet()) { if (securityException != null) { - if (this.Members[i].MemberInfo is FieldInfo) + if (Members[i].MemberInfo is FieldInfo) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityException(SR.Format( SR.PartialTrustDataContractFieldSetNotPublic, DataContract.GetClrTypeFullName(UnderlyingType), - this.Members[i].MemberInfo.Name), + Members[i].MemberInfo.Name), securityException)); } else @@ -589,7 +475,7 @@ internal bool RequiresMemberAccessForRead(SecurityException? securityException) new SecurityException(SR.Format( SR.PartialTrustDataContractPropertySetNotPublic, DataContract.GetClrTypeFullName(UnderlyingType), - this.Members[i].MemberInfo.Name), + Members[i].MemberInfo.Name), securityException)); } } @@ -623,24 +509,24 @@ internal bool RequiresMemberAccessForWrite(SecurityException? securityException) return true; } - if (this.BaseContract != null && this.BaseContract.RequiresMemberAccessForWrite(securityException)) + if (BaseClassContract != null && BaseClassContract.RequiresMemberAccessForWrite(securityException)) return true; - if (MethodRequiresMemberAccess(this.OnSerializing)) + if (MethodRequiresMemberAccess(OnSerializing)) { if (securityException != null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityException(SR.Format( SR.PartialTrustDataContractOnSerializingNotPublic, - DataContract.GetClrTypeFullName(this.UnderlyingType), - this.OnSerializing!.Name), + DataContract.GetClrTypeFullName(UnderlyingType), + OnSerializing!.Name), securityException)); } return true; } - if (MethodRequiresMemberAccess(this.OnSerialized)) + if (MethodRequiresMemberAccess(OnSerialized)) { if (securityException != null) { @@ -648,27 +534,27 @@ internal bool RequiresMemberAccessForWrite(SecurityException? securityException) new SecurityException(SR.Format( SR.PartialTrustDataContractOnSerializedNotPublic, DataContract.GetClrTypeFullName(UnderlyingType), - this.OnSerialized!.Name), + OnSerialized!.Name), securityException)); } return true; } - if (this.Members != null) + if (Members != null) { - for (int i = 0; i < this.Members.Count; i++) + for (int i = 0; i < Members.Count; i++) { - if (this.Members[i].RequiresMemberAccessForGet()) + if (Members[i].RequiresMemberAccessForGet()) { if (securityException != null) { - if (this.Members[i].MemberInfo is FieldInfo) + if (Members[i].MemberInfo is FieldInfo) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityException(SR.Format( SR.PartialTrustDataContractFieldGetNotPublic, DataContract.GetClrTypeFullName(UnderlyingType), - this.Members[i].MemberInfo.Name), + Members[i].MemberInfo.Name), securityException)); } else @@ -677,7 +563,7 @@ internal bool RequiresMemberAccessForWrite(SecurityException? securityException) new SecurityException(SR.Format( SR.PartialTrustDataContractPropertyGetNotPublic, DataContract.GetClrTypeFullName(UnderlyingType), - this.Members[i].MemberInfo.Name), + Members[i].MemberInfo.Name), securityException)); } } @@ -692,8 +578,6 @@ internal bool RequiresMemberAccessForWrite(SecurityException? securityException) private sealed class ClassDataContractCriticalHelper : DataContract.DataContractCriticalHelper { private static Type[]? s_serInfoCtorArgs; - private static readonly MethodInfo s_getKeyValuePairMethod = typeof(KeyValuePairAdapter<,>).GetMethod("GetKeyValuePair", Globals.ScanAllMembers)!; - private static readonly ConstructorInfo s_ctorGenericMethod = typeof(KeyValuePairAdapter<,>).GetConstructor(Globals.ScanAllMembers, new Type[] { typeof(KeyValuePair<,>).MakeGenericType(typeof(KeyValuePairAdapter<,>).GetGenericArguments()) })!; private ClassDataContract? _baseContract; private List? _members; @@ -701,82 +585,65 @@ private sealed class ClassDataContractCriticalHelper : DataContract.DataContract private MethodInfo? _onDeserializing, _onDeserialized; private MethodInfo? _extensionDataSetMethod; private DataContractDictionary? _knownDataContracts; - private bool _isISerializable; + private string? _serializationExceptionMessage; private bool _isKnownTypeAttributeChecked; private bool _isMethodChecked; + /// - /// in serialization/deserialization we base the decision whether to Demand SerializationFormatter permission on this value and hasDataContract + /// in serialization/deserialization we base the decision whether to Demand SerializationFormatter permission on this value and isNonAttributedType /// private bool _isNonAttributedType; /// - /// in serialization/deserialization we base the decision whether to Demand SerializationFormatter permission on this value and isNonAttributedType + /// in serialization/deserialization we base the decision whether to Demand SerializationFormatter permission on this value and hasDataContract /// private bool _hasDataContract; private bool _hasExtensionData; - private readonly bool _isScriptObject; - - private XmlDictionaryString?[]? _childElementNamespaces; - private XmlFormatClassReaderDelegate? _xmlFormatReaderDelegate; - private XmlFormatClassWriterDelegate? _xmlFormatWriterDelegate; - public XmlDictionaryString[]? ContractNamespaces; - public XmlDictionaryString[]? MemberNames; - public XmlDictionaryString[]? MemberNamespaces; + internal XmlDictionaryString[]? ContractNamespaces; + internal XmlDictionaryString[]? MemberNames; + internal XmlDictionaryString[]? MemberNamespaces; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal ClassDataContractCriticalHelper([DynamicallyAccessedMembers(DataContractPreserveMemberTypes)] Type type) : base(type) { - XmlQualifiedName stableName = GetStableNameAndSetHasDataContract(type); + XmlQualifiedName xmlName = GetXmlNameAndSetHasDataContract(type); if (type == Globals.TypeOfDBNull) { - this.StableName = stableName; + XmlName = xmlName; _members = new List(); XmlDictionary dictionary = new XmlDictionary(2); - this.Name = dictionary.Add(StableName.Name); - this.Namespace = dictionary.Add(StableName.Namespace); - this.ContractNamespaces = this.MemberNames = this.MemberNamespaces = Array.Empty(); + Name = dictionary.Add(XmlName.Name); + Namespace = dictionary.Add(XmlName.Namespace); + ContractNamespaces = MemberNames = MemberNamespaces = Array.Empty(); EnsureMethodsImported(); return; } Type? baseType = type.BaseType; - _isISerializable = (Globals.TypeOfISerializable.IsAssignableFrom(type)); + IsISerializable = (Globals.TypeOfISerializable.IsAssignableFrom(type)); SetIsNonAttributedType(type); - if (_isISerializable) + if (IsISerializable) { if (HasDataContract) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.ISerializableCannotHaveDataContract, DataContract.GetClrTypeFullName(type)))); if (baseType != null && !(baseType.IsSerializable && Globals.TypeOfISerializable.IsAssignableFrom(baseType))) baseType = null; } - SetKeyValuePairAdapterFlags(type); - this.IsValueType = type.IsValueType; + IsValueType = type.IsValueType; if (baseType != null && baseType != Globals.TypeOfObject && baseType != Globals.TypeOfValueType && baseType != Globals.TypeOfUri) { - // https://github.com/dotnet/corefx/pull/19629: - // A DataContract created at runtime does not work with the base DataContract pre-generated by SG. - // It's because the runtime DataContract requires full information of a base DataContract while a - // pre-generated DataContract is incomplete. - // - // At this point in code, we're in the midlle of creating a new DataContract at runtime, so we need to make - // sure that we create our own base type DataContract when the base type could potentially have SG generated - // DataContract. - // - // We wanted to enable the fix for the issue described above only when SG generated DataContracts are available. - // Currently we don't have a good way of detecting usage of SG (either globally or per data contract). - DataContract baseContract = DataContract.GetDataContract(baseType); - if (baseContract is CollectionDataContract) + if (baseContract is CollectionDataContract collectionDC) { - this.BaseContract = ((CollectionDataContract)baseContract).SharedTypeContract as ClassDataContract; + BaseClassContract = collectionDC.SharedTypeContract as ClassDataContract; } else { - this.BaseContract = baseContract as ClassDataContract; + BaseClassContract = baseContract as ClassDataContract; } - if (this.BaseContract != null && this.BaseContract.IsNonAttributedType && !_isNonAttributedType) + if (BaseClassContract != null && BaseClassContract.IsNonAttributedType && !_isNonAttributedType) { throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError (new InvalidDataContractException(SR.Format(SR.AttributedTypesCannotInheritFromNonAttributedSerializableTypes, @@ -785,7 +652,7 @@ internal ClassDataContractCriticalHelper([DynamicallyAccessedMembers(DataContrac } else { - this.BaseContract = null; + BaseClassContract = null; } _hasExtensionData = (Globals.TypeOfIExtensibleDataObject.IsAssignableFrom(type)); @@ -794,21 +661,21 @@ internal ClassDataContractCriticalHelper([DynamicallyAccessedMembers(DataContrac throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.OnlyDataContractTypesCanHaveExtensionData, DataContract.GetClrTypeFullName(type)))); } - if (_isISerializable) + if (IsISerializable) { - SetDataContractName(stableName); + SetDataContractName(xmlName); } else { - this.StableName = stableName; + XmlName = xmlName; ImportDataMembers(); XmlDictionary dictionary = new XmlDictionary(2 + Members.Count); - Name = dictionary.Add(StableName.Name); - Namespace = dictionary.Add(StableName.Namespace); + Name = dictionary.Add(XmlName.Name); + Namespace = dictionary.Add(XmlName.Namespace); int baseMemberCount = 0; int baseContractCount = 0; - if (BaseContract == null) + if (BaseClassContract == null) { MemberNames = new XmlDictionaryString[Members.Count]; MemberNamespaces = new XmlDictionaryString[Members.Count]; @@ -816,14 +683,18 @@ internal ClassDataContractCriticalHelper([DynamicallyAccessedMembers(DataContrac } else { - baseMemberCount = BaseContract.MemberNames!.Length; + if (BaseClassContract.IsReadOnlyContract) + { + _serializationExceptionMessage = BaseClassContract.SerializationExceptionMessage; + } + baseMemberCount = BaseClassContract.MemberNames!.Length; MemberNames = new XmlDictionaryString[Members.Count + baseMemberCount]; - Array.Copy(BaseContract.MemberNames, MemberNames, baseMemberCount); + Array.Copy(BaseClassContract.MemberNames, MemberNames, baseMemberCount); MemberNamespaces = new XmlDictionaryString[Members.Count + baseMemberCount]; - Array.Copy(BaseContract.MemberNamespaces!, MemberNamespaces, baseMemberCount); - baseContractCount = BaseContract.ContractNamespaces!.Length; + Array.Copy(BaseClassContract.MemberNamespaces!, MemberNamespaces, baseMemberCount); + baseContractCount = BaseClassContract.ContractNamespaces!.Length; ContractNamespaces = new XmlDictionaryString[1 + baseContractCount]; - Array.Copy(BaseContract.ContractNamespaces, ContractNamespaces, baseContractCount); + Array.Copy(BaseClassContract.ContractNamespaces, ContractNamespaces, baseContractCount); } ContractNamespaces[baseContractCount] = Namespace; for (int i = 0; i < Members.Count; i++) @@ -834,8 +705,6 @@ internal ClassDataContractCriticalHelper([DynamicallyAccessedMembers(DataContrac } EnsureMethodsImported(); - _isScriptObject = this.IsNonAttributedType && - Globals.TypeOfScriptObject_IsAssignableFrom(this.UnderlyingType); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -843,10 +712,10 @@ internal ClassDataContractCriticalHelper( [DynamicallyAccessedMembers(DataContractPreserveMemberTypes)] Type type, XmlDictionaryString ns, string[] memberNames) : base(type) { - this.StableName = new XmlQualifiedName(GetStableNameAndSetHasDataContract(type).Name, ns.Value); + XmlName = new XmlQualifiedName(GetXmlNameAndSetHasDataContract(type).Name, ns.Value); ImportDataMembers(); XmlDictionary dictionary = new XmlDictionary(1 + Members.Count); - Name = dictionary.Add(StableName.Name); + Name = dictionary.Add(XmlName.Name); Namespace = ns; ContractNamespaces = new XmlDictionaryString[] { Namespace }; MemberNames = new XmlDictionaryString[Members.Count]; @@ -862,15 +731,14 @@ internal ClassDataContractCriticalHelper( private void EnsureIsReferenceImported(Type type) { - DataContractAttribute? dataContractAttribute; bool isReference = false; - bool hasDataContractAttribute = TryGetDCAttribute(type, out dataContractAttribute); + bool hasDataContractAttribute = TryGetDCAttribute(type, out DataContractAttribute? dataContractAttribute); - if (BaseContract != null) + if (BaseClassContract != null) { if (hasDataContractAttribute && dataContractAttribute!.IsReferenceSetExplicitly) { - bool baseIsReference = this.BaseContract.IsReference; + bool baseIsReference = BaseClassContract.IsReference; if ((baseIsReference && !dataContractAttribute.IsReference) || (!baseIsReference && dataContractAttribute.IsReference)) { @@ -878,8 +746,8 @@ private void EnsureIsReferenceImported(Type type) SR.Format(SR.InconsistentIsReference, DataContract.GetClrTypeFullName(type), dataContractAttribute.IsReference, - DataContract.GetClrTypeFullName(this.BaseContract.UnderlyingType), - this.BaseContract.IsReference), + DataContract.GetClrTypeFullName(BaseClassContract.UnderlyingType), + BaseClassContract.IsReference), type); } else @@ -889,7 +757,7 @@ private void EnsureIsReferenceImported(Type type) } else { - isReference = this.BaseContract.IsReference; + isReference = BaseClassContract.IsReference; } } else if (hasDataContractAttribute) @@ -909,7 +777,7 @@ private void EnsureIsReferenceImported(Type type) return; } - this.IsReference = isReference; + IsReference = isReference; } [MemberNotNull(nameof(_members))] @@ -917,16 +785,14 @@ private void EnsureIsReferenceImported(Type type) [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private void ImportDataMembers() { - Type type = this.UnderlyingType; + Type type = UnderlyingType; EnsureIsReferenceImported(type); List tempMembers = new List(); Dictionary memberNamesTable = new Dictionary(); MemberInfo[] memberInfos; - bool isPodSerializable = !_isNonAttributedType || IsKnownSerializableType(type); - - if (!isPodSerializable) + if (_isNonAttributedType) { memberInfos = type.GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public); } @@ -960,9 +826,9 @@ private void ImportDataMembers() ThrowInvalidDataContractException(SR.Format(SR.NoGetMethodForProperty, property.DeclaringType, property.Name)); if (setMethod == null) { - if (!SetIfGetOnlyCollection(memberContract)) + if (!SetIfGetOnlyCollection(memberContract, skipIfReadOnlyContract: false)) { - ThrowInvalidDataContractException(SR.Format(SR.NoSetMethodForProperty, property.DeclaringType, property.Name)); + _serializationExceptionMessage = SR.Format(SR.NoSetMethodForProperty, property.DeclaringType, property.Name); } } if (getMethod.GetParameters().Length > 0) @@ -984,7 +850,7 @@ private void ImportDataMembers() memberContract.Name = DataContract.EncodeLocalName(memberContract.Name); memberContract.IsNullable = DataContract.IsTypeNullable(memberContract.MemberType); memberContract.IsRequired = memberAttribute.IsRequired; - if (memberAttribute.IsRequired && this.IsReference) + if (memberAttribute.IsRequired && IsReference) { ThrowInvalidDataContractException( SR.Format(SR.IsRequiredDataMemberOnIsReferenceDataContractType, @@ -996,7 +862,7 @@ private void ImportDataMembers() CheckAndAddMember(tempMembers, memberContract, memberNamesTable); } } - else if (!isPodSerializable) + else if (_isNonAttributedType) { FieldInfo? field = member as FieldInfo; PropertyInfo? property = member as PropertyInfo; @@ -1021,7 +887,7 @@ private void ImportDataMembers() MethodInfo? setMethod = property.SetMethod; if (setMethod == null) { - if (!SetIfGetOnlyCollection(memberContract)) + if (!SetIfGetOnlyCollection(memberContract, skipIfReadOnlyContract: true)) continue; } else @@ -1029,6 +895,11 @@ private void ImportDataMembers() if (!setMethod.IsPublic || IsMethodOverriding(setMethod)) continue; } + + //skip ExtensionData member of type ExtensionDataObject if IExtensibleDataObject is implemented in non-attributed type + if (_hasExtensionData && memberContract.MemberType == Globals.TypeOfExtensionDataObject + && member.Name == Globals.ExtensionDataObjectPropertyName) + continue; } memberContract.Name = DataContract.EncodeLocalName(member.Name); @@ -1039,23 +910,7 @@ private void ImportDataMembers() { FieldInfo? field = member as FieldInfo; - bool canSerializeMember; - - // Previously System.SerializableAttribute was not available in NetCore, so we had - // a list of known [Serializable] types for type in the framework. Although now SerializableAttribute - // is available in NetCore, some framework types still do not have [Serializable] - // yet, e.g. ReadOnlyDictionary. So, we still need to maintain the known serializable - // type list. - if (IsKnownSerializableType(type)) - { - canSerializeMember = CanSerializeMember(field); - } - else - { - canSerializeMember = field != null && !field.IsNotSerialized; - } - - if (canSerializeMember) + if (field != null && !field.IsNotSerialized) { DataMember memberContract = new DataMember(member); @@ -1063,7 +918,7 @@ private void ImportDataMembers() object[] optionalFields = field!.GetCustomAttributes(Globals.TypeOfOptionalFieldAttribute, false); if (optionalFields == null || optionalFields.Length == 0) { - if (this.IsReference) + if (IsReference) { ThrowInvalidDataContractException( SR.Format(SR.NonOptionalFieldMemberOnIsReferenceSerializableType, @@ -1087,16 +942,11 @@ private void ImportDataMembers() Debug.Assert(Members != null); } - private static bool CanSerializeMember(FieldInfo? field) - { - return field != null && !ClassDataContract.IsNonSerializedMember(field.DeclaringType!, field.Name); - } - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private static bool SetIfGetOnlyCollection(DataMember memberContract) + private static bool SetIfGetOnlyCollection(DataMember memberContract, bool skipIfReadOnlyContract) { //OK to call IsCollection here since the use of surrogated collection types is not supported in get-only scenarios - if (CollectionDataContract.IsCollection(memberContract.MemberType, false /*isConstructorRequired*/) && !memberContract.MemberType.IsValueType) + if (CollectionDataContract.IsCollection(memberContract.MemberType, false /*isConstructorRequired*/, skipIfReadOnlyContract) && !memberContract.MemberType.IsValueType) { memberContract.IsGetOnlyCollection = true; return true; @@ -1106,25 +956,25 @@ private static bool SetIfGetOnlyCollection(DataMember memberContract) private void SetIfMembersHaveConflict(List members) { - if (BaseContract == null) + if (BaseClassContract == null) return; int baseTypeIndex = 0; List membersInHierarchy = new List(); foreach (DataMember member in members) { - membersInHierarchy.Add(new Member(member, this.StableName!.Namespace, baseTypeIndex)); + membersInHierarchy.Add(new Member(member, XmlName!.Namespace, baseTypeIndex)); } - ClassDataContract? currContract = BaseContract; + ClassDataContract? currContract = BaseClassContract; while (currContract != null) { baseTypeIndex++; foreach (DataMember member in currContract.Members!) { - membersInHierarchy.Add(new Member(member, currContract.StableName!.Namespace, baseTypeIndex)); + membersInHierarchy.Add(new Member(member, currContract.XmlName!.Namespace, baseTypeIndex)); } - currContract = currContract.BaseContract; + currContract = currContract.BaseClassContract; } IComparer comparer = DataMemberConflictComparer.Singleton; @@ -1136,19 +986,19 @@ private void SetIfMembersHaveConflict(List members) int endIndex = i; bool hasConflictingType = false; while (endIndex < membersInHierarchy.Count - 1 - && string.CompareOrdinal(membersInHierarchy[endIndex].member.Name, membersInHierarchy[endIndex + 1].member.Name) == 0 - && string.CompareOrdinal(membersInHierarchy[endIndex].ns, membersInHierarchy[endIndex + 1].ns) == 0) + && string.CompareOrdinal(membersInHierarchy[endIndex]._member.Name, membersInHierarchy[endIndex + 1]._member.Name) == 0 + && string.CompareOrdinal(membersInHierarchy[endIndex]._ns, membersInHierarchy[endIndex + 1]._ns) == 0) { - membersInHierarchy[endIndex].member.ConflictingMember = membersInHierarchy[endIndex + 1].member; + membersInHierarchy[endIndex]._member.ConflictingMember = membersInHierarchy[endIndex + 1]._member; if (!hasConflictingType) { - if (membersInHierarchy[endIndex + 1].member.HasConflictingNameAndType) + if (membersInHierarchy[endIndex + 1]._member.HasConflictingNameAndType) { hasConflictingType = true; } else { - hasConflictingType = (membersInHierarchy[endIndex].member.MemberType != membersInHierarchy[endIndex + 1].member.MemberType); + hasConflictingType = (membersInHierarchy[endIndex]._member.MemberType != membersInHierarchy[endIndex + 1]._member.MemberType); } } endIndex++; @@ -1158,7 +1008,7 @@ private void SetIfMembersHaveConflict(List members) { for (int j = startIndex; j <= endIndex; j++) { - membersInHierarchy[j].member.HasConflictingNameAndType = true; + membersInHierarchy[j]._member.HasConflictingNameAndType = true; } } @@ -1167,15 +1017,15 @@ private void SetIfMembersHaveConflict(List members) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private XmlQualifiedName GetStableNameAndSetHasDataContract(Type type) + private XmlQualifiedName GetXmlNameAndSetHasDataContract(Type type) { - return DataContract.GetStableName(type, out _hasDataContract); + return DataContract.GetXmlName(type, out _hasDataContract); } /// /// RequiresReview - marked SRR because callers may need to depend on isNonAttributedType for a security decision /// isNonAttributedType must be calculated correctly - /// SetIsNonAttributedType should not be called before GetStableNameAndSetHasDataContract since it + /// SetIsNonAttributedType should not be called before GetXmlNameAndSetHasDataContract since it /// is dependent on the correct calculation of hasDataContract /// Safe - does not let caller influence isNonAttributedType calculation; no harm in leaking value /// @@ -1199,7 +1049,7 @@ internal void EnsureMethodsImported() { if (!_isMethodChecked) { - Type type = this.UnderlyingType; + Type type = UnderlyingType; MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); for (int i = 0; i < methods.Length; i++) { @@ -1272,20 +1122,21 @@ private static bool IsValidCallback(MethodInfo method, ParameterInfo[] parameter return false; } - internal ClassDataContract? BaseContract + internal ClassDataContract? BaseClassContract { - get { return _baseContract; } + get => _baseContract; set { _baseContract = value; if (_baseContract != null && IsValueType) - ThrowInvalidDataContractException(SR.Format(SR.ValueTypeCannotHaveBaseType, StableName!.Name, StableName.Namespace, _baseContract.StableName!.Name, _baseContract.StableName.Namespace)); + ThrowInvalidDataContractException(SR.Format(SR.ValueTypeCannotHaveBaseType, XmlName!.Name, XmlName.Namespace, _baseContract.XmlName!.Name, _baseContract.XmlName.Namespace)); } } internal List? Members { - get { return _members; } + get => _members; + set => _members = value; } internal MethodInfo? OnSerializing @@ -1335,99 +1186,39 @@ internal MethodInfo? ExtensionDataSetMethod internal override DataContractDictionary? KnownDataContracts { - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] get { - if (_knownDataContracts != null) - { - return _knownDataContracts; - } - if (!_isKnownTypeAttributeChecked && UnderlyingType != null) { lock (this) { if (!_isKnownTypeAttributeChecked) { - _knownDataContracts = DataContract.ImportKnownTypeAttributes(this.UnderlyingType); + _knownDataContracts = DataContract.ImportKnownTypeAttributes(UnderlyingType); Interlocked.MemoryBarrier(); _isKnownTypeAttributeChecked = true; } + _knownDataContracts ??= new DataContractDictionary(); } } return _knownDataContracts; } - set - { _knownDataContracts = value; } - } - - internal override bool IsISerializable - { - get { return _isISerializable; } - set { _isISerializable = value; } - } - - internal bool HasDataContract - { - get { return _hasDataContract; } - } - - internal bool HasExtensionData - { - get { return _hasExtensionData; } - set { _hasExtensionData = value; } - } - - internal bool IsNonAttributedType - { - get { return _isNonAttributedType; } + set { _knownDataContracts = value; } } - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private void SetKeyValuePairAdapterFlags( - [DynamicallyAccessedMembers(DataContractPreserveMemberTypes)] - Type type) - { - if (type.IsGenericType && type.GetGenericTypeDefinition() == Globals.TypeOfKeyValuePairAdapter) - { - _isKeyValuePairAdapter = true; - _keyValuePairGenericArguments = type.GetGenericArguments(); - _keyValuePairCtorInfo = (ConstructorInfo)type.GetMemberWithSameMetadataDefinitionAs(s_ctorGenericMethod); - _getKeyValuePairMethodInfo = (MethodInfo)type.GetMemberWithSameMetadataDefinitionAs(s_getKeyValuePairMethod); - } - } + internal string? SerializationExceptionMessage => _serializationExceptionMessage; - private bool _isKeyValuePairAdapter; - private Type[]? _keyValuePairGenericArguments; - private ConstructorInfo? _keyValuePairCtorInfo; - private MethodInfo? _getKeyValuePairMethodInfo; + internal string? DeserializationExceptionMessage => (_serializationExceptionMessage == null) ? null : SR.Format(SR.ReadOnlyClassDeserialization, _serializationExceptionMessage); - internal bool IsKeyValuePairAdapter - { - get { return _isKeyValuePairAdapter; } - } + internal override bool IsISerializable { get; set; } - internal bool IsScriptObject - { - get { return _isScriptObject; } - } - - internal Type[]? KeyValuePairGenericArguments - { - get { return _keyValuePairGenericArguments; } - } + internal bool HasDataContract => _hasDataContract; - internal ConstructorInfo? KeyValuePairAdapterConstructorInfo - { - get { return _keyValuePairCtorInfo; } - } + internal bool HasExtensionData => _hasExtensionData; - internal MethodInfo? GetKeyValuePairMethodInfo - { - get { return _getKeyValuePairMethodInfo; } - } + internal bool IsNonAttributedType => _isNonAttributedType; internal ConstructorInfo? GetISerializableConstructor() { @@ -1443,7 +1234,7 @@ internal MethodInfo? GetKeyValuePairMethodInfo internal ConstructorInfo? GetNonAttributedTypeConstructor() { - if (!this.IsNonAttributedType) + if (!IsNonAttributedType) return null; Type type = UnderlyingType; @@ -1458,23 +1249,11 @@ internal MethodInfo? GetKeyValuePairMethodInfo return ctor; } - internal XmlFormatClassWriterDelegate? XmlFormatWriterDelegate - { - get { return _xmlFormatWriterDelegate; } - set { _xmlFormatWriterDelegate = value; } - } + internal XmlFormatClassWriterDelegate? XmlFormatWriterDelegate { get; set; } - internal XmlFormatClassReaderDelegate? XmlFormatReaderDelegate - { - get { return _xmlFormatReaderDelegate; } - set { _xmlFormatReaderDelegate = value; } - } + internal XmlFormatClassReaderDelegate? XmlFormatReaderDelegate { get; set; } - public XmlDictionaryString?[]? ChildElementNamespaces - { - get { return _childElementNamespaces; } - set { _childElementNamespaces = value; } - } + internal XmlDictionaryString?[]? ChildElementNamespaces { get; set; } private static Type[] SerInfoCtorArgs => s_serInfoCtorArgs ??= new Type[] { typeof(SerializationInfo), typeof(StreamingContext) }; @@ -1482,70 +1261,207 @@ internal struct Member { internal Member(DataMember member, string ns, int baseTypeIndex) { - this.member = member; - this.ns = ns; - this.baseTypeIndex = baseTypeIndex; + _member = member; + _ns = ns; + _baseTypeIndex = baseTypeIndex; } - internal DataMember member; - internal string ns; - internal int baseTypeIndex; + internal DataMember _member; + internal string _ns; + internal int _baseTypeIndex; } internal sealed class DataMemberConflictComparer : IComparer { public int Compare(Member x, Member y) { - int nsCompare = string.CompareOrdinal(x.ns, y.ns); + int nsCompare = string.CompareOrdinal(x._ns, y._ns); if (nsCompare != 0) return nsCompare; - int nameCompare = string.CompareOrdinal(x.member.Name, y.member.Name); + int nameCompare = string.CompareOrdinal(x._member.Name, y._member.Name); if (nameCompare != 0) return nameCompare; - return x.baseTypeIndex - y.baseTypeIndex; + return x._baseTypeIndex - y._baseTypeIndex; } internal static DataMemberConflictComparer Singleton = new DataMemberConflictComparer(); } + } - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal ClassDataContractCriticalHelper Clone() + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal override DataContract BindGenericParameters(DataContract[] paramContracts, Dictionary? boundContracts = null) + { + Type type = UnderlyingType; + if (!type.IsGenericType || !type.ContainsGenericParameters) + return this; + + lock (this) + { + if (boundContracts != null && boundContracts.TryGetValue(this, out DataContract? boundContract)) + return boundContract; + + XmlQualifiedName xmlName; + object[] genericParams; + Type boundType; + if (type.IsGenericTypeDefinition) + { + xmlName = XmlName; + genericParams = paramContracts; + + // This type-binding ('boundType') stuff is new. We did not do this in NetFx. We used to use default contract constructors and let the + // underlying type get filled in later. But default constructors for DataContracts runs afoul of requiring an underlying type. Our web of nullable + // notations make it hard to get around. But it also allows us to feel good about using .UnderlyingType from matching parameter contracts. + Type[] underlyingParamTypes = new Type[paramContracts.Length]; + for (int i = 0; i < paramContracts.Length; i++) + underlyingParamTypes[i] = paramContracts[i].UnderlyingType; + boundType = type.MakeGenericType(underlyingParamTypes); + } + else + { + //partial Generic: Construct xml name from its open generic type definition + xmlName = DataContract.GetXmlName(type.GetGenericTypeDefinition()); + Type[] paramTypes = type.GetGenericArguments(); + genericParams = new object[paramTypes.Length]; + for (int i = 0; i < paramTypes.Length; i++) + { + Type paramType = paramTypes[i]; + if (paramType.IsGenericParameter) + { + genericParams[i] = paramContracts[paramType.GenericParameterPosition]; + paramTypes[i] = paramContracts[paramType.GenericParameterPosition].UnderlyingType; + } + else + { + genericParams[i] = paramType; + } + } + boundType = type.MakeGenericType(paramTypes); + } + ClassDataContract boundClassContract = new ClassDataContract(boundType); + boundContracts ??= new Dictionary(); + boundContracts.Add(this, boundClassContract); + boundClassContract.XmlName = CreateQualifiedName(DataContract.ExpandGenericParameters(XmlConvert.DecodeName(xmlName.Name), new GenericNameProvider(DataContract.GetClrTypeFullName(UnderlyingType), genericParams)), xmlName.Namespace); + if (BaseClassContract != null) + boundClassContract.BaseClassContract = (ClassDataContract)BaseClassContract.BindGenericParameters(paramContracts, boundContracts); + boundClassContract.IsISerializable = IsISerializable; + boundClassContract.IsValueType = IsValueType; + boundClassContract.IsReference = IsReference; + if (Members != null) + { + boundClassContract.Members = new List(Members.Count); + foreach (DataMember member in Members) + boundClassContract.Members.Add(member.BindGenericParameters(paramContracts, boundContracts)); + } + return boundClassContract; + } + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "All ctor's required to create an instance of this type are marked with RequiresUnreferencedCode.")] + internal override bool Equals(object? other, HashSet? checkedContracts) + { + if (IsEqualOrChecked(other, checkedContracts)) + return true; + + if (base.Equals(other, checkedContracts)) { - ClassDataContractCriticalHelper clonedHelper = new ClassDataContractCriticalHelper(this.UnderlyingType); - - clonedHelper._baseContract = this._baseContract; - clonedHelper._childElementNamespaces = this._childElementNamespaces; - clonedHelper.ContractNamespaces = this.ContractNamespaces; - clonedHelper._hasDataContract = this._hasDataContract; - clonedHelper._isMethodChecked = this._isMethodChecked; - clonedHelper._isNonAttributedType = this._isNonAttributedType; - clonedHelper.IsReference = this.IsReference; - clonedHelper.IsValueType = this.IsValueType; - clonedHelper.MemberNames = this.MemberNames; - clonedHelper.MemberNamespaces = this.MemberNamespaces; - clonedHelper._members = this._members; - clonedHelper.Name = this.Name; - clonedHelper.Namespace = this.Namespace; - clonedHelper._onDeserialized = this._onDeserialized; - clonedHelper._onDeserializing = this._onDeserializing; - clonedHelper._onSerialized = this._onSerialized; - clonedHelper._onSerializing = this._onSerializing; - clonedHelper.StableName = this.StableName; - clonedHelper.TopLevelElementName = this.TopLevelElementName; - clonedHelper.TopLevelElementNamespace = this.TopLevelElementNamespace; - clonedHelper._xmlFormatReaderDelegate = this._xmlFormatReaderDelegate; - clonedHelper._xmlFormatWriterDelegate = this._xmlFormatWriterDelegate; - - return clonedHelper; + if (other is ClassDataContract dataContract) + { + if (IsISerializable) + { + if (!dataContract.IsISerializable) + return false; + } + else + { + if (dataContract.IsISerializable) + return false; + + if (Members == null) + { + if (dataContract.Members != null) + { + // check that all the datamembers in dataContract.Members are optional + if (!IsEveryDataMemberOptional(dataContract.Members)) + return false; + } + } + else if (dataContract.Members == null) + { + // check that all the datamembers in Members are optional + if (!IsEveryDataMemberOptional(Members)) + return false; + } + else + { + Dictionary membersDictionary = new Dictionary(Members.Count); + List dataContractMembersList = new List(); + for (int i = 0; i < Members.Count; i++) + { + membersDictionary.Add(Members[i].Name, Members[i]); + } + + for (int i = 0; i < dataContract.Members.Count; i++) + { + // check that all datamembers common to both datacontracts match + if (membersDictionary.TryGetValue(dataContract.Members[i].Name, out DataMember? dataMember)) + { + if (dataMember.Equals(dataContract.Members[i], checkedContracts)) + { + membersDictionary.Remove(dataMember.Name); + } + else + { + return false; + } + } + // otherwise save the non-matching datamembers for later verification + else + { + dataContractMembersList.Add(dataContract.Members[i]); + } + } + + // check that datamembers left over from either datacontract are optional + if (!IsEveryDataMemberOptional(membersDictionary.Values)) + return false; + if (!IsEveryDataMemberOptional(dataContractMembersList)) + return false; + } + } + + if (BaseClassContract == null) + return (dataContract.BaseClassContract == null); + else if (dataContract.BaseClassContract == null) + return false; + else + return BaseClassContract.Equals(dataContract.BaseClassContract, checkedContracts); + } } + return false; + } + + private static bool IsEveryDataMemberOptional(IEnumerable dataMembers) + { + return !dataMembers.Any(dm => dm.IsRequired); + } + + public override int GetHashCode() + { + return base.GetHashCode(); } internal sealed class DataMemberComparer : IComparer { public int Compare(DataMember? x, DataMember? y) { - int orderCompare = x!.Order - y!.Order; + if (x == null && y == null) + return 0; + if (x == null || y == null) + return -1; + + int orderCompare = (int)(x.Order - y.Order); if (orderCompare != 0) return orderCompare; @@ -1556,7 +1472,7 @@ public int Compare(DataMember? x, DataMember? y) } /// - /// Get object type for Xml/JsonFormmatReaderGenerator + /// Get object type for Xml/JsonFormatReaderGenerator /// internal Type ObjectType { @@ -1570,37 +1486,5 @@ internal Type ObjectType return type; } } - - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal ClassDataContract Clone() - { - ClassDataContract clonedDc = new ClassDataContract(this.UnderlyingType); - clonedDc._helper = _helper.Clone(); - clonedDc.ContractNamespaces = this.ContractNamespaces; - clonedDc.ChildElementNamespaces = this.ChildElementNamespaces; - clonedDc.MemberNames = this.MemberNames; - clonedDc.MemberNamespaces = this.MemberNamespaces; - clonedDc.XmlFormatWriterDelegate = this.XmlFormatWriterDelegate; - clonedDc.XmlFormatReaderDelegate = this.XmlFormatReaderDelegate; - return clonedDc; - } - - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal void UpdateNamespaceAndMembers(Type type, XmlDictionaryString ns, string[] memberNames) - { - this.StableName = new XmlQualifiedName(GetStableName(type).Name, ns.Value); - this.Namespace = ns; - XmlDictionary dictionary = new XmlDictionary(1 + memberNames.Length); - this.Name = dictionary.Add(StableName.Name); - this.Namespace = ns; - this.ContractNamespaces = new XmlDictionaryString[] { ns }; - this.MemberNames = new XmlDictionaryString[memberNames.Length]; - this.MemberNamespaces = new XmlDictionaryString[memberNames.Length]; - for (int i = 0; i < memberNames.Length; i++) - { - this.MemberNames[i] = dictionary.Add(memberNames[i]); - this.MemberNamespaces[i] = ns; - } - } } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs index b39d1af3d439ff..201bdc3e93e833 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs @@ -2,17 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections; using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; -using System.Xml; using System.Reflection; using System.Reflection.Emit; -using System.IO; -using System.Security; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.Serialization.Json; +using System.Runtime.Serialization.DataContracts; namespace System.Runtime.Serialization { @@ -88,6 +84,14 @@ private static MethodInfo StringFormat } } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", + Justification = "The trimmer will never remove the Invoke method from delegates.")] + internal static MethodInfo GetInvokeMethod(Type delegateType) + { + Debug.Assert(typeof(Delegate).IsAssignableFrom(delegateType)); + return delegateType.GetMethod("Invoke")!; + } + private Type _delegateType = null!; // initialized in BeginMethod private static Module? s_serializationModule; @@ -100,17 +104,9 @@ private static MethodInfo StringFormat private Stack _blockStack = null!; // initialized in BeginMethod private Label _methodEndLabel; - private readonly Dictionary _localNames = new Dictionary(); - - private enum CodeGenTrace { None, Save, Tron }; - private readonly CodeGenTrace _codeGenTrace; private LocalBuilder? _stringFormatArray; - internal CodeGenerator() - { - //Defaulting to None as thats the default value in WCF - _codeGenTrace = CodeGenTrace.None; - } + internal CodeGenerator() { } internal void BeginMethod(DynamicMethod dynamicMethod, Type delegateType, string methodName, Type[] argTypes, bool allowPrivateMemberAccess) { @@ -123,7 +119,7 @@ internal void BeginMethod(DynamicMethod dynamicMethod, Type delegateType, string internal void BeginMethod(string methodName, Type delegateType, bool allowPrivateMemberAccess) { - MethodInfo signature = JsonFormatWriterGenerator.GetInvokeMethod(delegateType); + MethodInfo signature = GetInvokeMethod(delegateType); ParameterInfo[] parameters = signature.GetParameters(); Type[] paramTypes = new Type[parameters.Length]; for (int i = 0; i < parameters.Length; i++) @@ -148,15 +144,11 @@ private void InitILGeneration(string methodName, Type[] argTypes) _argList = new List(); for (int i = 0; i < argTypes.Length; i++) _argList.Add(new ArgBuilder(i, argTypes[i])); - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceLabel("Begin method " + methodName + " {"); } internal Delegate EndMethod() { MarkLabel(_methodEndLabel); - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceLabel("} End method"); Ret(); Delegate? retVal; @@ -180,15 +172,15 @@ internal MethodInfo CurrentMethod internal ArgBuilder GetArg(int index) { - return (ArgBuilder)_argList[index]; + return _argList[index]; } internal static Type GetVariableType(object var) { - if (var is ArgBuilder) - return ((ArgBuilder)var).ArgType; - else if (var is LocalBuilder) - return ((LocalBuilder)var).LocalType; + if (var is ArgBuilder argBuilder) + return argBuilder.ArgType; + else if (var is LocalBuilder localBuilder) + return localBuilder.LocalType; else return var.GetType(); } @@ -203,18 +195,12 @@ internal LocalBuilder DeclareLocal(Type type, string name, object initialValue) internal LocalBuilder DeclareLocal(Type type, string name) { - return DeclareLocal(type, name, false); + return DeclareLocal(type, false); } - internal LocalBuilder DeclareLocal(Type type, string name, bool isPinned) + internal LocalBuilder DeclareLocal(Type type, bool isPinned) { - LocalBuilder local = _ilGen.DeclareLocal(type, isPinned); - if (_codeGenTrace != CodeGenTrace.None) - { - _localNames[local] = name; - EmitSourceComment("Declare local '" + name + "' of type " + type); - } - return local; + return _ilGen.DeclareLocal(type, isPinned); } internal void Set(LocalBuilder local, object value) @@ -277,16 +263,14 @@ internal void InternalBreakFor(object userForState, OpCode branchInstruction) { foreach (object block in _blockStack) { - ForState? forState = block as ForState; - if (forState != null && (object)forState == userForState) + if (block == userForState && block is ForState forState) { if (!forState.RequiresEndLabel) { forState.EndLabel = DefineLabel(); forState.RequiresEndLabel = true; } - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction(branchInstruction + " " + forState.EndLabel.GetHashCode()); + _ilGen.Emit(branchInstruction, forState.EndLabel); break; } @@ -329,7 +313,7 @@ internal void EndForEach(MethodInfo moveNextMethod) internal void IfNotDefaultValue(object value) { Type type = GetVariableType(value); - TypeCode typeCode = type.GetTypeCode(); + TypeCode typeCode = Type.GetTypeCode(type); if ((typeCode == TypeCode.Object && type.IsValueType) || typeCode == TypeCode.DateTime || typeCode == TypeCode.Decimal) { @@ -507,51 +491,36 @@ internal void Call(MethodInfo methodInfo) { if (methodInfo.IsVirtual && !methodInfo.DeclaringType!.IsValueType) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Callvirt " + methodInfo.ToString() + " on type " + methodInfo.DeclaringType.ToString()); _ilGen.Emit(OpCodes.Callvirt, methodInfo); } else if (methodInfo.IsStatic) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Static Call " + methodInfo.ToString() + " on type " + methodInfo.DeclaringType!.ToString()); _ilGen.Emit(OpCodes.Call, methodInfo); } else { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Call " + methodInfo.ToString() + " on type " + methodInfo.DeclaringType!.ToString()); _ilGen.Emit(OpCodes.Call, methodInfo); } } internal void Call(ConstructorInfo ctor) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Call " + ctor.ToString() + " on type " + ctor.DeclaringType!.ToString()); _ilGen.Emit(OpCodes.Call, ctor); } internal void New(ConstructorInfo constructorInfo) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Newobj " + constructorInfo.ToString() + " on type " + constructorInfo.DeclaringType!.ToString()); _ilGen.Emit(OpCodes.Newobj, constructorInfo); } - internal void InitObj(Type valueType) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Initobj " + valueType); _ilGen.Emit(OpCodes.Initobj, valueType); } internal void NewArray(Type elementType, object len) { Load(len); - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Newarr " + elementType); _ilGen.Emit(OpCodes.Newarr, elementType); } @@ -605,27 +574,20 @@ internal Type LoadMember(MemberInfo memberInfo) memberType = fieldInfo.FieldType; if (fieldInfo.IsStatic) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldsfld " + fieldInfo + " on type " + fieldInfo.DeclaringType); _ilGen.Emit(OpCodes.Ldsfld, fieldInfo); } else { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldfld " + fieldInfo + " on type " + fieldInfo.DeclaringType); _ilGen.Emit(OpCodes.Ldfld, fieldInfo); } } else if (memberInfo is PropertyInfo property) { memberType = property.PropertyType; - if (property != null) - { - MethodInfo? getMethod = property.GetMethod; - if (getMethod == null) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.NoGetMethodForProperty, property.DeclaringType, property))); - Call(getMethod); - } + MethodInfo? getMethod = property.GetMethod; + if (getMethod == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.NoGetMethodForProperty, property.DeclaringType, property))); + Call(getMethod); } else if (memberInfo is MethodInfo method) { @@ -635,7 +597,6 @@ internal Type LoadMember(MemberInfo memberInfo) else throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.CannotLoadMemberType, "Unknown", memberInfo.DeclaringType, memberInfo.Name))); - EmitStackTop(memberType); return memberType; } @@ -645,30 +606,22 @@ internal void StoreMember(MemberInfo memberInfo) { if (fieldInfo.IsStatic) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Stsfld " + fieldInfo + " on type " + fieldInfo.DeclaringType); _ilGen.Emit(OpCodes.Stsfld, fieldInfo); } else { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Stfld " + fieldInfo + " on type " + fieldInfo.DeclaringType); _ilGen.Emit(OpCodes.Stfld, fieldInfo); } } - else if (memberInfo is PropertyInfo) + else if (memberInfo is PropertyInfo property) { - PropertyInfo? property = memberInfo as PropertyInfo; - if (property != null) - { - MethodInfo? setMethod = property.SetMethod; - if (setMethod == null) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.NoSetMethodForProperty, property.DeclaringType, property))); - Call(setMethod); - } + MethodInfo? setMethod = property.SetMethod; + if (setMethod == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.NoSetMethodForProperty, property.DeclaringType, property))); + Call(setMethod); } - else if (memberInfo is MethodInfo) - Call((MethodInfo)memberInfo); + else if (memberInfo is MethodInfo method) + Call(method); else throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.CannotLoadMemberType, "Unknown"))); } @@ -677,7 +630,7 @@ internal void LoadDefaultValue(Type type) { if (type.IsValueType) { - switch (type.GetTypeCode()) + switch (Type.GetTypeCode(type)) { case TypeCode.Boolean: Ldc(false); @@ -719,24 +672,22 @@ internal void Load(object? obj) { if (obj == null) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldnull"); _ilGen.Emit(OpCodes.Ldnull); } - else if (obj is ArgBuilder) - Ldarg((ArgBuilder)obj); - else if (obj is LocalBuilder) - Ldloc((LocalBuilder)obj); + else if (obj is ArgBuilder argBuilder) + Ldarg(argBuilder); + else if (obj is LocalBuilder localBuilder) + Ldloc(localBuilder); else Ldc(obj); } internal void Store(object var) { - if (var is ArgBuilder) - Starg((ArgBuilder)var); - else if (var is LocalBuilder) - Stloc((LocalBuilder)var); + if (var is ArgBuilder argBuilder) + Starg(argBuilder); + else if (var is LocalBuilder localBuilder) + Stloc(localBuilder); else { DiagnosticUtility.DebugAssert("Data can only be stored into ArgBuilder or LocalBuilder."); @@ -754,10 +705,10 @@ internal void Dec(object var) internal void LoadAddress(object obj) { - if (obj is ArgBuilder) - LdargAddress((ArgBuilder)obj); - else if (obj is LocalBuilder) - LdlocAddress((LocalBuilder)obj); + if (obj is ArgBuilder argBuilder) + LdargAddress(argBuilder); + else if (obj is LocalBuilder localBuilder) + LdlocAddress(localBuilder); else Load(obj); } @@ -776,22 +727,16 @@ internal void ConvertValue(Type source, Type target) internal void Castclass(Type target) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Castclass " + target); _ilGen.Emit(OpCodes.Castclass, target); } internal void Box(Type type) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Box " + type); _ilGen.Emit(OpCodes.Box, type); } internal void Unbox(Type type) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Unbox " + type); _ilGen.Emit(OpCodes.Unbox, type); } @@ -816,67 +761,53 @@ private static OpCode GetLdindOpCode(TypeCode typeCode) => internal void Ldobj(Type type) { - OpCode opCode = GetLdindOpCode(type.GetTypeCode()); + OpCode opCode = GetLdindOpCode(Type.GetTypeCode(type)); if (!opCode.Equals(OpCodes.Nop)) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction(opCode.ToString()!); _ilGen.Emit(opCode); } else { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldobj " + type); _ilGen.Emit(OpCodes.Ldobj, type); } } internal void Stobj(Type type) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Stobj " + type); _ilGen.Emit(OpCodes.Stobj, type); } internal void Ceq() { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ceq"); _ilGen.Emit(OpCodes.Ceq); } internal void Throw() { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Throw"); _ilGen.Emit(OpCodes.Throw); } internal void Ldtoken(Type t) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldtoken " + t); _ilGen.Emit(OpCodes.Ldtoken, t); } internal void Ldc(object o) { Type valueType = o.GetType(); - if (o is Type) + if (o is Type t) { - Ldtoken((Type)o); + Ldtoken(t); Call(GetTypeFromHandle); } else if (valueType.IsEnum) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceComment("Ldc " + o.GetType() + "." + o); Ldc(Convert.ChangeType(o, Enum.GetUnderlyingType(valueType), null)); } else { - switch (valueType.GetTypeCode()) + switch (Type.GetTypeCode(valueType)) { case TypeCode.Boolean: Ldc((bool)o); @@ -915,6 +846,7 @@ internal void Ldc(object o) case TypeCode.Decimal: case TypeCode.DateTime: case TypeCode.Empty: + case TypeCode.DBNull: default: throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.UnknownConstantType, DataContract.GetClrTypeFullName(valueType)))); } @@ -925,50 +857,36 @@ internal void Ldc(bool boolVar) { if (boolVar) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldc.i4 1"); _ilGen.Emit(OpCodes.Ldc_I4_1); } else { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldc.i4 0"); _ilGen.Emit(OpCodes.Ldc_I4_0); } } internal void Ldc(int intVar) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldc.i4 " + intVar); _ilGen.Emit(OpCodes.Ldc_I4, intVar); } internal void Ldc(long l) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldc.i8 " + l); _ilGen.Emit(OpCodes.Ldc_I8, l); } internal void Ldc(float f) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldc.r4 " + f); _ilGen.Emit(OpCodes.Ldc_R4, f); } internal void Ldc(double d) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldc.r8 " + d); _ilGen.Emit(OpCodes.Ldc_R8, d); } internal void Ldstr(string strVar) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldstr " + strVar); _ilGen.Emit(OpCodes.Ldstr, strVar); } @@ -982,27 +900,17 @@ internal void LdlocAddress(LocalBuilder localBuilder) internal void Ldloc(LocalBuilder localBuilder) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldloc " + _localNames[localBuilder]); _ilGen.Emit(OpCodes.Ldloc, localBuilder); - EmitStackTop(localBuilder.LocalType); } internal void Stloc(LocalBuilder local) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Stloc " + _localNames[local]); - EmitStackTop(local.LocalType); _ilGen.Emit(OpCodes.Stloc, local); } - internal void Ldloca(LocalBuilder localBuilder) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldloca " + _localNames[localBuilder]); _ilGen.Emit(OpCodes.Ldloca, localBuilder); - EmitStackTop(localBuilder.LocalType); } internal void LdargAddress(ArgBuilder argBuilder) @@ -1025,17 +933,11 @@ internal void Starg(ArgBuilder arg) internal void Ldarg(int slot) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldarg " + slot); - _ilGen.Emit(OpCodes.Ldarg, slot); } internal void Starg(int slot) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Starg " + slot); - _ilGen.Emit(OpCodes.Starg, slot); } @@ -1046,26 +948,19 @@ internal void Ldarga(ArgBuilder argBuilder) internal void Ldarga(int slot) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldarga " + slot); - _ilGen.Emit(OpCodes.Ldarga, slot); } internal void Ldlen() { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldlen"); _ilGen.Emit(OpCodes.Ldlen); - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Conv.i4"); _ilGen.Emit(OpCodes.Conv_I4); } private static OpCode GetLdelemOpCode(TypeCode typeCode) => typeCode switch { - TypeCode.Object => OpCodes.Ldelem_Ref, // TypeCode.Object: + TypeCode.Object or TypeCode.DBNull => OpCodes.Ldelem_Ref, // TypeCode.Object: TypeCode.Boolean => OpCodes.Ldelem_I1, // TypeCode.Boolean: TypeCode.Char => OpCodes.Ldelem_I2, // TypeCode.Char: TypeCode.SByte => OpCodes.Ldelem_I1, // TypeCode.SByte: @@ -1090,29 +985,22 @@ internal void Ldelem(Type arrayElementType) } else { - OpCode opCode = GetLdelemOpCode(arrayElementType.GetTypeCode()); + OpCode opCode = GetLdelemOpCode(Type.GetTypeCode(arrayElementType)); if (opCode.Equals(OpCodes.Nop)) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.ArrayTypeIsNotSupported_GeneratingCode, DataContract.GetClrTypeFullName(arrayElementType)))); - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction(opCode.ToString()!); _ilGen.Emit(opCode); - EmitStackTop(arrayElementType); } } internal void Ldelema(Type arrayElementType) { OpCode opCode = OpCodes.Ldelema; - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction(opCode.ToString()!); _ilGen.Emit(opCode, arrayElementType); - - EmitStackTop(arrayElementType); } private static OpCode GetStelemOpCode(TypeCode typeCode) => typeCode switch { - TypeCode.Object => OpCodes.Stelem_Ref, // TypeCode.Object: + TypeCode.Object or TypeCode.DBNull => OpCodes.Stelem_Ref, // TypeCode.Object: TypeCode.Boolean => OpCodes.Stelem_I1, // TypeCode.Boolean: TypeCode.Char => OpCodes.Stelem_I2, // TypeCode.Char: TypeCode.SByte => OpCodes.Stelem_I1, // TypeCode.SByte: @@ -1135,12 +1023,10 @@ internal void Stelem(Type arrayElementType) Stelem(Enum.GetUnderlyingType(arrayElementType)); else { - OpCode opCode = GetStelemOpCode(arrayElementType.GetTypeCode()); + OpCode opCode = GetStelemOpCode(Type.GetTypeCode(arrayElementType)); if (opCode.Equals(OpCodes.Nop)) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.ArrayTypeIsNotSupported_GeneratingCode, DataContract.GetClrTypeFullName(arrayElementType)))); - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction(opCode.ToString()!); - EmitStackTop(arrayElementType); + _ilGen.Emit(opCode); } } @@ -1153,92 +1039,64 @@ internal Label DefineLabel() internal void MarkLabel(Label label) { _ilGen.MarkLabel(label); - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceLabel(label.GetHashCode() + ":"); } internal void Add() { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Add"); _ilGen.Emit(OpCodes.Add); } internal void Subtract() { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Sub"); _ilGen.Emit(OpCodes.Sub); } internal void And() { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("And"); _ilGen.Emit(OpCodes.And); } internal void Or() { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Or"); _ilGen.Emit(OpCodes.Or); } internal void Not() { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Not"); _ilGen.Emit(OpCodes.Not); } internal void Ret() { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ret"); _ilGen.Emit(OpCodes.Ret); } internal void Br(Label label) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Br " + label.GetHashCode()); _ilGen.Emit(OpCodes.Br, label); } internal void Blt(Label label) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Blt " + label.GetHashCode()); _ilGen.Emit(OpCodes.Blt, label); } internal void Brfalse(Label label) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Brfalse " + label.GetHashCode()); _ilGen.Emit(OpCodes.Brfalse, label); } internal void Brtrue(Label label) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Brtrue " + label.GetHashCode()); _ilGen.Emit(OpCodes.Brtrue, label); } - - internal void Pop() { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Pop"); _ilGen.Emit(OpCodes.Pop); } internal void Dup() { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Dup"); _ilGen.Emit(OpCodes.Dup); } @@ -1296,13 +1154,11 @@ private void InternalConvert(Type source, Type target, bool isAddress) { if (source.IsValueType) { - OpCode opCode = GetConvOpCode(target.GetTypeCode()); + OpCode opCode = GetConvOpCode(Type.GetTypeCode(target)); if (opCode.Equals(OpCodes.Nop)) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.NoConversionPossibleTo, DataContract.GetClrTypeFullName(target)))); else { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction(opCode.ToString()!); _ilGen.Emit(opCode); } } @@ -1351,26 +1207,6 @@ private static void ThrowMismatchException(object expected) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.ExpectingEnd, expected.ToString()))); } - - internal static void EmitSourceInstruction(string line) - { - } - - internal static void EmitSourceLabel(string line) - { - } - - internal static void EmitSourceComment(string comment) - { - } - - - internal void EmitStackTop(Type stackTopType) - { - if (_codeGenTrace != CodeGenTrace.Tron) - return; - } - internal Label[] Switch(int labelCount) { SwitchState switchState = new SwitchState(DefineLabel(), DefineLabel()); @@ -1385,8 +1221,6 @@ internal Label[] Switch(int labelCount) } internal void Case(Label caseLabel1, string caseLabelName) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("case " + caseLabelName + "{"); MarkLabel(caseLabel1); } @@ -1397,8 +1231,6 @@ internal void EndCase() if (switchState == null) ThrowMismatchException(stackTop); Br(switchState.EndOfSwitchLabel); - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("} //end case "); } internal void EndSwitch() @@ -1407,8 +1239,6 @@ internal void EndSwitch() SwitchState? switchState = stackTop as SwitchState; if (switchState == null) ThrowMismatchException(stackTop); - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("} //end switch"); if (!switchState.DefaultDefined) MarkLabel(switchState.DefaultLabel); MarkLabel(switchState.EndOfSwitchLabel); @@ -1490,8 +1320,8 @@ internal sealed class ArgBuilder internal Type ArgType; internal ArgBuilder(int index, Type argType) { - this.Index = index; - this.ArgType = argType; + Index = index; + ArgType = argType; } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CollectionDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CollectionDataContract.cs index df57f68205cf2e..948817e1d31a29 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CollectionDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CollectionDataContract.cs @@ -8,62 +8,14 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; -using System.Runtime.CompilerServices; using System.Security; using System.Threading; using System.Xml; -using DataContractDictionary = System.Collections.Generic.Dictionary; + +using DataContractDictionary = System.Collections.Generic.Dictionary; namespace System.Runtime.Serialization { - // The interface is a perf optimization. - // Only KeyValuePairAdapter should implement the interface. - internal interface IKeyValuePairAdapter { } - - //Special Adapter class to serialize KeyValuePair as Dictionary needs it when polymorphism is involved - [DataContract(Namespace = "http://schemas.datacontract.org/2004/07/System.Collections.Generic")] - internal sealed class KeyValuePairAdapter : IKeyValuePairAdapter - { - private K _kvpKey; - private T _kvpValue; - - public KeyValuePairAdapter(KeyValuePair kvPair) - { - _kvpKey = kvPair.Key; - _kvpValue = kvPair.Value; - } - - [DataMember(Name = "key")] - public K Key - { - get { return _kvpKey; } - set { _kvpKey = value; } - } - - [DataMember(Name = "value")] - public T Value - { - get - { - return _kvpValue; - } - set - { - _kvpValue = value; - } - } - - internal KeyValuePair GetKeyValuePair() - { - return new KeyValuePair(_kvpKey, _kvpValue); - } - - internal static KeyValuePairAdapter GetKeyValuePairAdapter(KeyValuePair kvPair) - { - return new KeyValuePairAdapter(kvPair); - } - } - internal interface IKeyValue { object? Key { get; set; } @@ -73,39 +25,28 @@ internal interface IKeyValue [DataContract(Namespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays")] internal struct KeyValue : IKeyValue { - private K _key; - private V _value; - internal KeyValue(K key, V value) { - _key = key; - _value = value; + Key = key; + Value = value; } [DataMember(IsRequired = true)] - public K Key - { - get { return _key; } - set { _key = value; } - } + public K Key { get; set; } [DataMember(IsRequired = true)] - public V Value - { - get { return _value; } - set { _value = value; } - } + public V Value { get; set; } object? IKeyValue.Key { - get { return _key; } - set { _key = (K)value!; } + get => this.Key; + set => this.Key = (K)value!; } object? IKeyValue.Value { - get { return _value; } - set { _value = (V)value!; } + get => this.Value; + set => this.Value = (V)value!; } } @@ -122,9 +63,15 @@ internal enum CollectionKind : byte Enumerable, Array, } +} +namespace System.Runtime.Serialization.DataContracts +{ internal sealed class CollectionDataContract : DataContract { + internal const string ContractTypeString = nameof(CollectionDataContract); + public override string? ContractType => ContractTypeString; + private XmlDictionaryString _collectionItemName; private XmlDictionaryString? _childElementNamespace; @@ -140,8 +87,20 @@ internal CollectionDataContract(Type type) : base(new CollectionDataContractCrit } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private CollectionDataContract(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, string? deserializationExceptionMessage) - : base(new CollectionDataContractCriticalHelper(type, kind, itemType, getEnumeratorMethod, deserializationExceptionMessage)) + internal CollectionDataContract(Type type, DataContract itemContract) : base(new CollectionDataContractCriticalHelper(type, itemContract)) + { + InitCollectionDataContract(this); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal CollectionDataContract(Type type, CollectionKind kind) : base(new CollectionDataContractCriticalHelper(type, kind)) + { + InitCollectionDataContract(this); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private CollectionDataContract(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, string? serializationExceptionMessage, string? deserializationExceptionMessage) + : base(new CollectionDataContractCriticalHelper(type, kind, itemType, getEnumeratorMethod, serializationExceptionMessage, deserializationExceptionMessage)) { InitCollectionDataContract(GetSharedTypeContract(type)); } @@ -180,32 +139,16 @@ private void InitCollectionDataContract(DataContract? sharedTypeContract) _helper.SharedTypeContract = sharedTypeContract; } - private static Type[] KnownInterfaces - { - get - { return CollectionDataContractCriticalHelper.KnownInterfaces; } - } + private static Type[] KnownInterfaces => CollectionDataContractCriticalHelper.KnownInterfaces; - internal CollectionKind Kind - { - get - { return _helper.Kind; } - } + internal CollectionKind Kind => _helper.Kind; - public Type ItemType - { - get - { return _helper.ItemType; } - set { _helper.ItemType = value; } - } + internal Type ItemType => _helper.ItemType; - public DataContract ItemContract + internal DataContract ItemContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - get - { - return _itemContract ?? _helper.ItemContract; - } + get => _itemContract ?? _helper.ItemContract; set { @@ -214,51 +157,45 @@ public DataContract ItemContract } } - internal DataContract? SharedTypeContract - { - get - { return _helper.SharedTypeContract; } - } + internal DataContract? SharedTypeContract => _helper.SharedTypeContract; - public string ItemName + internal string ItemName { - get - { return _helper.ItemName; } - - set - { _helper.ItemName = value; } + get => _helper.ItemName; + set => _helper.ItemName = value; } - public XmlDictionaryString CollectionItemName + internal XmlDictionaryString CollectionItemName => _collectionItemName; + + internal string? KeyName { - get { return _collectionItemName; } - set { _collectionItemName = value; } + get => _helper.KeyName; + set => _helper.KeyName = value; } - public string? KeyName + internal string? ValueName { - get - { return _helper.KeyName; } - - set - { _helper.KeyName = value; } + get => _helper.ValueName; + set => _helper.ValueName = value; } - public string? ValueName + public override bool IsDictionaryLike([NotNullWhen(true)] out string? keyName, [NotNullWhen(true)] out string? valueName, [NotNullWhen(true)] out string? itemName) { - get - { return _helper.ValueName; } - - set - { _helper.ValueName = value; } + keyName = KeyName; + valueName = ValueName; + itemName = ItemName; + return IsDictionary; } - internal bool IsDictionary + public override DataContract BaseContract { - get { return KeyName != null; } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + get => ItemContract; } - public XmlDictionaryString? ChildElementNamespace + internal bool IsDictionary => KeyName != null; + + internal XmlDictionaryString? ChildElementNamespace { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] get @@ -285,62 +222,38 @@ public XmlDictionaryString? ChildElementNamespace internal bool IsItemTypeNullable { - get { return _helper.IsItemTypeNullable; } - set { _helper.IsItemTypeNullable = value; } + get => _helper.IsItemTypeNullable; + set => _helper.IsItemTypeNullable = value; } internal bool IsConstructorCheckRequired { - get - { return _helper.IsConstructorCheckRequired; } - - set - { _helper.IsConstructorCheckRequired = value; } + get => _helper.IsConstructorCheckRequired; + set => _helper.IsConstructorCheckRequired = value; } - internal MethodInfo? GetEnumeratorMethod - { - get - { return _helper.GetEnumeratorMethod; } - } + internal MethodInfo? GetEnumeratorMethod => _helper.GetEnumeratorMethod; - internal MethodInfo? AddMethod - { - get - { return _helper.AddMethod; } - } + internal MethodInfo? AddMethod => _helper.AddMethod; - internal ConstructorInfo? Constructor - { - get - { return _helper.Constructor; } - } + internal ConstructorInfo? Constructor => _helper.Constructor; public override DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - get - { return _helper.KnownDataContracts; } - - set - { _helper.KnownDataContracts = value; } + get => _helper.KnownDataContracts; + internal set => _helper.KnownDataContracts = value; } - internal string? InvalidCollectionInSharedContractMessage - { - get - { return _helper.InvalidCollectionInSharedContractMessage; } - } + internal string? InvalidCollectionInSharedContractMessage => _helper.InvalidCollectionInSharedContractMessage; - internal string? DeserializationExceptionMessage - { - get { return _helper.DeserializationExceptionMessage; } - } + internal string? SerializationExceptionMessage => _helper.SerializationExceptionMessage; - internal bool IsReadOnlyContract - { - get { return DeserializationExceptionMessage != null; } - } + internal string? DeserializationExceptionMessage => _helper.DeserializationExceptionMessage; + + internal bool IsReadOnlyContract => DeserializationExceptionMessage != null; + + private bool ItemNameSetExplicit => _helper.ItemNameSetExplicit; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private XmlFormatCollectionWriterDelegate CreateXmlFormatWriterDelegate() @@ -367,9 +280,6 @@ internal XmlFormatCollectionWriterDelegate XmlFormatWriterDelegate } return _helper.XmlFormatWriterDelegate; } - set - { - } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -402,9 +312,6 @@ internal XmlFormatCollectionReaderDelegate XmlFormatReaderDelegate } return _helper.XmlFormatReaderDelegate; } - set - { - } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -479,6 +386,7 @@ private sealed class CollectionDataContractCriticalHelper : DataContract.DataCon private readonly MethodInfo? _getEnumeratorMethod; private readonly MethodInfo? _addMethod; private readonly ConstructorInfo? _constructor; + private readonly string? _serializationExceptionMessage; private readonly string? _deserializationExceptionMessage; private DataContract? _itemContract; private DataContract? _sharedTypeContract; @@ -549,9 +457,9 @@ private void Init(CollectionKind kind, Type? itemType, CollectionDataContractAtt } XmlDictionary dictionary = isDictionary ? new XmlDictionary(5) : new XmlDictionary(3); - this.Name = dictionary.Add(this.StableName.Name); - this.Namespace = dictionary.Add(this.StableName.Namespace); - _itemName = itemName ?? DataContract.GetStableName(DataContract.UnwrapNullableType(itemType)).Name; + Name = dictionary.Add(XmlName.Name); + Namespace = dictionary.Add(XmlName.Namespace); + _itemName = itemName ?? DataContract.GetXmlName(DataContract.UnwrapNullableType(itemType)).Name; _collectionItemName = dictionary.Add(_itemName); if (isDictionary) { @@ -561,11 +469,10 @@ private void Init(CollectionKind kind, Type? itemType, CollectionDataContractAtt } if (collectionContractAttribute != null) { - this.IsReference = collectionContractAttribute.IsReference; + IsReference = collectionContractAttribute.IsReference; } } - // array [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal CollectionDataContractCriticalHelper( [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] @@ -575,7 +482,31 @@ internal CollectionDataContractCriticalHelper( type = Globals.TypeOfObjectArray; if (type.GetArrayRank() > 1) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.SupportForMultidimensionalArraysNotPresent)); - this.StableName = DataContract.GetStableName(type); + XmlName = DataContract.GetXmlName(type); + Init(CollectionKind.Array, type.GetElementType(), null); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal CollectionDataContractCriticalHelper( + [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] + Type type, + CollectionKind kind) : base(type) + { + XmlName = DataContract.GetXmlName(type); + Init(kind, type.GetElementType(), null); + } + + // array + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal CollectionDataContractCriticalHelper( + [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] + Type type, + DataContract itemContract) : base(type) + { + if (type.GetArrayRank() > 1) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.SupportForMultidimensionalArraysNotPresent)); + XmlName = CreateQualifiedName(Globals.ArrayPrefix + itemContract.XmlName.Name, itemContract.XmlName.Namespace); + _itemContract = itemContract; Init(CollectionKind.Array, type.GetElementType(), null); } @@ -584,7 +515,7 @@ internal CollectionDataContractCriticalHelper( internal CollectionDataContractCriticalHelper( [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] Type type, - CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, string? deserializationExceptionMessage) + CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, string? serializationExceptionMessage, string? deserializationExceptionMessage) : base(type) { if (getEnumeratorMethod == null) @@ -593,10 +524,11 @@ internal CollectionDataContractCriticalHelper( throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.CollectionMustHaveItemType, GetClrTypeFullName(type)))); CollectionDataContractAttribute? collectionContractAttribute; - this.StableName = DataContract.GetCollectionStableName(type, itemType, out collectionContractAttribute); + XmlName = DataContract.GetCollectionXmlName(type, itemType, out collectionContractAttribute); Init(kind, itemType, collectionContractAttribute); _getEnumeratorMethod = getEnumeratorMethod; + _serializationExceptionMessage = serializationExceptionMessage; _deserializationExceptionMessage = deserializationExceptionMessage; } @@ -605,20 +537,12 @@ internal CollectionDataContractCriticalHelper( internal CollectionDataContractCriticalHelper( [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] Type type, - CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, MethodInfo? addMethod, ConstructorInfo? constructor) : base(type) + CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, MethodInfo? addMethod, ConstructorInfo? constructor) + : this(type, kind, itemType, getEnumeratorMethod, (string?)null, (string?)null) { - if (getEnumeratorMethod == null) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.CollectionMustHaveGetEnumeratorMethod, DataContract.GetClrTypeFullName(type)))); if (addMethod == null && !type.IsInterface) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.CollectionMustHaveAddMethod, DataContract.GetClrTypeFullName(type)))); - if (itemType == null) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.CollectionMustHaveItemType, DataContract.GetClrTypeFullName(type)))); - - CollectionDataContractAttribute? collectionContractAttribute; - this.StableName = DataContract.GetCollectionStableName(type, itemType, out collectionContractAttribute); - Init(kind, itemType, collectionContractAttribute); - _getEnumeratorMethod = getEnumeratorMethod; _addMethod = addMethod; _constructor = constructor; } @@ -641,16 +565,9 @@ internal CollectionDataContractCriticalHelper( _invalidCollectionInSharedContractMessage = invalidCollectionInSharedContractMessage; } - internal CollectionKind Kind - { - get { return _kind; } - } + internal CollectionKind Kind => _kind; - internal Type ItemType - { - get { return _itemType; } - set { _itemType = value; } - } + internal Type ItemType => _itemType; internal DataContract ItemContract { @@ -675,9 +592,7 @@ internal DataContract ItemContract } else { - _itemContract = - DataContract.GetDataContractFromGeneratedAssembly(ItemType) ?? - DataContract.GetDataContract(ItemType); + _itemContract = DataContract.GetDataContract(ItemType); } } return _itemContract; @@ -690,53 +605,52 @@ internal DataContract ItemContract internal DataContract? SharedTypeContract { - get { return _sharedTypeContract; } - set { _sharedTypeContract = value; } + get => _sharedTypeContract; + set => _sharedTypeContract = value; } internal string ItemName { - get { return _itemName; } - set { _itemName = value; } + get => _itemName; + set => _itemName = value; } internal bool IsConstructorCheckRequired { - get { return _isConstructorCheckRequired; } - set { _isConstructorCheckRequired = value; } + get => _isConstructorCheckRequired; + set => _isConstructorCheckRequired = value; } - public XmlDictionaryString CollectionItemName - { - get { return _collectionItemName; } - } + internal XmlDictionaryString CollectionItemName => _collectionItemName; internal string? KeyName { - get { return _keyName; } - set { _keyName = value; } + get => _keyName; + set => _keyName = value; } internal string? ValueName { - get { return _valueName; } - set { _valueName = value; } + get => _valueName; + set => _valueName = value; } internal bool IsDictionary => KeyName != null; - public string? DeserializationExceptionMessage => _deserializationExceptionMessage; + internal string? SerializationExceptionMessage => _serializationExceptionMessage; + + internal string? DeserializationExceptionMessage => _deserializationExceptionMessage; - public XmlDictionaryString? ChildElementNamespace + internal XmlDictionaryString? ChildElementNamespace { - get { return _childElementNamespace; } - set { _childElementNamespace = value; } + get => _childElementNamespace; + set => _childElementNamespace = value; } internal bool IsItemTypeNullable { - get { return _isItemTypeNullable; } - set { _isItemTypeNullable = value; } + get => _isItemTypeNullable; + set => _isItemTypeNullable = value; } internal MethodInfo? GetEnumeratorMethod => _getEnumeratorMethod; @@ -756,10 +670,11 @@ internal override DataContractDictionary? KnownDataContracts { if (!_isKnownTypeAttributeChecked) { - _knownDataContracts = DataContract.ImportKnownTypeAttributes(this.UnderlyingType); + _knownDataContracts = DataContract.ImportKnownTypeAttributes(UnderlyingType); Interlocked.MemoryBarrier(); _isKnownTypeAttributeChecked = true; } + _knownDataContracts ??= new DataContractDictionary(); } } return _knownDataContracts; @@ -775,20 +690,20 @@ internal override DataContractDictionary? KnownDataContracts internal XmlFormatCollectionWriterDelegate? XmlFormatWriterDelegate { - get { return _xmlFormatWriterDelegate; } - set { _xmlFormatWriterDelegate = value; } + get => _xmlFormatWriterDelegate; + set => _xmlFormatWriterDelegate = value; } internal XmlFormatCollectionReaderDelegate? XmlFormatReaderDelegate { - get { return _xmlFormatReaderDelegate; } - set { _xmlFormatReaderDelegate = value; } + get => _xmlFormatReaderDelegate; + set => _xmlFormatReaderDelegate = value; } internal XmlFormatGetOnlyCollectionReaderDelegate? XmlFormatGetOnlyCollectionReaderDelegate { - get { return _xmlFormatGetOnlyCollectionReaderDelegate; } - set { _xmlFormatGetOnlyCollectionReaderDelegate = value; } + get => _xmlFormatGetOnlyCollectionReaderDelegate; + set => _xmlFormatGetOnlyCollectionReaderDelegate = value; } private delegate void IncrementCollectionCountDelegate(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context); @@ -816,13 +731,13 @@ internal void IncrementCollectionCount(XmlWriterDelegator xmlWriter, object obj, case CollectionKind.GenericCollection: case CollectionKind.GenericList: { - var buildIncrementCollectionCountDelegate = GetBuildIncrementCollectionCountGenericDelegate(ItemType); + MethodInfo? buildIncrementCollectionCountDelegate = GetBuildIncrementCollectionCountGenericDelegate(ItemType); _incrementCollectionCountDelegate = (IncrementCollectionCountDelegate)buildIncrementCollectionCountDelegate.Invoke(null, Array.Empty())!; } break; case CollectionKind.GenericDictionary: { - var buildIncrementCollectionCountDelegate = GetBuildIncrementCollectionCountGenericDelegate(typeof(KeyValuePair<,>).MakeGenericType(ItemType.GetGenericArguments())); + MethodInfo? buildIncrementCollectionCountDelegate = GetBuildIncrementCollectionCountGenericDelegate(typeof(KeyValuePair<,>).MakeGenericType(ItemType.GetGenericArguments())); _incrementCollectionCountDelegate = (IncrementCollectionCountDelegate)buildIncrementCollectionCountDelegate.Invoke(null, Array.Empty())!; } break; @@ -868,7 +783,7 @@ internal IEnumerator GetEnumeratorForCollection(object obj) { if (_createGenericDictionaryEnumeratorDelegate == null) { - var keyValueTypes = ItemType.GetGenericArguments(); + Type[]? keyValueTypes = ItemType.GetGenericArguments(); MethodInfo buildCreateGenericDictionaryEnumerator = GetBuildCreateGenericDictionaryEnumeratorGenericMethod(keyValueTypes); _createGenericDictionaryEnumeratorDelegate = (CreateGenericDictionaryEnumeratorDelegate)buildCreateGenericDictionaryEnumerator.Invoke(null, Array.Empty())!; } @@ -962,7 +877,7 @@ private static CreateGenericDictionaryEnumeratorDelegate BuildCreateGenericDicti { return this; } - if (type.IsDefined(Globals.TypeOfDataContractAttribute, false)) + if (type.IsSerializable || type.IsDefined(Globals.TypeOfDataContractAttribute, false)) { return new ClassDataContract(type); } @@ -989,20 +904,20 @@ internal static bool IsCollection(Type type, [NotNullWhen(true)] out Type? itemT } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static bool IsCollection(Type type, bool constructorRequired) + internal static bool IsCollection(Type type, bool constructorRequired, bool skipIfReadOnlyContract) { - return IsCollectionHelper(type, out _, constructorRequired); + return IsCollectionHelper(type, out _, constructorRequired, skipIfReadOnlyContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private static bool IsCollectionHelper(Type type, [NotNullWhen(true)] out Type? itemType, bool constructorRequired) + private static bool IsCollectionHelper(Type type, [NotNullWhen(true)] out Type? itemType, bool constructorRequired, bool skipIfReadOnlyContract = false) { if (type.IsArray && DataContract.GetBuiltInDataContract(type) == null) { itemType = type.GetElementType()!; return true; } - return IsCollectionOrTryCreate(type, tryCreate: false, out _, out itemType, constructorRequired); + return IsCollectionOrTryCreate(type, tryCreate: false, out _, out itemType, constructorRequired, skipIfReadOnlyContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -1012,7 +927,7 @@ internal static bool TryCreate(Type type, [NotNullWhen(true)] out DataContract? } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static bool CreateGetOnlyCollectionDataContract(Type type, [NotNullWhen(true)] out DataContract? dataContract) + internal static bool TryCreateGetOnlyCollectionDataContract(Type type, [NotNullWhen(true)] out DataContract? dataContract) { if (type.IsArray) { @@ -1025,36 +940,6 @@ internal static bool CreateGetOnlyCollectionDataContract(Type type, [NotNullWhen } } - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static bool TryCreateGetOnlyCollectionDataContract(Type type, [NotNullWhen(true)] out DataContract? dataContract) - { - dataContract = DataContract.GetDataContractFromGeneratedAssembly(type); - if (dataContract == null) - { - if (type.IsArray) - { - dataContract = new CollectionDataContract(type); - return true; - } - else - { - return IsCollectionOrTryCreate(type, tryCreate: true, out dataContract!, out _, constructorRequired: false); - } - } - else - { - if (dataContract is CollectionDataContract) - { - return true; - } - else - { - dataContract = null; - return false; - } - } - } - // Once https://github.com/mono/linker/issues/1731 is fixed we can remove the suppression from here as it won't be needed any longer. [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:GetMethod", Justification = "The DynamicallyAccessedMembers declarations will ensure the interface methods will be preserved.")] @@ -1074,7 +959,7 @@ private static bool IsArraySegment(Type t) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private static bool IsCollectionOrTryCreate(Type type, bool tryCreate, out DataContract? dataContract, out Type itemType, bool constructorRequired) + private static bool IsCollectionOrTryCreate(Type type, bool tryCreate, out DataContract? dataContract, out Type itemType, bool constructorRequired, bool skipIfReadOnlyContract = false) { dataContract = null; itemType = Globals.TypeOfObject; @@ -1088,6 +973,7 @@ private static bool IsCollectionOrTryCreate(Type type, bool tryCreate, out DataC MethodInfo? addMethod, getEnumeratorMethod; bool hasCollectionDataContract = IsCollectionDataContract(type); bool isReadOnlyContract = false; + string? serializationExceptionMessage = null; string? deserializationExceptionMessage = null; Type? baseType = type.BaseType; bool isBaseTypeCollection = (baseType != null && baseType != Globals.TypeOfObject @@ -1184,7 +1070,7 @@ private static bool IsCollectionOrTryCreate(Type type, bool tryCreate, out DataC else { isReadOnlyContract = true; - GetReadOnlyCollectionExceptionMessages(type, SR.CollectionTypeDoesNotHaveDefaultCtor, null, out deserializationExceptionMessage); + GetReadOnlyCollectionExceptionMessages(type, hasCollectionDataContract, SR.CollectionTypeDoesNotHaveDefaultCtor, null, out serializationExceptionMessage, out deserializationExceptionMessage); } } } @@ -1238,22 +1124,22 @@ private static bool IsCollectionOrTryCreate(Type type, bool tryCreate, out DataC // All collection types could be considered read-only collections except collection types that are marked [Serializable]. // Collection types marked [Serializable] cannot be read-only collections for backward compatibility reasons. // DataContract types and POCO types cannot be collection types, so they don't need to be factored in. - if (type.IsSerializable) + if (type.IsSerializable || skipIfReadOnlyContract) { - return HandleIfInvalidCollection(type, tryCreate, hasCollectionDataContract, createContractWithException, + return HandleIfInvalidCollection(type, tryCreate, hasCollectionDataContract, createContractWithException && !skipIfReadOnlyContract, SR.CollectionTypeDoesNotHaveAddMethod, DataContract.GetClrTypeFullName(itemType), ref dataContract); } else { isReadOnlyContract = true; - GetReadOnlyCollectionExceptionMessages(type, SR.CollectionTypeDoesNotHaveAddMethod, DataContract.GetClrTypeFullName(itemType), out deserializationExceptionMessage); + GetReadOnlyCollectionExceptionMessages(type, hasCollectionDataContract, SR.CollectionTypeDoesNotHaveAddMethod, DataContract.GetClrTypeFullName(itemType), out serializationExceptionMessage, out deserializationExceptionMessage); } } if (tryCreate) { dataContract = isReadOnlyContract ? - new CollectionDataContract(type, kind, itemType, getEnumeratorMethod, deserializationExceptionMessage) : + new CollectionDataContract(type, kind, itemType, getEnumeratorMethod, serializationExceptionMessage, deserializationExceptionMessage) : new CollectionDataContract(type, kind, itemType, getEnumeratorMethod, addMethod, defaultCtor, !constructorRequired); } } @@ -1298,12 +1184,12 @@ private static bool IsCollectionOrTryCreate(Type type, bool tryCreate, out DataC Debug.Assert(getEnumeratorMethod != null); dataContract = isReadOnlyContract ? - new CollectionDataContract(type, kind, itemType, getEnumeratorMethod, deserializationExceptionMessage) : + new CollectionDataContract(type, kind, itemType, getEnumeratorMethod, serializationExceptionMessage, deserializationExceptionMessage) : new CollectionDataContract(type, kind, itemType, getEnumeratorMethod, addMethod, defaultCtor, !constructorRequired); } } - return true; + return !(isReadOnlyContract && skipIfReadOnlyContract); } internal static bool IsCollectionDataContract(Type type) @@ -1331,8 +1217,9 @@ private static bool HandleIfInvalidCollection(Type type, bool tryCreate, bool ha return false; } - private static void GetReadOnlyCollectionExceptionMessages(Type type, string message, string? param, out string deserializationExceptionMessage) + private static void GetReadOnlyCollectionExceptionMessages(Type type, bool hasCollectionDataContract, string message, string? param, out string serializationExceptionMessage, out string deserializationExceptionMessage) { + serializationExceptionMessage = GetInvalidCollectionMessage(message, SR.Format(hasCollectionDataContract ? SR.InvalidCollectionDataContract : SR.InvalidCollectionType, GetClrTypeFullName(type)), param); deserializationExceptionMessage = GetInvalidCollectionMessage(message, SR.Format(SR.ReadOnlyCollectionDeserialization, GetClrTypeFullName(type)), param); } @@ -1435,36 +1322,64 @@ private static bool IsKnownInterface(Type type) return false; } - internal override DataContract GetValidContract(SerializationMode mode) + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal override DataContract BindGenericParameters(DataContract[] paramContracts, Dictionary? boundContracts = null) { - if (InvalidCollectionInSharedContractMessage != null) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(InvalidCollectionInSharedContractMessage)); + DataContract boundContract; + if (boundContracts != null && boundContracts.TryGetValue(this, out boundContract!)) + return boundContract; - return this; + // This type-binding ('boundType') stuff is new. We did not do this in NetFx. We used to use default contract constructors and let the + // underlying type get filled in later. But default constructors for DataContracts runs afoul of requiring an underlying type. Our web of nullable + // notations make it hard to get around. But it also allows us to feel good about using .UnderlyingType from matching parameter contracts. + Type type = UnderlyingType; + Type[] paramTypes = type.GetGenericArguments(); + for (int i = 0; i < paramTypes.Length; i++) + { + if (paramTypes[i].IsGenericParameter) + paramTypes[i] = paramContracts[paramTypes[i].GenericParameterPosition].UnderlyingType; + } + Type boundType = type.MakeGenericType(paramTypes); + + CollectionDataContract boundCollectionContract = new CollectionDataContract(boundType); + boundContracts ??= new Dictionary(); + boundContracts.Add(this, boundCollectionContract); + boundCollectionContract.ItemContract = ItemContract.BindGenericParameters(paramContracts, boundContracts); + boundCollectionContract.IsItemTypeNullable = !boundCollectionContract.ItemContract.IsValueType; + boundCollectionContract.ItemName = ItemNameSetExplicit ? ItemName : boundCollectionContract.ItemContract.XmlName.Name; + boundCollectionContract.KeyName = KeyName; + boundCollectionContract.ValueName = ValueName; + boundCollectionContract.XmlName = CreateQualifiedName(DataContract.ExpandGenericParameters(XmlConvert.DecodeName(XmlName.Name), new GenericNameProvider(DataContract.GetClrTypeFullName(UnderlyingType), paramContracts)), + IsCollectionDataContract(UnderlyingType) ? XmlName.Namespace : DataContract.GetCollectionNamespace(boundCollectionContract.ItemContract.XmlName.Namespace)); + return boundCollectionContract; } - internal override DataContract GetValidContract() + internal override DataContract GetValidContract(bool verifyConstructor = false) { - if (this.IsConstructorCheckRequired) + if (verifyConstructor && IsConstructorCheckRequired) { CheckConstructor(); + return this; } + + if (InvalidCollectionInSharedContractMessage != null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(InvalidCollectionInSharedContractMessage)); return this; } private void CheckConstructor() { - if (this.Constructor == null) + if (Constructor == null) { - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.CollectionTypeDoesNotHaveDefaultCtor, DataContract.GetClrTypeFullName(this.UnderlyingType)))); + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.CollectionTypeDoesNotHaveDefaultCtor, DataContract.GetClrTypeFullName(UnderlyingType)))); } else { - this.IsConstructorCheckRequired = false; + IsConstructorCheckRequired = false; } } - internal override bool IsValidContract(SerializationMode mode) + internal override bool IsValidContract() { return (InvalidCollectionInSharedContractMessage == null); } @@ -1512,7 +1427,7 @@ internal bool RequiresMemberAccessForRead(SecurityException? securityException) } return true; } - if (MethodRequiresMemberAccess(this.AddMethod)) + if (MethodRequiresMemberAccess(AddMethod)) { if (securityException != null) { @@ -1520,7 +1435,7 @@ internal bool RequiresMemberAccessForRead(SecurityException? securityException) new SecurityException(SR.Format( SR.PartialTrustCollectionContractAddMethodNotPublic, DataContract.GetClrTypeFullName(UnderlyingType), - this.AddMethod!.Name), + AddMethod!.Name), securityException)); } return true; @@ -1564,8 +1479,31 @@ internal bool RequiresMemberAccessForWrite(SecurityException? securityException) return false; } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "All ctor's required to create an instance of this type are marked with RequiresUnreferencedCode.")] + internal override bool Equals(object? other, HashSet? checkedContracts) + { + if (IsEqualOrChecked(other, checkedContracts)) + return true; + + if (base.Equals(other, checkedContracts)) + { + if (other is CollectionDataContract dataContract) + { + bool thisItemTypeIsNullable = (ItemContract == null) ? false : !ItemContract.IsValueType; + bool otherItemTypeIsNullable = (dataContract.ItemContract == null) ? false : !dataContract.ItemContract.IsValueType; + return ItemName == dataContract.ItemName && + (IsItemTypeNullable || thisItemTypeIsNullable) == (dataContract.IsItemTypeNullable || otherItemTypeIsNullable) && + ItemContract != null && ItemContract.Equals(dataContract.ItemContract, checkedContracts); + } + } + return false; + } + + public override int GetHashCode() => base.GetHashCode(); + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { Debug.Assert(context != null); @@ -1575,7 +1513,7 @@ public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, Xml } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) { Debug.Assert(context != null); @@ -1614,15 +1552,9 @@ public bool MoveNext() return _enumerator.MoveNext(); } - public KeyValue Current - { - get { return new KeyValue(_enumerator.Key, _enumerator.Value); } - } + public KeyValue Current => new KeyValue(_enumerator.Key, _enumerator.Value); - object System.Collections.IEnumerator.Current - { - get { return Current; } - } + object System.Collections.IEnumerator.Current => Current; public void Reset() { @@ -1658,10 +1590,7 @@ public KeyValue Current } } - object System.Collections.IEnumerator.Current - { - get { return Current; } - } + object System.Collections.IEnumerator.Current => Current; public void Reset() { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs index f859f2f518ceff..d61002c23a1234 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs @@ -3,39 +3,40 @@ using System; using System.Buffers.Binary; -using System.Collections.Concurrent; +using System.Collections; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; +using System.Runtime.Serialization.DataContracts; using System.Text; -using System.Text.RegularExpressions; using System.Xml; -using System.Xml.Schema; -using DataContractDictionary = System.Collections.Generic.Dictionary; -namespace System.Runtime.Serialization +using DataContractDictionary = System.Collections.Generic.Dictionary; + +namespace System.Runtime.Serialization.DataContracts { - internal abstract class DataContract + public abstract class DataContract { - private XmlDictionaryString _name; - - private XmlDictionaryString _ns; - - // this the global dictionary for data contracts introduced for multi-file. - private static readonly Dictionary s_dataContracts = new Dictionary(); - internal const string SerializerTrimmerWarning = "Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the " + "required types are preserved."; - public static Dictionary GetDataContracts() - { - return s_dataContracts; - } + internal const DynamicallyAccessedMemberTypes DataContractPreserveMemberTypes = + DynamicallyAccessedMemberTypes.PublicMethods | + DynamicallyAccessedMemberTypes.NonPublicMethods | + DynamicallyAccessedMemberTypes.PublicConstructors | + DynamicallyAccessedMemberTypes.NonPublicConstructors | + DynamicallyAccessedMemberTypes.PublicFields | + DynamicallyAccessedMemberTypes.PublicProperties; + + internal static ReadOnlyCollection s_emptyDataMemberList = new List().AsReadOnly(); + private XmlDictionaryString _name; + private XmlDictionaryString _ns; private readonly DataContractCriticalHelper _helper; internal DataContract(DataContractCriticalHelper helper) @@ -45,29 +46,9 @@ internal DataContract(DataContractCriticalHelper helper) _ns = helper.Namespace; } - private static DataContract? GetGeneratedDataContract(Type type) - { - // this method used to be rewritten by an IL transform - // with the restructuring for multi-file, it has become a regular method - DataContract? result; - return s_dataContracts.TryGetValue(type, out result) ? result : null; - } - - internal static bool TryGetDataContractFromGeneratedAssembly(Type type, out DataContract? dataContract) - { - dataContract = GetGeneratedDataContract(type); - return dataContract != null; - } + public virtual string? ContractType => null; - internal static DataContract? GetDataContractFromGeneratedAssembly(Type? type) - { - return null; - } - - internal MethodInfo? ParseMethod - { - get { return _helper.ParseMethod; } - } + internal MethodInfo? ParseMethod => _helper.ParseMethod; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal static DataContract GetDataContract(Type type) @@ -76,24 +57,18 @@ internal static DataContract GetDataContract(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type type) - { - return GetDataContract(typeHandle, type, SerializationMode.SharedContract); - } - - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type? type, SerializationMode mode) + internal static DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type? type) { int id = GetId(typeHandle); DataContract dataContract = GetDataContractSkipValidation(id, typeHandle, null); - return dataContract.GetValidContract(mode); + return dataContract.GetValidContract(); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static DataContract GetDataContract(int id, RuntimeTypeHandle typeHandle, SerializationMode mode) + internal static DataContract GetDataContract(int id, RuntimeTypeHandle typeHandle) { DataContract dataContract = GetDataContractSkipValidation(id, typeHandle, null); - return dataContract.GetValidContract(mode); + return dataContract.GetValidContract(); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -103,10 +78,10 @@ internal static DataContract GetDataContractSkipValidation(int id, RuntimeTypeHa } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static DataContract GetGetOnlyCollectionDataContract(int id, RuntimeTypeHandle typeHandle, Type? type, SerializationMode mode) + internal static DataContract GetGetOnlyCollectionDataContract(int id, RuntimeTypeHandle typeHandle, Type? type) { DataContract dataContract = GetGetOnlyCollectionDataContractSkipValidation(id, typeHandle, type); - dataContract = dataContract.GetValidContract(mode); + dataContract = dataContract.GetValidContract(); if (dataContract is ClassDataContract) { throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SerializationException(SR.Format(SR.ErrorDeserializing, SR.Format(SR.ErrorTypeInfo, DataContract.GetClrTypeFullName(dataContract.UnderlyingType)), SR.Format(SR.NoSetMethodForProperty, string.Empty, string.Empty)))); @@ -136,7 +111,7 @@ internal static int GetId(RuntimeTypeHandle typeHandle) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public static DataContract? GetBuiltInDataContract(Type type) + internal static DataContract? GetBuiltInDataContract(Type type) { return DataContractCriticalHelper.GetBuiltInDataContract(type); } @@ -148,7 +123,7 @@ internal static int GetId(RuntimeTypeHandle typeHandle) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public static DataContract? GetBuiltInDataContract(string typeName) + internal static DataContract? GetBuiltInDataContract(string typeName) { return DataContractCriticalHelper.GetBuiltInDataContract(typeName); } @@ -169,157 +144,118 @@ internal static void ThrowInvalidDataContractException(string? message, Type? ty DataContractCriticalHelper.ThrowInvalidDataContractException(message, type); } - protected DataContractCriticalHelper Helper - { - get - { return _helper; } - } + internal DataContractCriticalHelper Helper => _helper; [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] - public Type UnderlyingType - { - get - { return _helper.UnderlyingType; } - set { _helper.UnderlyingType = value; } - } + public virtual Type UnderlyingType => _helper.UnderlyingType; - public Type OriginalUnderlyingType - { - get { return _helper.OriginalUnderlyingType; } - set { _helper.OriginalUnderlyingType = value; } - } + public virtual Type OriginalUnderlyingType => _helper.OriginalUnderlyingType; - public virtual bool IsBuiltInDataContract - { - get - { return _helper.IsBuiltInDataContract; } - set { } - } + public virtual bool IsBuiltInDataContract => _helper.IsBuiltInDataContract; - internal Type TypeForInitialization - { - get - { return _helper.TypeForInitialization; } - } + internal Type TypeForInitialization => _helper.TypeForInitialization; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public virtual void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) + internal virtual void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(this.GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public virtual object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) + internal virtual object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) { - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(this.GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public virtual void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal virtual void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(this.GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); } - public virtual object ReadXmlElement(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context) + internal virtual object ReadXmlElement(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context) { - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(this.GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); } - public bool IsValueType + public virtual bool IsValueType { - get - { return _helper.IsValueType; } - - set - { _helper.IsValueType = value; } + get => _helper.IsValueType; + internal set => _helper.IsValueType = value; } - public bool IsReference + public virtual bool IsReference { - get - { return _helper.IsReference; } - - set - { _helper.IsReference = value; } + get => _helper.IsReference; + internal set => _helper.IsReference = value; } - public XmlQualifiedName StableName + public virtual XmlQualifiedName XmlName { - get - { return _helper.StableName; } - - set - { _helper.StableName = value; } + get => _helper.XmlName; + internal set => _helper.XmlName = value; } - public virtual DataContractDictionary? KnownDataContracts + public virtual DataContract? BaseContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - get - { return _helper.KnownDataContracts; } - - set - { _helper.KnownDataContracts = value; } + get => null; } - public virtual bool IsISerializable + internal GenericInfo? GenericInfo { - get { return _helper.IsISerializable; } - - set { _helper.IsISerializable = value; } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + get => _helper.GenericInfo; + set => _helper.GenericInfo = value; } - public XmlDictionaryString Name + public virtual DataContractDictionary? KnownDataContracts { - get - { return _name; } - set { _name = value; } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + get => _helper.KnownDataContracts; + internal set => _helper.KnownDataContracts = value; } - public virtual XmlDictionaryString Namespace + public virtual bool IsISerializable { - get - { return _ns; } - set { _ns = value; } + get => _helper.IsISerializable; + internal set => _helper.IsISerializable = value; } - public virtual bool HasRoot - { - get - { return true; } + internal XmlDictionaryString Name => _name; + + internal virtual XmlDictionaryString Namespace => _ns; - set - { } + internal virtual bool HasRoot + { + get => _helper.HasRoot; + set => _helper.HasRoot = value; } public virtual XmlDictionaryString? TopLevelElementName { - get - { return _helper.TopLevelElementName; } - - set - { _helper.TopLevelElementName = value; } + get => _helper.TopLevelElementName; + internal set => _helper.TopLevelElementName = value; } public virtual XmlDictionaryString? TopLevelElementNamespace { - get - { return _helper.TopLevelElementNamespace; } - - set - { _helper.TopLevelElementNamespace = value; } + get => _helper.TopLevelElementNamespace; + internal set => _helper.TopLevelElementNamespace = value; } - internal virtual bool CanContainReferences - { - get { return true; } - } + internal virtual bool CanContainReferences => true; - internal virtual bool IsPrimitive + internal virtual bool IsPrimitive => false; + + public virtual bool IsDictionaryLike([NotNullWhen(true)] out string? keyName, [NotNullWhen(true)] out string? valueName, [NotNullWhen(true)] out string? itemName) { - get { return false; } + keyName = valueName = itemName = null; + return false; } + public virtual ReadOnlyCollection DataMembers => s_emptyDataMemberList; + internal virtual void WriteRootElement(XmlWriterDelegator writer, XmlDictionaryString name, XmlDictionaryString? ns) { if (object.ReferenceEquals(ns, DictionaryGlobals.SerializationNamespace) && !IsPrimitive) @@ -328,32 +264,36 @@ internal virtual void WriteRootElement(XmlWriterDelegator writer, XmlDictionaryS writer.WriteStartElement(name, ns); } - internal virtual DataContract GetValidContract(SerializationMode mode) + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal virtual DataContract BindGenericParameters(DataContract[] paramContracts, Dictionary? boundContracts = null) { return this; } - internal virtual DataContract GetValidContract() + internal virtual DataContract GetValidContract(bool verifyConstructor = false) { return this; } - internal virtual bool IsValidContract(SerializationMode mode) + internal virtual bool IsValidContract() { return true; } internal class DataContractCriticalHelper { - private static readonly Dictionary s_typeToIDCache = new Dictionary(new TypeHandleRefEqualityComparer()); + private static readonly Hashtable s_typeToIDCache = new Hashtable(new HashTableEqualityComparer()); private static DataContract[] s_dataContractCache = new DataContract[32]; private static int s_dataContractID; private static Dictionary? s_typeToBuiltInContract; private static Dictionary? s_nameToBuiltInContract; - private static Dictionary? s_namespaces; + private static Dictionary? s_typeNameToBuiltInContract; + private static Hashtable s_namespaces = new Hashtable(); private static Dictionary? s_clrTypeStrings; private static XmlDictionary? s_clrTypeStringsDictionary; - private static readonly TypeHandleRef s_typeHandleRef = new TypeHandleRef(); + + [ThreadStatic] + private static TypeHandleRef? s_typeHandleRef; private static readonly object s_cacheLock = new object(); private static readonly object s_createDataContractLock = new object(); @@ -362,11 +302,11 @@ internal class DataContractCriticalHelper private static readonly object s_clrTypeStringsLock = new object(); [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] - private Type _underlyingType; + private readonly Type _underlyingType; private Type? _originalUnderlyingType; - private bool _isReference; private bool _isValueType; - private XmlQualifiedName _stableName = null!; // StableName is always set in concrete ctors set except for the "invalid" CollectionDataContract + private GenericInfo? _genericInfo; + private XmlQualifiedName _xmlName = null!; // XmlName is always set in concrete ctors set except for the "invalid" CollectionDataContract private XmlDictionaryString _name = null!; // Name is always set in concrete ctors set except for the "invalid" CollectionDataContract private XmlDictionaryString _ns = null!; // Namespace is always set in concrete ctors set except for the "invalid" CollectionDataContract @@ -385,11 +325,10 @@ internal static DataContract GetDataContractSkipValidation(int id, RuntimeTypeHa if (dataContract == null) { dataContract = CreateDataContract(id, typeHandle, type); - AssignDataContractToId(dataContract, id); } else { - return dataContract.GetValidContract(); + return dataContract.GetValidContract(verifyConstructor: true); } return dataContract; } @@ -401,7 +340,6 @@ internal static DataContract GetGetOnlyCollectionDataContractSkipValidation(int if (dataContract == null) { dataContract = CreateGetOnlyCollectionDataContract(id, typeHandle, type); - s_dataContractCache[id] = dataContract; } return dataContract; } @@ -418,7 +356,7 @@ internal static DataContract GetDataContractForInitialization(int id) internal static int GetIdForInitialization(ClassDataContract classContract) { - int id = DataContract.GetId(classContract.TypeForInitialization!.TypeHandle); + int id = DataContract.GetId(classContract.TypeForInitialization.TypeHandle); if (id < s_dataContractCache.Length && ContractMatches(classContract, s_dataContractCache[id])) { return id; @@ -441,35 +379,45 @@ private static bool ContractMatches(DataContract contract, DataContract cachedCo internal static int GetId(RuntimeTypeHandle typeHandle) { - lock (s_cacheLock) + typeHandle = GetDataContractAdapterTypeHandle(typeHandle); + s_typeHandleRef ??= new TypeHandleRef(); + s_typeHandleRef.Value = typeHandle; + + object? value = s_typeToIDCache[s_typeHandleRef]; + if (value != null) + return ((IntRef)value).Value; + + try { - IntRef? id; - typeHandle = GetDataContractAdapterTypeHandle(typeHandle); - s_typeHandleRef.Value = typeHandle; - if (!s_typeToIDCache.TryGetValue(s_typeHandleRef, out id)) + lock (s_cacheLock) { - int value = s_dataContractID++; - if (value >= s_dataContractCache.Length) + value = s_typeToIDCache[s_typeHandleRef]; + if (value != null) + return ((IntRef)value).Value; + + int nextId = s_dataContractID++; + if (nextId >= s_dataContractCache.Length) { - int newSize = (value < int.MaxValue / 2) ? value * 2 : int.MaxValue; - if (newSize <= value) + int newSize = (nextId < int.MaxValue / 2) ? nextId * 2 : int.MaxValue; + if (newSize <= nextId) { DiagnosticUtility.DebugAssert("DataContract cache overflow"); throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SerializationException(SR.DataContractCacheOverflow)); } Array.Resize(ref s_dataContractCache, newSize); } - id = new IntRef(value); - try - { - s_typeToIDCache.Add(new TypeHandleRef(typeHandle), id); - } - catch (Exception ex) - { - throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(ex.Message, ex); - } + IntRef id = new IntRef(nextId); + + s_typeToIDCache.Add(new TypeHandleRef(typeHandle), id); + return id.Value; } - return id.Value; + } + catch (Exception ex) + { + if (Fx.IsFatal(ex)) + throw; + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(ex.Message, ex); } } @@ -486,16 +434,8 @@ private static DataContract CreateDataContract(int id, RuntimeTypeHandle typeHan if (dataContract == null) { type ??= Type.GetTypeFromHandle(typeHandle)!; - type = UnwrapNullableType(type); - - dataContract = DataContract.GetDataContractFromGeneratedAssembly(type); - if (dataContract != null) - { - AssignDataContractToId(dataContract, id); - return dataContract; - } - dataContract = CreateDataContract(type); + AssignDataContractToId(dataContract, id); } } } @@ -517,18 +457,18 @@ private static DataContract CreateDataContract(Type type) dataContract = new CollectionDataContract(type); else if (type.IsEnum) dataContract = new EnumDataContract(type); + else if (type.IsGenericParameter) + dataContract = new GenericParameterDataContract(type); else if (Globals.TypeOfIXmlSerializable.IsAssignableFrom(type)) dataContract = new XmlDataContract(type); - else if (Globals.TypeOfScriptObject_IsAssignableFrom(type)) - dataContract = Globals.CreateScriptObjectClassDataContract(); else { - //if (type.ContainsGenericParameters) - // ThrowInvalidDataContractException(SR.Format(SR.TypeMustNotBeOpenGeneric, type), type); + if (type.IsPointer) + type = Globals.TypeOfReflectionPointer; if (!CollectionDataContract.TryCreate(type, out dataContract)) { - if (!type.IsSerializable && !type.IsDefined(Globals.TypeOfDataContractAttribute, false) && !ClassDataContract.IsNonAttributedTypeValidForSerialization(type) && !ClassDataContract.IsKnownSerializableType(type)) + if (!type.IsSerializable && !type.IsDefined(Globals.TypeOfDataContractAttribute, false) && !ClassDataContract.IsNonAttributedTypeValidForSerialization(type)) { ThrowInvalidDataContractException(SR.Format(SR.TypeNotSerializable, type), type); } @@ -536,10 +476,10 @@ private static DataContract CreateDataContract(Type type) if (type != originalType) { var originalDataContract = new ClassDataContract(originalType); - if (dataContract.StableName != originalDataContract.StableName) + if (dataContract.XmlName != originalDataContract.XmlName) { - // for non-DC types, type adapters will not have the same stable name (contract name). - dataContract.StableName = originalDataContract.StableName; + // for non-DC types, type adapters will not have the same xml name (contract name). + dataContract.XmlName = originalDataContract.XmlName; } } } @@ -574,8 +514,10 @@ private static DataContract CreateGetOnlyCollectionDataContract(int id, RuntimeT { ThrowInvalidDataContractException(SR.Format(SR.TypeNotSerializable, type), type); } + AssignDataContractToId(dataContract, id); } } + // !; // If null after the lookup and creation attempts above, the 'ThrowInvalidDataContractException' kicks in. return dataContract; } @@ -594,10 +536,6 @@ internal static Type GetDataContractAdapterType(Type type) { return Globals.TypeOfMemoryStreamAdapter; } - if (type.IsGenericType && type.GetGenericTypeDefinition() == Globals.TypeOfKeyValuePair) - { - return Globals.TypeOfKeyValuePairAdapter.MakeGenericType(type.GetGenericArguments()); - } return type; } @@ -629,7 +567,7 @@ private static RuntimeTypeHandle GetDataContractAdapterTypeHandle(RuntimeTypeHan } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public static DataContract? GetBuiltInDataContract(Type type) + internal static DataContract? GetBuiltInDataContract(Type type) { if (type.IsInterface && !CollectionDataContract.IsCollectionInterface(type)) type = Globals.TypeOfObject; @@ -638,8 +576,7 @@ private static RuntimeTypeHandle GetDataContractAdapterTypeHandle(RuntimeTypeHan { s_typeToBuiltInContract ??= new Dictionary(); - DataContract? dataContract; - if (!s_typeToBuiltInContract.TryGetValue(type, out dataContract)) + if (!s_typeToBuiltInContract.TryGetValue(type, out DataContract? dataContract)) { TryCreateBuiltInDataContract(type, out dataContract); s_typeToBuiltInContract.Add(type, dataContract); @@ -649,36 +586,35 @@ private static RuntimeTypeHandle GetDataContractAdapterTypeHandle(RuntimeTypeHan } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public static DataContract? GetBuiltInDataContract(string name, string ns) + internal static DataContract? GetBuiltInDataContract(string name, string ns) { lock (s_initBuiltInContractsLock) { s_nameToBuiltInContract ??= new Dictionary(); - DataContract? dataContract; XmlQualifiedName qname = new XmlQualifiedName(name, ns); - if (!s_nameToBuiltInContract.TryGetValue(qname, out dataContract)) + if (!s_nameToBuiltInContract.TryGetValue(qname, out DataContract? dataContract)) { - TryCreateBuiltInDataContract(name, ns, out dataContract); - s_nameToBuiltInContract.Add(qname, dataContract); + if (TryCreateBuiltInDataContract(name, ns, out dataContract)) + { + s_nameToBuiltInContract.Add(qname, dataContract); + } } return dataContract; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public static DataContract? GetBuiltInDataContract(string typeName) + internal static DataContract? GetBuiltInDataContract(string typeName) { if (!typeName.StartsWith("System.", StringComparison.Ordinal)) return null; lock (s_initBuiltInContractsLock) { - s_nameToBuiltInContract ??= new Dictionary(); + s_typeNameToBuiltInContract ??= new Dictionary(); - DataContract? dataContract; - XmlQualifiedName qname = new XmlQualifiedName(typeName); - if (!s_nameToBuiltInContract.TryGetValue(qname, out dataContract)) + if (!s_typeNameToBuiltInContract.TryGetValue(typeName, out DataContract? dataContract)) { Type? type = null; string name = typeName.Substring(7); @@ -738,14 +674,14 @@ private static RuntimeTypeHandle GetDataContractAdapterTypeHandle(RuntimeTypeHan if (type != null) TryCreateBuiltInDataContract(type, out dataContract); - s_nameToBuiltInContract.Add(qname, dataContract); + s_typeNameToBuiltInContract.Add(typeName, dataContract); } return dataContract; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public static bool TryCreateBuiltInDataContract(Type type, [NotNullWhen(true)] out DataContract? dataContract) + internal static bool TryCreateBuiltInDataContract(Type type, [NotNullWhen(true)] out DataContract? dataContract) { if (type.IsEnum) // Type.GetTypeCode will report Enums as TypeCode.IntXX { @@ -753,7 +689,7 @@ public static bool TryCreateBuiltInDataContract(Type type, [NotNullWhen(true)] o return false; } dataContract = null; - switch (type.GetTypeCode()) + switch (Type.GetTypeCode(type)) { case TypeCode.Boolean: dataContract = new BooleanDataContract(); @@ -827,7 +763,7 @@ public static bool TryCreateBuiltInDataContract(Type type, [NotNullWhen(true)] o } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public static bool TryCreateBuiltInDataContract(string name, string ns, [NotNullWhen(true)] out DataContract? dataContract) + internal static bool TryCreateBuiltInDataContract(string name, string ns, [NotNullWhen(true)] out DataContract? dataContract) { dataContract = null; if (ns == DictionaryGlobals.SchemaNamespace.Value) @@ -951,23 +887,30 @@ public static bool TryCreateBuiltInDataContract(string name, string ns, [NotNull internal static string GetNamespace(string key) { - lock (s_namespacesLock) + object? value = s_namespaces[key]; + + if (value != null) + return (string)value; + + try { - s_namespaces ??= new Dictionary(); - if (s_namespaces.TryGetValue(key, out string? value)) + lock (s_namespacesLock) { - return value; - } + value = s_namespaces[key]; + + if (value != null) + return (string)value; - try - { s_namespaces.Add(key, key); + return key; } - catch (Exception ex) - { - throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(ex.Message, ex); - } - return key; + } + catch (Exception ex) + { + if (Fx.IsFatal(ex)) + throw; + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(ex.Message, ex); } } @@ -985,11 +928,13 @@ internal static XmlDictionaryString GetClrTypeString(string key) } catch (Exception ex) { + if (Fx.IsFatal(ex)) + throw; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(ex.Message, ex); } } - XmlDictionaryString? value; - if (s_clrTypeStrings.TryGetValue(key, out value)) + if (s_clrTypeStrings.TryGetValue(key, out XmlDictionaryString? value)) return value; value = s_clrTypeStringsDictionary!.Add(key); try @@ -998,6 +943,9 @@ internal static XmlDictionaryString GetClrTypeString(string key) } catch (Exception ex) { + if (Fx.IsFatal(ex)) + throw; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(ex.Message, ex); } return value; @@ -1011,14 +959,15 @@ internal static void ThrowInvalidDataContractException(string? message, Type? ty { lock (s_cacheLock) { + s_typeHandleRef ??= new TypeHandleRef(); s_typeHandleRef.Value = GetDataContractAdapterTypeHandle(type.TypeHandle); - try - { - s_typeToIDCache.Remove(s_typeHandleRef); - } - catch (Exception ex) + + if (s_typeToIDCache.ContainsKey(s_typeHandleRef)) { - throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(ex.Message, ex); + lock (s_cacheLock) + { + s_typeToIDCache.Remove(s_typeHandleRef); + } } } } @@ -1036,95 +985,78 @@ internal DataContractCriticalHelper( } [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] - internal Type UnderlyingType - { - get { return _underlyingType; } - set { _underlyingType = value; } - } + internal Type UnderlyingType => _underlyingType; - internal Type OriginalUnderlyingType - { - get => _originalUnderlyingType ??= GetDataContractOriginalType(this._underlyingType); - set => _originalUnderlyingType = value; - } + internal Type OriginalUnderlyingType => _originalUnderlyingType ??= GetDataContractOriginalType(_underlyingType); - internal virtual bool IsBuiltInDataContract - { - get - { - return false; - } - } + internal virtual bool IsBuiltInDataContract => false; - internal Type TypeForInitialization - { - get { return _typeForInitialization; } - } + internal Type TypeForInitialization => _typeForInitialization; [MemberNotNull(nameof(_typeForInitialization))] private void SetTypeForInitialization(Type classType) { + // TODO - This 'if' was not commented out in 4.8. But 4.8 was not dealing with nullable notations, which we do have here in Core. + // With the absence of schema importing, it does not make sense to have a data contract without a valid serializable underlying type. (Even + // with schema importing it doesn't make sense, but there is a building period while we're still figuring out all the data types and contracts + // where the underlying type may be null.) Anyway... might it make sense to re-instate this if clause - but use it to throw an exception if + // we don't meet the criteria? That way we can maintain nullable semantics and not do anything silly trying to keep them simple. //if (classType.IsSerializable || classType.IsDefined(Globals.TypeOfDataContractAttribute, false)) { _typeForInitialization = classType; } } - internal bool IsReference + internal bool IsReference { get; set; } + + internal bool IsValueType { - get { return _isReference; } - set - { - _isReference = value; - } + get => _isValueType; + set => _isValueType = value; } - internal bool IsValueType + internal XmlQualifiedName XmlName { - get { return _isValueType; } - set { _isValueType = value; } + get => _xmlName; + set => _xmlName = value; } - internal XmlQualifiedName StableName + internal GenericInfo? GenericInfo { - get { return _stableName; } - set { _stableName = value; } + get => _genericInfo; + set => _genericInfo = value; } internal virtual DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - get { return null; } + get => null; set { /* do nothing */ } } internal virtual bool IsISerializable { - get { return false; } - set { ThrowInvalidDataContractException(SR.RequiresClassDataContractToSetIsISerializable); } + get => false; + set => ThrowInvalidDataContractException(SR.RequiresClassDataContractToSetIsISerializable); } internal XmlDictionaryString Name { - get { return _name; } - set { _name = value; } + get => _name; + set => _name = value; } - public XmlDictionaryString Namespace + internal XmlDictionaryString Namespace { - get { return _ns; } - set { _ns = value; } + get => _ns; + set => _ns = value; } - internal virtual bool HasRoot - { - get { return true; } - set { } - } + internal virtual bool HasRoot { get; set; } = true; internal virtual XmlDictionaryString? TopLevelElementName { - get { return _name; } + get => _name; set { Debug.Assert(value != null); @@ -1134,7 +1066,7 @@ internal virtual XmlDictionaryString? TopLevelElementName internal virtual XmlDictionaryString? TopLevelElementNamespace { - get { return _ns; } + get => _ns; set { Debug.Assert(value != null); @@ -1142,15 +1074,9 @@ internal virtual XmlDictionaryString? TopLevelElementNamespace } } - internal virtual bool CanContainReferences - { - get { return true; } - } + internal virtual bool CanContainReferences => true; - internal virtual bool IsPrimitive - { - get { return false; } - } + internal virtual bool IsPrimitive => false; internal MethodInfo? ParseMethod { @@ -1171,19 +1097,19 @@ internal MethodInfo? ParseMethod } } - internal void SetDataContractName(XmlQualifiedName stableName) + internal void SetDataContractName(XmlQualifiedName xmlName) { XmlDictionary dictionary = new XmlDictionary(2); - this.Name = dictionary.Add(stableName.Name); - this.Namespace = dictionary.Add(stableName.Namespace); - this.StableName = stableName; + Name = dictionary.Add(xmlName.Name); + Namespace = dictionary.Add(xmlName.Namespace); + XmlName = xmlName; } internal void SetDataContractName(XmlDictionaryString name, XmlDictionaryString ns) { - this.Name = name; - this.Namespace = ns; - this.StableName = CreateQualifiedName(name.Value, ns.Value); + Name = name; + Namespace = ns; + XmlName = CreateQualifiedName(name.Value, ns.Value); } [DoesNotReturn] @@ -1194,10 +1120,14 @@ internal void ThrowInvalidDataContractException(string message) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static bool IsTypeSerializable(Type type, HashSet? previousCollectionTypes = null) + internal static bool IsTypeSerializable(Type type) { - Type? itemType; + return IsTypeSerializable(type, null); + } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private static bool IsTypeSerializable(Type type, HashSet? previousCollectionTypes) + { if (type.IsSerializable || type.IsEnum || type.IsDefined(Globals.TypeOfDataContractAttribute, false) || @@ -1209,7 +1139,7 @@ internal static bool IsTypeSerializable(Type type, HashSet? previousCollec { return true; } - if (CollectionDataContract.IsCollection(type, out itemType)) + if (CollectionDataContract.IsCollection(type, out Type? itemType)) { previousCollectionTypes ??= new HashSet(); ValidatePreviousCollectionTypes(type, itemType, previousCollectionTypes); @@ -1229,9 +1159,34 @@ private static void ValidatePreviousCollectionTypes(Type collectionType, Type it { itemType = itemType.GetElementType()!; } - if (previousCollectionTypes.Contains(itemType)) + + // Do a breadth first traversal of the generic type tree to + // produce the closure of all generic argument types and + // check that none of these is in the previousCollectionTypes + List itemTypeClosure = new List(); + Queue itemTypeQueue = new Queue(); + + itemTypeQueue.Enqueue(itemType); + itemTypeClosure.Add(itemType); + + while (itemTypeQueue.Count > 0) { - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.RecursiveCollectionType, GetClrTypeFullName(itemType)))); + itemType = itemTypeQueue.Dequeue(); + if (previousCollectionTypes.Contains(itemType)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.RecursiveCollectionType, GetClrTypeFullName(itemType)))); + } + if (itemType.IsGenericType) + { + foreach (Type argType in itemType.GetGenericArguments()) + { + if (!itemTypeClosure.Contains(argType)) + { + itemTypeQueue.Enqueue(argType); + itemTypeClosure.Add(argType); + } + } + } } } @@ -1290,47 +1245,47 @@ internal static bool IsValidNCName(string name) { return false; } - catch (Exception) - { - return false; - } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static XmlQualifiedName GetStableName(Type type) + public static XmlQualifiedName GetXmlName(Type type) + { + return GetXmlName(type, out _); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal static XmlQualifiedName GetXmlName(Type type, out bool hasDataContract) { - return GetStableName(type, out _); + return GetXmlName(type, new HashSet(), out hasDataContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static XmlQualifiedName GetStableName(Type type, out bool hasDataContract) + internal static XmlQualifiedName GetXmlName(Type type, HashSet previousCollectionTypes, out bool hasDataContract) { type = UnwrapRedundantNullableType(type); - XmlQualifiedName? stableName; - if (TryGetBuiltInXmlAndArrayTypeStableName(type, out stableName)) + if (TryGetBuiltInXmlAndArrayTypeXmlName(type, previousCollectionTypes, out XmlQualifiedName? xmlName)) { hasDataContract = false; } else { - DataContractAttribute? dataContractAttribute; - if (TryGetDCAttribute(type, out dataContractAttribute)) + if (TryGetDCAttribute(type, out DataContractAttribute? dataContractAttribute)) { - stableName = GetDCTypeStableName(type, dataContractAttribute); + xmlName = GetDCTypeXmlName(type, dataContractAttribute); hasDataContract = true; } else { - stableName = GetNonDCTypeStableName(type); + xmlName = GetNonDCTypeXmlName(type, previousCollectionTypes); hasDataContract = false; } } - return stableName; + return xmlName; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private static XmlQualifiedName GetDCTypeStableName(Type type, DataContractAttribute dataContractAttribute) + private static XmlQualifiedName GetDCTypeXmlName(Type type, DataContractAttribute dataContractAttribute) { string? name, ns; if (dataContractAttribute.IsNameSetExplicitly) @@ -1343,7 +1298,7 @@ private static XmlQualifiedName GetDCTypeStableName(Type type, DataContractAttri name = DataContract.EncodeLocalName(name); } else - name = GetDefaultStableLocalName(type); + name = GetDefaultXmlLocalName(type); if (dataContractAttribute.IsNamespaceSetExplicitly) { @@ -1359,14 +1314,16 @@ private static XmlQualifiedName GetDCTypeStableName(Type type, DataContractAttri } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private static XmlQualifiedName GetNonDCTypeStableName(Type type) + private static XmlQualifiedName GetNonDCTypeXmlName(Type type, HashSet previousCollectionTypes) { string? name, ns; - Type? itemType; - if (CollectionDataContract.IsCollection(type, out itemType)) - return GetCollectionStableName(type, itemType, out _); - name = GetDefaultStableLocalName(type); + if (CollectionDataContract.IsCollection(type, out Type? itemType)) + { + ValidatePreviousCollectionTypes(type, itemType, previousCollectionTypes); + return GetCollectionXmlName(type, itemType, previousCollectionTypes, out _); + } + name = GetDefaultXmlLocalName(type); // ensures that ContractNamespaceAttribute is honored when used with non-attributed types if (ClassDataContract.IsNonAttributedTypeValidForSerialization(type)) @@ -1375,32 +1332,33 @@ private static XmlQualifiedName GetNonDCTypeStableName(Type type) } else { - ns = GetDefaultStableNamespace(type); + ns = GetDefaultXmlNamespace(type); } return CreateQualifiedName(name, ns); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private static bool TryGetBuiltInXmlAndArrayTypeStableName(Type type, [NotNullWhen(true)] out XmlQualifiedName? stableName) + private static bool TryGetBuiltInXmlAndArrayTypeXmlName(Type type, HashSet previousCollectionTypes, [NotNullWhen(true)] out XmlQualifiedName? xmlName) { - stableName = null; + xmlName = null; DataContract? builtInContract = GetBuiltInDataContract(type); if (builtInContract != null) { - stableName = builtInContract.StableName; + xmlName = builtInContract.XmlName; } else if (Globals.TypeOfIXmlSerializable.IsAssignableFrom(type)) { - XmlQualifiedName xmlTypeStableName; - SchemaExporter.GetXmlTypeInfo(type, out xmlTypeStableName, out _, out _); - stableName = xmlTypeStableName; + SchemaExporter.GetXmlTypeInfo(type, out XmlQualifiedName xmlTypeName, out _, out _); + xmlName = xmlTypeName; } else if (type.IsArray) { - stableName = GetCollectionStableName(type, type.GetElementType()!, out _); + Type itemType = type.GetElementType()!; + ValidatePreviousCollectionTypes(type, itemType, previousCollectionTypes); + xmlName = GetCollectionXmlName(type, itemType, previousCollectionTypes, out _); } - return stableName != null; + return xmlName != null; } internal static bool TryGetDCAttribute(Type type, [NotNullWhen(true)] out DataContractAttribute? dataContractAttribute) @@ -1421,7 +1379,13 @@ internal static bool TryGetDCAttribute(Type type, [NotNullWhen(true)] out DataCo } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static XmlQualifiedName GetCollectionStableName(Type type, Type itemType, out CollectionDataContractAttribute? collectionContractAttribute) + internal static XmlQualifiedName GetCollectionXmlName(Type type, Type itemType, out CollectionDataContractAttribute? collectionContractAttribute) + { + return GetCollectionXmlName(type, itemType, new HashSet(), out collectionContractAttribute); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal static XmlQualifiedName GetCollectionXmlName(Type type, Type itemType, HashSet previousCollectionTypes, out CollectionDataContractAttribute? collectionContractAttribute) { string? name, ns; object[] collectionContractAttributes = type.GetCustomAttributes(Globals.TypeOfCollectionDataContractAttribute, false).ToArray(); @@ -1442,7 +1406,7 @@ internal static XmlQualifiedName GetCollectionStableName(Type type, Type itemTyp name = DataContract.EncodeLocalName(name); } else - name = GetDefaultStableLocalName(type); + name = GetDefaultXmlLocalName(type); if (collectionContractAttribute.IsNamespaceSetExplicitly) { @@ -1458,9 +1422,9 @@ internal static XmlQualifiedName GetCollectionStableName(Type type, Type itemTyp { collectionContractAttribute = null; string arrayOfPrefix = Globals.ArrayPrefix + GetArrayPrefix(ref itemType); - XmlQualifiedName elementStableName = GetStableName(itemType); - name = arrayOfPrefix + elementStableName.Name; - ns = GetCollectionNamespace(elementStableName.Namespace); + XmlQualifiedName elementXmlName = GetXmlName(itemType, previousCollectionTypes, out _); + name = arrayOfPrefix + elementXmlName.Name; + ns = GetCollectionNamespace(elementXmlName.Namespace); } return CreateQualifiedName(name, ns); } @@ -1485,13 +1449,33 @@ internal static string GetCollectionNamespace(string elementNs) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static XmlQualifiedName GetDefaultStableName(Type type) + public virtual XmlQualifiedName GetArrayTypeName(bool isNullable) + { + XmlQualifiedName itemName; + if (IsValueType && isNullable) + { + GenericInfo genericInfo = new GenericInfo(DataContract.GetXmlName(Globals.TypeOfNullable), Globals.TypeOfNullable.FullName!); + genericInfo.Add(new GenericInfo(XmlName, null)); + genericInfo.AddToLevel(0, 1); + itemName = genericInfo.GetExpandedXmlName(); + } + else + { + itemName = XmlName; + } + string ns = GetCollectionNamespace(itemName.Namespace); + string name = Globals.ArrayPrefix + itemName.Name; + return new XmlQualifiedName(name, ns); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal static XmlQualifiedName GetDefaultXmlName(Type type) { - return CreateQualifiedName(GetDefaultStableLocalName(type), GetDefaultStableNamespace(type)); + return CreateQualifiedName(GetDefaultXmlLocalName(type), GetDefaultXmlNamespace(type)); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private static string GetDefaultStableLocalName(Type type) + private static string GetDefaultXmlLocalName(Type type) { if (type.IsGenericParameter) return "{" + type.GenericParameterPosition + "}"; @@ -1528,7 +1512,7 @@ private static string GetDefaultStableLocalName(Type type) localName.Append('{').Append(i).Append('}'); else { - XmlQualifiedName qname = DataContract.GetStableName(genParam); + XmlQualifiedName qname = DataContract.GetXmlName(genParam); localName.Append(qname.Name); namespaces.Append(' ').Append(qname.Namespace); if (parametersFromBuiltInNamespaces) @@ -1557,7 +1541,7 @@ private static string GetDefaultDataContractNamespace(Type type) if (ns == null) { - ns = GetDefaultStableNamespace(type); + ns = GetDefaultXmlNamespace(type); } else { @@ -1582,7 +1566,7 @@ internal static List GetDataContractNameForGenericName(string typeName, Str if (localName != null) { string tempLocalName = typeName.Substring(startIndex, endIndex - startIndex); - localName.Append((tempLocalName.Equals("KeyValuePairAdapter") ? "KeyValuePair" : tempLocalName)); + localName.Append(tempLocalName); } while ((startIndex = typeName.IndexOf('.', startIndex + 1, endIndex - startIndex - 1)) >= 0) nestedParamCounts.Add(0); @@ -1604,11 +1588,11 @@ internal static bool IsBuiltInNamespace(string ns) return (ns == Globals.SchemaNamespace || ns == Globals.SerializationNamespace); } - internal static string GetDefaultStableNamespace(Type type) + internal static string GetDefaultXmlNamespace(Type type) { if (type.IsGenericParameter) return "{ns}"; - return GetDefaultStableNamespace(type.Namespace); + return GetDefaultXmlNamespace(type.Namespace); } internal static XmlQualifiedName CreateQualifiedName(string localName, string ns) @@ -1616,27 +1600,27 @@ internal static XmlQualifiedName CreateQualifiedName(string localName, string ns return new XmlQualifiedName(localName, GetNamespace(ns)); } - internal static string GetDefaultStableNamespace(string? clrNs) + internal static string GetDefaultXmlNamespace(string? clrNs) { return new Uri(Globals.DataContractXsdBaseNamespaceUri, clrNs ?? string.Empty).AbsoluteUri; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static void GetDefaultStableName(string fullTypeName, out string localName, out string ns) + internal static void GetDefaultXmlName(string fullTypeName, out string localName, out string ns) { CodeTypeReference typeReference = new CodeTypeReference(fullTypeName); - GetDefaultStableName(typeReference, out localName, out ns); + GetDefaultName(typeReference, out localName, out ns); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private static void GetDefaultStableName(CodeTypeReference typeReference, out string localName, out string ns) + private static void GetDefaultName(CodeTypeReference typeReference, out string localName, out string ns) { string fullTypeName = typeReference.BaseType; DataContract? dataContract = GetBuiltInDataContract(fullTypeName); if (dataContract != null) { - localName = dataContract.StableName.Name; - ns = dataContract.StableName.Namespace; + localName = dataContract.XmlName.Name; + ns = dataContract.XmlName.Namespace; return; } @@ -1649,8 +1633,7 @@ private static void GetDefaultStableName(CodeTypeReference typeReference, out st List nestedParamCounts = GetDataContractNameForGenericName(localName, localNameBuilder); foreach (CodeTypeReference typeArg in typeReference.TypeArguments) { - string typeArgName, typeArgNs; - GetDefaultStableName(typeArg, out typeArgName, out typeArgNs); + GetDefaultName(typeArg, out string typeArgName, out string typeArgNs); localNameBuilder.Append(typeArgName); argNamespacesBuilder.Append(' ').Append(typeArgNs); if (parametersFromBuiltInNamespaces) @@ -1663,7 +1646,7 @@ private static void GetDefaultStableName(CodeTypeReference typeReference, out st { foreach (int count in nestedParamCounts) { - argNamespacesBuilder.Insert(0, count).Insert(0, ' '); + argNamespacesBuilder.Insert(0, count.ToString(CultureInfo.InvariantCulture)).Insert(0, ' '); } localNameBuilder.Append(GetNamespacesDigest(argNamespacesBuilder.ToString())); @@ -1673,7 +1656,7 @@ private static void GetDefaultStableName(CodeTypeReference typeReference, out st } localName = DataContract.EncodeLocalName(localName); - ns = GetDefaultStableNamespace(ns); + ns = GetDefaultXmlNamespace(ns); } private static void CheckExplicitDataContractNamespaceUri(string dataContractNs, Type type) @@ -1686,8 +1669,7 @@ private static void CheckExplicitDataContractNamespaceUri(string dataContractNs, ThrowInvalidDataContractException(SR.Format(SR.DataContractNamespaceIsNotValid, dataContractNs), type); dataContractNs = trimmedNs; } - Uri? uri; - if (Uri.TryCreate(dataContractNs, UriKind.RelativeOrAbsolute, out uri)) + if (Uri.TryCreate(dataContractNs, UriKind.RelativeOrAbsolute, out Uri? uri)) { if (uri.ToString() == Globals.SerializationNamespace) ThrowInvalidDataContractException(SR.Format(SR.DataContractNamespaceReserved, Globals.SerializationNamespace), type); @@ -1933,8 +1915,7 @@ internal static string ExpandGenericParameters(string format, IGenericNameProvid } else { - int paramIndex; - if (!int.TryParse(format.AsSpan(start, i - start), out paramIndex) || paramIndex < 0 || paramIndex >= genericNameProvider.GetParameterCount()) + if (!int.TryParse(format.AsSpan(start, i - start), out int paramIndex) || paramIndex < 0 || paramIndex >= genericNameProvider.GetParameterCount()) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.GenericParameterNotValid, format.Substring(start, i - start), genericNameProvider.GetGenericTypeName(), genericNameProvider.GetParameterCount() - 1))); typeName.Append(genericNameProvider.GetParameterName(paramIndex)); } @@ -2030,25 +2011,36 @@ private static void ImportKnownTypeAttributes(Type? type, Dictionary } } - //For Json we need to add KeyValuePair to KnownTypes if the UnderLyingType is a Dictionary - try + // After careful consideration, I don't think this code is necessary anymore. After trying to + // decipher the intent of the comments here, my best guess is that this was DCJS's way of working + // around a non-[Serializable] KVP in Silverlight/early .Net Core. The regular DCS went with a + // KVPAdapter approach. Neither is needed now. + // + // But this code does produce additional artifacts in schema handling. To be cautious, just in case + // somebody needs a KVP contract in addition to the KV contract in their schema, I've kept this + // here behind this AppContext switch. + AppContext.TryGetSwitch("Switch.System.Runtime.Serialization.DataContracts.Auto_Import_KVP", out bool autoImportKVP); + if (autoImportKVP) { - CollectionDataContract? collectionDataContract = DataContract.GetDataContract(type) as CollectionDataContract; - if (collectionDataContract != null && collectionDataContract.IsDictionary && - collectionDataContract.ItemType.GetGenericTypeDefinition() == Globals.TypeOfKeyValue) + //For Json we need to add KeyValuePair to KnownTypes if the UnderLyingType is a Dictionary + try { - DataContract itemDataContract = DataContract.GetDataContract(Globals.TypeOfKeyValuePair.MakeGenericType(collectionDataContract.ItemType.GetGenericArguments())); - knownDataContracts ??= new DataContractDictionary(); + if (DataContract.GetDataContract(type) is CollectionDataContract collectionDataContract && collectionDataContract.IsDictionary && + collectionDataContract.ItemType.GetGenericTypeDefinition() == Globals.TypeOfKeyValue) + { + DataContract itemDataContract = DataContract.GetDataContract(Globals.TypeOfKeyValuePair.MakeGenericType(collectionDataContract.ItemType.GetGenericArguments())); + knownDataContracts ??= new DataContractDictionary(); - knownDataContracts.TryAdd(itemDataContract.StableName, itemDataContract); + knownDataContracts.TryAdd(itemDataContract.XmlName, itemDataContract); + } + } + catch (InvalidDataContractException) + { + //Ignore any InvalidDataContractException as this phase is a workaround for lack of ISerializable. + //InvalidDataContractException may happen as we walk the type hierarchy back to Object and encounter + //types that may not be valid DC. This step is purely for KeyValuePair and shouldn't fail the (de)serialization. + //Any IDCE in this case fails the serialization/deserialization process which is not the optimal experience. } - } - catch (InvalidDataContractException) - { - //Ignore any InvalidDataContractException as this phase is a workaround for lack of ISerializable. - //InvalidDataContractException may happen as we walk the type hierarchy back to Object and encounter - //types that may not be valid DC. This step is purely for KeyValuePair and shouldn't fail the (de)serialization. - //Any IDCE in this case fails the serialization/deserialization process which is not the optimal experience. } type = type.BaseType; @@ -2060,23 +2052,62 @@ internal static void CheckAndAdd(Type type, Dictionary typesChecked, { type = DataContract.UnwrapNullableType(type); DataContract dataContract = DataContract.GetDataContract(type); - DataContract? alreadyExistingContract; if (nameToDataContractTable == null) { nameToDataContractTable = new DataContractDictionary(); } - else if (nameToDataContractTable.TryGetValue(dataContract.StableName, out alreadyExistingContract)) + else if (nameToDataContractTable.TryGetValue(dataContract.XmlName, out DataContract? alreadyExistingContract)) { + // The alreadyExistingContract type was used as-is in NetFx. The call to get the appropriate adapter type was added in CoreFx with https://github.com/dotnet/runtime/commit/50c0a70c52fa66fafa1227be552ccdab5e4cf8e4 // Don't throw duplicate if its a KeyValuePair as it could have been added by Dictionary - if (DataContractCriticalHelper.GetDataContractAdapterType(alreadyExistingContract.UnderlyingType) != DataContractCriticalHelper.GetDataContractAdapterType(type) && - !(alreadyExistingContract is ClassDataContract && ((ClassDataContract)alreadyExistingContract).IsKeyValuePairAdapter)) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.DupContractInKnownTypes, type, alreadyExistingContract.UnderlyingType, dataContract.StableName.Namespace, dataContract.StableName.Name))); + if (DataContractCriticalHelper.GetDataContractAdapterType(alreadyExistingContract.UnderlyingType) != DataContractCriticalHelper.GetDataContractAdapterType(type)) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.DupContractInKnownTypes, type, alreadyExistingContract.UnderlyingType, dataContract.XmlName.Namespace, dataContract.XmlName.Name))); return; } - nameToDataContractTable.Add(dataContract.StableName, dataContract); + nameToDataContractTable.Add(dataContract.XmlName, dataContract); ImportKnownTypeAttributes(type, typesChecked, ref nameToDataContractTable); } + public sealed override bool Equals(object? obj) + { + if ((object)this == obj) + return true; + return Equals(obj, new HashSet()); + } + + internal virtual bool Equals(object? other, HashSet? checkedContracts) + { + if (other is DataContract dataContract) + { + return (XmlName.Name == dataContract.XmlName.Name && XmlName.Namespace == dataContract.XmlName.Namespace && IsReference == dataContract.IsReference); + } + return false; + } + + internal bool IsEqualOrChecked(object? other, HashSet? checkedContracts) + { + if (other == null) + return false; + + if ((object)this == other) + return true; + + if (checkedContracts != null) + { + DataContractPairKey contractPairKey = new DataContractPairKey(this, other); + if (checkedContracts.Contains(contractPairKey)) + return true; + checkedContracts.Add(contractPairKey); + } + + return false; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + /// /// Review - checks type visibility to calculate if access to it requires MemberAccessPermission. /// since this information is used to determine whether to give the generated code access @@ -2084,18 +2115,46 @@ internal static void CheckAndAdd(Type type, Dictionary typesChecked, /// internal static bool IsTypeVisible(Type t) { - if (!t.IsVisible && !IsTypeVisibleInSerializationModule(t)) + // Generic parameters are always considered visible. + if (t.IsGenericParameter) + { + return true; + } + + // The normal Type.IsVisible check requires all nested types to be IsNestedPublic. + // This does not comply with our convention where they can also have InternalsVisibleTo + // with our assembly. The following method performs a recursive walk back the declaring + // type hierarchy to perform this enhanced IsVisible check. + if (!IsTypeAndDeclaringTypeVisible(t)) return false; foreach (Type genericType in t.GetGenericArguments()) { - if (!genericType.IsGenericParameter && !IsTypeVisible(genericType)) + if (!IsTypeVisible(genericType)) return false; } return true; } + internal static bool IsTypeAndDeclaringTypeVisible(Type t) + { + // Arrays, etc. must consider the underlying element type because the + // non-element type does not reflect the same type nesting. For example, + // MyClass[] would not show as a nested type, even when MyClass is nested. + if (t.HasElementType) + { + return IsTypeVisible(t.GetElementType()!); + } + + // Nested types are not visible unless their declaring type is visible. + // Additionally, they must be either IsNestedPublic or in an assembly with InternalsVisibleTo this current assembly. + // Non-nested types must be public or have this same InternalsVisibleTo relation. + return t.IsNested + ? (t.IsNestedPublic || IsTypeVisibleInSerializationModule(t)) && IsTypeVisible(t.DeclaringType!) + : t.IsPublic || IsTypeVisibleInSerializationModule(t); + } + /// /// Review - checks constructor visibility to calculate if access to it requires MemberAccessPermission. /// note: does local check for visibility, assuming that the declaring Type visibility has been checked. @@ -2200,7 +2259,7 @@ internal interface IGenericNameProvider string GetParameterName(int paramIndex); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] string GetNamespaces(); - string GetGenericTypeName(); + string? GetGenericTypeName(); bool ParametersFromBuiltInNamespaces { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -2224,8 +2283,7 @@ internal GenericNameProvider(string genericTypeName, object[] genericParams) _genericParams = new object[genericParams.Length]; genericParams.CopyTo(_genericParams, 0); - string name; - DataContract.GetClrNameAndNamespace(genericTypeName, out name, out _); + DataContract.GetClrNameAndNamespace(genericTypeName, out string name, out _); _nestedParamCounts = DataContract.GetDataContractNameForGenericName(name, null); } @@ -2242,7 +2300,7 @@ public IList GetNestedParameterCounts() [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public string GetParameterName(int paramIndex) { - return GetStableName(paramIndex).Name; + return GetXmlName(paramIndex).Name; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -2250,11 +2308,11 @@ public string GetNamespaces() { StringBuilder namespaces = new StringBuilder(); for (int j = 0; j < GetParameterCount(); j++) - namespaces.Append(' ').Append(GetStableName(j).Namespace); + namespaces.Append(' ').Append(GetXmlName(j).Namespace); return namespaces.ToString(); } - public string GetGenericTypeName() + public string? GetGenericTypeName() { return _genericTypeName; } @@ -2268,7 +2326,7 @@ public bool ParametersFromBuiltInNamespaces for (int j = 0; j < GetParameterCount(); j++) { if (parametersFromBuiltInNamespaces) - parametersFromBuiltInNamespaces = DataContract.IsBuiltInNamespace(GetStableName(j).Namespace); + parametersFromBuiltInNamespaces = DataContract.IsBuiltInNamespace(GetXmlName(j).Namespace); else break; } @@ -2277,7 +2335,7 @@ public bool ParametersFromBuiltInNamespaces } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private XmlQualifiedName GetStableName(int i) + private XmlQualifiedName GetXmlName(int i) { object o = _genericParams[i]; XmlQualifiedName? qname = o as XmlQualifiedName; @@ -2285,15 +2343,158 @@ private XmlQualifiedName GetStableName(int i) { Type? paramType = o as Type; if (paramType != null) - _genericParams[i] = qname = DataContract.GetStableName(paramType); + _genericParams[i] = qname = DataContract.GetXmlName(paramType); else - _genericParams[i] = qname = ((DataContract)o).StableName; + _genericParams[i] = qname = ((DataContract)o).XmlName; } return qname; } } + internal sealed class GenericInfo : IGenericNameProvider + { + private string? _genericTypeName; + private XmlQualifiedName _xmlName; + private List? _paramGenericInfos; + private List _nestedParamCounts; + + internal GenericInfo(XmlQualifiedName xmlName, string? genericTypeName) + { + _xmlName = xmlName; + _genericTypeName = genericTypeName; + _nestedParamCounts = new List(); + _nestedParamCounts.Add(0); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + public XmlQualifiedName GetExpandedXmlName() + { + if (_paramGenericInfos == null) + return _xmlName; + return new XmlQualifiedName(DataContract.EncodeLocalName(DataContract.ExpandGenericParameters(XmlConvert.DecodeName(_xmlName.Name), this)), _xmlName.Namespace); + } + + public XmlQualifiedName XmlName => _xmlName; + + public IList? Parameters => _paramGenericInfos; + + internal void Add(GenericInfo actualParamInfo) + { + if (_paramGenericInfos == null) + _paramGenericInfos = new List(); + _paramGenericInfos.Add(actualParamInfo); + } + + internal void AddToLevel(int level, int count) + { + if (level >= _nestedParamCounts.Count) + { + do + { + _nestedParamCounts.Add((level == _nestedParamCounts.Count) ? count : 0); + } while (level >= _nestedParamCounts.Count); + } + else + _nestedParamCounts[level] = _nestedParamCounts[level] + count; + } + + internal string GetXmlNamespace() + { + return _xmlName.Namespace; + } + + int IGenericNameProvider.GetParameterCount() + { + return _paramGenericInfos?.Count ?? 0; + } + + IList IGenericNameProvider.GetNestedParameterCounts() + { + return _nestedParamCounts; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + string IGenericNameProvider.GetParameterName(int paramIndex) + { + Debug.Assert(_paramGenericInfos != null); + return _paramGenericInfos[paramIndex].GetExpandedXmlName().Name; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + string IGenericNameProvider.GetNamespaces() + { + if (_paramGenericInfos == null || _paramGenericInfos.Count == 0) + return ""; + + StringBuilder namespaces = new StringBuilder(); + for (int j = 0; j < _paramGenericInfos.Count; j++) + namespaces.Append(' ').Append(_paramGenericInfos[j].GetXmlNamespace()); + return namespaces.ToString(); + } + + string? IGenericNameProvider.GetGenericTypeName() + { + return _genericTypeName; + } + + bool IGenericNameProvider.ParametersFromBuiltInNamespaces + { + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + get + { + bool parametersFromBuiltInNamespaces = true; + + if (_paramGenericInfos == null || _paramGenericInfos.Count == 0) + return parametersFromBuiltInNamespaces; + + for (int j = 0; j < _paramGenericInfos.Count; j++) + { + if (parametersFromBuiltInNamespaces) + parametersFromBuiltInNamespaces = DataContract.IsBuiltInNamespace(_paramGenericInfos[j].GetXmlNamespace()); + else + break; + } + return parametersFromBuiltInNamespaces; + } + } + } + + internal sealed class DataContractPairKey + { + private object _object1; + private object _object2; + + internal DataContractPairKey(object object1, object object2) + { + _object1 = object1; + _object2 = object2; + } + + public override bool Equals(object? other) + { + if (other is not DataContractPairKey otherKey) + return false; + return ((otherKey._object1 == _object1 && otherKey._object2 == _object2) || (otherKey._object1 == _object2 && otherKey._object2 == _object1)); + } + + public override int GetHashCode() + { + return _object1.GetHashCode() ^ _object2.GetHashCode(); + } + } + + internal sealed class HashTableEqualityComparer : IEqualityComparer + { + bool IEqualityComparer.Equals(object? x, object? y) + { + return ((TypeHandleRef)x!).Value.Equals(((TypeHandleRef)y!).Value); + } + public int GetHashCode(object obj) + { + return ((TypeHandleRef)obj).Value.GetHashCode(); + } + } internal sealed class TypeHandleRefEqualityComparer : IEqualityComparer { @@ -2323,14 +2524,8 @@ public TypeHandleRef(RuntimeTypeHandle value) public RuntimeTypeHandle Value { - get - { - return _value; - } - set - { - _value = value; - } + get => _value; + set => _value = value; } } @@ -2343,12 +2538,6 @@ public IntRef(int value) _value = value; } - public int Value - { - get - { - return _value; - } - } + public int Value => _value; } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractResolver.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractResolver.cs index 2b9345dc2c5f23..6983923059fb41 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractResolver.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractResolver.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.DataContracts; using System.Xml; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs index cd8b099284e332..97596f18babdfb 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs @@ -2,18 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.IO; -using System.Reflection; using System.Runtime.CompilerServices; -using System.Text; +using System.Runtime.Serialization.DataContracts; using System.Xml; -using DataContractDictionary = System.Collections.Generic.Dictionary; + +using DataContractDictionary = System.Collections.Generic.Dictionary; namespace System.Runtime.Serialization { @@ -28,25 +24,25 @@ public sealed class DataContractSerializer : XmlObjectSerializer private bool _ignoreExtensionDataObject; private bool _preserveObjectReferences; private ReadOnlyCollection? _knownTypeCollection; - internal IList? knownTypeList; - internal DataContractDictionary? knownDataContracts; + internal IList? _knownTypeList; + internal DataContractDictionary? _knownDataContracts; private DataContractResolver? _dataContractResolver; private ISerializationSurrogateProvider? _serializationSurrogateProvider; private bool _serializeReadOnlyTypes; - private static SerializationOption _option = IsReflectionBackupAllowed() ? SerializationOption.ReflectionAsBackup : SerializationOption.CodeGenOnly; - private static bool _optionAlreadySet; + private static SerializationOption s_option = IsReflectionBackupAllowed() ? SerializationOption.ReflectionAsBackup : SerializationOption.CodeGenOnly; + private static bool s_optionAlreadySet; internal static SerializationOption Option { - get { return RuntimeFeature.IsDynamicCodeSupported ? _option : SerializationOption.ReflectionOnly; } + get { return RuntimeFeature.IsDynamicCodeSupported ? s_option : SerializationOption.ReflectionOnly; } set { - if (_optionAlreadySet) + if (s_optionAlreadySet) { throw new InvalidOperationException(SR.CannotSetTwice); } - _optionAlreadySet = true; - _option = value; + s_optionAlreadySet = true; + s_option = value; } } @@ -61,22 +57,26 @@ public DataContractSerializer(Type type) } public DataContractSerializer(Type type, IEnumerable? knownTypes) + : this(type, knownTypes, int.MaxValue, false, false) { - Initialize(type, knownTypes, int.MaxValue, false, false, null, false); } - public DataContractSerializer(Type type, string rootName, string rootNamespace) : this(type, rootName, rootNamespace, null) { } public DataContractSerializer(Type type, string rootName, string rootNamespace, IEnumerable? knownTypes) + : this(type, rootName, rootNamespace, knownTypes, int.MaxValue, false, false) { - XmlDictionary dictionary = new XmlDictionary(2); - Initialize(type, dictionary.Add(rootName), dictionary.Add(DataContract.GetNamespace(rootNamespace)), knownTypes, int.MaxValue, false, false, null, false); } + internal DataContractSerializer(Type type, string rootName, string rootNamespace, IEnumerable? knownTypes, int maxItemsInObjectGraph, + bool ignoreExtensionDataObject, bool preserveObjectReferences) + { + XmlDictionary dictionary = new XmlDictionary(2); + Initialize(type, dictionary.Add(rootName), dictionary.Add(DataContract.GetNamespace(rootNamespace)), knownTypes, int.MaxValue, ignoreExtensionDataObject, preserveObjectReferences, null, false); + } public DataContractSerializer(Type type, XmlDictionaryString rootName, XmlDictionaryString rootNamespace) : this(type, rootName, rootNamespace, null) @@ -88,7 +88,7 @@ public DataContractSerializer(Type type, XmlDictionaryString rootName, XmlDictio Initialize(type, rootName, rootNamespace, knownTypes, int.MaxValue, false, false, null, false); } - internal DataContractSerializer(Type type, IEnumerable knownTypes, int maxItemsInObjectGraph, bool ignoreExtensionDataObject, bool preserveObjectReferences) + internal DataContractSerializer(Type type, IEnumerable? knownTypes, int maxItemsInObjectGraph, bool ignoreExtensionDataObject, bool preserveObjectReferences) { Initialize(type, knownTypes, maxItemsInObjectGraph, ignoreExtensionDataObject, preserveObjectReferences, null, false); } @@ -96,7 +96,7 @@ internal DataContractSerializer(Type type, IEnumerable knownTypes, int max public DataContractSerializer(Type type, DataContractSerializerSettings? settings) { settings ??= new DataContractSerializerSettings(); - Initialize(type, settings.RootName, settings.RootNamespace, settings.KnownTypes, settings.MaxItemsInObjectGraph, false, + Initialize(type, settings.RootName, settings.RootNamespace, settings.KnownTypes, settings.MaxItemsInObjectGraph, settings.IgnoreExtensionDataObject, settings.PreserveObjectReferences, settings.DataContractResolver, settings.SerializeReadOnlyTypes); } @@ -115,10 +115,10 @@ private void Initialize(Type type, if (knownTypes != null) { - this.knownTypeList = new List(); + _knownTypeList = new List(); foreach (Type knownType in knownTypes) { - this.knownTypeList.Add(knownType); + _knownTypeList.Add(knownType); } } @@ -154,9 +154,9 @@ public ReadOnlyCollection KnownTypes { if (_knownTypeCollection == null) { - if (knownTypeList != null) + if (_knownTypeList != null) { - _knownTypeCollection = new ReadOnlyCollection(knownTypeList); + _knownTypeCollection = new ReadOnlyCollection(_knownTypeList); } else { @@ -172,15 +172,15 @@ internal override DataContractDictionary? KnownDataContracts [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] get { - if (this.knownDataContracts == null && this.knownTypeList != null) + if (_knownDataContracts == null && _knownTypeList != null) { // This assignment may be performed concurrently and thus is a race condition. // It's safe, however, because at worse a new (and identical) dictionary of // data contracts will be created and re-assigned to this field. Introduction // of a lock here could lead to deadlocks. - this.knownDataContracts = XmlObjectSerializerContext.GetDataContractsForKnownTypes(this.knownTypeList); + _knownDataContracts = XmlObjectSerializerContext.GetDataContractsForKnownTypes(_knownTypeList); } - return this.knownDataContracts; + return _knownDataContracts; } } @@ -354,7 +354,7 @@ internal void InternalWriteObjectContent(XmlWriterDelegator writer, object? grap graph = SurrogateToDataContractType(_serializationSurrogateProvider, graph, declaredType, ref graphType); } - dataContractResolver ??= this.DataContractResolver; + dataContractResolver ??= DataContractResolver; if (graph == null) { @@ -408,7 +408,7 @@ internal static DataContract GetDataContract(DataContract declaredTypeContract, } else { - return DataContract.GetDataContract(objectType.TypeHandle, objectType, SerializationMode.SharedContract); + return DataContract.GetDataContract(objectType.TypeHandle, objectType); } } @@ -433,7 +433,7 @@ internal override void InternalWriteEndObject(XmlWriterDelegator writer) if (MaxItemsInObjectGraph == 0) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.ExceededMaxItemsQuota, MaxItemsInObjectGraph))); - dataContractResolver ??= this.DataContractResolver; + dataContractResolver ??= DataContractResolver; if (verifyObjectName) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSet.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSet.cs index 41bae413f816aa..129540177a69d3 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSet.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSet.cs @@ -1,48 +1,48 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Xml; using System.Collections; using System.Collections.Generic; -using System.Text; -using DataContractDictionary = System.Collections.Generic.Dictionary; using System.Diagnostics.CodeAnalysis; +using System.Text; +using System.Xml; +using System.Xml.Schema; -namespace System.Runtime.Serialization +using DataContractDictionary = System.Collections.Generic.Dictionary; + +namespace System.Runtime.Serialization.DataContracts { - internal sealed class DataContractSet + public sealed class DataContractSet { - private Dictionary? _contracts; + private DataContractDictionary? _contracts; private Dictionary? _processedContracts; - private readonly ICollection _referencedTypes; - private readonly ICollection _referencedCollectionTypes; - -#if SUPPORT_SURROGATE - private IDataContractSurrogate _dataContractSurrogate; - private Hashtable _surrogateDataTable; - - internal DataContractSet(IDataContractSurrogate dataContractSurrogate) : this(dataContractSurrogate, null, null) { } - - internal DataContractSet(IDataContractSurrogate dataContractSurrogate, ICollection referencedTypes, ICollection referencedCollectionTypes) + private readonly ISerializationSurrogateProvider? _surrogateProvider; + private readonly ISerializationSurrogateProvider2? _extendedSurrogateProvider; + private Hashtable? _surrogateData; + private DataContractDictionary? _knownTypesForObject; + private readonly List? _referencedTypes; + private readonly List? _referencedCollectionTypes; + + public DataContractSet(ISerializationSurrogateProvider? dataContractSurrogate, IEnumerable? referencedTypes, IEnumerable? referencedCollectionTypes) { - _dataContractSurrogate = dataContractSurrogate; - _referencedTypes = referencedTypes; - _referencedCollectionTypes = referencedCollectionTypes; + _surrogateProvider = dataContractSurrogate; + _extendedSurrogateProvider = dataContractSurrogate as ISerializationSurrogateProvider2; + _referencedTypes = referencedTypes != null ? new List(referencedTypes) : null; + _referencedCollectionTypes = referencedCollectionTypes != null ? new List(referencedCollectionTypes) : null; } -#endif [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal DataContractSet(DataContractSet dataContractSet) + public DataContractSet(DataContractSet dataContractSet) { ArgumentNullException.ThrowIfNull(dataContractSet); - //this.dataContractSurrogate = dataContractSet.dataContractSurrogate; _referencedTypes = dataContractSet._referencedTypes; _referencedCollectionTypes = dataContractSet._referencedCollectionTypes; + _extendedSurrogateProvider = dataContractSet._extendedSurrogateProvider; - foreach (KeyValuePair pair in dataContractSet) + foreach (KeyValuePair pair in dataContractSet.Contracts) { - Add(pair.Key, pair.Value); + InternalAdd(pair.Key, pair.Value); } if (dataContractSet._processedContracts != null) @@ -54,22 +54,18 @@ internal DataContractSet(DataContractSet dataContractSet) } } - private Dictionary Contracts => - _contracts ??= new Dictionary(); + public DataContractDictionary Contracts => + _contracts ??= new DataContractDictionary(); - private Dictionary ProcessedContracts => + public Dictionary ProcessedContracts => _processedContracts ??= new Dictionary(); -#if SUPPORT_SURROGATE - private Hashtable SurrogateDataTable => _surrogateDataTable ??= new Hashtable(); -#endif + public Hashtable SurrogateData => _surrogateData ??= new Hashtable(); - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal void Add(Type type) + public DataContractDictionary? KnownTypesForObject { - DataContract dataContract = GetDataContract(type); - EnsureTypeNotGeneric(dataContract.UnderlyingType); - Add(dataContract); + get => _knownTypesForObject; + internal set => _knownTypesForObject = value; } internal static void EnsureTypeNotGeneric(Type type) @@ -78,14 +74,22 @@ internal static void EnsureTypeNotGeneric(Type type) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.GenericTypeNotExportable, type))); } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal void Add(Type type) + { + DataContract dataContract = GetDataContract(type); + EnsureTypeNotGeneric(dataContract.UnderlyingType); + Add(dataContract); + } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private void Add(DataContract dataContract) { - Add(dataContract.StableName, dataContract); + Add(dataContract.XmlName, dataContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public void Add(XmlQualifiedName name, DataContract dataContract) + internal void Add(XmlQualifiedName name, DataContract dataContract) { if (dataContract.IsBuiltInDataContract) return; @@ -95,17 +99,16 @@ public void Add(XmlQualifiedName name, DataContract dataContract) [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal void InternalAdd(XmlQualifiedName name, DataContract dataContract) { - DataContract? dataContractInSet; - if (Contracts.TryGetValue(name, out dataContractInSet)) + if (Contracts.TryGetValue(name, out DataContract? dataContractInSet)) { if (!dataContractInSet.Equals(dataContract)) { if (dataContract.UnderlyingType == null || dataContractInSet.UnderlyingType == null) - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.DupContractInDataContractSet, dataContract.StableName.Name, dataContract.StableName.Namespace))); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.DupContractInDataContractSet, dataContract.XmlName.Name, dataContract.XmlName.Namespace))); else { bool typeNamesEqual = (DataContract.GetClrTypeFullName(dataContract.UnderlyingType) == DataContract.GetClrTypeFullName(dataContractInSet.UnderlyingType)); - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.DupTypeContractInDataContractSet, (typeNamesEqual ? dataContract.UnderlyingType.AssemblyQualifiedName : DataContract.GetClrTypeFullName(dataContract.UnderlyingType)), (typeNamesEqual ? dataContractInSet.UnderlyingType.AssemblyQualifiedName : DataContract.GetClrTypeFullName(dataContractInSet.UnderlyingType)), dataContract.StableName.Name, dataContract.StableName.Namespace))); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.DupTypeContractInDataContractSet, (typeNamesEqual ? dataContract.UnderlyingType.AssemblyQualifiedName : DataContract.GetClrTypeFullName(dataContract.UnderlyingType)), (typeNamesEqual ? dataContractInSet.UnderlyingType.AssemblyQualifiedName : DataContract.GetClrTypeFullName(dataContractInSet.UnderlyingType)), dataContract.XmlName.Name, dataContract.XmlName.Namespace))); } } } @@ -113,17 +116,17 @@ internal void InternalAdd(XmlQualifiedName name, DataContract dataContract) { Contracts.Add(name, dataContract); - if (dataContract is ClassDataContract) + if (dataContract is ClassDataContract classDC) { - AddClassDataContract((ClassDataContract)dataContract); + AddClassDataContract(classDC); } - else if (dataContract is CollectionDataContract) + else if (dataContract is CollectionDataContract collectionDC) { - AddCollectionDataContract((CollectionDataContract)dataContract); + AddCollectionDataContract(collectionDC); } - else if (dataContract is XmlDataContract) + else if (dataContract is XmlDataContract xmlDC) { - AddXmlDataContract((XmlDataContract)dataContract); + AddXmlDataContract(xmlDC); } } } @@ -131,9 +134,9 @@ internal void InternalAdd(XmlQualifiedName name, DataContract dataContract) [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private void AddClassDataContract(ClassDataContract classDataContract) { - if (classDataContract.BaseContract != null) + if (classDataContract.BaseClassContract != null) { - Add(classDataContract.BaseContract.StableName, classDataContract.BaseContract); + Add(classDataContract.BaseClassContract.XmlName, classDataContract.BaseClassContract); } if (!classDataContract.IsISerializable) { @@ -143,18 +146,18 @@ private void AddClassDataContract(ClassDataContract classDataContract) { DataMember dataMember = classDataContract.Members[i]; DataContract memberDataContract = GetMemberTypeDataContract(dataMember); -#if SUPPORT_SURROGATE - if (_dataContractSurrogate != null && dataMember.MemberInfo != null) + + if (_extendedSurrogateProvider != null && dataMember.MemberInfo != null) { - object customData = DataContractSurrogateCaller.GetCustomDataToExport( - _dataContractSurrogate, - dataMember.MemberInfo, - memberDataContract.UnderlyingType); + object? customData = DataContractSurrogateCaller.GetCustomDataToExport( + _extendedSurrogateProvider, + dataMember.MemberInfo, + memberDataContract.UnderlyingType); if (customData != null) - SurrogateDataTable.Add(dataMember, customData); + SurrogateData.Add(dataMember, customData); } -#endif - Add(memberDataContract.StableName, memberDataContract); + + Add(memberDataContract.XmlName, memberDataContract); } } } @@ -164,16 +167,19 @@ private void AddClassDataContract(ClassDataContract classDataContract) [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private void AddCollectionDataContract(CollectionDataContract collectionDataContract) { - if (collectionDataContract.IsDictionary) + if (collectionDataContract.UnderlyingType != Globals.TypeOfSchemaDefinedType) { - ClassDataContract keyValueContract = (collectionDataContract.ItemContract as ClassDataContract)!; - AddClassDataContract(keyValueContract); - } - else - { - DataContract itemContract = GetItemTypeDataContract(collectionDataContract); - if (itemContract != null) - Add(itemContract.StableName, itemContract); + if (collectionDataContract.IsDictionary) + { + ClassDataContract keyValueContract = (collectionDataContract.ItemContract as ClassDataContract)!; + AddClassDataContract(keyValueContract); + } + else + { + DataContract itemContract = GetItemTypeDataContract(collectionDataContract); + if (itemContract != null) + Add(itemContract.XmlName, itemContract); + } } AddKnownDataContracts(collectionDataContract.KnownDataContracts); } @@ -187,7 +193,7 @@ private void AddXmlDataContract(XmlDataContract xmlDataContract) [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private void AddKnownDataContracts(DataContractDictionary? knownDataContracts) { - if (knownDataContracts != null) + if (knownDataContracts?.Count > 0) { foreach (DataContract knownDataContract in knownDataContracts.Values) { @@ -197,88 +203,406 @@ private void AddKnownDataContracts(DataContractDictionary? knownDataContracts) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static DataContract GetDataContract(Type clrType) + internal XmlQualifiedName GetXmlName(Type clrType) + { + if (_surrogateProvider != null) + { + Type dcType = DataContractSurrogateCaller.GetDataContractType(_surrogateProvider, clrType); + return DataContract.GetXmlName(dcType); + } + return DataContract.GetXmlName(clrType); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + public DataContract GetDataContract(Type type) { -#if SUPPORT_SURROGATE - if (_dataContractSurrogate == null) - return DataContract.GetDataContract(clrType); -#endif - DataContract? dataContract = DataContract.GetBuiltInDataContract(clrType); + if (_surrogateProvider == null) + return DataContract.GetDataContract(type); + + DataContract? dataContract = DataContract.GetBuiltInDataContract(type); if (dataContract != null) return dataContract; -#if SUPPORT_SURROGATE - Type dcType = DataContractSurrogateCaller.GetDataContractType(_dataContractSurrogate, clrType); - if (clrType.IsValueType != dcType.IsValueType) - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.ValueTypeMismatchInSurrogatedType, dcType, clrType))); -#endif - Type dcType = clrType; + Type dcType = DataContractSurrogateCaller.GetDataContractType(_surrogateProvider, type); dataContract = DataContract.GetDataContract(dcType); -#if SUPPORT_SURROGATE - if (!SurrogateDataTable.Contains(dataContract)) + if (_extendedSurrogateProvider != null && !SurrogateData.Contains(dataContract)) { - object customData = DataContractSurrogateCaller.GetCustomDataToExport( - _dataContractSurrogate, clrType, dcType); + object? customData = DataContractSurrogateCaller.GetCustomDataToExport(_extendedSurrogateProvider, type, dcType); if (customData != null) - SurrogateDataTable.Add(dataContract, customData); + SurrogateData.Add(dataContract, customData); + } + + return dataContract; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + public DataContract? GetDataContract(XmlQualifiedName key) + { + DataContract? dataContract = DataContract.GetBuiltInDataContract(key.Name, key.Namespace); + if (dataContract == null) + { + Contracts.TryGetValue(key, out dataContract); } -#endif return dataContract; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static DataContract GetMemberTypeDataContract(DataMember dataMember) + internal DataContract GetMemberTypeDataContract(DataMember dataMember) { - Type dataMemberType = dataMember.MemberType; - if (dataMember.IsGetOnlyCollection) + if (dataMember.MemberInfo is not Type) { -#if SUPPORT_SURROGATE - if (_dataContractSurrogate != null) + Type dataMemberType = dataMember.MemberType; + if (dataMember.IsGetOnlyCollection) + { + if (_surrogateProvider != null) { - Type dcType = DataContractSurrogateCaller.GetDataContractType(_dataContractSurrogate, dataMemberType); + Type dcType = DataContractSurrogateCaller.GetDataContractType(_surrogateProvider, dataMemberType); if (dcType != dataMemberType) { - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.SurrogatesWithGetOnlyCollectionsNotSupported, - DataContract.GetClrTypeFullName(dataMemberType), DataContract.GetClrTypeFullName(dataMember.MemberInfo.DeclaringType), dataMember.MemberInfo.Name))); + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.SurrogatesWithGetOnlyCollectionsNotSupported, + DataContract.GetClrTypeFullName(dataMemberType), + (dataMember.MemberInfo.DeclaringType != null) ? DataContract.GetClrTypeFullName(dataMember.MemberInfo.DeclaringType) : dataMember.MemberInfo.DeclaringType, + dataMember.MemberInfo.Name))); } } -#endif - return DataContract.GetGetOnlyCollectionDataContract(DataContract.GetId(dataMemberType.TypeHandle), dataMemberType.TypeHandle, dataMemberType, SerializationMode.SharedContract); - } - else - { - return GetDataContract(dataMemberType); + return DataContract.GetGetOnlyCollectionDataContract(DataContract.GetId(dataMemberType.TypeHandle), dataMemberType.TypeHandle, dataMemberType); + } + else + { + return GetDataContract(dataMemberType); + } } + return dataMember.MemberTypeContract; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static DataContract GetItemTypeDataContract(CollectionDataContract collectionContract) + internal DataContract GetItemTypeDataContract(CollectionDataContract collectionContract) { if (collectionContract.ItemType != null) return GetDataContract(collectionContract.ItemType); return collectionContract.ItemContract; } -#if SUPPORT_SURROGATE - internal object GetSurrogateData(object key) + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal bool Remove(XmlQualifiedName key) { - return SurrogateDataTable[key]; + if (DataContract.GetBuiltInDataContract(key.Name, key.Namespace) != null) + return false; + return Contracts.Remove(key); } - internal void SetSurrogateData(object key, object surrogateData) + private Dictionary? _referencedTypesDictionary; + private Dictionary? _referencedCollectionTypesDictionary; + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private Dictionary GetReferencedTypes() { - SurrogateDataTable[key] = surrogateData; + if (_referencedTypesDictionary == null) + { + _referencedTypesDictionary = new Dictionary(); + //Always include Nullable as referenced type + //Do not allow surrogating Nullable + _referencedTypesDictionary.Add(DataContract.GetXmlName(Globals.TypeOfNullable), Globals.TypeOfNullable); + if (_referencedTypes != null) + { + foreach (Type type in _referencedTypes) + { + if (type == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.ReferencedTypesCannotContainNull))); + + AddReferencedType(_referencedTypesDictionary, type); + } + } + } + return _referencedTypesDictionary; } - public IDataContractSurrogate DataContractSurrogate + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private Dictionary GetReferencedCollectionTypes() { - get { return _dataContractSurrogate; } + if (_referencedCollectionTypesDictionary == null) + { + _referencedCollectionTypesDictionary = new Dictionary(); + if (_referencedCollectionTypes != null) + { + foreach (Type type in _referencedCollectionTypes) + { + if (type == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.ReferencedCollectionTypesCannotContainNull))); + AddReferencedType(_referencedCollectionTypesDictionary, type); + } + } + XmlQualifiedName genericDictionaryName = DataContract.GetXmlName(Globals.TypeOfDictionaryGeneric); + if (!_referencedCollectionTypesDictionary.ContainsKey(genericDictionaryName) && GetReferencedTypes().ContainsKey(genericDictionaryName)) + AddReferencedType(_referencedCollectionTypesDictionary, Globals.TypeOfDictionaryGeneric); + } + return _referencedCollectionTypesDictionary; } -#endif - public IEnumerator> GetEnumerator() + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private void AddReferencedType(Dictionary referencedTypes, Type type) { - return Contracts.GetEnumerator(); + if (IsTypeReferenceable(type)) + { + XmlQualifiedName xmlName; + try + { + xmlName = GetXmlName(type); + } + catch (InvalidDataContractException) + { + // Type not referenceable if we can't get a xml name. + return; + } + catch (InvalidOperationException) + { + // Type not referenceable if we can't get a xml name. + return; + } + + if (referencedTypes.TryGetValue(xmlName, out object? value)) + { + if (value is Type referencedType) + { + if (referencedType != type) + { + referencedTypes.Remove(xmlName); + List types = new List(); + types.Add(referencedType); + types.Add(type); + referencedTypes.Add(xmlName, types); + } + } + else + { + List types = (List)value; + if (!types.Contains(type)) + types.Add(type); + } + } + else + referencedTypes.Add(xmlName, type); + } + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private static bool IsTypeReferenceable(Type type) + { + try + { + return (type.IsSerializable || + type.IsDefined(Globals.TypeOfDataContractAttribute, false) || + (Globals.TypeOfIXmlSerializable.IsAssignableFrom(type) && !type.IsGenericTypeDefinition) || + CollectionDataContract.IsCollection(type, out _) || + ClassDataContract.IsNonAttributedTypeValidForSerialization(type)); + } + catch (Exception ex) + { + // An exception can be thrown in the designer when a project has a runtime binding redirection for a referenced assembly or a reference dependent assembly. + // Type.IsDefined is known to throw System.IO.FileLoadException. + // ClassDataContract.IsNonAttributedTypeValidForSerialization is known to throw System.IO.FileNotFoundException. + // We guard against all non-critical exceptions. + if (Fx.IsFatal(ex)) + throw; + } + + return false; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + public Type? GetReferencedType(XmlQualifiedName xmlName, DataContract dataContract, out DataContract? referencedContract, out object[]? genericParameters, bool? supportGenericTypes = null) + { + Type? type = GetReferencedTypeInternal(xmlName, dataContract); + referencedContract = null; + genericParameters = null; + + if (supportGenericTypes == null) + return type; + + if (type != null && !type.IsGenericTypeDefinition && !type.ContainsGenericParameters) + return type; + + if (dataContract.GenericInfo == null) + return null; + + XmlQualifiedName genericXmlName = dataContract.GenericInfo.GetExpandedXmlName(); + if (genericXmlName != dataContract.XmlName) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.GenericTypeNameMismatch, dataContract.XmlName.Name, dataContract.XmlName.Namespace, genericXmlName.Name, genericXmlName.Namespace))); + + // This check originally came "here" in the old code. Its tempting to move it up with the GenericInfo check. + if (!supportGenericTypes.Value) + return null; + + type = GetReferencedGenericTypeInternal(dataContract.GenericInfo, out referencedContract, out genericParameters); + return type; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private Type? GetReferencedGenericTypeInternal(GenericInfo genInfo, out DataContract? referencedContract, out object[]? genericParameters) + { + genericParameters = null; + referencedContract = null; + + Type? type = GetReferencedTypeInternal(genInfo.XmlName, null); + + if (type == null) + { + if (genInfo.Parameters != null) + return null; + + referencedContract = GetDataContract(genInfo.XmlName); + if (referencedContract != null && referencedContract.GenericInfo != null) + referencedContract = null; + + return null; // No type, but maybe we found a suitable referenced contract? + } + + // We've got a type. But its generic. So we need some parameter contracts. + // referencedContract is still null, but will be set if we can verify all parameters. + if (genInfo.Parameters != null) + { + bool enableStructureCheck = (type != Globals.TypeOfNullable); + genericParameters = new object[genInfo.Parameters.Count]; + DataContract[] structureCheckContracts = new DataContract[genInfo.Parameters.Count]; + for (int i = 0; i < genInfo.Parameters.Count; i++) + { + GenericInfo paramInfo = genInfo.Parameters[i]; + XmlQualifiedName paramXmlName = paramInfo.GetExpandedXmlName(); + DataContract? paramContract = GetDataContract(paramXmlName); + + if (paramContract != null) + { + genericParameters[i] = paramContract; + } + else + { + Type? paramType = GetReferencedGenericTypeInternal(paramInfo, out paramContract, out object[]? paramParameters); + if (paramType != null) + { + genericParameters[i] = new Tuple(paramType, paramParameters); + } + else + { + genericParameters[i] = paramContract!; + } + } + + structureCheckContracts[i] = paramContract!; // This is ok. If it's null, we disable the use of this array in the next line. + if (paramContract == null) + enableStructureCheck = false; + } + if (enableStructureCheck) + referencedContract = DataContract.GetDataContract(type).BindGenericParameters(structureCheckContracts); + } + + return type; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private Type? GetReferencedTypeInternal(XmlQualifiedName xmlName, DataContract? dataContract) + { + Type? type; + + if (dataContract == null) + { + if (TryGetReferencedCollectionType(xmlName, null, out type)) + return type; + if (TryGetReferencedType(xmlName, null, out type)) + { + // enforce that collection types only be specified via ReferencedCollectionTypes + if (CollectionDataContract.IsCollection(type)) + return null; + + return type; + } + } + else if (dataContract is CollectionDataContract) + { + if (TryGetReferencedCollectionType(xmlName, dataContract, out type)) + return type; + } + else + { + if (dataContract is XmlDataContract xmlDataContract && xmlDataContract.IsAnonymous) + xmlName = SchemaImporter.ImportActualType(xmlDataContract.XsdType?.Annotation, xmlName, dataContract.XmlName); + + if (TryGetReferencedType(xmlName, dataContract, out type)) + return type; + } + return null; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal bool TryGetReferencedType(XmlQualifiedName xmlName, DataContract? dataContract, [NotNullWhen(true)] out Type? type) + { + return TryGetReferencedType(xmlName, dataContract, false/*useReferencedCollectionTypes*/, out type); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal bool TryGetReferencedCollectionType(XmlQualifiedName xmlName, DataContract? dataContract, [NotNullWhen(true)] out Type? type) + { + return TryGetReferencedType(xmlName, dataContract, true/*useReferencedCollectionTypes*/, out type); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private bool TryGetReferencedType(XmlQualifiedName xmlName, DataContract? dataContract, bool useReferencedCollectionTypes, [NotNullWhen(true)] out Type? type) + { + Dictionary referencedTypes = useReferencedCollectionTypes ? GetReferencedCollectionTypes() : GetReferencedTypes(); + if (referencedTypes.TryGetValue(xmlName, out object? value)) + { + type = value as Type; + if (type != null) + { + return true; + } + else + { + // Throw ambiguous type match exception + List types = (List)value; + StringBuilder errorMessage = new StringBuilder(); + bool containsGenericType = false; + for (int i = 0; i < types.Count; i++) + { + Type conflictingType = types[i]; + if (!containsGenericType) + containsGenericType = conflictingType.IsGenericTypeDefinition; + errorMessage.AppendFormat("{0}\"{1}\" ", Environment.NewLine, conflictingType.AssemblyQualifiedName); + if (dataContract != null) + { + DataContract other = GetDataContract(conflictingType); + errorMessage.Append(SR.Format(((other != null && other.Equals(dataContract)) ? SR.ReferencedTypeMatchingMessage : SR.ReferencedTypeNotMatchingMessage))); + } + } + if (containsGenericType) + { + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format( + (useReferencedCollectionTypes ? SR.AmbiguousReferencedCollectionTypes1 : SR.AmbiguousReferencedTypes1), + errorMessage.ToString()))); + } + else + { + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format( + (useReferencedCollectionTypes ? SR.AmbiguousReferencedCollectionTypes3 : SR.AmbiguousReferencedTypes3), + XmlConvert.DecodeName(xmlName.Name), + xmlName.Namespace, + errorMessage.ToString()))); + } + } + } + type = null; + return false; + } + + internal ISerializationSurrogateProvider2? SerializationExtendedSurrogateProvider => _extendedSurrogateProvider; + + internal object? GetSurrogateData(object key) + { + return SurrogateData[key]; + } + + internal void SetSurrogateData(object key, object? surrogateData) + { + SurrogateData[key] = surrogateData; } internal bool IsContractProcessed(DataContract dataContract) @@ -291,19 +615,24 @@ internal void SetContractProcessed(DataContract dataContract) ProcessedContracts.Add(dataContract, dataContract); } -#if SUPPORT_SURROGATE - internal ContractCodeDomInfo GetContractCodeDomInfo(DataContract dataContract) + internal IEnumerator> GetEnumerator() { - object info; - if (ProcessedContracts.TryGetValue(dataContract, out info)) - return (ContractCodeDomInfo)info; - return null; + return Contracts.GetEnumerator(); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + public void ImportSchemaSet(XmlSchemaSet schemaSet, IEnumerable? typeNames, bool importXmlDataType) + { + SchemaImporter importer = new SchemaImporter(schemaSet, typeNames, null, this, importXmlDataType); + importer.Import(out List _); } - internal void SetContractCodeDomInfo(DataContract dataContract, ContractCodeDomInfo info) + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + public List ImportSchemaSet(XmlSchemaSet schemaSet, IEnumerable elements, bool importXmlDataType) { - ProcessedContracts.Add(dataContract, info); + SchemaImporter importer = new SchemaImporter(schemaSet, Array.Empty() /* Needs to be empty, not null for 'elements' to be used. */, elements, this, importXmlDataType); + importer.Import(out List? elementNames); + return elementNames!; // Not null when we have provided non-null 'typeNames' and 'elements' } -#endif } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSurrogateCaller.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSurrogateCaller.cs index 03d145ba53b884..a0554e10fd6286 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSurrogateCaller.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSurrogateCaller.cs @@ -2,10 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.CodeDom; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Runtime.Serialization.DataContracts; namespace System.Runtime.Serialization { @@ -40,5 +40,31 @@ internal static Type GetDataContractType(ISerializationSurrogateProvider surroga return obj; return surrogateProvider.GetDeserializedObject(obj, memberType); } + + internal static object? GetCustomDataToExport(ISerializationSurrogateProvider2 surrogateProvider, MemberInfo memberInfo, Type dataContractType) + { + return surrogateProvider.GetCustomDataToExport(memberInfo, dataContractType); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal static object? GetCustomDataToExport(ISerializationSurrogateProvider2 surrogateProvider, Type clrType, Type dataContractType) + { + if (DataContract.GetBuiltInDataContract(clrType) != null) + return null; + return surrogateProvider.GetCustomDataToExport(clrType, dataContractType); + } + + internal static void GetKnownCustomDataTypes(ISerializationSurrogateProvider2 surrogateProvider, Collection customDataTypes) + { + surrogateProvider.GetKnownCustomDataTypes(customDataTypes); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal static Type? GetReferencedTypeOnImport(ISerializationSurrogateProvider2 surrogateProvider, string typeName, string typeNamespace, object? customData) + { + if (DataContract.GetBuiltInDataContract(typeName, typeNamespace) != null) + return null; + return surrogateProvider.GetReferencedTypeOnImport(typeName, typeNamespace, customData); + } } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataMember.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataMember.cs index 251e896e864880..2f1ed959e98dc5 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataMember.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataMember.cs @@ -2,17 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections; using System.Collections.Generic; -using System.Globalization; -using System.Reflection; -using System.Xml; -using System.Security; using System.Diagnostics.CodeAnalysis; +using System.Reflection; -namespace System.Runtime.Serialization +namespace System.Runtime.Serialization.DataContracts { - internal sealed class DataMember + public sealed class DataMember { private readonly CriticalHelper _helper; @@ -21,104 +17,73 @@ internal DataMember(MemberInfo memberInfo) _helper = new CriticalHelper(memberInfo); } - internal MemberInfo MemberInfo + internal DataMember(DataContract memberTypeContract, string name, bool isNullable, bool isRequired, bool emitDefaultValue, long order) { - get - { return _helper.MemberInfo; } + _helper = new CriticalHelper(memberTypeContract, name, isNullable, isRequired, emitDefaultValue, order); } + internal MemberInfo MemberInfo => _helper.MemberInfo; + public string Name { - get - { return _helper.Name; } - - set - { _helper.Name = value; } + get => _helper.Name; + internal set => _helper.Name = value; } - public int Order + public long Order { - get - { return _helper.Order; } - - set - { _helper.Order = value; } + get => _helper.Order; + internal set => _helper.Order = value; } public bool IsRequired { - get - { return _helper.IsRequired; } - - set - { _helper.IsRequired = value; } + get => _helper.IsRequired; + internal set => _helper.IsRequired = value; } public bool EmitDefaultValue { - get - { return _helper.EmitDefaultValue; } - - set - { _helper.EmitDefaultValue = value; } + get => _helper.EmitDefaultValue; + internal set => _helper.EmitDefaultValue = value; } public bool IsNullable { - get - { return _helper.IsNullable; } - - set - { _helper.IsNullable = value; } + get => _helper.IsNullable; + internal set => _helper.IsNullable = value; } - public bool IsGetOnlyCollection + internal bool IsGetOnlyCollection { - get - { return _helper.IsGetOnlyCollection; } - - set - { _helper.IsGetOnlyCollection = value; } + get => _helper.IsGetOnlyCollection; + set => _helper.IsGetOnlyCollection = value; } - internal Type MemberType - { - get - { return _helper.MemberType; } - } + internal Type MemberType => _helper.MemberType; - internal DataContract MemberTypeContract + public DataContract MemberTypeContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - get - { return _helper.MemberTypeContract; } + get => _helper.MemberTypeContract; } internal PrimitiveDataContract? MemberPrimitiveContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - get - { - return _helper.MemberPrimitiveContract; - } + get => _helper.MemberPrimitiveContract; } - public bool HasConflictingNameAndType + internal bool HasConflictingNameAndType { - get - { return _helper.HasConflictingNameAndType; } - - set - { _helper.HasConflictingNameAndType = value; } + get => _helper.HasConflictingNameAndType; + set => _helper.HasConflictingNameAndType = value; } internal DataMember? ConflictingMember { - get - { return _helper.ConflictingMember; } - - set - { _helper.ConflictingMember = value; } + get => _helper.ConflictingMember; + set => _helper.ConflictingMember = value; } private FastInvokerBuilder.Getter? _getter; @@ -131,12 +96,13 @@ private sealed class CriticalHelper { private DataContract? _memberTypeContract; private string _name = null!; // Name is always initialized right after construction - private int _order; + private long _order; private bool _isRequired; private bool _emitDefaultValue; private bool _isNullable; private bool _isGetOnlyCollection; private readonly MemberInfo _memberInfo; + private Type? _memberType; private bool _hasConflictingNameAndType; private DataMember? _conflictingMember; @@ -147,60 +113,67 @@ internal CriticalHelper(MemberInfo memberInfo) _memberPrimitiveContract = PrimitiveDataContract.NullContract; } - internal MemberInfo MemberInfo + internal CriticalHelper(DataContract memberTypeContract, string name, bool isNullable, bool isRequired, bool emitDefaultValue, long order) { - get { return _memberInfo; } + _memberTypeContract = memberTypeContract; + _name = name; + _isNullable = isNullable; + _isRequired = isRequired; + _emitDefaultValue = emitDefaultValue; + _order = order; + _memberInfo = memberTypeContract.UnderlyingType; } + internal MemberInfo MemberInfo => _memberInfo; + internal string Name { - get { return _name; } - set { _name = value; } + get => _name; + set => _name = value; } - internal int Order + internal long Order { - get { return _order; } - set { _order = value; } + get => _order; + set => _order = value; } internal bool IsRequired { - get { return _isRequired; } - set { _isRequired = value; } + get => _isRequired; + set => _isRequired = value; } internal bool EmitDefaultValue { - get { return _emitDefaultValue; } - set { _emitDefaultValue = value; } + get => _emitDefaultValue; + set => _emitDefaultValue = value; } internal bool IsNullable { - get { return _isNullable; } - set { _isNullable = value; } + get => _isNullable; + set => _isNullable = value; } internal bool IsGetOnlyCollection { - get { return _isGetOnlyCollection; } - set { _isGetOnlyCollection = value; } + get => _isGetOnlyCollection; + set => _isGetOnlyCollection = value; } - private Type? _memberType; - internal Type MemberType { get { if (_memberType == null) { - FieldInfo? field = MemberInfo as FieldInfo; - if (field != null) + if (MemberInfo is FieldInfo field) _memberType = field.FieldType; + else if (MemberInfo is PropertyInfo prop) + _memberType = prop.PropertyType; else - _memberType = ((PropertyInfo)MemberInfo).PropertyType; + _memberType = (Type)MemberInfo!; } return _memberType; @@ -214,9 +187,9 @@ internal DataContract MemberTypeContract { if (_memberTypeContract == null) { - if (this.IsGetOnlyCollection) + if (IsGetOnlyCollection) { - _memberTypeContract = DataContract.GetGetOnlyCollectionDataContract(DataContract.GetId(MemberType.TypeHandle), MemberType.TypeHandle, MemberType, SerializationMode.SharedContract); + _memberTypeContract = DataContract.GetGetOnlyCollectionDataContract(DataContract.GetId(MemberType.TypeHandle), MemberType.TypeHandle, MemberType); } else { @@ -234,14 +207,14 @@ internal DataContract MemberTypeContract internal bool HasConflictingNameAndType { - get { return _hasConflictingNameAndType; } - set { _hasConflictingNameAndType = value; } + get => _hasConflictingNameAndType; + set => _hasConflictingNameAndType = value; } internal DataMember? ConflictingMember { - get { return _conflictingMember; } - set { _conflictingMember = value; } + get => _conflictingMember; + set => _conflictingMember = value; } private PrimitiveDataContract? _memberPrimitiveContract; @@ -310,5 +283,38 @@ internal bool RequiresMemberAccessForSet() } return false; } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal DataMember BindGenericParameters(DataContract[] paramContracts, Dictionary? boundContracts = null) + { + DataContract memberTypeContract = MemberTypeContract.BindGenericParameters(paramContracts, boundContracts); + DataMember boundDataMember = new DataMember(memberTypeContract, + Name, + !memberTypeContract.IsValueType, + IsRequired, + EmitDefaultValue, + Order); + return boundDataMember; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal bool Equals(object? other, HashSet? checkedContracts) + { + if (this == other) + return true; + + if (other is DataMember dataMember) + { + // Note: comparison does not use Order hint since it influences element order but does not specify exact order + bool thisIsNullable = (MemberTypeContract == null) ? false : !MemberTypeContract.IsValueType; + bool dataMemberIsNullable = (dataMember.MemberTypeContract == null) ? false : !dataMember.MemberTypeContract.IsValueType; + return (Name == dataMember.Name + && (IsNullable || thisIsNullable) == (dataMember.IsNullable || dataMemberIsNullable) + && IsRequired == dataMember.IsRequired + && EmitDefaultValue == dataMember.EmitDefaultValue + && MemberTypeContract!.Equals(dataMember.MemberTypeContract, checkedContracts)); + } + return false; + } } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DateTimeOffsetAdapter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DateTimeOffsetAdapter.cs index 20cb05471fe63e..da469b0c337c61 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DateTimeOffsetAdapter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DateTimeOffsetAdapter.cs @@ -66,7 +66,7 @@ public static DateTimeOffsetAdapter GetDateTimeOffsetAdapter(DateTimeOffset valu public string ToString(IFormatProvider provider) { - return "DateTime: " + this.UtcDateTime + ", Offset: " + this.OffsetMinutes; + return "DateTime: " + UtcDateTime + ", Offset: " + OffsetMinutes; } } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DiagnosticUtility.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DiagnosticUtility.cs index 012b8ec54929b3..a6217253340dc5 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DiagnosticUtility.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DiagnosticUtility.cs @@ -3,6 +3,8 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Threading; namespace System.Runtime.Serialization { @@ -20,6 +22,50 @@ public static void Assert(string message) { Assert(false, message); } + + public static bool IsFatal(Exception exception) + { + while (exception != null) + { + // NetFx checked for FatalException and FatalInternalException as well, which were ServiceModel constructs. + if ((exception is OutOfMemoryException && !(exception is InsufficientMemoryException)) || + exception is ThreadAbortException) + { + return true; + } + + // These exceptions aren't themselves fatal, but since the CLR uses them to wrap other exceptions, + // we want to check to see whether they've been used to wrap a fatal exception. If so, then they + // count as fatal. + if (exception is TypeInitializationException || + exception is TargetInvocationException) + { + exception = exception.InnerException!; + } + else if (exception is AggregateException) + { + // AggregateExceptions have a collection of inner exceptions, which may themselves be other + // wrapping exceptions (including nested AggregateExceptions). Recursively walk this + // hierarchy. The (singular) InnerException is included in the collection. + var innerExceptions = ((AggregateException)exception).InnerExceptions; + foreach (Exception innerException in innerExceptions) + { + if (IsFatal(innerException)) + { + return true; + } + } + + break; + } + else + { + break; + } + } + + return false; + } } internal static class DiagnosticUtility diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/EnumDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/EnumDataContract.cs index 9014a71b47d401..ac13799e9e78f2 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/EnumDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/EnumDataContract.cs @@ -2,75 +2,80 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections; using System.Collections.Generic; -using System.Globalization; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Reflection; using System.Threading; -using System.Text; using System.Xml; -using System.Security; -using System.Linq; -using System.Diagnostics.CodeAnalysis; -namespace System.Runtime.Serialization +namespace System.Runtime.Serialization.DataContracts { internal sealed class EnumDataContract : DataContract { - private readonly EnumDataContractCriticalHelper _helper; + internal const string ContractTypeString = nameof(EnumDataContract); + public override string? ContractType => ContractTypeString; - public XmlQualifiedName? BaseContractName { get; set; } + private readonly EnumDataContractCriticalHelper _helper; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal EnumDataContract(Type type) : base(new EnumDataContractCriticalHelper(type)) { _helper = (base.Helper as EnumDataContractCriticalHelper)!; } - public List Members + + internal static Type? GetBaseType(XmlQualifiedName baseContractName) { - get - { return _helper.Members; } - set { _helper.Members = value; } + return EnumDataContractCriticalHelper.GetBaseType(baseContractName); } - public List? Values + public override DataContract BaseContract { - get - { return _helper.Values; } - set { _helper.Values = value; } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + get => _helper.BaseContract; } - public bool IsFlags + internal XmlQualifiedName BaseContractName { - get - { return _helper.IsFlags; } - set { _helper.IsFlags = value; } + get => _helper.BaseContractName; + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + set => _helper.BaseContractName = value; } - public bool IsULong + internal List Members { - get - { return _helper.IsULong; } - set { _helper.IsULong = value; } + get => _helper.Members; + set => _helper.Members = value; } - public XmlDictionaryString[]? ChildElementNames + public override ReadOnlyCollection DataMembers => (Members == null) ? DataContract.s_emptyDataMemberList : Members.AsReadOnly(); + + internal List? Values { - get - { return _helper.ChildElementNames; } - set { _helper.ChildElementNames = value; } + get => _helper.Values; + set => _helper.Values = value; } - internal override bool CanContainReferences + internal bool IsFlags { - get { return false; } + get => _helper.IsFlags; + set => _helper.IsFlags = value; } + internal bool IsULong => _helper.IsULong; + + internal XmlDictionaryString[]? ChildElementNames => _helper.ChildElementNames; + + internal override bool CanContainReferences => false; + private sealed class EnumDataContractCriticalHelper : DataContract.DataContractCriticalHelper { private static readonly Dictionary s_typeToName = new Dictionary(); private static readonly Dictionary s_nameToType = new Dictionary(); + private DataContract _baseContract; private List _members; private List? _values; private bool _isULong; @@ -80,21 +85,35 @@ private sealed class EnumDataContractCriticalHelper : DataContract.DataContractC static EnumDataContractCriticalHelper() { - Add(typeof(sbyte), "byte"); - Add(typeof(byte), "unsignedByte"); - Add(typeof(short), "short"); - Add(typeof(ushort), "unsignedShort"); - Add(typeof(int), "int"); - Add(typeof(uint), "unsignedInt"); - Add(typeof(long), "long"); - Add(typeof(ulong), "unsignedLong"); + Add(typeof(sbyte), DictionaryGlobals.SignedByteLocalName.Value); // "byte" + Add(typeof(byte), DictionaryGlobals.UnsignedByteLocalName.Value); // "unsignedByte" + Add(typeof(short), DictionaryGlobals.ShortLocalName.Value); // "short" + Add(typeof(ushort), DictionaryGlobals.UnsignedShortLocalName.Value); // "unsignedShort" + Add(typeof(int), DictionaryGlobals.IntLocalName.Value); // "int" + Add(typeof(uint), DictionaryGlobals.UnsignedIntLocalName.Value); // "unsignedInt" + Add(typeof(long), DictionaryGlobals.LongLocalName.Value); // "long" + Add(typeof(ulong), DictionaryGlobals.UnsignedLongLocalName.Value); // "unsignedLong" } internal static void Add(Type type, string localName) { - XmlQualifiedName stableName = CreateQualifiedName(localName, Globals.SchemaNamespace); - s_typeToName.Add(type, stableName); - s_nameToType.Add(stableName, type); + XmlQualifiedName xmlName = CreateQualifiedName(localName, Globals.SchemaNamespace); + s_typeToName.Add(type, xmlName); + s_nameToType.Add(xmlName, type); + } + + internal static XmlQualifiedName GetBaseContractName(Type type) + { + s_typeToName.TryGetValue(type, out XmlQualifiedName? retVal); + + Debug.Assert(retVal != null); // Enums can only have certain base types. We shouldn't come up empty here. + return retVal; + } + + internal static Type? GetBaseType(XmlQualifiedName baseContractName) + { + s_nameToType.TryGetValue(baseContractName, out Type? retVal); + return retVal; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -102,20 +121,23 @@ internal EnumDataContractCriticalHelper( [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] Type type) : base(type) { - this.StableName = DataContract.GetStableName(type, out _hasDataContract); + XmlName = DataContract.GetXmlName(type, out _hasDataContract); Type baseType = Enum.GetUnderlyingType(type); + XmlQualifiedName baseTypeName = GetBaseContractName(baseType); + _baseContract = DataContract.GetBuiltInDataContract(baseTypeName.Name, baseTypeName.Namespace)!; + // Setting XmlName might be redundant. But I don't want to miss an edge case. + _baseContract.XmlName = baseTypeName; ImportBaseType(baseType); IsFlags = type.IsDefined(Globals.TypeOfFlagsAttribute, false); ImportDataMembers(); XmlDictionary dictionary = new XmlDictionary(2 + Members.Count); - Name = dictionary.Add(StableName.Name); - Namespace = dictionary.Add(StableName.Namespace); + Name = dictionary.Add(XmlName.Name); + Namespace = dictionary.Add(XmlName.Namespace); _childElementNames = new XmlDictionaryString[Members.Count]; for (int i = 0; i < Members.Count; i++) _childElementNames[i] = dictionary.Add(Members[i].Name); - DataContractAttribute? dataContractAttribute; - if (TryGetDCAttribute(type, out dataContractAttribute)) + if (TryGetDCAttribute(type, out DataContractAttribute? dataContractAttribute)) { if (dataContractAttribute.IsReference) { @@ -128,34 +150,55 @@ internal EnumDataContractCriticalHelper( } } } + + internal DataContract BaseContract => _baseContract; + + internal XmlQualifiedName BaseContractName + { + get => _baseContract.XmlName; + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + set + { + Type? baseType = GetBaseType(value); + if (baseType == null) + ThrowInvalidDataContractException( + SR.Format(SR.InvalidEnumBaseType, value.Name, value.Namespace, XmlName.Name, XmlName.Namespace)); + ImportBaseType(baseType); + _baseContract = DataContract.GetBuiltInDataContract(value.Name, value.Namespace)!; + // Setting XmlName might be redundant. But I don't want to miss an edge case. + _baseContract.XmlName = value; + } + } + internal List Members { - get { return _members; } - set { _members = value; } + get => _members; + set => _members = value; } internal List? Values { - get { return _values; } - set { _values = value; } + get => _values; + set => _values = value; } internal bool IsFlags { - get { return _isFlags; } - set { _isFlags = value; } + get => _isFlags; + set => _isFlags = value; } internal bool IsULong { - get { return _isULong; } - set { _isULong = value; } + get => _isULong; + set => _isULong = value; } internal XmlDictionaryString[]? ChildElementNames { - get { return _childElementNames; } - set { _childElementNames = value; } + get => _childElementNames; + set => _childElementNames = value; } private void ImportBaseType(Type baseType) @@ -166,7 +209,7 @@ private void ImportBaseType(Type baseType) [MemberNotNull(nameof(_members))] private void ImportDataMembers() { - Type type = this.UnderlyingType; + Type type = UnderlyingType; FieldInfo[] fields = type.GetFields(BindingFlags.Static | BindingFlags.Public); Dictionary memberValuesTable = new Dictionary(); List tempMembers = new List(fields.Length); @@ -194,6 +237,7 @@ private void ImportDataMembers() } else memberContract.Name = field.Name; + memberContract.Order = _isULong ? (long)Convert.ToUInt64(field.GetValue(null)) : Convert.ToInt64(field.GetValue(null)); ClassDataContract.CheckAndAddMember(tempMembers, memberContract, memberValuesTable); enumMemberValid = true; } @@ -206,8 +250,8 @@ private void ImportDataMembers() { if (!field.IsNotSerialized) { - DataMember memberContract = new DataMember(field); - memberContract.Name = field.Name; + DataMember memberContract = new DataMember(field) { Name = field.Name }; + memberContract.Order = _isULong ? (long)Convert.ToUInt64(field.GetValue(null)) : Convert.ToInt64(field.GetValue(null)); ClassDataContract.CheckAndAddMember(tempMembers, memberContract, memberValuesTable); enumMemberValid = true; } @@ -359,14 +403,45 @@ internal long GetEnumValueFromString(string value) } } + internal override bool Equals(object? other, HashSet? checkedContracts) + { + if (IsEqualOrChecked(other, checkedContracts)) + return true; + + if (base.Equals(other, null)) + { + if (other is EnumDataContract enumContract) + { + if (Members.Count != enumContract.Members.Count || Values?.Count != enumContract.Values?.Count) + return false; + string[] memberNames1 = new string[Members.Count], memberNames2 = new string[Members.Count]; + for (int i = 0; i < Members.Count; i++) + { + memberNames1[i] = Members[i].Name; + memberNames2[i] = enumContract.Members[i].Name; + } + Array.Sort(memberNames1); + Array.Sort(memberNames2); + for (int i = 0; i < Members.Count; i++) + { + if (memberNames1[i] != memberNames2[i]) + return false; + } + + return (IsFlags == enumContract.IsFlags); + } + } + return false; + } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { WriteEnumValue(xmlWriter, obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) + internal override object ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) { object obj = ReadEnumValue(xmlReader); context?.AddNewObject(obj); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExportOptions.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExportOptions.cs index 435415d1ca5659..8034f326ea09ec 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExportOptions.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExportOptions.cs @@ -5,24 +5,26 @@ namespace System.Runtime.Serialization { + /// + /// Represents the options that can be set for an . + /// + /// + /// The is used to generate XSD schemas from a type or assembly. You can also use the XsdDataContractImporter to generate .NET Framework code from a schema document. + /// + /// The property is used by the to include types that can be read in an object graph. + /// public class ExportOptions { private Collection? _knownTypes; -#if SUPPORT_SURROGATE - private IDataContractSurrogate? _dataContractSurrogate; - public IDataContractSurrogate? DataContractSurrogate - { - get { return _dataContractSurrogate; } - set { _dataContractSurrogate = value; } - } - - internal IDataContractSurrogate? GetSurrogate() - { - return _dataContractSurrogate; - } -#endif + /// + /// Gets or sets a serialization surrogate provider. + /// + public ISerializationSurrogateProvider? DataContractSurrogate { get; set; } + /// + /// Gets the collection of types that may be encountered during serialization or deserialization. + /// public Collection KnownTypes => _knownTypes ??= new Collection(); } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataObject.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataObject.cs index be5723fb3b09a0..6004b41c84e3c4 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataObject.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataObject.cs @@ -36,7 +36,7 @@ public ExtensionDataMember(string name, string ns) public string Name { get; } - public string? Namespace { get; } + public string Namespace { get; } public IDataNode? Value { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataReader.cs index df8b296a45713d..d4309b86b3d4c3 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataReader.cs @@ -1,9 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Xml; using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Runtime.Serialization.DataContracts; +using System.Xml; namespace System.Runtime.Serialization { @@ -27,7 +31,7 @@ private enum ExtensionDataNodeType private ElementData? _nextElement; private ReadState _readState = ReadState.Initial; - private readonly ExtensionDataNodeType _internalNodeType; + private ExtensionDataNodeType _internalNodeType; private XmlNodeType _nodeType; private int _depth; private string? _localName; @@ -37,11 +41,12 @@ private enum ExtensionDataNodeType private int _attributeCount; private int _attributeIndex; + private Hashtable _cache = new Hashtable(); + private XmlNodeReader? _xmlNodeReader; + private Queue? _deserializedDataNodes; + private static readonly object s_prefixLock = new object(); -#pragma warning disable 0649 - private readonly XmlNodeReader? _xmlNodeReader; -#pragma warning restore 0649 private readonly XmlObjectSerializerReadContext _context; @@ -62,6 +67,16 @@ internal ExtensionDataReader(XmlObjectSerializerReadContext context) _context = context; } + internal void SetDeserializedValue(object? obj) + { + IDataNode? deserializedDataNode = (_deserializedDataNodes == null || _deserializedDataNodes.Count == 0) ? null : _deserializedDataNodes.Dequeue(); + if (deserializedDataNode != null && !(obj is IDataNode)) + { + deserializedDataNode.Value = obj; + deserializedDataNode.IsFinalValue = true; + } + } + internal IDataNode? GetCurrentNode() { IDataNode? retVal = _element!.dataNode; @@ -69,6 +84,14 @@ internal ExtensionDataReader(XmlObjectSerializerReadContext context) return retVal; } + internal void SetDataNode(IDataNode dataNode, string? name, string? ns) + { + SetNextElement(dataNode, name, ns, null); + _element = _nextElement; + _nextElement = null; + SetElement(); + } + internal void Reset() { _localName = null; @@ -83,11 +106,9 @@ internal void Reset() _elements = null; } -#pragma warning disable CS8775 // Member must have a non-null value when exiting in some condition. [MemberNotNullWhen(true, nameof(_xmlNodeReader))] [MemberNotNullWhen(false, nameof(_element))] private bool IsXmlDataNode { get { return (_internalNodeType == ExtensionDataNodeType.Xml); } } -#pragma warning restore CS8775 // Member must have a non-null value when exiting in some condition. public override XmlNodeType NodeType { get { return IsXmlDataNode ? _xmlNodeReader.NodeType : _nodeType; } } public override string LocalName { get { return IsXmlDataNode ? _xmlNodeReader.LocalName : _localName!; } } @@ -427,9 +448,294 @@ public override bool ReadAttributeValue() return false; } - private static void MoveNext(IDataNode? dataNode) + private void MoveNext(IDataNode? dataNode) + { + switch (_internalNodeType) + { + case ExtensionDataNodeType.Text: + case ExtensionDataNodeType.ReferencedElement: + case ExtensionDataNodeType.NullElement: + _internalNodeType = ExtensionDataNodeType.EndElement; + return; + default: + Type? dataNodeType = dataNode?.DataType; + if (dataNodeType == Globals.TypeOfClassDataNode) + MoveNextInClass((ClassDataNode)dataNode!); + else if (dataNodeType == Globals.TypeOfCollectionDataNode) + MoveNextInCollection((CollectionDataNode)dataNode!); + else if (dataNodeType == Globals.TypeOfISerializableDataNode) + MoveNextInISerializable((ISerializableDataNode)dataNode!); + else if (dataNodeType == Globals.TypeOfXmlDataNode) + MoveNextInXml((XmlDataNode)dataNode!); + else if (dataNode?.Value != null) + MoveToDeserializedObject(dataNode!); + else + { + Fx.Assert("Encountered invalid data node when deserializing unknown data"); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SerializationException(SR.Format(SR.InvalidStateInExtensionDataReader))); + } + break; + } + } + + private void SetNextElement(IDataNode? node, string? name, string? ns, string? prefix) + { + _internalNodeType = ExtensionDataNodeType.Element; + _nextElement = GetNextElement(); + _nextElement.localName = name; + _nextElement.ns = ns; + _nextElement.prefix = prefix; + if (node == null) + { + _nextElement.attributeCount = 0; + _nextElement.AddAttribute(Globals.XsiPrefix, Globals.SchemaInstanceNamespace, Globals.XsiNilLocalName, Globals.True); + _internalNodeType = ExtensionDataNodeType.NullElement; + } + else if (!CheckIfNodeHandled(node)) + { + AddDeserializedDataNode(node); + node.GetData(_nextElement); + if (node is XmlDataNode xdn) + MoveNextInXml(xdn); + } + } + + private void AddDeserializedDataNode(IDataNode node) + { + if (node.Id != Globals.NewObjectId && (node.Value == null || !node.IsFinalValue)) + { + if (_deserializedDataNodes == null) + _deserializedDataNodes = new Queue(); + _deserializedDataNodes.Enqueue(node); + } + } + + private bool CheckIfNodeHandled(IDataNode node) + { + bool handled = false; + if (node.Id != Globals.NewObjectId) + { + handled = (_cache[node] != null); + if (handled) + { + if (_nextElement == null) + _nextElement = GetNextElement(); + _nextElement.attributeCount = 0; + _nextElement.AddAttribute(Globals.SerPrefix, Globals.SerializationNamespace, Globals.RefLocalName, node.Id.ToString(NumberFormatInfo.InvariantInfo)); + _nextElement.AddAttribute(Globals.XsiPrefix, Globals.SchemaInstanceNamespace, Globals.XsiNilLocalName, Globals.True); + _internalNodeType = ExtensionDataNodeType.ReferencedElement; + } + else + { + _cache.Add(node, node); + } + } + return handled; + } + + private void MoveNextInClass(ClassDataNode dataNode) + { + // Two frames above here in Read(), _element is asserted not null. + Fx.Assert(_element != null, ""); + if (dataNode.Members != null && _element.childElementIndex < dataNode.Members.Count) + { + if (_element.childElementIndex == 0) + _context.IncrementItemCount(-dataNode.Members.Count); + + ExtensionDataMember member = dataNode.Members[_element.childElementIndex++]; + SetNextElement(member.Value, member.Name, member.Namespace, GetPrefix(member.Namespace)); + } + else + { + _internalNodeType = ExtensionDataNodeType.EndElement; + _element.childElementIndex = 0; + } + } + + private void MoveNextInCollection(CollectionDataNode dataNode) + { + // Two frames above here in Read(), _element is asserted not null. + Fx.Assert(_element != null, ""); + if (dataNode.Items != null && _element.childElementIndex < dataNode.Items.Count) + { + if (_element.childElementIndex == 0) + _context.IncrementItemCount(-dataNode.Items.Count); + + IDataNode? item = dataNode.Items[_element.childElementIndex++]; + SetNextElement(item, dataNode.ItemName, dataNode.ItemNamespace, GetPrefix(dataNode.ItemNamespace)); + } + else + { + _internalNodeType = ExtensionDataNodeType.EndElement; + _element.childElementIndex = 0; + } + } + + private void MoveNextInISerializable(ISerializableDataNode dataNode) + { + // Two frames above here in Read(), _element is asserted not null. + Fx.Assert(_element != null, ""); + if (dataNode.Members != null && _element.childElementIndex < dataNode.Members.Count) + { + if (_element.childElementIndex == 0) + _context.IncrementItemCount(-dataNode.Members.Count); + + ISerializableDataMember member = dataNode.Members[_element.childElementIndex++]; + SetNextElement(member.Value, member.Name, string.Empty, string.Empty); + } + else + { + _internalNodeType = ExtensionDataNodeType.EndElement; + _element.childElementIndex = 0; + } + } + + private void MoveNextInXml(XmlDataNode dataNode) { - throw NotImplemented.ByDesign; + if (IsXmlDataNode) + { + _xmlNodeReader.Read(); + if (_xmlNodeReader.Depth == 0) + { + _internalNodeType = ExtensionDataNodeType.EndElement; + _xmlNodeReader = null; + } + } + else + { + _internalNodeType = ExtensionDataNodeType.Xml; + if (_element == null) + _element = _nextElement; + else + PushElement(); + + Debug.Assert(dataNode.OwnerDocument != null); // OwnerDocument is always set on initialized dataNodes + + XmlElement wrapperElement = XmlObjectSerializerReadContext.CreateWrapperXmlElement(dataNode.OwnerDocument, + dataNode.XmlAttributes, dataNode.XmlChildNodes, _element?.prefix, _element?.localName, _element?.ns); + if (_element != null) + { + for (int i = 0; i < _element.attributeCount; i++) + { + AttributeData a = _element.attributes![i]; + XmlAttribute xmlAttr = dataNode.OwnerDocument.CreateAttribute(a.prefix, a.localName!, a.ns); + xmlAttr.Value = a.value; + wrapperElement.Attributes.Append(xmlAttr); + } + } + _xmlNodeReader = new XmlNodeReader(wrapperElement); + _xmlNodeReader.Read(); + } + } + + private void MoveToDeserializedObject(IDataNode dataNode) + { + Type type = dataNode.DataType; + bool isTypedNode = true; + if (type == Globals.TypeOfObject && dataNode.Value != null) + { + type = dataNode.Value.GetType(); + if (type == Globals.TypeOfObject) + { + _internalNodeType = ExtensionDataNodeType.EndElement; + return; + } + isTypedNode = false; + } + + if (!MoveToText(type, dataNode, isTypedNode)) + { + if (dataNode.IsFinalValue) + { + _internalNodeType = ExtensionDataNodeType.EndElement; + } + else + { + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.InvalidDataNode, DataContract.GetClrTypeFullName(type)))); + } + } + } + + private bool MoveToText(Type type, IDataNode dataNode, bool isTypedNode) + { + Fx.Assert(dataNode.Value != null, ""); + + bool handled = true; + switch (Type.GetTypeCode(type)) + { + case TypeCode.Boolean: + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (bool)dataNode.Value); + break; + case TypeCode.Char: + _value = XmlConvert.ToString((int)(isTypedNode ? ((DataNode)dataNode).GetValue() : (char)dataNode.Value)); + break; + case TypeCode.Byte: + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (byte)dataNode.Value); + break; + case TypeCode.Int16: + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (short)dataNode.Value); + break; + case TypeCode.Int32: + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (int)dataNode.Value); + break; + case TypeCode.Int64: + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (long)dataNode.Value); + break; + case TypeCode.Single: + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (float)dataNode.Value); + break; + case TypeCode.Double: + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (double)dataNode.Value); + break; + case TypeCode.Decimal: + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (decimal)dataNode.Value); + break; + case TypeCode.DateTime: + DateTime dateTime = isTypedNode ? ((DataNode)dataNode).GetValue() : (DateTime)dataNode.Value; + _value = dateTime.ToString("yyyy-MM-ddTHH:mm:ss.fffffffK", DateTimeFormatInfo.InvariantInfo); + break; + case TypeCode.String: + _value = isTypedNode ? ((DataNode)dataNode).GetValue() : (string?)dataNode.Value; + break; + case TypeCode.SByte: + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (sbyte)dataNode.Value); + break; + case TypeCode.UInt16: + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (ushort)dataNode.Value); + break; + case TypeCode.UInt32: + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (uint)dataNode.Value); + break; + case TypeCode.UInt64: + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (ulong)dataNode.Value); + break; + case TypeCode.Object: + default: + if (type == Globals.TypeOfByteArray) + { + byte[]? bytes = isTypedNode ? ((DataNode)dataNode).GetValue() : (byte[])dataNode.Value; + _value = (bytes == null) ? string.Empty : Convert.ToBase64String(bytes); + } + else if (type == Globals.TypeOfTimeSpan) + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (TimeSpan)dataNode.Value); + else if (type == Globals.TypeOfGuid) + { + Guid guid = isTypedNode ? ((DataNode)dataNode).GetValue() : (Guid)dataNode.Value; + _value = guid.ToString(); + } + else if (type == Globals.TypeOfUri) + { + Uri uri = isTypedNode ? ((DataNode)dataNode).GetValue() : (Uri)dataNode.Value; + _value = uri.GetComponents(UriComponents.SerializationInfoString, UriFormat.UriEscaped); + } + else + handled = false; + break; + } + + if (handled) + _internalNodeType = ExtensionDataNodeType.Text; + return handled; } private void PushElement() @@ -477,11 +783,11 @@ private void GrowElementsIfNeeded() } } - private ElementData? GetNextElement() + private ElementData GetNextElement() { int nextDepth = _depth + 1; return (_elements == null || _elements.Length <= nextDepth || _elements[nextDepth] == null) - ? new ElementData() : _elements[nextDepth]; + ? new ElementData() : _elements[nextDepth]!; } internal static string GetPrefix(string? ns) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/GenericParameterDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/GenericParameterDataContract.cs index 8f5bd20805c9de..1d5eca774631c5 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/GenericParameterDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/GenericParameterDataContract.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -namespace System.Runtime.Serialization +namespace System.Runtime.Serialization.DataContracts { internal sealed class GenericParameterDataContract : DataContract { @@ -18,19 +18,9 @@ internal GenericParameterDataContract(Type type) _helper = (base.Helper as GenericParameterDataContractCriticalHelper)!; } - internal int ParameterPosition - { - get - { return _helper.ParameterPosition; } - } + internal int ParameterPosition => _helper.ParameterPosition; - public override bool IsBuiltInDataContract - { - get - { - return true; - } - } + public override bool IsBuiltInDataContract => true; private sealed class GenericParameterDataContractCriticalHelper : DataContract.DataContractCriticalHelper { @@ -42,17 +32,15 @@ internal GenericParameterDataContractCriticalHelper( Type type) : base(type) { - SetDataContractName(DataContract.GetStableName(type)); + SetDataContractName(DataContract.GetXmlName(type)); _parameterPosition = type.GenericParameterPosition; } - internal int ParameterPosition - { - get { return _parameterPosition; } - } + internal int ParameterPosition => _parameterPosition; } - internal DataContract BindGenericParameters(DataContract[] paramContracts, Dictionary boundContracts) + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal override DataContract BindGenericParameters(DataContract[] paramContracts, Dictionary? boundContracts = null) { return paramContracts[ParameterPosition]; } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Globals.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Globals.cs index 48167f42d04f67..3fa9493884c754 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Globals.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Globals.cs @@ -2,13 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.ComponentModel; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; +using System.Runtime.Serialization.DataContracts; using System.Text.RegularExpressions; using System.Xml; using System.Xml.Schema; @@ -227,6 +227,10 @@ internal static partial class Globals internal static Type TypeOfNullable => s_typeOfNullable ??= typeof(Nullable<>); + private static Type? s_typeOfReflectionPointer; + internal static Type TypeOfReflectionPointer => + s_typeOfReflectionPointer ??= typeof(System.Reflection.Pointer); + private static Type? s_typeOfIDictionaryGeneric; internal static Type TypeOfIDictionaryGeneric => s_typeOfIDictionaryGeneric ??= typeof(IDictionary<,>); @@ -271,10 +275,6 @@ internal static partial class Globals internal static Type TypeOfKeyValuePair => s_typeOfKeyValuePair ??= typeof(KeyValuePair<,>); - private static Type? s_typeOfKeyValuePairAdapter; - internal static Type TypeOfKeyValuePairAdapter => - s_typeOfKeyValuePairAdapter ??= typeof(KeyValuePairAdapter<,>); - private static Type? s_typeOfKeyValue; internal static Type TypeOfKeyValue => s_typeOfKeyValue ??= typeof(KeyValue<,>); @@ -314,21 +314,25 @@ internal static Type TypeOfHashtable internal static Type TypeOfDBNull => s_typeOfDBNull ??= typeof(DBNull); - private static Uri? s_dataContractXsdBaseNamespaceUri; - internal static Uri DataContractXsdBaseNamespaceUri => - s_dataContractXsdBaseNamespaceUri ??= new Uri(DataContractXsdBaseNamespace); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicFields)] + private static Type? s_typeOfSchemaDefinedType; + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicFields)] + internal static Type TypeOfSchemaDefinedType => + s_typeOfSchemaDefinedType ??= typeof(SchemaDefinedType); - private static readonly Type? s_typeOfScriptObject; + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicFields)] + private static Type? s_typeOfSchemaDefinedEnum; + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicFields)] + internal static Type TypeOfSchemaDefinedEnum => + s_typeOfSchemaDefinedEnum ??= typeof(SchemaDefinedEnum); - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static ClassDataContract CreateScriptObjectClassDataContract() - { - Debug.Assert(s_typeOfScriptObject != null); - return new ClassDataContract(s_typeOfScriptObject); - } + private static MemberInfo? s_schemaMemberInfoPlaceholder; + internal static MemberInfo SchemaMemberInfoPlaceholder => + s_schemaMemberInfoPlaceholder ??= TypeOfSchemaDefinedType.GetField(nameof(SchemaDefinedType._xmlName), BindingFlags.NonPublic | BindingFlags.Instance)!; - internal static bool TypeOfScriptObject_IsAssignableFrom(Type type) => - s_typeOfScriptObject != null && s_typeOfScriptObject.IsAssignableFrom(type); + private static Uri? s_dataContractXsdBaseNamespaceUri; + internal static Uri DataContractXsdBaseNamespaceUri => + s_dataContractXsdBaseNamespaceUri ??= new Uri(DataContractXsdBaseNamespace); public const bool DefaultIsRequired = false; public const bool DefaultEmitDefaultValue = true; @@ -341,6 +345,10 @@ internal static bool TypeOfScriptObject_IsAssignableFrom(Type type) => public const string FullSRSInternalsVisiblePattern = @"^[\s]*System\.Runtime\.Serialization[\s]*,[\s]*PublicKey[\s]*=[\s]*(?i:00240000048000009400000006020000002400005253413100040000010001008d56c76f9e8649383049f383c44be0ec204181822a6c31cf5eb7ef486944d032188ea1d3920763712ccb12d75fb77e9811149e6148e5d32fbaab37611c1878ddc19e20ef135d0cb2cff2bfec3d115810c3d9069638fe4be215dbf795861920e5ab6f7db2e2ceef136ac23d5dd2bf031700aec232f6c6b1c785b4305c123b37ab)[\s]*$"; [GeneratedRegex(FullSRSInternalsVisiblePattern)] public static partial Regex FullSRSInternalsVisibleRegex(); + public const char SpaceChar = ' '; + public const char OpenBracketChar = '['; + public const char CloseBracketChar = ']'; + public const char CommaChar = ','; public const string Space = " "; public const string XsiPrefix = "i"; public const string XsdPrefix = "x"; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs index 4ab7ae4006ca95..208edd8125982b 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs @@ -10,10 +10,12 @@ using System.IO; using System.Reflection; using System.Runtime.Serialization; +using System.Runtime.Serialization.DataContracts; using System.Security; using System.Text; using System.Xml; -using DataContractDictionary = System.Collections.Generic.Dictionary; + +using DataContractDictionary = System.Collections.Generic.Dictionary; namespace System.Runtime.Serialization.Json { @@ -32,7 +34,7 @@ public sealed class DataContractJsonSerializer : XmlObjectSerializer private XmlDictionaryString? _rootName; private bool _rootNameRequiresMapping; private Type _rootType; - + private ISerializationSurrogateProvider? _serializationSurrogateProvider; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public DataContractJsonSerializer(Type type) @@ -92,6 +94,12 @@ internal DataContractJsonSerializer(Type type, Initialize(type, rootName, knownTypes, maxItemsInObjectGraph, ignoreExtensionDataObject, emitTypeInformation, false, null, false); } + internal ISerializationSurrogateProvider? SerializationSurrogateProvider + { + get { return _serializationSurrogateProvider; } + set { _serializationSurrogateProvider = value; } + } + public bool IgnoreExtensionDataObject { get { return _ignoreExtensionDataObject; } @@ -193,6 +201,19 @@ private XmlDictionaryString RootName } } + // These Get/Set methods mirror the extensions that were added to DCS in the early days of Core, which allowed + // using a slimmed-down surrogate on both NetFx and Core via type-forwarding mechanisms. That's why these are + // a pair of methods instead of making the property itself public. + public ISerializationSurrogateProvider? GetSerializationSurrogateProvider() + { + return SerializationSurrogateProvider; + } + + public void SetSerializationSurrogateProvider(ISerializationSurrogateProvider? provider) + { + SerializationSurrogateProvider = provider; + } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public override bool IsStartObject(XmlReader reader) { @@ -451,6 +472,11 @@ internal override void InternalWriteObjectContent(XmlWriterDelegator writer, obj Type declaredType = contract.UnderlyingType; Type graphType = (graph == null) ? declaredType : graph.GetType(); + if (_serializationSurrogateProvider != null) + { + graph = DataContractSerializer.SurrogateToDataContractType(_serializationSurrogateProvider, graph, declaredType, ref graphType); + } + if (graph == null) { WriteJsonNull(writer); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializerExtensions.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializerExtensions.cs new file mode 100644 index 00000000000000..0c52dee3789a8c --- /dev/null +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializerExtensions.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 System.Runtime.Serialization.Json +{ + public static class DataContractJsonSerializerExtensions + { + public static ISerializationSurrogateProvider? GetSerializationSurrogateProvider(this DataContractJsonSerializer serializer) + { + return serializer.SerializationSurrogateProvider; + } + + public static void SetSerializationSurrogateProvider(this DataContractJsonSerializer serializer, ISerializationSurrogateProvider? provider) + { + serializer.SerializationSurrogateProvider = provider; + } + } +} diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonByteArrayDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonByteArrayDataContract.cs index 5518442c8649da..8a6ca1fb0f4c4e 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonByteArrayDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonByteArrayDataContract.cs @@ -3,10 +3,11 @@ using System; using System.Collections.Generic; -using System.Text; using System.Diagnostics; -using System.Xml; using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.DataContracts; +using System.Text; +using System.Xml; namespace System.Runtime.Serialization.Json { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonClassDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonClassDataContract.cs index 6ff59d9eaa7f2b..509b99f9682174 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonClassDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonClassDataContract.cs @@ -1,12 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Threading; -using System.Xml; -using System.Diagnostics; using System.Collections.Generic; -using System.Runtime.CompilerServices; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization.DataContracts; +using System.Threading; +using System.Xml; namespace System.Runtime.Serialization.Json { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonCollectionDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonCollectionDataContract.cs index 9d0a40cc5d1c9a..cbf92bf3e43e33 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonCollectionDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonCollectionDataContract.cs @@ -5,6 +5,7 @@ using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.CompilerServices; +using System.Runtime.Serialization.DataContracts; using System.Threading; using System.Xml; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonDataContract.cs index 4330703dc3ab89..88258b3b397cd9 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonDataContract.cs @@ -2,12 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; using System.Runtime; using System.Runtime.Serialization; -using System.Reflection; +using System.Runtime.Serialization.DataContracts; using System.Xml; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; + +using DataContractDictionary = System.Collections.Generic.Dictionary; namespace System.Runtime.Serialization.Json { @@ -32,7 +35,7 @@ protected JsonDataContract(JsonDataContractCriticalHelper helper) protected DataContract TraditionalDataContract => _helper.TraditionalDataContract; - private Dictionary? KnownDataContracts => _helper.KnownDataContracts; + private DataContractDictionary? KnownDataContracts => _helper.KnownDataContracts; public static JsonReadWriteDelegates? GetGeneratedReadWriteDelegates(DataContract c) { @@ -144,7 +147,7 @@ internal class JsonDataContractCriticalHelper private static readonly TypeHandleRef s_typeHandleRef = new TypeHandleRef(); private static readonly Dictionary s_typeToIDCache = new Dictionary(new TypeHandleRefEqualityComparer()); - private Dictionary? _knownDataContracts; + private DataContractDictionary? _knownDataContracts; private readonly DataContract _traditionalDataContract; private readonly string _typeName; @@ -156,7 +159,7 @@ internal JsonDataContractCriticalHelper(DataContract traditionalDataContract) _typeName = string.IsNullOrEmpty(traditionalDataContract.Namespace.Value) ? traditionalDataContract.Name.Value : string.Concat(traditionalDataContract.Name.Value, JsonGlobals.NameValueSeparatorString, XmlObjectSerializerWriteContextComplexJson.TruncateDefaultDataContractNamespace(traditionalDataContract.Namespace.Value)); } - internal Dictionary? KnownDataContracts => _knownDataContracts; + internal DataContractDictionary? KnownDataContracts => _knownDataContracts; internal DataContract TraditionalDataContract => _traditionalDataContract; @@ -201,6 +204,9 @@ internal static int GetId(RuntimeTypeHandle typeHandle) } catch (Exception ex) { + if (Fx.IsFatal(ex)) + throw; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(ex.Message, ex); } } @@ -283,15 +289,15 @@ private void AddCollectionItemContractsToKnownDataContracts() while (collectionDataContract != null) { DataContract itemContract = collectionDataContract.ItemContract; - _knownDataContracts ??= new Dictionary(); + _knownDataContracts ??= new DataContractDictionary(); - _knownDataContracts.TryAdd(itemContract.StableName, itemContract); + _knownDataContracts.TryAdd(itemContract.XmlName, itemContract); if (collectionDataContract.ItemType.IsGenericType && collectionDataContract.ItemType.GetGenericTypeDefinition() == typeof(KeyValue<,>)) { DataContract itemDataContract = DataContract.GetDataContract(Globals.TypeOfKeyValuePair.MakeGenericType(collectionDataContract.ItemType.GenericTypeArguments)); - _knownDataContracts.TryAdd(itemDataContract.StableName, itemDataContract); + _knownDataContracts.TryAdd(itemDataContract.XmlName, itemDataContract); } if (!(itemContract is CollectionDataContract)) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEnumDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEnumDataContract.cs index 76f774cde91f09..e20b49ca92b4e9 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEnumDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEnumDataContract.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.DataContracts; namespace System.Runtime.Serialization.Json { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatGeneratorStatics.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatGeneratorStatics.cs index 0df641f90eea0b..b094e6fcde6889 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatGeneratorStatics.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatGeneratorStatics.cs @@ -7,6 +7,7 @@ using System.Xml; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.DataContracts; using System.Runtime.Serialization.Json; namespace System.Runtime.Serialization diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatReaderGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatReaderGenerator.cs index ceee570b7475ef..9b7dd72c90c936 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatReaderGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatReaderGenerator.cs @@ -8,6 +8,7 @@ using System.Reflection; using System.Reflection.Emit; using System.Runtime; +using System.Runtime.Serialization.DataContracts; using System.Security; using System.Xml; @@ -62,7 +63,7 @@ public JsonFormatClassReaderDelegate GenerateClassReader(ClassDataContract class bool memberAccessFlag = classContract.RequiresMemberAccessForRead(null); try { - BeginMethod(_ilg, "Read" + DataContract.SanitizeTypeName(classContract.StableName!.Name) + "FromJson", typeof(JsonFormatClassReaderDelegate), memberAccessFlag); + BeginMethod(_ilg, "Read" + DataContract.SanitizeTypeName(classContract.XmlName!.Name) + "FromJson", typeof(JsonFormatClassReaderDelegate), memberAccessFlag); } catch (SecurityException securityException) { @@ -109,12 +110,6 @@ public JsonFormatClassReaderDelegate GenerateClassReader(ClassDataContract class _ilg.Call(XmlFormatGeneratorStatics.GetMemoryStreamMethod); _ilg.ConvertValue(Globals.TypeOfMemoryStream, _ilg.CurrentMethod.ReturnType); } - //Copy the KeyValuePairAdapter to a KeyValuePair. - else if (classContract.IsKeyValuePairAdapter) - { - _ilg.Call(classContract.GetKeyValuePairMethodInfo); - _ilg.ConvertValue(Globals.TypeOfKeyValuePair.MakeGenericType(classContract.KeyValuePairGenericArguments), _ilg.CurrentMethod.ReturnType); - } else { _ilg.ConvertValue(_objectLocal.LocalType, _ilg.CurrentMethod.ReturnType); @@ -149,11 +144,11 @@ private CodeGenerator GenerateCollectionReaderHelper(CollectionDataContract coll { if (isGetOnlyCollection) { - BeginMethod(_ilg, "Read" + DataContract.SanitizeTypeName(collectionContract.StableName.Name) + "FromJson" + "IsGetOnly", typeof(JsonFormatGetOnlyCollectionReaderDelegate), memberAccessFlag); + BeginMethod(_ilg, "Read" + DataContract.SanitizeTypeName(collectionContract.XmlName.Name) + "FromJson" + "IsGetOnly", typeof(JsonFormatGetOnlyCollectionReaderDelegate), memberAccessFlag); } else { - BeginMethod(_ilg, "Read" + DataContract.SanitizeTypeName(collectionContract.StableName.Name) + "FromJson", typeof(JsonFormatCollectionReaderDelegate), memberAccessFlag); + BeginMethod(_ilg, "Read" + DataContract.SanitizeTypeName(collectionContract.XmlName.Name) + "FromJson", typeof(JsonFormatCollectionReaderDelegate), memberAccessFlag); } } catch (SecurityException securityException) @@ -174,7 +169,7 @@ private CodeGenerator GenerateCollectionReaderHelper(CollectionDataContract coll private static void BeginMethod(CodeGenerator ilg, string methodName, Type delegateType, bool allowPrivateMemberAccess) { - MethodInfo signature = JsonFormatWriterGenerator.GetInvokeMethod(delegateType); + MethodInfo signature = CodeGenerator.GetInvokeMethod(delegateType); ParameterInfo[] parameters = signature.GetParameters(); Type[] paramTypes = new Type[parameters.Length]; for (int i = 0; i < parameters.Length; i++) @@ -228,8 +223,8 @@ private void CreateObject(ClassDataContract classContract) private void InvokeOnDeserializing(ClassDataContract classContract) { - if (classContract.BaseContract != null) - InvokeOnDeserializing(classContract.BaseContract); + if (classContract.BaseClassContract != null) + InvokeOnDeserializing(classContract.BaseClassContract); if (classContract.OnDeserializing != null) { _ilg.LoadAddress(_objectLocal); @@ -242,8 +237,8 @@ private void InvokeOnDeserializing(ClassDataContract classContract) private void InvokeOnDeserialized(ClassDataContract classContract) { - if (classContract.BaseContract != null) - InvokeOnDeserialized(classContract.BaseContract); + if (classContract.BaseClassContract != null) + InvokeOnDeserialized(classContract.BaseClassContract); if (classContract.OnDeserialized != null) { _ilg.LoadAddress(_objectLocal); @@ -291,7 +286,7 @@ private void ReadClass(ClassDataContract classContract) MethodInfo? extensionDataSetMethod = currentContract.ExtensionDataSetMethod; if (extensionDataSetMethod != null) _ilg.Call(_objectLocal, extensionDataSetMethod, extensionDataLocal); - currentContract = currentContract.BaseContract; + currentContract = currentContract.BaseClassContract; } } else @@ -352,8 +347,8 @@ private void ReadMembers(ClassDataContract classContract, LocalBuilder? extensio private int ReadMembers(ClassDataContract classContract, BitFlagsGenerator expectedElements, Label[] memberLabels, Label throwDuplicateMemberLabel, LocalBuilder memberIndexLocal) { - int memberCount = (classContract.BaseContract == null) ? 0 : - ReadMembers(classContract.BaseContract, expectedElements, memberLabels, throwDuplicateMemberLabel, memberIndexLocal); + int memberCount = (classContract.BaseClassContract == null) ? 0 : + ReadMembers(classContract.BaseClassContract, expectedElements, memberLabels, throwDuplicateMemberLabel, memberIndexLocal); for (int i = 0; i < classContract.Members!.Count; i++, memberCount++) { @@ -415,8 +410,8 @@ private void LoadArray(byte[] array, string name) private int SetRequiredElements(ClassDataContract contract, byte[] requiredElements) { - int memberCount = (contract.BaseContract == null) ? 0 : - SetRequiredElements(contract.BaseContract, requiredElements); + int memberCount = (contract.BaseClassContract == null) ? 0 : + SetRequiredElements(contract.BaseClassContract, requiredElements); List members = contract.Members!; for (int i = 0; i < members.Count; i++, memberCount++) { @@ -906,7 +901,7 @@ private bool TryReadPrimitiveArray(Type itemType) return false; string? readArrayMethod = null; - switch (itemType.GetTypeCode()) + switch (Type.GetTypeCode(itemType)) { case TypeCode.Boolean: readArrayMethod = "TryReadBooleanArray"; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs index afe02b062b6a7b..a57df0b14d279b 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs @@ -7,6 +7,7 @@ using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Reflection.Emit; +using System.Runtime.Serialization.DataContracts; using System.Security; using System.Xml; @@ -36,14 +37,6 @@ internal JsonFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionD return _helper.GenerateCollectionWriter(collectionContract); } - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", - Justification = "The trimmer will never remove the Invoke method from delegates.")] - internal static MethodInfo GetInvokeMethod(Type delegateType) - { - Debug.Assert(typeof(Delegate).IsAssignableFrom(delegateType)); - return delegateType.GetMethod("Invoke")!; - } - private sealed class CriticalHelper { private CodeGenerator _ilg = null!; // initialized in GenerateXXXWriter @@ -64,7 +57,7 @@ internal JsonFormatClassWriterDelegate GenerateClassWriter(ClassDataContract cla bool memberAccessFlag = classContract.RequiresMemberAccessForWrite(null); try { - BeginMethod(_ilg, "Write" + DataContract.SanitizeTypeName(classContract.StableName.Name) + "ToJson", typeof(JsonFormatClassWriterDelegate), memberAccessFlag); + BeginMethod(_ilg, "Write" + DataContract.SanitizeTypeName(classContract.XmlName.Name) + "ToJson", typeof(JsonFormatClassWriterDelegate), memberAccessFlag); } catch (SecurityException securityException) { @@ -90,7 +83,7 @@ internal JsonFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionD bool memberAccessFlag = collectionContract.RequiresMemberAccessForWrite(null); try { - BeginMethod(_ilg, "Write" + DataContract.SanitizeTypeName(collectionContract.StableName.Name) + "ToJson", typeof(JsonFormatCollectionWriterDelegate), memberAccessFlag); + BeginMethod(_ilg, "Write" + DataContract.SanitizeTypeName(collectionContract.XmlName.Name) + "ToJson", typeof(JsonFormatCollectionWriterDelegate), memberAccessFlag); } catch (SecurityException securityException) { @@ -114,7 +107,7 @@ internal JsonFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionD private static void BeginMethod(CodeGenerator ilg, string methodName, Type delegateType, bool allowPrivateMemberAccess) { - MethodInfo signature = GetInvokeMethod(delegateType); + MethodInfo signature = CodeGenerator.GetInvokeMethod(delegateType); ParameterInfo[] parameters = signature.GetParameters(); Type[] paramTypes = new Type[parameters.Length]; for (int i = 0; i < parameters.Length; i++) @@ -149,13 +142,6 @@ private void InitArgs(Type objType) _ilg.ConvertValue(objectArg.ArgType, Globals.TypeOfMemoryStream); _ilg.Call(XmlFormatGeneratorStatics.GetMemoryStreamAdapterMethod); } - //Copy the KeyValuePair to a KeyValuePairAdapter. - else if (objType.IsGenericType && objType.GetGenericTypeDefinition() == Globals.TypeOfKeyValuePairAdapter) - { - ClassDataContract dc = (ClassDataContract)DataContract.GetDataContract(objType); - _ilg.ConvertValue(objectArg.ArgType, Globals.TypeOfKeyValuePair.MakeGenericType(dc.KeyValuePairGenericArguments!)); - _ilg.New(dc.KeyValuePairAdapterConstructorInfo!); - } else { _ilg.ConvertValue(objectArg.ArgType, objType); @@ -182,8 +168,8 @@ private void ThrowIfCannotSerializeReadOnlyTypes(PropertyInfo serializationExcep private void InvokeOnSerializing(ClassDataContract classContract) { - if (classContract.BaseContract != null) - InvokeOnSerializing(classContract.BaseContract); + if (classContract.BaseClassContract != null) + InvokeOnSerializing(classContract.BaseClassContract); if (classContract.OnSerializing != null) { _ilg.LoadAddress(_objectLocal); @@ -195,8 +181,8 @@ private void InvokeOnSerializing(ClassDataContract classContract) private void InvokeOnSerialized(ClassDataContract classContract) { - if (classContract.BaseContract != null) - InvokeOnSerialized(classContract.BaseContract); + if (classContract.BaseClassContract != null) + InvokeOnSerialized(classContract.BaseClassContract); if (classContract.OnSerialized != null) { _ilg.LoadAddress(_objectLocal); @@ -238,8 +224,8 @@ private void WriteClass(ClassDataContract classContract) [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private int WriteMembers(ClassDataContract classContract, LocalBuilder? extensionDataLocal, ClassDataContract derivedMostClassContract) { - int memberCount = (classContract.BaseContract == null) ? 0 : - WriteMembers(classContract.BaseContract, extensionDataLocal, derivedMostClassContract); + int memberCount = (classContract.BaseClassContract == null) ? 0 : + WriteMembers(classContract.BaseClassContract, extensionDataLocal, derivedMostClassContract); int classMemberCount = classContract.Members!.Count; _ilg.Call(thisObj: _contextArg, XmlFormatGeneratorStatics.IncrementItemCountMethod, classMemberCount); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs index 078c4c51793393..7e745db47e92f3 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs @@ -1,10 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Xml; -using System.Runtime.Serialization; -using System.Globalization; using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Runtime.Serialization; +using System.Runtime.Serialization.DataContracts; +using System.Xml; namespace System.Runtime.Serialization.Json { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonQNameDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonQNameDataContract.cs index 8bc4a2d1f50424..5cf4f54cec7ad0 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonQNameDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonQNameDataContract.cs @@ -3,10 +3,11 @@ using System; using System.Collections.Generic; -using System.Text; using System.Diagnostics; -using System.Xml; using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.DataContracts; +using System.Text; +using System.Xml; namespace System.Runtime.Serialization.Json { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonStringDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonStringDataContract.cs index e65c58c9caed53..1e3379ceb62f27 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonStringDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonStringDataContract.cs @@ -3,10 +3,11 @@ using System; using System.Collections.Generic; -using System.Text; using System.Diagnostics; -using System.Xml; using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.DataContracts; +using System.Xml; +using System.Text; namespace System.Runtime.Serialization.Json { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonUriDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonUriDataContract.cs index bd4b68daf4103a..3dc37084b1d747 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonUriDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonUriDataContract.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.DataContracts; namespace System.Runtime.Serialization.Json { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonXmlDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonXmlDataContract.cs index 99640f43461ac6..f08ce85a82ffba 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonXmlDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonXmlDataContract.cs @@ -4,9 +4,12 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Runtime.Serialization.DataContracts; using System.Text; using System.Xml; +using DataContractDictionary = System.Collections.Generic.Dictionary; + namespace System.Runtime.Serialization.Json { internal sealed class JsonXmlDataContract : JsonDataContract @@ -58,20 +61,20 @@ private static List GetKnownTypesFromContext(XmlObjectSerializerContext? c List knownTypesList = new List(); if (context != null) { - List stableNames = new List(); - Dictionary[] entries = context.scopedKnownTypes.dataContractDictionaries; + List xmlNames = new List(); + DataContractDictionary[] entries = context.scopedKnownTypes.dataContractDictionaries; if (entries != null) { for (int i = 0; i < entries.Length; i++) { - Dictionary entry = entries[i]; + DataContractDictionary entry = entries[i]; if (entry != null) { foreach (KeyValuePair pair in entry) { - if (!stableNames.Contains(pair.Key)) + if (!xmlNames.Contains(pair.Key)) { - stableNames.Add(pair.Key); + xmlNames.Add(pair.Key); knownTypesList.Add(pair.Value.UnderlyingType); } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatReader.cs index 4161f53a29aece..b8564fda299413 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatReader.cs @@ -8,6 +8,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; +using System.Runtime.Serialization.DataContracts; using System.Text; using System.Threading.Tasks; using System.Xml; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatWriter.cs index 74fbefe525979e..b0417289512dbe 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatWriter.cs @@ -8,6 +8,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; +using System.Runtime.Serialization.DataContracts; using System.Text; using System.Threading.Tasks; using System.Xml; @@ -27,8 +28,7 @@ public void ReflectionWriteClass(XmlWriterDelegator xmlWriter, object obj, XmlOb [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public static void ReflectionWriteCollection(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContextComplexJson context, CollectionDataContract collectionContract) { - JsonWriterDelegator? jsonWriter = xmlWriter as JsonWriterDelegator; - if (jsonWriter == null) + if (xmlWriter is not JsonWriterDelegator jsonWriter) { throw new ArgumentException(nameof(xmlWriter)); } @@ -146,7 +146,7 @@ private static bool ReflectionTryWritePrimitiveArray(JsonWriterDelegator jsonWri XmlDictionaryString? itemNamespace = null; - switch (itemType.GetTypeCode()) + switch (Type.GetTypeCode(itemType)) { case TypeCode.Boolean: ReflectionWriteArrayAttribute(jsonWriter); @@ -199,8 +199,8 @@ protected override int ReflectionWriteMembers(XmlWriterDelegator xmlWriter, obje { Debug.Assert(memberNames != null); - int memberCount = (classContract.BaseContract == null) ? 0 : - ReflectionWriteMembers(xmlWriter, obj, context, classContract.BaseContract, derivedMostClassContract, childElementIndex, memberNames); + int memberCount = (classContract.BaseClassContract == null) ? 0 : + ReflectionWriteMembers(xmlWriter, obj, context, classContract.BaseClassContract, derivedMostClassContract, childElementIndex, memberNames); childElementIndex += memberCount; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerReadContextComplexJson.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerReadContextComplexJson.cs index 9c61887a150b4e..e94d7ff80848a9 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerReadContextComplexJson.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerReadContextComplexJson.cs @@ -3,11 +3,12 @@ using System; using System.Collections.Generic; -using System.Text; +using System.Diagnostics.CodeAnalysis; using System.Reflection; -using System.Xml; using System.Runtime.Serialization; -using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.DataContracts; +using System.Text; +using System.Xml; namespace System.Runtime.Serialization.Json { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerWriteContextComplexJson.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerWriteContextComplexJson.cs index 8a644b13bec9e4..2c5fa8f44a2b5f 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerWriteContextComplexJson.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerWriteContextComplexJson.cs @@ -2,13 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Reflection; +using System.Runtime.Serialization.DataContracts; using System.Text; using System.Xml; -using System.Reflection; -using System.Collections; -using System.IO; -using System.Diagnostics.CodeAnalysis; namespace System.Runtime.Serialization.Json { @@ -255,7 +256,7 @@ internal override void SerializeWithXsiTypeAtTopLevel(DataContract dataContract, private void VerifyType(DataContract dataContract, Type declaredType) { bool knownTypesAddedInCurrentScope = false; - if (dataContract.KnownDataContracts != null) + if (dataContract.KnownDataContracts?.Count > 0) { scopedKnownTypes.Push(dataContract.KnownDataContracts); knownTypesAddedInCurrentScope = true; @@ -263,7 +264,7 @@ private void VerifyType(DataContract dataContract, Type declaredType) if (!IsKnownType(dataContract, declaredType)) { - throw XmlObjectSerializer.CreateSerializationException(SR.Format(SR.DcTypeNotFoundOnSerialize, DataContract.GetClrTypeFullName(dataContract.UnderlyingType), dataContract.StableName.Name, dataContract.StableName.Namespace)); + throw XmlObjectSerializer.CreateSerializationException(SR.Format(SR.DcTypeNotFoundOnSerialize, DataContract.GetClrTypeFullName(dataContract.UnderlyingType), dataContract.XmlName.Name, dataContract.XmlName.Namespace)); } if (knownTypesAddedInCurrentScope) @@ -383,7 +384,7 @@ internal override DataContract GetDataContract(int id, RuntimeTypeHandle typeHan [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal static DataContract? ResolveJsonDataContractFromRootDataContract(XmlObjectSerializerContext context, XmlQualifiedName typeQName, DataContract rootTypeDataContract) { - if (rootTypeDataContract.StableName == typeQName) + if (rootTypeDataContract.XmlName == typeQName) return rootTypeDataContract; CollectionDataContract? collectionContract = rootTypeDataContract as CollectionDataContract; @@ -399,7 +400,7 @@ internal override DataContract GetDataContract(int id, RuntimeTypeHandle typeHan { itemContract = context.GetDataContract(context.GetSurrogatedType(collectionContract.ItemType)); } - if (itemContract.StableName == typeQName) + if (itemContract.XmlName == typeQName) { return itemContract; } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/KnownTypeDataContractResolver.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/KnownTypeDataContractResolver.cs index ab1d030913b578..ca96ede659dabc 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/KnownTypeDataContractResolver.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/KnownTypeDataContractResolver.cs @@ -1,9 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Xml; -using System.Reflection; using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.DataContracts; +using System.Xml; namespace System.Runtime.Serialization { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/PrimitiveDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/PrimitiveDataContract.cs index 8cd388f9601b03..638ba22d8842f5 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/PrimitiveDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/PrimitiveDataContract.cs @@ -9,10 +9,13 @@ using System.Reflection; using System.Xml; -namespace System.Runtime.Serialization +namespace System.Runtime.Serialization.DataContracts { internal abstract class PrimitiveDataContract : DataContract { + internal const string ContractTypeString = nameof(PrimitiveDataContract); + public override string? ContractType => ContractTypeString; + internal static readonly PrimitiveDataContract NullContract = new NullPrimitiveDataContract(); private readonly PrimitiveDataContractCriticalHelper _helper; @@ -44,7 +47,7 @@ public override XmlDictionaryString? TopLevelElementNamespace get { return DictionaryGlobals.SerializationNamespace; } - set + internal set { } } @@ -90,7 +93,7 @@ internal MethodInfo XmlFormatContentWriterMethod _helper.XmlFormatReaderMethod ??= typeof(XmlReaderDelegator).GetMethod(ReadMethodName, Globals.ScanAllMembers)!; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { xmlWriter.WriteAnyType(obj); } @@ -115,6 +118,17 @@ protected static bool TryReadNullAtTopLevel(XmlReaderDelegator reader) return false; } + internal override bool Equals(object? other, HashSet? checkedContracts) + { + if (other is PrimitiveDataContract) + { + Type thisType = GetType(); + Type otherType = other.GetType(); + return (thisType.Equals(otherType) || thisType.IsSubclassOf(otherType) || otherType.IsSubclassOf(thisType)); + } + return false; + } + private sealed class PrimitiveDataContractCriticalHelper : DataContract.DataContractCriticalHelper { private MethodInfo? _xmlFormatWriterMethod; @@ -131,20 +145,20 @@ internal PrimitiveDataContractCriticalHelper( internal MethodInfo? XmlFormatWriterMethod { - get { return _xmlFormatWriterMethod; } - set { _xmlFormatWriterMethod = value; } + get => _xmlFormatWriterMethod; + set => _xmlFormatWriterMethod = value; } internal MethodInfo? XmlFormatContentWriterMethod { - get { return _xmlFormatContentWriterMethod; } - set { _xmlFormatContentWriterMethod = value; } + get => _xmlFormatContentWriterMethod; + set => _xmlFormatContentWriterMethod = value; } internal MethodInfo? XmlFormatReaderMethod { - get { return _xmlFormatReaderMethod; } - set { _xmlFormatReaderMethod = value; } + get => _xmlFormatReaderMethod; + set => _xmlFormatReaderMethod = value; } } } @@ -159,24 +173,24 @@ internal CharDataContract(XmlDictionaryString name, XmlDictionaryString ns) : ba { } - internal override string WriteMethodName { get { return "WriteChar"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsChar"; } } + internal override string WriteMethodName => "WriteChar"; + internal override string ReadMethodName => "ReadElementContentAsChar"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteChar((char)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsChar() : HandleReadValue(reader.ReadElementContentAsChar(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteChar((char)obj!, name, ns); } @@ -193,24 +207,24 @@ public BooleanDataContract() : base(typeof(bool), DictionaryGlobals.BooleanLocal { } - internal override string WriteMethodName { get { return "WriteBoolean"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsBoolean"; } } + internal override string WriteMethodName => "WriteBoolean"; + internal override string ReadMethodName => "ReadElementContentAsBoolean"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteBoolean((bool)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsBoolean() : HandleReadValue(reader.ReadElementContentAsBoolean(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteBoolean((bool)obj!, name, ns); } @@ -222,24 +236,24 @@ public SignedByteDataContract() : base(typeof(sbyte), DictionaryGlobals.SignedBy { } - internal override string WriteMethodName { get { return "WriteSignedByte"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsSignedByte"; } } + internal override string WriteMethodName => "WriteSignedByte"; + internal override string ReadMethodName => "ReadElementContentAsSignedByte"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteSignedByte((sbyte)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsSignedByte() : HandleReadValue(reader.ReadElementContentAsSignedByte(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteSignedByte((sbyte)obj!, name, ns); } @@ -251,24 +265,24 @@ public UnsignedByteDataContract() : base(typeof(byte), DictionaryGlobals.Unsigne { } - internal override string WriteMethodName { get { return "WriteUnsignedByte"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsUnsignedByte"; } } + internal override string WriteMethodName => "WriteUnsignedByte"; + internal override string ReadMethodName => "ReadElementContentAsUnsignedByte"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteUnsignedByte((byte)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsUnsignedByte() : HandleReadValue(reader.ReadElementContentAsUnsignedByte(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteUnsignedByte((byte)obj!, name, ns); } @@ -280,24 +294,24 @@ public ShortDataContract() : base(typeof(short), DictionaryGlobals.ShortLocalNam { } - internal override string WriteMethodName { get { return "WriteShort"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsShort"; } } + internal override string WriteMethodName => "WriteShort"; + internal override string ReadMethodName => "ReadElementContentAsShort"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteShort((short)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsShort() : HandleReadValue(reader.ReadElementContentAsShort(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteShort((short)obj!, name, ns); } @@ -309,24 +323,24 @@ public UnsignedShortDataContract() : base(typeof(ushort), DictionaryGlobals.Unsi { } - internal override string WriteMethodName { get { return "WriteUnsignedShort"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsUnsignedShort"; } } + internal override string WriteMethodName => "WriteUnsignedShort"; + internal override string ReadMethodName => "ReadElementContentAsUnsignedShort"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteUnsignedShort((ushort)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsUnsignedShort() : HandleReadValue(reader.ReadElementContentAsUnsignedShort(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteUnsignedShort((ushort)obj!, name, ns); } @@ -364,19 +378,19 @@ internal override string WriteMethodName } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { throw new NotImplementedException(); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { throw new NotImplementedException(); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { throw new NotImplementedException(); } @@ -388,24 +402,24 @@ public IntDataContract() : base(typeof(int), DictionaryGlobals.IntLocalName, Dic { } - internal override string WriteMethodName { get { return "WriteInt"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsInt"; } } + internal override string WriteMethodName => "WriteInt"; + internal override string ReadMethodName => "ReadElementContentAsInt"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteInt((int)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsInt() : HandleReadValue(reader.ReadElementContentAsInt(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteInt((int)obj!, name, ns); } @@ -417,24 +431,24 @@ public UnsignedIntDataContract() : base(typeof(uint), DictionaryGlobals.Unsigned { } - internal override string WriteMethodName { get { return "WriteUnsignedInt"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsUnsignedInt"; } } + internal override string WriteMethodName => "WriteUnsignedInt"; + internal override string ReadMethodName => "ReadElementContentAsUnsignedInt"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteUnsignedInt((uint)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsUnsignedInt() : HandleReadValue(reader.ReadElementContentAsUnsignedInt(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteUnsignedInt((uint)obj!, name, ns); } @@ -450,24 +464,24 @@ internal LongDataContract(XmlDictionaryString name, XmlDictionaryString ns) : ba { } - internal override string WriteMethodName { get { return "WriteLong"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsLong"; } } + internal override string WriteMethodName => "WriteLong"; + internal override string ReadMethodName => "ReadElementContentAsLong"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteLong((long)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsLong() : HandleReadValue(reader.ReadElementContentAsLong(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteLong((long)obj!, name, ns); } @@ -504,24 +518,24 @@ public UnsignedLongDataContract() : base(typeof(ulong), DictionaryGlobals.Unsign { } - internal override string WriteMethodName { get { return "WriteUnsignedLong"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsUnsignedLong"; } } + internal override string WriteMethodName => "WriteUnsignedLong"; + internal override string ReadMethodName => "ReadElementContentAsUnsignedLong"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteUnsignedLong((ulong)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsUnsignedLong() : HandleReadValue(reader.ReadElementContentAsUnsignedLong(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteUnsignedLong((ulong)obj!, name, ns); } @@ -533,24 +547,24 @@ public FloatDataContract() : base(typeof(float), DictionaryGlobals.FloatLocalNam { } - internal override string WriteMethodName { get { return "WriteFloat"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsFloat"; } } + internal override string WriteMethodName => "WriteFloat"; + internal override string ReadMethodName => "ReadElementContentAsFloat"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteFloat((float)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsFloat() : HandleReadValue(reader.ReadElementContentAsFloat(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteFloat((float)obj!, name, ns); } @@ -562,24 +576,24 @@ public DoubleDataContract() : base(typeof(double), DictionaryGlobals.DoubleLocal { } - internal override string WriteMethodName { get { return "WriteDouble"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsDouble"; } } + internal override string WriteMethodName => "WriteDouble"; + internal override string ReadMethodName => "ReadElementContentAsDouble"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteDouble((double)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsDouble() : HandleReadValue(reader.ReadElementContentAsDouble(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteDouble((double)obj!, name, ns); } @@ -591,24 +605,24 @@ public DecimalDataContract() : base(typeof(decimal), DictionaryGlobals.DecimalLo { } - internal override string WriteMethodName { get { return "WriteDecimal"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsDecimal"; } } + internal override string WriteMethodName => "WriteDecimal"; + internal override string ReadMethodName => "ReadElementContentAsDecimal"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteDecimal((decimal)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsDecimal() : HandleReadValue(reader.ReadElementContentAsDecimal(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteDecimal((decimal)obj!, name, ns); } @@ -624,24 +638,24 @@ public DateTimeDataContract() : base(typeof(DateTime), DictionaryGlobals.DateTim { } - internal override string WriteMethodName { get { return "WriteDateTime"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsDateTime"; } } + internal override string WriteMethodName => "WriteDateTime"; + internal override string ReadMethodName => "ReadElementContentAsDateTime"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteDateTime((DateTime)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsDateTime() : HandleReadValue(reader.ReadElementContentAsDateTime(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteDateTime((DateTime)obj!, name, ns); } @@ -657,17 +671,17 @@ internal StringDataContract(XmlDictionaryString name, XmlDictionaryString ns) : { } - internal override string WriteMethodName { get { return "WriteString"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsString"; } } + internal override string WriteMethodName => "WriteString"; + internal override string ReadMethodName => "ReadElementContentAsString"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteString((string)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { if (context == null) { @@ -680,7 +694,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { context.WriteString(xmlWriter, (string?)obj, name, ns); } @@ -792,17 +806,17 @@ public ByteArrayDataContract() : base(typeof(byte[]), DictionaryGlobals.ByteArra { } - internal override string WriteMethodName { get { return "WriteBase64"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsBase64"; } } + internal override string WriteMethodName => "WriteBase64"; + internal override string ReadMethodName => "ReadElementContentAsBase64"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteBase64((byte[])obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { if (context == null) { @@ -815,7 +829,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteStartElement(name, ns); xmlWriter.WriteBase64((byte[]?)obj); @@ -829,20 +843,20 @@ public ObjectDataContract() : base(typeof(object), DictionaryGlobals.ObjectLocal { } - internal override string WriteMethodName { get { return "WriteAnyType"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsAnyType"; } } + internal override string WriteMethodName => "WriteAnyType"; + internal override string ReadMethodName => "ReadElementContentAsAnyType"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { // write nothing } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { object obj; - if (XmlReaderDelegator.IsEmptyElement) + if (reader.IsEmptyElement) { reader.Skip(); obj = new object(); @@ -865,15 +879,9 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj return (context == null) ? obj : HandleReadValue(obj, context); } - internal override bool CanContainReferences - { - get { return true; } - } + internal override bool CanContainReferences => true; - internal override bool IsPrimitive - { - get { return false; } - } + internal override bool IsPrimitive => false; } internal class TimeSpanDataContract : PrimitiveDataContract @@ -886,24 +894,24 @@ internal TimeSpanDataContract(XmlDictionaryString name, XmlDictionaryString ns) { } - internal override string WriteMethodName { get { return "WriteTimeSpan"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsTimeSpan"; } } + internal override string WriteMethodName => "WriteTimeSpan"; + internal override string ReadMethodName => "ReadElementContentAsTimeSpan"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteTimeSpan((TimeSpan)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsTimeSpan() : HandleReadValue(reader.ReadElementContentAsTimeSpan(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator writer, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator writer, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { writer.WriteTimeSpan((TimeSpan)obj!, name, ns); } @@ -924,24 +932,24 @@ internal GuidDataContract(XmlDictionaryString name, XmlDictionaryString ns) : ba { } - internal override string WriteMethodName { get { return "WriteGuid"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsGuid"; } } + internal override string WriteMethodName => "WriteGuid"; + internal override string ReadMethodName => "ReadElementContentAsGuid"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteGuid((Guid)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsGuid() : HandleReadValue(reader.ReadElementContentAsGuid(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteGuid((Guid)obj!, name, ns); } @@ -958,17 +966,17 @@ public UriDataContract() : base(typeof(Uri), DictionaryGlobals.UriLocalName, Dic { } - internal override string WriteMethodName { get { return "WriteUri"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsUri"; } } + internal override string WriteMethodName => "WriteUri"; + internal override string ReadMethodName => "ReadElementContentAsUri"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteUri((Uri)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { if (context == null) { @@ -981,7 +989,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator writer, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator writer, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { writer.WriteUri((Uri?)obj, name, ns); } @@ -993,22 +1001,19 @@ public QNameDataContract() : base(typeof(XmlQualifiedName), DictionaryGlobals.QN { } - internal override string WriteMethodName { get { return "WriteQName"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsQName"; } } + internal override string WriteMethodName => "WriteQName"; + internal override string ReadMethodName => "ReadElementContentAsQName"; - internal override bool IsPrimitive - { - get { return false; } - } + internal override bool IsPrimitive => false; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteQName((XmlQualifiedName)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { if (context == null) { @@ -1021,7 +1026,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator writer, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator writer, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { context.WriteQName(writer, (XmlQualifiedName?)obj, name, ns); } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionClassWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionClassWriter.cs index 3cc3c97a7261e0..10c2cc1f199f60 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionClassWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionClassWriter.cs @@ -9,6 +9,7 @@ using System.IO; using System.Linq; using System.Reflection; +using System.Runtime.Serialization.DataContracts; using System.Text; using System.Threading.Tasks; using System.Xml; @@ -124,8 +125,8 @@ protected static bool ReflectionTryWritePrimitive(XmlWriterDelegator xmlWriter, private void InvokeOnSerializing(object obj, XmlObjectSerializerWriteContext context, ClassDataContract classContract) { - if (classContract.BaseContract != null) - InvokeOnSerializing(obj, context, classContract.BaseContract); + if (classContract.BaseClassContract != null) + InvokeOnSerializing(obj, context, classContract.BaseClassContract); if (classContract.OnSerializing != null) { var contextArg = context.GetStreamingContext(); @@ -135,8 +136,8 @@ private void InvokeOnSerializing(object obj, XmlObjectSerializerWriteContext con private void InvokeOnSerialized(object obj, XmlObjectSerializerWriteContext context, ClassDataContract classContract) { - if (classContract.BaseContract != null) - InvokeOnSerialized(obj, context, classContract.BaseContract); + if (classContract.BaseClassContract != null) + InvokeOnSerialized(obj, context, classContract.BaseClassContract); if (classContract.OnSerialized != null) { var contextArg = context.GetStreamingContext(); @@ -155,11 +156,6 @@ private static object ResolveAdapterType(object obj, ClassDataContract classCont { obj = MemoryStreamAdapter.GetMemoryStreamAdapter((MemoryStream)obj); } - else if (type.IsGenericType && type.GetGenericTypeDefinition() == Globals.TypeOfKeyValuePair) - { - obj = classContract.KeyValuePairAdapterConstructorInfo!.Invoke(new object[] { obj }); - } - return obj; } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionFeature.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionFeature.cs deleted file mode 100644 index d071056761ce34..00000000000000 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionFeature.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Runtime.Serialization -{ - internal static class ReflectionBasedSerializationFeature - { - public const string Name = "System.Runtime.Serialization.ReflectionBasedSerialization"; - } -} diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionReader.cs index b28f41784478c8..a1982aa1ac3db3 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionReader.cs @@ -8,6 +8,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; +using System.Runtime.Serialization.DataContracts; using System.Text; using System.Threading.Tasks; using System.Xml; @@ -197,7 +198,7 @@ protected virtual bool ReflectionReadSpecialCollection(XmlReaderDelegator xmlRea protected int ReflectionGetMembers(ClassDataContract classContract, DataMember[] members) { - int memberCount = (classContract.BaseContract == null) ? 0 : ReflectionGetMembers(classContract.BaseContract, members); + int memberCount = (classContract.BaseClassContract == null) ? 0 : ReflectionGetMembers(classContract.BaseClassContract, members); int childElementIndex = memberCount; for (int i = 0; i < classContract.Members!.Count; i++, memberCount++) { @@ -222,7 +223,7 @@ protected void ReflectionReadMember(XmlReaderDelegator xmlReader, XmlObjectSeria else { context.ResetCollectionMemberInfo(); - var value = ReflectionReadValue(xmlReader, context, dataMember, classContract.StableName.Namespace); + var value = ReflectionReadValue(xmlReader, context, dataMember, classContract.XmlName.Namespace); MemberInfo memberInfo = dataMember.MemberInfo; Debug.Assert(memberInfo != null); @@ -377,8 +378,8 @@ private static void ReflectionSetMemberValue(ref object obj, object? memberValue private void InvokeOnDeserializing(XmlObjectSerializerReadContext context, ClassDataContract classContract, object obj) { - if (classContract.BaseContract != null) - InvokeOnDeserializing(context, classContract.BaseContract, obj); + if (classContract.BaseClassContract != null) + InvokeOnDeserializing(context, classContract.BaseClassContract, obj); if (classContract.OnDeserializing != null) { var contextArg = context.GetStreamingContext(); @@ -388,8 +389,8 @@ private void InvokeOnDeserializing(XmlObjectSerializerReadContext context, Class private void InvokeOnDeserialized(XmlObjectSerializerReadContext context, ClassDataContract classContract, object obj) { - if (classContract.BaseContract != null) - InvokeOnDeserialized(context, classContract.BaseContract, obj); + if (classContract.BaseClassContract != null) + InvokeOnDeserialized(context, classContract.BaseClassContract, obj); if (classContract.OnDeserialized != null) { var contextArg = context.GetStreamingContext(); @@ -426,11 +427,6 @@ private static object ResolveAdapterObject(object obj, ClassDataContract classCo { obj = MemoryStreamAdapter.GetMemoryStream((MemoryStreamAdapter)obj); } - else if (obj is IKeyValuePairAdapter) - { - obj = classContract.GetKeyValuePairMethodInfo!.Invoke(obj, Array.Empty())!; - } - return obj; } @@ -612,7 +608,7 @@ private static bool ReflectionTryReadPrimitiveArray(XmlReaderDelegator xmlReader if (primitiveContract == null) return false; - switch (itemType.GetTypeCode()) + switch (Type.GetTypeCode(itemType)) { case TypeCode.Boolean: { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatReader.cs index 98e3c1ea9a4ff0..6c31a151498d72 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatReader.cs @@ -2,15 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Xml; -using System.Xml.Schema; -using System.Reflection; -using System.Reflection.Emit; using System.Collections; using System.Collections.Generic; -using System.Security; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.Serialization.DataContracts; +using System.Security; +using System.Xml; +using System.Xml.Schema; namespace System.Runtime.Serialization { @@ -101,7 +102,7 @@ protected override void ReflectionReadMembers(XmlReaderDelegator xmlReader, XmlO protected override string GetClassContractNamespace(ClassDataContract classContract) { - return classContract.StableName!.Namespace; + return classContract.XmlName!.Namespace; } protected override string GetCollectionContractItemName(CollectionDataContract collectionContract) @@ -111,7 +112,7 @@ protected override string GetCollectionContractItemName(CollectionDataContract c protected override string GetCollectionContractNamespace(CollectionDataContract collectionContract) { - return collectionContract.StableName.Namespace; + return collectionContract.XmlName.Namespace; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -135,7 +136,7 @@ private bool[] GetRequiredMembers(ClassDataContract contract, out int firstRequi private int GetRequiredMembers(ClassDataContract contract, bool[] requiredMembers) { - int memberCount = (contract.BaseContract == null) ? 0 : GetRequiredMembers(contract.BaseContract, requiredMembers); + int memberCount = (contract.BaseClassContract == null) ? 0 : GetRequiredMembers(contract.BaseClassContract, requiredMembers); List members = contract.Members!; for (int i = 0; i < members.Count; i++, memberCount++) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatWriter.cs index dc70a9cb4ae833..339cb9c1021fbe 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatWriter.cs @@ -7,6 +7,7 @@ using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Reflection.Emit; +using System.Runtime.Serialization.DataContracts; using System.Xml; namespace System.Runtime.Serialization @@ -95,7 +96,7 @@ private static bool ReflectionTryWritePrimitiveArray(XmlWriterDelegator xmlWrite if (primitiveContract == null) return false; - switch (itemType.GetTypeCode()) + switch (Type.GetTypeCode(itemType)) { case TypeCode.Boolean: xmlWriter.WriteBooleanArray((bool[])obj, collectionItemName, itemNamespace); @@ -131,8 +132,8 @@ internal sealed class ReflectionXmlClassWriter : ReflectionClassWriter [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] protected override int ReflectionWriteMembers(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context, ClassDataContract classContract, ClassDataContract derivedMostClassContract, int childElementIndex, XmlDictionaryString[]? emptyStringArray) { - int memberCount = (classContract.BaseContract == null) ? 0 : - ReflectionWriteMembers(xmlWriter, obj, context, classContract.BaseContract, derivedMostClassContract, childElementIndex, emptyStringArray); + int memberCount = (classContract.BaseClassContract == null) ? 0 : + ReflectionWriteMembers(xmlWriter, obj, context, classContract.BaseClassContract, derivedMostClassContract, childElementIndex, emptyStringArray); childElementIndex += memberCount; @@ -230,11 +231,11 @@ private static bool CheckIfMemberHasConflict(DataMember member, ClassDataContrac // Check for conflict with derived type members string? name = member.Name; - string? ns = classContract.StableName.Namespace; + string? ns = classContract.XmlName.Namespace; ClassDataContract? currentContract = derivedMostClassContract; while (currentContract != null && currentContract != classContract) { - if (ns == currentContract.StableName.Namespace) + if (ns == currentContract.XmlName.Namespace) { List members = currentContract.Members!; for (int j = 0; j < members.Count; j++) @@ -243,7 +244,7 @@ private static bool CheckIfMemberHasConflict(DataMember member, ClassDataContrac return CheckIfConflictingMembersHaveDifferentTypes(members[j]); } } - currentContract = currentContract.BaseContract; + currentContract = currentContract.BaseClassContract; } return false; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaExporter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaExporter.cs index 1c76e5760047ef..976d9880a41d4b 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaExporter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaExporter.cs @@ -10,6 +10,7 @@ using System.Globalization; using System.IO; using System.Reflection; +using System.Runtime.Serialization.DataContracts; using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; @@ -42,7 +43,7 @@ internal void Export() { // Remove this if we decide to publish serialization schema at well-known location ExportSerializationSchema(); - foreach (KeyValuePair pair in _dataContractSet) + foreach (KeyValuePair pair in _dataContractSet.Contracts) { DataContract dataContract = pair.Value; if (!_dataContractSet.IsContractProcessed(dataContract)) @@ -80,7 +81,7 @@ private void ExportDataContract(DataContract dataContract) ExportXmlDataContract((XmlDataContract)dataContract); else { - XmlSchema schema = GetSchema(dataContract.StableName.Namespace); + XmlSchema schema = GetSchema(dataContract.XmlName.Namespace); if (dataContract is ClassDataContract classDataContract) { @@ -100,7 +101,7 @@ private void ExportDataContract(DataContract dataContract) private XmlSchemaElement ExportTopLevelElement(DataContract dataContract, XmlSchema? schema) { - if (schema == null || dataContract.StableName.Namespace != dataContract.TopLevelElementNamespace!.Value) + if (schema == null || dataContract.XmlName.Namespace != dataContract.TopLevelElementNamespace!.Value) schema = GetSchema(dataContract.TopLevelElementNamespace!.Value); XmlSchemaElement topLevelElement = new XmlSchemaElement(); @@ -115,7 +116,7 @@ private XmlSchemaElement ExportTopLevelElement(DataContract dataContract, XmlSch private void ExportClassDataContract(ClassDataContract classDataContract, XmlSchema schema) { XmlSchemaComplexType type = new XmlSchemaComplexType(); - type.Name = classDataContract.StableName.Name; + type.Name = classDataContract.XmlName.Name; schema.Items.Add(type); XmlElement? genericInfoElement = null; if (classDataContract.UnderlyingType.IsGenericType) @@ -129,12 +130,12 @@ private void ExportClassDataContract(ClassDataContract classDataContract, XmlSch XmlSchemaElement element = new XmlSchemaElement(); element.Name = dataMember.Name; XmlElement? actualTypeElement = null; - DataContract memberTypeContract = DataContractSet.GetMemberTypeDataContract(dataMember); + DataContract memberTypeContract = _dataContractSet.GetMemberTypeDataContract(dataMember); if (CheckIfMemberHasConflict(dataMember)) { element.SchemaTypeName = AnytypeQualifiedName; - actualTypeElement = ExportActualType(memberTypeContract.StableName); - SchemaHelper.AddSchemaImport(memberTypeContract.StableName.Namespace, schema); + actualTypeElement = ExportActualType(memberTypeContract.XmlName); + SchemaHelper.AddSchemaImport(memberTypeContract.XmlName.Namespace, schema); } else SetElementType(element, memberTypeContract, schema); @@ -149,11 +150,11 @@ private void ExportClassDataContract(ClassDataContract classDataContract, XmlSch } XmlElement? isValueTypeElement = null; - if (classDataContract.BaseContract != null) + if (classDataContract.BaseClassContract != null) { - XmlSchemaComplexContentExtension extension = CreateTypeContent(type, classDataContract.BaseContract.StableName, schema); + XmlSchemaComplexContentExtension extension = CreateTypeContent(type, classDataContract.BaseClassContract.XmlName, schema); extension.Particle = rootSequence; - if (classDataContract.IsReference && !classDataContract.BaseContract.IsReference) + if (classDataContract.IsReference && !classDataContract.BaseClassContract.IsReference) { AddReferenceAttributes(extension.Attributes, schema); } @@ -179,19 +180,18 @@ private static void AddReferenceAttributes(XmlSchemaObjectCollection attributes, private static void SetElementType(XmlSchemaElement element, DataContract dataContract, XmlSchema schema) { - XmlDataContract? xmlDataContract = dataContract as XmlDataContract; - if (xmlDataContract != null && xmlDataContract.IsAnonymous) + if (dataContract is XmlDataContract xmlDataContract && xmlDataContract.IsAnonymous) { element.SchemaType = xmlDataContract.XsdType; } else { - element.SchemaTypeName = dataContract.StableName; + element.SchemaTypeName = dataContract.XmlName; if (element.SchemaTypeName.Namespace.Equals(Globals.SerializationNamespace)) schema.Namespaces.Add(Globals.SerPrefixForSchema, Globals.SerializationNamespace); - SchemaHelper.AddSchemaImport(dataContract.StableName.Namespace, schema); + SchemaHelper.AddSchemaImport(dataContract.XmlName.Namespace, schema); } } @@ -279,7 +279,7 @@ private XmlElement ExportGenericInfo(Type clrType, string elementName, string el genericArgumentCounts = DataContract.GetDataContractNameForGenericName(typeName, null); clrType = clrType.GetGenericTypeDefinition(); } - XmlQualifiedName dcqname = DataContract.GetStableName(clrType); + XmlQualifiedName dcqname = DataContract.GetXmlName(clrType); if (nestedCollectionLevel > 0) { string collectionName = dcqname.Name; @@ -329,17 +329,32 @@ private XmlElement ExportGenericInfo(Type clrType, string elementName, string el return typeElement; } - private static XmlElement? ExportSurrogateData(object key) + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private XmlElement? ExportSurrogateData(object key) { - // IDataContractSurrogate is not available on NetCore. - return null; + object? surrogateData = _dataContractSet.GetSurrogateData(key); + if (surrogateData == null) + return null; + StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture); + XmlWriterSettings writerSettings = new XmlWriterSettings(); + writerSettings.OmitXmlDeclaration = true; + XmlWriter xmlWriter = XmlWriter.Create(stringWriter, writerSettings); + Collection knownTypes = new Collection(); + if (_dataContractSet.SerializationExtendedSurrogateProvider != null) + DataContractSurrogateCaller.GetKnownCustomDataTypes(_dataContractSet.SerializationExtendedSurrogateProvider, knownTypes); + DataContractSerializer serializer = new DataContractSerializer(Globals.TypeOfObject, + SurrogateDataAnnotationName.Name, SurrogateDataAnnotationName.Namespace, knownTypes, int.MaxValue, + ignoreExtensionDataObject: false, preserveObjectReferences: true); + serializer.WriteObject(xmlWriter, surrogateData); + xmlWriter.Flush(); + return (XmlElement?)XmlDoc.ReadNode(XmlReader.Create(new StringReader(stringWriter.ToString()))); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private void ExportCollectionDataContract(CollectionDataContract collectionDataContract, XmlSchema schema) { XmlSchemaComplexType type = new XmlSchemaComplexType(); - type.Name = collectionDataContract.StableName.Name; + type.Name = collectionDataContract.XmlName.Name; schema.Items.Add(type); XmlElement? genericInfoElement = null, isDictionaryElement = null; if (collectionDataContract.UnderlyingType.IsGenericType && CollectionDataContract.IsCollectionDataContract(collectionDataContract.UnderlyingType)) @@ -363,7 +378,7 @@ private void ExportCollectionDataContract(CollectionDataContract collectionDataC { XmlSchemaElement keyValueElement = new XmlSchemaElement(); keyValueElement.Name = dataMember.Name; - SetElementType(keyValueElement, DataContractSet.GetMemberTypeDataContract(dataMember), schema); + SetElementType(keyValueElement, _dataContractSet.GetMemberTypeDataContract(dataMember), schema); SchemaHelper.AddElementForm(keyValueElement, schema); if (dataMember.IsNullable) keyValueElement.IsNillable = true; @@ -377,7 +392,7 @@ private void ExportCollectionDataContract(CollectionDataContract collectionDataC { if (collectionDataContract.IsItemTypeNullable) element.IsNillable = true; - DataContract itemContract = DataContractSet.GetItemTypeDataContract(collectionDataContract); + DataContract itemContract = _dataContractSet.GetItemTypeDataContract(collectionDataContract); SetElementType(element, itemContract, schema); } SchemaHelper.AddElementForm(element, schema); @@ -396,12 +411,11 @@ private XmlElement ExportIsDictionary() return isDictionaryElement; } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private void ExportEnumDataContract(EnumDataContract enumDataContract, XmlSchema schema) { XmlSchemaSimpleType type = new XmlSchemaSimpleType(); - type.Name = enumDataContract.StableName.Name; - // https://github.com/dotnet/runtime/issues/41448 - enumDataContract.BaseContractName is always null, but this method is not reachable - Debug.Assert(enumDataContract.BaseContractName != null, "BaseContractName is always null, but this method is not reachable. Suppressing compiler error."); + type.Name = enumDataContract.XmlName.Name; XmlElement? actualTypeElement = (enumDataContract.BaseContractName == DefaultEnumBaseTypeName) ? null : ExportActualType(enumDataContract.BaseContractName); type.Annotation = GetSchemaAnnotation(actualTypeElement, ExportSurrogateData(enumDataContract)); schema.Items.Add(type); @@ -441,16 +455,16 @@ internal static long GetDefaultEnumValue(bool isFlags, int index) private void ExportISerializableDataContract(ClassDataContract dataContract, XmlSchema schema) { XmlSchemaComplexType type = new XmlSchemaComplexType(); - type.Name = dataContract.StableName.Name; + type.Name = dataContract.XmlName.Name; schema.Items.Add(type); XmlElement? genericInfoElement = null; if (dataContract.UnderlyingType.IsGenericType) genericInfoElement = ExportGenericInfo(dataContract.UnderlyingType, Globals.GenericTypeLocalName, Globals.SerializationNamespace); XmlElement? isValueTypeElement = null; - if (dataContract.BaseContract != null) + if (dataContract.BaseClassContract != null) { - _ = CreateTypeContent(type, dataContract.BaseContract.StableName, schema); + _ = CreateTypeContent(type, dataContract.BaseClassContract.XmlName, schema); } else { @@ -491,7 +505,7 @@ private void ExportXmlDataContract(XmlDataContract dataContract) if (hasRoot) { - if (!(typeQName.Equals(dataContract.StableName))) + if (!(typeQName.Equals(dataContract.XmlName))) { Fx.Assert("XML data contract type name does not match schema name"); } @@ -565,26 +579,26 @@ private static void ReprocessAll(XmlSchemaSet schemas)// and remove duplicate it } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static void GetXmlTypeInfo(Type type, out XmlQualifiedName stableName, out XmlSchemaType? xsdType, out bool hasRoot) + internal static void GetXmlTypeInfo(Type type, out XmlQualifiedName xmlName, out XmlSchemaType? xsdType, out bool hasRoot) { - if (IsSpecialXmlType(type, out stableName!, out xsdType, out hasRoot)) + if (IsSpecialXmlType(type, out xmlName!, out xsdType, out hasRoot)) return; XmlSchemaSet schemas = new XmlSchemaSet(); schemas.XmlResolver = null; - InvokeSchemaProviderMethod(type, schemas, out stableName, out xsdType, out hasRoot); - if (stableName.Name == null || stableName.Name.Length == 0) + InvokeSchemaProviderMethod(type, schemas, out xmlName, out xsdType, out hasRoot); + if (xmlName.Name == null || xmlName.Name.Length == 0) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.InvalidXmlDataContractName, DataContract.GetClrTypeFullName(type)))); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private static bool InvokeSchemaProviderMethod(Type clrType, XmlSchemaSet schemas, out XmlQualifiedName stableName, out XmlSchemaType? xsdType, out bool hasRoot) + private static bool InvokeSchemaProviderMethod(Type clrType, XmlSchemaSet schemas, out XmlQualifiedName xmlName, out XmlSchemaType? xsdType, out bool hasRoot) { xsdType = null; hasRoot = true; object[] attrs = clrType.GetCustomAttributes(Globals.TypeOfXmlSchemaProviderAttribute, false); if (attrs == null || attrs.Length == 0) { - stableName = DataContract.GetDefaultStableName(clrType); + xmlName = DataContract.GetDefaultXmlName(clrType); return false; } @@ -599,7 +613,7 @@ private static bool InvokeSchemaProviderMethod(Type clrType, XmlSchemaSet schema { if (!provider.IsAny) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.InvalidGetSchemaMethod, DataContract.GetClrTypeFullName(clrType)))); - stableName = DataContract.GetDefaultStableName(clrType); + xmlName = DataContract.GetDefaultXmlName(clrType); } else { @@ -616,26 +630,25 @@ private static bool InvokeSchemaProviderMethod(Type clrType, XmlSchemaSet schema { if (typeInfo != null) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.InvalidNonNullReturnValueByIsAny, DataContract.GetClrTypeFullName(clrType), methodName))); - stableName = DataContract.GetDefaultStableName(clrType); + xmlName = DataContract.GetDefaultXmlName(clrType); } else if (typeInfo == null) { xsdType = CreateAnyElementType(); hasRoot = false; - stableName = DataContract.GetDefaultStableName(clrType); + xmlName = DataContract.GetDefaultXmlName(clrType); } else { - XmlSchemaType? providerXsdType = typeInfo as XmlSchemaType; - if (providerXsdType != null) + if (typeInfo is XmlSchemaType providerXsdType) { string? typeName = providerXsdType.Name; string? typeNs = null; if (typeName == null || typeName.Length == 0) { - DataContract.GetDefaultStableName(DataContract.GetClrTypeFullName(clrType), out typeName, out typeNs); - stableName = new XmlQualifiedName(typeName, typeNs); - providerXsdType.Annotation = GetSchemaAnnotation(ExportActualType(stableName, new XmlDocument())); + DataContract.GetDefaultXmlName(DataContract.GetClrTypeFullName(clrType), out typeName, out typeNs); + xmlName = new XmlQualifiedName(typeName, typeNs); + providerXsdType.Annotation = GetSchemaAnnotation(ExportActualType(xmlName, new XmlDocument())); xsdType = providerXsdType; } else @@ -655,11 +668,11 @@ private static bool InvokeSchemaProviderMethod(Type clrType, XmlSchemaSet schema } if (typeNs == null) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.MissingSchemaType, typeName, DataContract.GetClrTypeFullName(clrType)))); - stableName = new XmlQualifiedName(typeName, typeNs); + xmlName = new XmlQualifiedName(typeName, typeNs); } } else - stableName = (XmlQualifiedName)typeInfo; + xmlName = (XmlQualifiedName)typeInfo; } } return true; @@ -668,19 +681,19 @@ private static bool InvokeSchemaProviderMethod(Type clrType, XmlSchemaSet schema private static void InvokeGetSchemaMethod( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type clrType, - XmlSchemaSet schemas, XmlQualifiedName stableName) + XmlSchemaSet schemas, XmlQualifiedName xmlName) { IXmlSerializable ixmlSerializable = (IXmlSerializable)Activator.CreateInstance(clrType)!; XmlSchema? schema = ixmlSerializable.GetSchema(); if (schema == null) { - AddDefaultDatasetType(schemas, stableName.Name, stableName.Namespace); + AddDefaultDatasetType(schemas, xmlName.Name, xmlName.Namespace); } else { if (schema.Id == null || schema.Id.Length == 0) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.InvalidReturnSchemaOnGetSchemaMethod, DataContract.GetClrTypeFullName(clrType)))); - AddDefaultTypedDatasetType(schemas, schema, stableName.Name, stableName.Namespace); + AddDefaultTypedDatasetType(schemas, schema, xmlName.Name, xmlName.Namespace); } } @@ -738,7 +751,7 @@ internal static bool IsSpecialXmlType(Type type, [NotNullWhen(true)] out XmlQual name = "ArrayOfXmlNode"; hasRoot = true; } - typeName = new XmlQualifiedName(name, DataContract.GetDefaultStableNamespace(type)); + typeName = new XmlQualifiedName(name, DataContract.GetDefaultXmlNamespace(type)); return true; } typeName = null; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaHelper.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaHelper.cs index da820ba98babbe..2a632482b459ef 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaHelper.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaHelper.cs @@ -7,11 +7,40 @@ using System.Collections; using System.Collections.Generic; +using SchemaObjectDictionary = System.Collections.Generic.Dictionary; + namespace System.Runtime.Serialization { - internal static class SchemaHelper + internal sealed class SchemaObjectInfo { + internal XmlSchemaType? _type; + internal XmlSchemaElement? _element; + internal XmlSchema? _schema; + internal List? _knownTypes; + internal SchemaObjectInfo(XmlSchemaType? type, XmlSchemaElement? element, XmlSchema? schema, List? knownTypes) + { + _type = type; + _element = element; + _schema = schema; + _knownTypes = knownTypes; + } + } + + internal sealed class SchemaDefinedType + { + internal XmlQualifiedName _xmlName; + + public SchemaDefinedType(XmlQualifiedName xmlName) + { + _xmlName = xmlName; + } + } + + internal enum SchemaDefinedEnum { SchemaDefinedEnumValue }; + + internal static class SchemaHelper + { internal static bool NamespacesEqual(string? ns1, string? ns2) { if (ns1 == null || ns1.Length == 0) @@ -20,6 +49,16 @@ internal static bool NamespacesEqual(string? ns1, string? ns2) return ns1 == ns2; } + internal static XmlSchemaType? GetSchemaType(SchemaObjectDictionary schemaInfo, XmlQualifiedName typeName) + { + SchemaObjectInfo? schemaObjectInfo; + if (schemaInfo.TryGetValue(typeName, out schemaObjectInfo)) + { + return schemaObjectInfo._type; + } + return null; + } + internal static XmlSchemaType? GetSchemaType(XmlSchemaSet schemas, XmlQualifiedName typeQName, out XmlSchema? outSchema) { outSchema = null; @@ -32,8 +71,7 @@ internal static bool NamespacesEqual(string? ns1, string? ns2) outSchema = schema; foreach (XmlSchemaObject schemaObj in schema.Items) { - XmlSchemaType? schemaType = schemaObj as XmlSchemaType; - if (schemaType != null && schemaType.Name == typeQName.Name) + if (schemaObj is XmlSchemaType schemaType && schemaType.Name == typeQName.Name) { return schemaType; } @@ -43,6 +81,16 @@ internal static bool NamespacesEqual(string? ns1, string? ns2) return null; } + internal static XmlSchemaElement? GetSchemaElement(SchemaObjectDictionary schemaInfo, XmlQualifiedName elementName) + { + SchemaObjectInfo? schemaObjectInfo; + if (schemaInfo.TryGetValue(elementName, out schemaObjectInfo)) + { + return schemaObjectInfo._element; + } + return null; + } + internal static XmlSchemaElement? GetSchemaElement(XmlSchemaSet schemas, XmlQualifiedName elementQName, out XmlSchema? outSchema) { outSchema = null; @@ -55,8 +103,7 @@ internal static bool NamespacesEqual(string? ns1, string? ns2) outSchema = schema; foreach (XmlSchemaObject schemaObj in schema.Items) { - XmlSchemaElement? schemaElement = schemaObj as XmlSchemaElement; - if (schemaElement != null && schemaElement.Name == elementQName.Name) + if (schemaObj is XmlSchemaElement schemaElement && schemaElement.Name == elementQName.Name) { return schemaElement; } @@ -124,5 +171,59 @@ internal static void AddSchemaImport(string ns, XmlSchema schema) import.Namespace = ns; schema.Includes.Add(import); } + + internal static XmlSchema? GetSchemaWithType(SchemaObjectDictionary schemaInfo, XmlSchemaSet schemas, XmlQualifiedName typeName) + { + SchemaObjectInfo? schemaObjectInfo; + if (schemaInfo.TryGetValue(typeName, out schemaObjectInfo)) + { + if (schemaObjectInfo._schema != null) + return schemaObjectInfo._schema; + } + ICollection currentSchemas = schemas.Schemas(); + string ns = typeName.Namespace; + foreach (XmlSchema schema in currentSchemas) + { + if (NamespacesEqual(ns, schema.TargetNamespace)) + { + return schema; + } + } + return null; + } + + internal static XmlSchema? GetSchemaWithGlobalElementDeclaration(XmlSchemaElement element, XmlSchemaSet schemas) + { + ICollection currentSchemas = schemas.Schemas(); + foreach (XmlSchema schema in currentSchemas) + { + foreach (XmlSchemaObject schemaObject in schema.Items) + { + if (schemaObject is XmlSchemaElement schemaElement && schemaElement == element) + { + return schema; + } + } + } + return null; + } + + internal static XmlQualifiedName? GetGlobalElementDeclaration(XmlSchemaSet schemas, XmlQualifiedName typeQName, out bool isNullable) + { + ICollection currentSchemas = schemas.Schemas(); + isNullable = false; + foreach (XmlSchema schema in currentSchemas) + { + foreach (XmlSchemaObject schemaObject in schema.Items) + { + if (schemaObject is XmlSchemaElement schemaElement && schemaElement.SchemaTypeName.Equals(typeQName)) + { + isNullable = schemaElement.IsNillable; + return new XmlQualifiedName(schemaElement.Name, schema.TargetNamespace); + } + } + } + return null; + } } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaImporter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaImporter.cs new file mode 100644 index 00000000000000..2590d866c26f87 --- /dev/null +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaImporter.cs @@ -0,0 +1,1459 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Runtime.Serialization.DataContracts; +using System.Xml; +using System.Xml.Schema; + +using DataContractDictionary = System.Collections.Generic.Dictionary; +using SchemaObjectDictionary = System.Collections.Generic.Dictionary; + +namespace System.Runtime.Serialization +{ + + internal sealed class SchemaImporter + { + private DataContractSet _dataContractSet; + private XmlSchemaSet _schemaSet; + private IEnumerable? _typeNames; + private IEnumerable? _elements; + private bool _importXmlDataType; + private SchemaObjectDictionary _schemaObjects = null!; // Not directly referenced. Always lazy initialized by property getter. + private List _redefineList = null!; // Not directly referenced. Always lazy initialized by property getter. + private bool _needToImportKnownTypesForObject; + + private static Hashtable? s_serializationSchemaElements; + + internal SchemaImporter(XmlSchemaSet schemas, IEnumerable? typeNames, IEnumerable? elements, DataContractSet dataContractSet, bool importXmlDataType) + { + _dataContractSet = dataContractSet; + _schemaSet = schemas; + _typeNames = typeNames; + _elements = elements; + _importXmlDataType = importXmlDataType; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal void Import([NotNullIfNotNull("_elements")] out List? elementTypeNames) + { + elementTypeNames = null!; + + if (!_schemaSet.Contains(Globals.SerializationNamespace)) + { + StringReader reader = new StringReader(Globals.SerializationSchema); + XmlSchema? schema = XmlSchema.Read(new XmlTextReader(reader) { DtdProcessing = DtdProcessing.Prohibit }, null); + if (schema == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.CouldNotReadSerializationSchema, Globals.SerializationNamespace))); + _schemaSet.Add(schema); + } + + try + { + CompileSchemaSet(_schemaSet); + } +#pragma warning suppress 56500 // covered by FxCOP + catch (Exception ex) + { + if (Fx.IsFatal(ex)) + throw; + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.Format(SR.CannotImportInvalidSchemas), ex)); + } + + if (_typeNames == null) + { + ICollection schemaList = _schemaSet.Schemas(); + foreach (object schemaObj in schemaList) + { + if (schemaObj == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.Format(SR.CannotImportNullSchema))); + + XmlSchema schema = (XmlSchema)schemaObj; + if (schema.TargetNamespace != Globals.SerializationNamespace + && schema.TargetNamespace != Globals.SchemaNamespace) + { + foreach (XmlSchemaObject typeObj in schema.SchemaTypes.Values) + { + ImportType((XmlSchemaType)typeObj); + } + foreach (XmlSchemaElement element in schema.Elements.Values) + { + if (element.SchemaType != null) + ImportAnonymousGlobalElement(element, element.QualifiedName, schema.TargetNamespace); + } + } + } + } + else + { + foreach (XmlQualifiedName typeName in _typeNames) + { + if (typeName == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.Format(SR.CannotImportNullDataContractName))); + ImportType(typeName); + } + + if (_elements != null) + { + var elementNameList = new List(); + foreach (XmlSchemaElement element in _elements) + { + XmlQualifiedName typeName = element.SchemaTypeName; + if (typeName != null && typeName.Name.Length > 0) + { + elementNameList.Add(ImportType(typeName).XmlName); + } + else + { + XmlSchema? schema = SchemaHelper.GetSchemaWithGlobalElementDeclaration(element, _schemaSet); + if (schema == null) + { + elementNameList.Add(ImportAnonymousElement(element, element.QualifiedName).XmlName); + } + else + { + elementNameList.Add(ImportAnonymousGlobalElement(element, element.QualifiedName, schema.TargetNamespace).XmlName); + } + } + } + + if (elementNameList.Count > 0) + elementTypeNames = elementNameList; + } + } + ImportKnownTypesForObject(); + } + + internal static void CompileSchemaSet(XmlSchemaSet schemaSet) + { + if (schemaSet.Contains(XmlSchema.Namespace)) + schemaSet.Compile(); + else + { + // Add base XSD schema with top level element named "schema" + XmlSchema xsdSchema = new XmlSchema(); + xsdSchema.TargetNamespace = XmlSchema.Namespace; + XmlSchemaElement element = new XmlSchemaElement(); + element.Name = Globals.SchemaLocalName; + element.SchemaType = new XmlSchemaComplexType(); + xsdSchema.Items.Add(element); + schemaSet.Add(xsdSchema); + schemaSet.Compile(); + } + } + + private SchemaObjectDictionary SchemaObjects + { + get + { + if (_schemaObjects == null) + _schemaObjects = CreateSchemaObjects(); + return _schemaObjects; + } + } + + private List RedefineList + { + get + { + if (_redefineList == null) + _redefineList = CreateRedefineList(); + return _redefineList; + } + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private void ImportKnownTypes(XmlQualifiedName typeName) + { + SchemaObjectInfo? schemaObjectInfo; + if (SchemaObjects.TryGetValue(typeName, out schemaObjectInfo)) + { + List? knownTypes = schemaObjectInfo._knownTypes; + if (knownTypes != null) + { + foreach (XmlSchemaType knownType in knownTypes) + ImportType(knownType); + } + } + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal static bool IsObjectContract(DataContract dataContract) + { + Dictionary previousCollectionTypes = new Dictionary(); + while (dataContract is CollectionDataContract) + { + if (dataContract.OriginalUnderlyingType == null) + { + dataContract = ((CollectionDataContract)dataContract).ItemContract; + continue; + } + + if (!previousCollectionTypes.ContainsKey(dataContract.OriginalUnderlyingType)) + { + previousCollectionTypes.Add(dataContract.OriginalUnderlyingType, dataContract.OriginalUnderlyingType); + dataContract = ((CollectionDataContract)dataContract).ItemContract; + } + else + { + break; + } + } + + return dataContract is PrimitiveDataContract && ((PrimitiveDataContract)dataContract).UnderlyingType == Globals.TypeOfObject; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private void ImportKnownTypesForObject() + { + if (!_needToImportKnownTypesForObject) + return; + _needToImportKnownTypesForObject = false; + if (_dataContractSet.KnownTypesForObject == null) + { + SchemaObjectInfo? schemaObjectInfo; + if (SchemaObjects.TryGetValue(SchemaExporter.AnytypeQualifiedName, out schemaObjectInfo)) + { + List? knownTypes = schemaObjectInfo._knownTypes; + if (knownTypes != null) + { + DataContractDictionary knownDataContracts = new DataContractDictionary(); + foreach (XmlSchemaType knownType in knownTypes) + { + // Expected: will throw exception if schema set contains types that are not supported + DataContract dataContract = ImportType(knownType); + if (!knownDataContracts.TryGetValue(dataContract.XmlName, out _)) + { + knownDataContracts.Add(dataContract.XmlName, dataContract); + } + } + _dataContractSet.KnownTypesForObject = knownDataContracts; + } + } + } + } + + internal SchemaObjectDictionary CreateSchemaObjects() + { + SchemaObjectDictionary schemaObjects = new SchemaObjectDictionary(); + ICollection schemaList = _schemaSet.Schemas(); + List knownTypesForObject = new List(); + schemaObjects.Add(SchemaExporter.AnytypeQualifiedName, new SchemaObjectInfo(null, null, null, knownTypesForObject)); + + foreach (XmlSchema schema in schemaList) + { + if (schema.TargetNamespace != Globals.SerializationNamespace) + { + foreach (XmlSchemaObject schemaObj in schema.SchemaTypes.Values) + { + if (schemaObj is XmlSchemaType schemaType) + { + knownTypesForObject.Add(schemaType); + + XmlQualifiedName currentTypeName = new XmlQualifiedName(schemaType.Name, schema.TargetNamespace); + SchemaObjectInfo? schemaObjectInfo; + if (schemaObjects.TryGetValue(currentTypeName, out schemaObjectInfo)) + { + schemaObjectInfo._type = schemaType; + schemaObjectInfo._schema = schema; + } + else + { + schemaObjects.Add(currentTypeName, new SchemaObjectInfo(schemaType, null, schema, null)); + } + + XmlQualifiedName? baseTypeName = GetBaseTypeName(schemaType); + if (baseTypeName != null) + { + SchemaObjectInfo? baseTypeInfo; + if (schemaObjects.TryGetValue(baseTypeName, out baseTypeInfo)) + { + if (baseTypeInfo._knownTypes == null) + { + baseTypeInfo._knownTypes = new List(); + } + } + else + { + baseTypeInfo = new SchemaObjectInfo(null, null, null, new List()); + schemaObjects.Add(baseTypeName, baseTypeInfo); + } + baseTypeInfo._knownTypes!.Add(schemaType); // Verified not-null above, or created not-null. + } + } + } + foreach (XmlSchemaObject schemaObj in schema.Elements.Values) + { + if (schemaObj is XmlSchemaElement schemaElement) + { + XmlQualifiedName currentElementName = new XmlQualifiedName(schemaElement.Name, schema.TargetNamespace); + SchemaObjectInfo? schemaObjectInfo; + if (schemaObjects.TryGetValue(currentElementName, out schemaObjectInfo)) + { + schemaObjectInfo._element = schemaElement; + schemaObjectInfo._schema = schema; + } + else + { + schemaObjects.Add(currentElementName, new SchemaObjectInfo(null, schemaElement, schema, null)); + } + } + } + } + } + return schemaObjects; + } + + private static XmlQualifiedName? GetBaseTypeName(XmlSchemaType type) + { + XmlQualifiedName? baseTypeName = null; + if (type is XmlSchemaComplexType complexType) + { + if (complexType.ContentModel != null) + { + if (complexType.ContentModel is XmlSchemaComplexContent complexContent) + { + if (complexContent.Content is XmlSchemaComplexContentExtension extension) + { + baseTypeName = extension.BaseTypeName; + } + } + } + } + return baseTypeName; + } + + private List CreateRedefineList() + { + List list = new List(); + + ICollection schemaList = _schemaSet.Schemas(); + foreach (object schemaObj in schemaList) + { + if (schemaObj is XmlSchema schema) + { + foreach (XmlSchemaExternal ext in schema.Includes) + { + if (ext is XmlSchemaRedefine redefine) + list.Add(redefine); + } + } + } + + return list; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private DataContract ImportAnonymousGlobalElement(XmlSchemaElement element, XmlQualifiedName typeQName, string? ns) + { + DataContract contract = ImportAnonymousElement(element, typeQName); + if (contract is XmlDataContract xmlDataContract) + { + xmlDataContract.SetTopLevelElementName(new XmlQualifiedName(element.Name, ns)); + xmlDataContract.IsTopLevelElementNullable = element.IsNillable; + } + return contract; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private DataContract ImportAnonymousElement(XmlSchemaElement element, XmlQualifiedName typeQName) + { + if (SchemaHelper.GetSchemaType(SchemaObjects, typeQName) != null) + { + for (int i = 1;; i++) + { + typeQName = new XmlQualifiedName(typeQName.Name + i.ToString(NumberFormatInfo.InvariantInfo), typeQName.Namespace); + if (SchemaHelper.GetSchemaType(SchemaObjects, typeQName) == null) + break; + if (i == int.MaxValue) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.CannotComputeUniqueName, element.Name))); + } + } + if (element.SchemaType == null) + return ImportType(SchemaExporter.AnytypeQualifiedName); + else + return ImportType(element.SchemaType, typeQName, true/*isAnonymous*/); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private DataContract ImportType(XmlQualifiedName typeName) + { + DataContract? dataContract = DataContract.GetBuiltInDataContract(typeName.Name, typeName.Namespace); + if (dataContract == null) + { + XmlSchemaType? type = SchemaHelper.GetSchemaType(SchemaObjects, typeName); + if (type == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.SpecifiedTypeNotFoundInSchema, typeName.Name, typeName.Namespace))); + dataContract = ImportType(type); + } + if (IsObjectContract(dataContract)) + _needToImportKnownTypesForObject = true; + return dataContract; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private DataContract ImportType(XmlSchemaType type) + { + return ImportType(type, type.QualifiedName, false/*isAnonymous*/); + } + + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private DataContract ImportType(XmlSchemaType type, XmlQualifiedName typeName, bool isAnonymous) + { + DataContract? dataContract = _dataContractSet.GetDataContract(typeName); + if (dataContract != null) + return dataContract; + + InvalidDataContractException invalidContractException; + try + { + foreach (XmlSchemaRedefine redefine in RedefineList) + { + if (redefine.SchemaTypes[typeName] != null) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.RedefineNotSupported)); + } + + if (type is XmlSchemaSimpleType simpleType) + { + XmlSchemaSimpleTypeContent? content = simpleType.Content; + if (content is XmlSchemaSimpleTypeUnion) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.SimpleTypeUnionNotSupported)); + else if (content is XmlSchemaSimpleTypeList) + dataContract = ImportFlagsEnum(typeName, (XmlSchemaSimpleTypeList)content, simpleType.Annotation); + else if (content is XmlSchemaSimpleTypeRestriction restriction) + { + if (CheckIfEnum(restriction)) + { + dataContract = ImportEnum(typeName, restriction, false /*isFlags*/, simpleType.Annotation); + } + else + { + dataContract = ImportSimpleTypeRestriction(typeName, restriction); + if (dataContract.IsBuiltInDataContract && !isAnonymous) + { + _dataContractSet.InternalAdd(typeName, dataContract); + } + } + } + } + else if (type is XmlSchemaComplexType complexType) + { + if (complexType.ContentModel == null) + { + CheckComplexType(typeName, complexType); + dataContract = ImportType(typeName, complexType.Particle, complexType.Attributes, complexType.AnyAttribute, null /* baseTypeName */, complexType.Annotation); + } + else + { + XmlSchemaContentModel contentModel = complexType.ContentModel; + if (contentModel is XmlSchemaSimpleContent) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.SimpleContentNotSupported)); + else if (contentModel is XmlSchemaComplexContent complexContent) + { + if (complexContent.IsMixed) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.MixedContentNotSupported)); + + if (complexContent.Content is XmlSchemaComplexContentExtension extension) + { + dataContract = ImportType(typeName, extension.Particle, extension.Attributes, extension.AnyAttribute, extension.BaseTypeName, complexType.Annotation); + } + else if (complexContent.Content is XmlSchemaComplexContentRestriction restriction) + { + XmlQualifiedName baseTypeName = restriction.BaseTypeName; + if (baseTypeName == SchemaExporter.AnytypeQualifiedName) + dataContract = ImportType(typeName, restriction.Particle, restriction.Attributes, restriction.AnyAttribute, null /* baseTypeName */, complexType.Annotation); + else + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ComplexTypeRestrictionNotSupported)); + } + } + } + } + + if (dataContract == null) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, string.Empty); + if (type.QualifiedName != XmlQualifiedName.Empty) + ImportTopLevelElement(typeName); + Debug.Assert(dataContract != null); // Throws above if false + ImportDataContractExtension(type, dataContract); + ImportGenericInfo(type, dataContract); + ImportKnownTypes(typeName); + + return dataContract; + } + catch (InvalidDataContractException e) + { + invalidContractException = e; + } + + // Execution gets to this point if InvalidDataContractException was thrown + if (_importXmlDataType) + { + RemoveFailedContract(typeName); + return ImportXmlDataType(typeName, type, isAnonymous); + } + Type? referencedType; + if (_dataContractSet.TryGetReferencedType(typeName, dataContract, out referencedType) + || (string.IsNullOrEmpty(type.Name) && _dataContractSet.TryGetReferencedType(ImportActualType(type.Annotation, typeName, typeName), dataContract, out referencedType))) + { + if (Globals.TypeOfIXmlSerializable.IsAssignableFrom(referencedType)) + { + RemoveFailedContract(typeName); + return ImportXmlDataType(typeName, type, isAnonymous); + } + } + XmlDataContract? specialContract = ImportSpecialXmlDataType(type, isAnonymous); + if (specialContract != null) + { + _dataContractSet.Remove(typeName); + return specialContract; + } + + throw invalidContractException; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private void RemoveFailedContract(XmlQualifiedName typeName) + { + ClassDataContract? oldContract = _dataContractSet.GetDataContract(typeName) as ClassDataContract; + _dataContractSet.Remove(typeName); + if (oldContract != null) + { + ClassDataContract? ancestorDataContract = oldContract.BaseClassContract; + while (ancestorDataContract != null) + { + ancestorDataContract.KnownDataContracts?.Remove(typeName); + ancestorDataContract = ancestorDataContract.BaseClassContract; + } + if (_dataContractSet.KnownTypesForObject != null) + _dataContractSet.KnownTypesForObject.Remove(typeName); + } + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private bool CheckIfEnum(XmlSchemaSimpleTypeRestriction restriction) + { + foreach (XmlSchemaFacet facet in restriction.Facets) + { + if (facet is not XmlSchemaEnumerationFacet) + return false; + } + + XmlQualifiedName expectedBase = SchemaExporter.StringQualifiedName; + if (restriction.BaseTypeName != XmlQualifiedName.Empty) + { + return ((restriction.BaseTypeName == expectedBase && restriction.Facets.Count > 0) || ImportType(restriction.BaseTypeName) is EnumDataContract); + } + else if (restriction.BaseType != null) + { + DataContract baseContract = ImportType(restriction.BaseType); + return (baseContract.XmlName == expectedBase || baseContract is EnumDataContract); + } + + return false; + } + + private static bool CheckIfCollection(XmlSchemaSequence rootSequence) + { + if (rootSequence.Items == null || rootSequence.Items.Count == 0) + return false; + RemoveOptionalUnknownSerializationElements(rootSequence.Items); + if (rootSequence.Items.Count != 1) + return false; + + XmlSchemaObject o = rootSequence.Items[0]; + if (o is XmlSchemaElement localElement) + return (localElement.MaxOccursString == Globals.OccursUnbounded || localElement.MaxOccurs > 1); + + return false; + } + + private static bool CheckIfISerializable(XmlSchemaSequence rootSequence, XmlSchemaObjectCollection attributes) + { + if (rootSequence.Items == null || rootSequence.Items.Count == 0) + return false; + + RemoveOptionalUnknownSerializationElements(rootSequence.Items); + + if (attributes == null || attributes.Count == 0) + return false; + + return (rootSequence.Items.Count == 1 && rootSequence.Items[0] is XmlSchemaAny); + } + + private static void RemoveOptionalUnknownSerializationElements(XmlSchemaObjectCollection items) + { + for (int i = 0; i < items.Count; i++) + { + if (items[i] is XmlSchemaElement element && element.RefName != null && + element.RefName.Namespace == Globals.SerializationNamespace && + element.MinOccurs == 0) + { + if (s_serializationSchemaElements == null) + { + XmlSchema serializationSchema = XmlSchema.Read(XmlReader.Create(new StringReader(Globals.SerializationSchema)), null)!; // Source is our constant. Schema is valid. + s_serializationSchemaElements = new Hashtable(); + foreach (XmlSchemaObject schemaObject in serializationSchema.Items) + { + if (schemaObject is XmlSchemaElement schemaElement) + if (schemaElement.Name != null) + s_serializationSchemaElements.Add(schemaElement.Name, schemaElement); + } + } + if (!s_serializationSchemaElements.ContainsKey(element.RefName.Name)) + { + items.RemoveAt(i); + i--; + } + } + } + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private DataContract? ImportType(XmlQualifiedName typeName, XmlSchemaParticle? rootParticle, XmlSchemaObjectCollection attributes, XmlSchemaAnyAttribute? anyAttribute, XmlQualifiedName? baseTypeName, XmlSchemaAnnotation? annotation) + { + DataContract? dataContract = null; + bool isDerived = (baseTypeName != null); + + bool isReference; + ImportAttributes(typeName, attributes, anyAttribute, out isReference); + + if (rootParticle == null) + dataContract = ImportClass(typeName, new XmlSchemaSequence(), baseTypeName, annotation, isReference); + else if (rootParticle is XmlSchemaSequence rootSequence) + { + if (rootSequence.MinOccurs != 1) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.RootSequenceMustBeRequired)); + if (rootSequence.MaxOccurs != 1) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.RootSequenceMaxOccursMustBe)); + + if (!isDerived && CheckIfCollection(rootSequence)) + dataContract = ImportCollection(typeName, rootSequence, attributes, annotation, isReference); + else if (CheckIfISerializable(rootSequence, attributes)) + dataContract = ImportISerializable(typeName, rootSequence, baseTypeName, attributes, annotation); + else + dataContract = ImportClass(typeName, rootSequence, baseTypeName, annotation, isReference); + } + else + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.RootParticleMustBeSequence)); + return dataContract; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private ClassDataContract ImportClass(XmlQualifiedName typeName, XmlSchemaSequence rootSequence, XmlQualifiedName? baseTypeName, XmlSchemaAnnotation? annotation, bool isReference) + { + ClassDataContract dataContract = new ClassDataContract(Globals.TypeOfSchemaDefinedType); + dataContract.XmlName = typeName; + AddDataContract(dataContract); + + dataContract.IsValueType = IsValueType(typeName, annotation); + dataContract.IsReference = isReference; + if (baseTypeName != null) + { + ImportBaseContract(baseTypeName, dataContract); + Debug.Assert(dataContract.BaseClassContract != null); // ImportBaseContract will always set this... or throw. + if (dataContract.BaseClassContract.IsISerializable) + { + if (IsISerializableDerived(typeName, rootSequence)) + dataContract.IsISerializable = true; + else + ThrowTypeCannotBeImportedException(dataContract.XmlName.Name, dataContract.XmlName.Namespace, SR.Format(SR.DerivedTypeNotISerializable, baseTypeName.Name, baseTypeName.Namespace)); + } + if (dataContract.BaseClassContract.IsReference) + { + dataContract.IsReference = true; + } + } + + if (!dataContract.IsISerializable) + { + dataContract.Members = new List(); + RemoveOptionalUnknownSerializationElements(rootSequence.Items); + for (int memberIndex = 0; memberIndex < rootSequence.Items.Count; memberIndex++) + { + XmlSchemaElement? element = rootSequence.Items[memberIndex] as XmlSchemaElement; + if (element == null) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.MustContainOnlyLocalElements)); + ImportClassMember(element!, dataContract); + } + } + + return dataContract; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private DataContract ImportXmlDataType(XmlQualifiedName typeName, XmlSchemaType xsdType, bool isAnonymous) + { + DataContract? dataContract = _dataContractSet.GetDataContract(typeName); + if (dataContract != null) + return dataContract; + + XmlDataContract? xmlDataContract = ImportSpecialXmlDataType(xsdType, isAnonymous); + if (xmlDataContract != null) + return xmlDataContract; + + xmlDataContract = new XmlDataContract(Globals.TypeOfSchemaDefinedType); + xmlDataContract.XmlName = typeName; + xmlDataContract.IsValueType = false; + AddDataContract(xmlDataContract); + if (xsdType != null) + { + ImportDataContractExtension(xsdType, xmlDataContract); + xmlDataContract.IsValueType = IsValueType(typeName, xsdType.Annotation); + xmlDataContract.IsTypeDefinedOnImport = true; + xmlDataContract.XsdType = isAnonymous ? xsdType : null; + xmlDataContract.HasRoot = !IsXmlAnyElementType(xsdType as XmlSchemaComplexType); + } + if (!isAnonymous) + { + bool isNullable; + xmlDataContract.SetTopLevelElementName(SchemaHelper.GetGlobalElementDeclaration(_schemaSet, typeName, out isNullable)); + xmlDataContract.IsTopLevelElementNullable = isNullable; + } + return xmlDataContract; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private XmlDataContract? ImportSpecialXmlDataType(XmlSchemaType xsdType, bool isAnonymous) + { + if (!isAnonymous) + return null; + if (xsdType is not XmlSchemaComplexType complexType) + return null; + if (IsXmlAnyElementType(complexType)) + { + //check if the type is XElement + XmlQualifiedName xlinqTypeName = new XmlQualifiedName("XElement", "http://schemas.datacontract.org/2004/07/System.Xml.Linq"); + Type? referencedType; + if (_dataContractSet.TryGetReferencedType(xlinqTypeName, null, out referencedType) + && Globals.TypeOfIXmlSerializable.IsAssignableFrom(referencedType)) + { + XmlDataContract xmlDataContract = new XmlDataContract(referencedType); + AddDataContract(xmlDataContract); + return xmlDataContract; + } + //otherwise, assume XmlElement + return (XmlDataContract?)DataContract.GetBuiltInDataContract(Globals.TypeOfXmlElement); + } + if (IsXmlAnyType(complexType)) + return (XmlDataContract?)DataContract.GetBuiltInDataContract(Globals.TypeOfXmlNodeArray); + return null; + } + + private static bool IsXmlAnyElementType(XmlSchemaComplexType? xsdType) + { + if (xsdType == null) + return false; + + if (xsdType.Particle is XmlSchemaSequence sequence) + { + if (sequence.Items == null || sequence.Items.Count != 1) + return false; + + XmlSchemaAny? any = sequence.Items[0] as XmlSchemaAny; + if (any == null || any.Namespace != null) + return false; + + if (xsdType.AnyAttribute != null || (xsdType.Attributes != null && xsdType.Attributes.Count > 0)) + return false; + + return true; + } + + return false; + } + + private static bool IsXmlAnyType(XmlSchemaComplexType xsdType) + { + if (xsdType == null) + return false; + + if (xsdType.Particle is XmlSchemaSequence sequence) + { + if (sequence.Items == null || sequence.Items.Count != 1) + return false; + + XmlSchemaAny? any = sequence.Items[0] as XmlSchemaAny; + if (any == null || any.Namespace != null) + return false; + + if (any.MaxOccurs != decimal.MaxValue) + return false; + + if (xsdType.AnyAttribute == null || xsdType.Attributes.Count > 0) + return false; + + return true; + } + + return false; + } + + private static bool IsValueType(XmlQualifiedName typeName, XmlSchemaAnnotation? annotation) + { + string? isValueTypeInnerText = GetInnerText(typeName, ImportAnnotation(annotation, SchemaExporter.IsValueTypeName)); + if (isValueTypeInnerText != null) + { + try + { + return XmlConvert.ToBoolean(isValueTypeInnerText); + } + catch (FormatException fe) + { + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.IsValueTypeFormattedIncorrectly, isValueTypeInnerText, fe.Message)); + } + } + return false; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private ClassDataContract ImportISerializable(XmlQualifiedName typeName, XmlSchemaSequence rootSequence, XmlQualifiedName? baseTypeName, XmlSchemaObjectCollection attributes, XmlSchemaAnnotation? annotation) + { + ClassDataContract dataContract = new ClassDataContract(Globals.TypeOfSchemaDefinedType); + dataContract.XmlName = typeName; + dataContract.IsISerializable = true; + AddDataContract(dataContract); + + dataContract.IsValueType = IsValueType(typeName, annotation); + if (baseTypeName == null) + CheckISerializableBase(typeName, rootSequence, attributes); + else + { + ImportBaseContract(baseTypeName, dataContract); + Debug.Assert(dataContract.BaseClassContract != null); // ImportBaseContract will always set this... or throw. + if (!dataContract.BaseClassContract.IsISerializable) + ThrowISerializableTypeCannotBeImportedException(dataContract.XmlName.Name, dataContract.XmlName.Namespace, SR.Format(SR.BaseTypeNotISerializable, baseTypeName.Name, baseTypeName.Namespace)); + if (!IsISerializableDerived(typeName, rootSequence)) + ThrowISerializableTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ISerializableDerivedContainsOneOrMoreItems)); + } + + return dataContract; + } + + private static void CheckISerializableBase(XmlQualifiedName typeName, XmlSchemaSequence? rootSequence, XmlSchemaObjectCollection attributes) + { + if (rootSequence == null) + ThrowISerializableTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ISerializableDoesNotContainAny)); + + if (rootSequence.Items == null || rootSequence.Items.Count < 1) + ThrowISerializableTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ISerializableDoesNotContainAny)); + else if (rootSequence.Items.Count > 1) + ThrowISerializableTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ISerializableContainsMoreThanOneItems)); + + XmlSchemaObject o = rootSequence.Items[0]; + if (!(o is XmlSchemaAny)) + ThrowISerializableTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ISerializableDoesNotContainAny)); + + XmlSchemaAny wildcard = (XmlSchemaAny)o; + XmlSchemaAny iSerializableWildcardElement = SchemaExporter.ISerializableWildcardElement; + if (wildcard.MinOccurs != iSerializableWildcardElement.MinOccurs) + ThrowISerializableTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ISerializableWildcardMinOccursMustBe, iSerializableWildcardElement.MinOccurs)); + if (wildcard.MaxOccursString != iSerializableWildcardElement.MaxOccursString) + ThrowISerializableTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ISerializableWildcardMaxOccursMustBe, iSerializableWildcardElement.MaxOccursString)); + if (wildcard.Namespace != iSerializableWildcardElement.Namespace) + ThrowISerializableTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ISerializableWildcardNamespaceInvalid, iSerializableWildcardElement.Namespace)); + if (wildcard.ProcessContents != iSerializableWildcardElement.ProcessContents) + ThrowISerializableTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ISerializableWildcardProcessContentsInvalid, iSerializableWildcardElement.ProcessContents)); + + XmlQualifiedName factoryTypeAttributeRefName = SchemaExporter.ISerializableFactoryTypeAttribute.RefName; + bool containsFactoryTypeAttribute = false; + if (attributes != null) + { + for (int i = 0; i < attributes.Count; i++) + { + o = attributes[i]; + if (o is XmlSchemaAttribute) + { + if (((XmlSchemaAttribute)o).RefName == factoryTypeAttributeRefName) + { + containsFactoryTypeAttribute = true; + break; + } + } + } + } + if (!containsFactoryTypeAttribute) + ThrowISerializableTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ISerializableMustRefFactoryTypeAttribute, factoryTypeAttributeRefName.Name, factoryTypeAttributeRefName.Namespace)); + } + + private static bool IsISerializableDerived(XmlQualifiedName typeName, XmlSchemaSequence? rootSequence) + { + return (rootSequence == null || rootSequence.Items == null || rootSequence.Items.Count == 0); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private void ImportBaseContract(XmlQualifiedName baseTypeName, ClassDataContract dataContract) + { + ClassDataContract? baseContract = ImportType(baseTypeName) as ClassDataContract; + if (baseContract == null) + ThrowTypeCannotBeImportedException(dataContract.XmlName.Name, dataContract.XmlName.Namespace, SR.Format(dataContract.IsISerializable ? SR.InvalidISerializableDerivation : SR.InvalidClassDerivation, baseTypeName.Name, baseTypeName.Namespace)); + + // Note: code ignores IsValueType annotation if derived type exists + if (baseContract.IsValueType) + baseContract.IsValueType = false; + + ClassDataContract? ancestorDataContract = baseContract; + while (ancestorDataContract != null) + { + DataContractDictionary knownDataContracts = ancestorDataContract.KnownDataContracts!; // Might be .Count == 0, but always non-null for ClassDataContract + knownDataContracts.Add(dataContract.XmlName, dataContract); + ancestorDataContract = ancestorDataContract.BaseClassContract; + } + + dataContract.BaseClassContract = baseContract; + } + + private void ImportTopLevelElement(XmlQualifiedName typeName) + { + XmlSchemaElement? topLevelElement = SchemaHelper.GetSchemaElement(SchemaObjects, typeName); + // Top level element of same name is not required, but is validated if it is present + if (topLevelElement == null) + return; + else + { + XmlQualifiedName elementTypeName = topLevelElement.SchemaTypeName; + if (elementTypeName.IsEmpty) + { + if (topLevelElement.SchemaType != null) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.AnonymousTypeNotSupported, typeName.Name, typeName.Namespace)); + else + elementTypeName = SchemaExporter.AnytypeQualifiedName; + } + if (elementTypeName != typeName) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.TopLevelElementRepresentsDifferentType, topLevelElement.SchemaTypeName.Name, topLevelElement.SchemaTypeName.Namespace)); + CheckIfElementUsesUnsupportedConstructs(typeName, topLevelElement); + } + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private void ImportClassMember(XmlSchemaElement element, ClassDataContract dataContract) + { + XmlQualifiedName typeName = dataContract.XmlName; + + if (element.MinOccurs > 1) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ElementMinOccursMustBe, element.Name)); + if (element.MaxOccurs != 1) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ElementMaxOccursMustBe, element.Name)); + + DataContract? memberTypeContract = null; + string? memberName = element.Name; + bool memberIsRequired = (element.MinOccurs > 0); + bool memberIsNullable = element.IsNillable; + bool memberEmitDefaultValue; + int memberOrder = 0; + + XmlSchemaForm elementForm = element.Form; + if (elementForm == XmlSchemaForm.None) + { + XmlSchema? schema = SchemaHelper.GetSchemaWithType(SchemaObjects, _schemaSet, typeName); + if (schema != null) + elementForm = schema.ElementFormDefault; + } + if (elementForm != XmlSchemaForm.Qualified) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.FormMustBeQualified, element.Name)); + CheckIfElementUsesUnsupportedConstructs(typeName, element); + + if (element.SchemaTypeName.IsEmpty) + { + if (element.SchemaType != null) + memberTypeContract = ImportAnonymousElement(element, new XmlQualifiedName(string.Format(CultureInfo.InvariantCulture, "{0}.{1}Type", typeName.Name, element.Name), typeName.Namespace)); + else if (!element.RefName.IsEmpty) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ElementRefOnLocalElementNotSupported, element.RefName.Name, element.RefName.Namespace)); + else + memberTypeContract = ImportType(SchemaExporter.AnytypeQualifiedName); + } + else + { + XmlQualifiedName memberTypeName = ImportActualType(element.Annotation, element.SchemaTypeName, typeName); + memberTypeContract = ImportType(memberTypeName); + if (IsObjectContract(memberTypeContract)) + _needToImportKnownTypesForObject = true; + } + bool? emitDefaultValueFromAnnotation = ImportEmitDefaultValue(element.Annotation, typeName); + if (!memberTypeContract.IsValueType && !memberIsNullable) + { + if (emitDefaultValueFromAnnotation != null && emitDefaultValueFromAnnotation.Value) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.InvalidEmitDefaultAnnotation, memberName, typeName.Name, typeName.Namespace))); + memberEmitDefaultValue = false; + } + else + memberEmitDefaultValue = emitDefaultValueFromAnnotation != null ? emitDefaultValueFromAnnotation.Value : Globals.DefaultEmitDefaultValue; + + Debug.Assert(dataContract.Members != null); // This method is only called from ImportClass() after that method has initialized the Members collection. + Debug.Assert(memberName != null); // At this point, elements without a name should have been handled. + + int prevMemberIndex = dataContract.Members.Count - 1; + if (prevMemberIndex >= 0) + { + DataMember prevMember = dataContract.Members![prevMemberIndex]; + if (prevMember.Order > Globals.DefaultOrder) + memberOrder = dataContract.Members.Count; + DataMember currentMember = new DataMember(memberTypeContract, memberName, memberIsNullable, memberIsRequired, memberEmitDefaultValue, memberOrder); + int compare = ClassDataContract.DataMemberComparer.Singleton.Compare(prevMember, currentMember); + if (compare == 0) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.CannotHaveDuplicateElementNames, memberName)); + else if (compare > 0) + memberOrder = dataContract.Members.Count; + } + DataMember dataMember = new DataMember(memberTypeContract, memberName, memberIsNullable, memberIsRequired, memberEmitDefaultValue, memberOrder); + + XmlQualifiedName surrogateDataAnnotationName = SchemaExporter.SurrogateDataAnnotationName; + _dataContractSet.SetSurrogateData(dataMember, ImportSurrogateData(ImportAnnotation(element.Annotation, surrogateDataAnnotationName), surrogateDataAnnotationName.Name, surrogateDataAnnotationName.Namespace)); + + dataContract.Members.Add(dataMember); + } + + private static bool? ImportEmitDefaultValue(XmlSchemaAnnotation? annotation, XmlQualifiedName typeName) + { + XmlElement? defaultValueElement = ImportAnnotation(annotation, SchemaExporter.DefaultValueAnnotation); + if (defaultValueElement == null) + return null; + XmlNode? emitDefaultValueAttribute = defaultValueElement.Attributes.GetNamedItem(Globals.EmitDefaultValueAttribute); + if (emitDefaultValueAttribute?.Value == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.AnnotationAttributeNotFound, SchemaExporter.DefaultValueAnnotation.Name, typeName.Name, typeName.Namespace, Globals.EmitDefaultValueAttribute))); + return XmlConvert.ToBoolean(emitDefaultValueAttribute.Value); + } + + internal static XmlQualifiedName ImportActualType(XmlSchemaAnnotation? annotation, XmlQualifiedName defaultTypeName, XmlQualifiedName typeName) + { + XmlElement? actualTypeElement = ImportAnnotation(annotation, SchemaExporter.ActualTypeAnnotationName); + if (actualTypeElement == null) + return defaultTypeName; + + XmlNode? nameAttribute = actualTypeElement.Attributes.GetNamedItem(Globals.ActualTypeNameAttribute); + if (nameAttribute?.Value == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.AnnotationAttributeNotFound, SchemaExporter.ActualTypeAnnotationName.Name, typeName.Name, typeName.Namespace, Globals.ActualTypeNameAttribute))); + XmlNode? nsAttribute = actualTypeElement.Attributes.GetNamedItem(Globals.ActualTypeNamespaceAttribute); + if (nsAttribute?.Value == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.AnnotationAttributeNotFound, SchemaExporter.ActualTypeAnnotationName.Name, typeName.Name, typeName.Namespace, Globals.ActualTypeNamespaceAttribute))); + return new XmlQualifiedName(nameAttribute.Value, nsAttribute.Value); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private CollectionDataContract ImportCollection(XmlQualifiedName typeName, XmlSchemaSequence rootSequence, XmlSchemaObjectCollection attributes, XmlSchemaAnnotation? annotation, bool isReference) + { + CollectionDataContract dataContract = new CollectionDataContract(Globals.TypeOfSchemaDefinedType, CollectionKind.Array); + dataContract.XmlName = typeName; + AddDataContract(dataContract); + + dataContract.IsReference = isReference; + + // CheckIfCollection has already checked if sequence contains exactly one item with maxOccurs="unbounded" or maxOccurs > 1 + XmlSchemaElement element = (XmlSchemaElement)rootSequence.Items[0]; + + Debug.Assert(element.Name != null); + + dataContract.IsItemTypeNullable = element.IsNillable; + dataContract.ItemName = element.Name; + + XmlSchemaForm elementForm = element.Form; + if (elementForm == XmlSchemaForm.None) + { + XmlSchema? schema = SchemaHelper.GetSchemaWithType(SchemaObjects, _schemaSet, typeName); + if (schema != null) + elementForm = schema.ElementFormDefault; + } + if (elementForm != XmlSchemaForm.Qualified) + ThrowArrayTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ArrayItemFormMustBe, element.Name)); + CheckIfElementUsesUnsupportedConstructs(typeName, element); + + if (element.SchemaTypeName.IsEmpty) + { + if (element.SchemaType != null) + { + XmlQualifiedName shortName = new XmlQualifiedName(element.Name, typeName.Namespace); + DataContract? contract = _dataContractSet.GetDataContract(shortName); + if (contract == null) + { + dataContract.ItemContract = ImportAnonymousElement(element, shortName); + } + else + { + XmlQualifiedName fullName = new XmlQualifiedName(string.Format(CultureInfo.InvariantCulture, "{0}.{1}Type", typeName.Name, element.Name), typeName.Namespace); + dataContract.ItemContract = ImportAnonymousElement(element, fullName); + } + } + else if (!element.RefName.IsEmpty) + ThrowArrayTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ElementRefOnLocalElementNotSupported, element.RefName.Name, element.RefName.Namespace)); + else + dataContract.ItemContract = ImportType(SchemaExporter.AnytypeQualifiedName); + } + else + { + dataContract.ItemContract = ImportType(element.SchemaTypeName); + } + + if (IsDictionary(typeName, annotation)) + { + ClassDataContract? keyValueContract = dataContract.ItemContract as ClassDataContract; + DataMember key = null!, value = null!; // Set in the following || conditional chain. If the chain triggers before setting these, an exception is thrown. + if (keyValueContract == null || keyValueContract.Members == null || keyValueContract.Members.Count != 2 + || !(key = keyValueContract.Members[0]).IsRequired || !(value = keyValueContract.Members[1]).IsRequired) + { + ThrowArrayTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.InvalidKeyValueType, element.Name)); + } + if (keyValueContract.Namespace != dataContract.Namespace) + { + ThrowArrayTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.InvalidKeyValueTypeNamespace, element.Name, keyValueContract.Namespace)); + } + keyValueContract.IsValueType = true; + dataContract.KeyName = key.Name; + dataContract.ValueName = value.Name; + if (element.SchemaType != null) + { + _dataContractSet.Remove(keyValueContract.XmlName); + + GenericInfo genericInfo = new GenericInfo(DataContract.GetXmlName(Globals.TypeOfKeyValue), Globals.TypeOfKeyValue.FullName); + genericInfo.Add(GetGenericInfoForDataMember(key)); + genericInfo.Add(GetGenericInfoForDataMember(value)); + genericInfo.AddToLevel(0, 2); + dataContract.ItemContract.XmlName = new XmlQualifiedName(genericInfo.GetExpandedXmlName().Name, typeName.Namespace); + } + } + + return dataContract; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private static GenericInfo GetGenericInfoForDataMember(DataMember dataMember) + { + GenericInfo genericInfo; + if (dataMember.MemberTypeContract.IsValueType && dataMember.IsNullable) + { + genericInfo = new GenericInfo(DataContract.GetXmlName(Globals.TypeOfNullable), Globals.TypeOfNullable.FullName); + genericInfo.Add(new GenericInfo(dataMember.MemberTypeContract.XmlName, null)); + } + else + { + genericInfo = new GenericInfo(dataMember.MemberTypeContract.XmlName, null); + } + + return genericInfo; + } + + private static bool IsDictionary(XmlQualifiedName typeName, XmlSchemaAnnotation? annotation) + { + string? isDictionaryInnerText = GetInnerText(typeName, ImportAnnotation(annotation, SchemaExporter.IsDictionaryAnnotationName)); + if (isDictionaryInnerText != null) + { + try + { + return XmlConvert.ToBoolean(isDictionaryInnerText); + } + catch (FormatException fe) + { + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.IsDictionaryFormattedIncorrectly, isDictionaryInnerText, fe.Message)); + } + } + return false; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private EnumDataContract? ImportFlagsEnum(XmlQualifiedName typeName, XmlSchemaSimpleTypeList list, XmlSchemaAnnotation? annotation) + { + XmlSchemaSimpleType? anonymousType = list.ItemType; + if (anonymousType == null) + ThrowEnumTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.EnumListMustContainAnonymousType)); + + XmlSchemaSimpleTypeContent? content = anonymousType.Content; + if (content is XmlSchemaSimpleTypeUnion) + ThrowEnumTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.EnumUnionInAnonymousTypeNotSupported)); + else if (content is XmlSchemaSimpleTypeList) + ThrowEnumTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.EnumListInAnonymousTypeNotSupported)); + else if (content is XmlSchemaSimpleTypeRestriction) + { + if (content is XmlSchemaSimpleTypeRestriction restriction && CheckIfEnum(restriction)) + return ImportEnum(typeName, restriction, true /*isFlags*/, annotation); + else + ThrowEnumTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.EnumRestrictionInvalid)); + } + return null; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private EnumDataContract ImportEnum(XmlQualifiedName typeName, XmlSchemaSimpleTypeRestriction restriction, bool isFlags, XmlSchemaAnnotation? annotation) + { + EnumDataContract dataContract = new EnumDataContract(Globals.TypeOfSchemaDefinedEnum); + dataContract.XmlName = typeName; + dataContract.BaseContractName = ImportActualType(annotation, SchemaExporter.DefaultEnumBaseTypeName, typeName); + dataContract.IsFlags = isFlags; + AddDataContract(dataContract); + + // CheckIfEnum has already checked if baseType of restriction is string + dataContract.Values = new List(); + dataContract.Members = new List(); + foreach (XmlSchemaFacet facet in restriction.Facets) + { + XmlSchemaEnumerationFacet? enumFacet = facet as XmlSchemaEnumerationFacet; + if (enumFacet == null) + ThrowEnumTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.EnumOnlyEnumerationFacetsSupported)); + if (enumFacet.Value == null) + ThrowEnumTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.EnumEnumerationFacetsMustHaveValue)); + + string? valueInnerText = GetInnerText(typeName, ImportAnnotation(enumFacet.Annotation, SchemaExporter.EnumerationValueAnnotationName)); + long enumValue = (valueInnerText == null) ? SchemaExporter.GetDefaultEnumValue(isFlags, dataContract.Members.Count) + : dataContract.GetEnumValueFromString(valueInnerText); + dataContract.Values.Add(enumValue); + DataMember dataMember = new DataMember(Globals.SchemaMemberInfoPlaceholder) { Name = enumFacet.Value, Order = enumValue }; + dataContract.Members.Add(dataMember); + } + return dataContract; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private DataContract ImportSimpleTypeRestriction(XmlQualifiedName typeName, XmlSchemaSimpleTypeRestriction restriction) + { + DataContract dataContract = null!; // Always assigned by one of the ImportType()s, or exception is thrown. + + if (!restriction.BaseTypeName.IsEmpty) + dataContract = ImportType(restriction.BaseTypeName); + else if (restriction.BaseType != null) + dataContract = ImportType(restriction.BaseType); + else + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.SimpleTypeRestrictionDoesNotSpecifyBase)); + + return dataContract; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private void ImportDataContractExtension(XmlSchemaType type, DataContract dataContract) + { + if (type.Annotation == null || type.Annotation.Items == null) + return; + foreach (XmlSchemaObject schemaObject in type.Annotation.Items) + { + if (schemaObject is XmlSchemaAppInfo appInfo && appInfo.Markup != null) + { + foreach (XmlNode? xmlNode in appInfo.Markup) + { + XmlElement? typeElement = xmlNode as XmlElement; + XmlQualifiedName surrogateDataAnnotationName = SchemaExporter.SurrogateDataAnnotationName; + if (typeElement != null && typeElement.NamespaceURI == surrogateDataAnnotationName.Namespace && typeElement.LocalName == surrogateDataAnnotationName.Name) + { + object? surrogateData = ImportSurrogateData(typeElement, surrogateDataAnnotationName.Name, surrogateDataAnnotationName.Namespace); + _dataContractSet.SetSurrogateData(dataContract, surrogateData); + } + } + } + } + } + + private void ImportGenericInfo(XmlSchemaType type, DataContract dataContract) + { + if (type.Annotation == null || type.Annotation.Items == null) + return; + foreach (XmlSchemaObject schemaObject in type.Annotation.Items) + { + if (schemaObject is XmlSchemaAppInfo appInfo && appInfo.Markup != null) + { + foreach (XmlNode? xmlNode in appInfo.Markup) + { + XmlElement? typeElement = xmlNode as XmlElement; + if (typeElement != null && typeElement.NamespaceURI == Globals.SerializationNamespace) + { + if (typeElement.LocalName == Globals.GenericTypeLocalName) + dataContract.GenericInfo = ImportGenericInfo(typeElement, type); + } + } + } + } + } + + private GenericInfo ImportGenericInfo(XmlElement typeElement, XmlSchemaType type) + { + string? name = typeElement.Attributes.GetNamedItem(Globals.GenericNameAttribute)?.Value; + if (name == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.GenericAnnotationAttributeNotFound, type.Name, Globals.GenericNameAttribute))); + string? ns = typeElement.Attributes.GetNamedItem(Globals.GenericNamespaceAttribute)?.Value; + if (ns == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.GenericAnnotationAttributeNotFound, type.Name, Globals.GenericNamespaceAttribute))); + if (typeElement.ChildNodes.Count > 0) //Generic Type + name = DataContract.EncodeLocalName(name); + + int currentLevel = 0; + GenericInfo genInfo = new GenericInfo(new XmlQualifiedName(name, ns), type.Name); + foreach (XmlNode childNode in typeElement.ChildNodes) + { + if (childNode is XmlElement argumentElement) + { + if (argumentElement.LocalName != Globals.GenericParameterLocalName || + argumentElement.NamespaceURI != Globals.SerializationNamespace) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.GenericAnnotationHasInvalidElement, argumentElement.LocalName, argumentElement.NamespaceURI, type.Name))); + XmlNode? nestedLevelAttribute = argumentElement.Attributes.GetNamedItem(Globals.GenericParameterNestedLevelAttribute); + int argumentLevel = 0; + if (nestedLevelAttribute != null) + { + if (!int.TryParse(nestedLevelAttribute.Value, out argumentLevel)) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.GenericAnnotationHasInvalidAttributeValue, argumentElement.LocalName, argumentElement.NamespaceURI, type.Name, nestedLevelAttribute.Value, nestedLevelAttribute.LocalName, Globals.TypeOfInt.Name))); + } + if (argumentLevel < currentLevel) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.GenericAnnotationForNestedLevelMustBeIncreasing, argumentElement.LocalName, argumentElement.NamespaceURI, type.Name))); + genInfo.Add(ImportGenericInfo(argumentElement, type)); + genInfo.AddToLevel(argumentLevel, 1); + currentLevel = argumentLevel; + } + } + + XmlNode? typeNestedLevelsAttribute = typeElement.Attributes.GetNamedItem(Globals.GenericParameterNestedLevelAttribute); + if (typeNestedLevelsAttribute != null) + { + int nestedLevels; + if (!int.TryParse(typeNestedLevelsAttribute.Value, out nestedLevels)) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.GenericAnnotationHasInvalidAttributeValue, typeElement.LocalName, typeElement.NamespaceURI, type.Name, typeNestedLevelsAttribute.Value, typeNestedLevelsAttribute.LocalName, Globals.TypeOfInt.Name))); + if ((nestedLevels - 1) > currentLevel) + genInfo.AddToLevel(nestedLevels - 1, 0); + } + return genInfo; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private object? ImportSurrogateData(XmlElement? typeElement, string name, string ns) + { + if (_dataContractSet.SerializationExtendedSurrogateProvider != null && typeElement != null) + { + Collection knownTypes = new Collection(); + DataContractSurrogateCaller.GetKnownCustomDataTypes(_dataContractSet.SerializationExtendedSurrogateProvider, knownTypes); + DataContractSerializer serializer = new DataContractSerializer(Globals.TypeOfObject, name, ns, knownTypes, + int.MaxValue, false /*ignoreExtensionDataObject*/, true /*preserveObjectReferences*/); + return serializer.ReadObject(new XmlNodeReader(typeElement)); + } + return null; + } + + private static void CheckComplexType(XmlQualifiedName typeName, XmlSchemaComplexType type) + { + if (type.IsAbstract) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.AbstractTypeNotSupported)); + if (type.IsMixed) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.MixedContentNotSupported)); + } + + private static void CheckIfElementUsesUnsupportedConstructs(XmlQualifiedName typeName, XmlSchemaElement element) + { + if (element.IsAbstract) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.AbstractElementNotSupported, element.Name)); + if (element.DefaultValue != null) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.DefaultOnElementNotSupported, element.Name)); + if (element.FixedValue != null) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.FixedOnElementNotSupported, element.Name)); + if (!element.SubstitutionGroup.IsEmpty) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.SubstitutionGroupOnElementNotSupported, element.Name)); + } + + private static void ImportAttributes(XmlQualifiedName typeName, XmlSchemaObjectCollection attributes, XmlSchemaAnyAttribute? anyAttribute, out bool isReference) + { + if (anyAttribute != null) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.AnyAttributeNotSupported)); + + isReference = false; + if (attributes != null) + { + bool foundId = false, foundRef = false; + for (int i = 0; i < attributes.Count; i++) + { + XmlSchemaObject o = attributes[i]; + if (o is XmlSchemaAttribute attribute) + { + if (attribute.Use == XmlSchemaUse.Prohibited) + continue; + if (TryCheckIfAttribute(typeName, attribute, Globals.IdQualifiedName, ref foundId)) + continue; + if (TryCheckIfAttribute(typeName, attribute, Globals.RefQualifiedName, ref foundRef)) + continue; + if (attribute.RefName.IsEmpty || attribute.RefName.Namespace != Globals.SerializationNamespace || attribute.Use == XmlSchemaUse.Required) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.TypeShouldNotContainAttributes, Globals.SerializationNamespace)); + } + } + isReference = (foundId && foundRef); + } + } + + private static bool TryCheckIfAttribute(XmlQualifiedName typeName, XmlSchemaAttribute attribute, XmlQualifiedName refName, ref bool foundAttribute) + { + if (attribute.RefName != refName) + return false; + if (foundAttribute) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.CannotHaveDuplicateAttributeNames, refName.Name)); + foundAttribute = true; + return true; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private void AddDataContract(DataContract dataContract) + { + _dataContractSet.Add(dataContract.XmlName, dataContract); + } + + private static string? GetInnerText(XmlQualifiedName typeName, XmlElement? xmlElement) + { + if (xmlElement != null) + { + XmlNode? child = xmlElement.FirstChild; + while (child != null) + { + if (child.NodeType == XmlNodeType.Element) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.InvalidAnnotationExpectingText, xmlElement.LocalName, xmlElement.NamespaceURI, child.LocalName, child.NamespaceURI)); + child = child.NextSibling; + } + return xmlElement.InnerText; + } + return null; + } + + private static XmlElement? ImportAnnotation(XmlSchemaAnnotation? annotation, XmlQualifiedName annotationQualifiedName) + { + if (annotation != null && annotation.Items != null && annotation.Items.Count > 0 && annotation.Items[0] is XmlSchemaAppInfo) + { + XmlSchemaAppInfo appInfo = (XmlSchemaAppInfo)annotation.Items[0]; + XmlNode?[]? markup = appInfo.Markup; + if (markup != null) + { + for (int i = 0; i < markup.Length; i++) + { + if (markup[i] is XmlElement annotationElement && annotationElement.LocalName == annotationQualifiedName.Name && annotationElement.NamespaceURI == annotationQualifiedName.Namespace) + return annotationElement; + } + } + } + return null; + } + + [DoesNotReturn] + private static void ThrowTypeCannotBeImportedException(string name, string ns, string message) + { + ThrowTypeCannotBeImportedException(SR.Format(SR.TypeCannotBeImported, name, ns, message)); + } + + [DoesNotReturn] + private static void ThrowArrayTypeCannotBeImportedException(string name, string ns, string message) + { + ThrowTypeCannotBeImportedException(SR.Format(SR.ArrayTypeCannotBeImported, name, ns, message)); + } + + [DoesNotReturn] + private static void ThrowEnumTypeCannotBeImportedException(string name, string ns, string message) + { + ThrowTypeCannotBeImportedException(SR.Format(SR.EnumTypeCannotBeImported, name, ns, message)); + } + + [DoesNotReturn] + private static void ThrowISerializableTypeCannotBeImportedException(string name, string ns, string message) + { + ThrowTypeCannotBeImportedException(SR.Format(SR.ISerializableTypeCannotBeImported, name, ns, message)); + } + + [DoesNotReturn] + private static void ThrowTypeCannotBeImportedException(string message) + { + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.TypeCannotBeImportedHowToFix, message))); + } + } + +} diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ScopedKnownTypes.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ScopedKnownTypes.cs index 727fc4e6c49534..149a661a95bff8 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ScopedKnownTypes.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ScopedKnownTypes.cs @@ -2,8 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Runtime.Serialization.DataContracts; using System.Xml; -using DataContractDictionary = System.Collections.Generic.Dictionary; + +using DataContractDictionary = System.Collections.Generic.Dictionary; namespace System.Runtime.Serialization { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SerializationMode.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SerializationMode.cs deleted file mode 100644 index 142ae2c0096382..00000000000000 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SerializationMode.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Runtime.Serialization -{ - internal enum SerializationMode - { - SharedContract - } -} diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SpecialTypeDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SpecialTypeDataContract.cs index 010871e02e13ab..978de94bae99d0 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SpecialTypeDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SpecialTypeDataContract.cs @@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis; using System.Xml; -namespace System.Runtime.Serialization +namespace System.Runtime.Serialization.DataContracts { internal sealed class SpecialTypeDataContract : DataContract { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SurrogateDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SurrogateDataContract.cs index 02b82aaee0d804..77a2e181052fd6 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SurrogateDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SurrogateDataContract.cs @@ -7,7 +7,7 @@ using System.Runtime.CompilerServices; using System.Security; -namespace System.Runtime.Serialization +namespace System.Runtime.Serialization.DataContracts { internal sealed class SurrogateDataContract : DataContract { @@ -20,13 +20,10 @@ internal SurrogateDataContract(Type type, ISerializationSurrogate serializationS _helper = (base.Helper as SurrogateDataContractCriticalHelper)!; } - internal ISerializationSurrogate SerializationSurrogate - { - get { return _helper.SerializationSurrogate; } - } + internal ISerializationSurrogate SerializationSurrogate => _helper.SerializationSurrogate; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { Debug.Assert(context != null); @@ -62,7 +59,7 @@ private void SerializationSurrogateGetObjectData(object obj, SerializationInfo s } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) { Debug.Assert(context != null); @@ -95,14 +92,11 @@ internal SurrogateDataContractCriticalHelper( { this.serializationSurrogate = serializationSurrogate; string name, ns; - DataContract.GetDefaultStableName(DataContract.GetClrTypeFullName(type), out name, out ns); + DataContract.GetDefaultXmlName(DataContract.GetClrTypeFullName(type), out name, out ns); SetDataContractName(CreateQualifiedName(name, ns)); } - internal ISerializationSurrogate SerializationSurrogate - { - get { return serializationSurrogate; } - } + internal ISerializationSurrogate SerializationSurrogate => serializationSurrogate; } } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/TypeCode.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/TypeCode.cs deleted file mode 100644 index f9b2b9d4ca526d..00000000000000 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/TypeCode.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Runtime.Serialization -{ - internal static class TypeExtensionMethods - { - public static TypeCode GetTypeCode(this Type type) - { - if (type == null) - { - return TypeCode.Empty; - } - else if (type == typeof(bool)) - { - return TypeCode.Boolean; - } - else if (type == typeof(char)) - { - return TypeCode.Char; - } - else if (type == typeof(sbyte)) - { - return TypeCode.SByte; - } - else if (type == typeof(byte)) - { - return TypeCode.Byte; - } - else if (type == typeof(short)) - { - return TypeCode.Int16; - } - else if (type == typeof(ushort)) - { - return TypeCode.UInt16; - } - else if (type == typeof(int)) - { - return TypeCode.Int32; - } - else if (type == typeof(uint)) - { - return TypeCode.UInt32; - } - else if (type == typeof(long)) - { - return TypeCode.Int64; - } - else if (type == typeof(ulong)) - { - return TypeCode.UInt64; - } - else if (type == typeof(float)) - { - return TypeCode.Single; - } - else if (type == typeof(double)) - { - return TypeCode.Double; - } - else if (type == typeof(decimal)) - { - return TypeCode.Decimal; - } - else if (type == typeof(DateTime)) - { - return TypeCode.DateTime; - } - else if (type == typeof(string)) - { - return TypeCode.String; - } - else - { - return TypeCode.Object; - } - } - } -} diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XPathQueryGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XPathQueryGenerator.cs index 364038c247a3e2..2fac683e1d7276 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XPathQueryGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XPathQueryGenerator.cs @@ -2,12 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Text; -using System.Reflection; -using System.Globalization; using System.Collections.Generic; -using System.Xml; using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Reflection; +using System.Runtime.Serialization.DataContracts; +using System.Text; +using System.Xml; namespace System.Runtime.Serialization { @@ -80,9 +81,9 @@ private static DataContract ProcessClassDataContract(ClassDataContract contract, private static IEnumerable GetDataMembers(ClassDataContract contract) { - if (contract.BaseContract != null) + if (contract.BaseClassContract != null) { - foreach (DataMember baseClassMember in GetDataMembers(contract.BaseContract)) + foreach (DataMember baseClassMember in GetDataMembers(contract.BaseClassContract)) { yield return baseClassMember; } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlDataContract.cs index 9ca8757798163f..af769058a1f9af 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlDataContract.cs @@ -12,13 +12,17 @@ using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; -using DataContractDictionary = System.Collections.Generic.Dictionary; -namespace System.Runtime.Serialization +using DataContractDictionary = System.Collections.Generic.Dictionary; + +namespace System.Runtime.Serialization.DataContracts { internal delegate IXmlSerializable CreateXmlSerializableDelegate(); - internal sealed class XmlDataContract : DataContract + public sealed class XmlDataContract : DataContract { + internal const string ContractTypeString = nameof(XmlDataContract); + public override string? ContractType => ContractTypeString; + private readonly XmlDataContractCriticalHelper _helper; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -30,57 +34,55 @@ internal XmlDataContract(Type type) : base(new XmlDataContractCriticalHelper(typ public override DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - get - { return _helper.KnownDataContracts; } - - set - { _helper.KnownDataContracts = value; } + get => _helper.KnownDataContracts; + internal set => _helper.KnownDataContracts = value; } - internal XmlSchemaType? XsdType + public XmlSchemaType? XsdType { - get { return _helper.XsdType; } - set { _helper.XsdType = value; } + get => _helper.XsdType; + internal set => _helper.XsdType = value; } - - internal bool IsAnonymous + public bool IsAnonymous { - get - { return _helper.IsAnonymous; } + get => _helper.IsAnonymous; } - public override bool HasRoot + public new bool IsValueType { - get - { return _helper.HasRoot; } + get => _helper.IsValueType; + set => _helper.IsValueType = value; + } - set - { _helper.HasRoot = value; } + public new bool HasRoot + { + get => _helper.HasRoot; + internal set => _helper.HasRoot = value; } public override XmlDictionaryString? TopLevelElementName { - get - { return _helper.TopLevelElementName; } - - set - { _helper.TopLevelElementName = value; } + get => _helper.TopLevelElementName; + internal set => _helper.TopLevelElementName = value; } public override XmlDictionaryString? TopLevelElementNamespace { - get - { return _helper.TopLevelElementNamespace; } + get => _helper.TopLevelElementNamespace; + internal set => _helper.TopLevelElementNamespace = value; + } - set - { _helper.TopLevelElementNamespace = value; } + public bool IsTopLevelElementNullable + { + get => _helper.IsTopLevelElementNullable; + internal set => _helper.IsTopLevelElementNullable = value; } - internal bool IsTopLevelElementNullable + public bool IsTypeDefinedOnImport { - get { return _helper.IsTopLevelElementNullable; } - set { _helper.IsTopLevelElementNullable = value; } + get => _helper.IsTypeDefinedOnImport; + set => _helper.IsTypeDefinedOnImport = value; } internal CreateXmlSerializableDelegate CreateXmlSerializableDelegate @@ -122,7 +124,7 @@ private sealed class XmlDataContractCriticalHelper : DataContract.DataContractCr private XmlDictionaryString? _topLevelElementName; private XmlDictionaryString? _topLevelElementNamespace; private bool _isTopLevelElementNullable; - private bool _hasRoot; + private bool _isTypeDefinedOnImport; private CreateXmlSerializableDelegate? _createXmlSerializable; private XmlSchemaType? _xsdType; @@ -136,20 +138,22 @@ internal XmlDataContractCriticalHelper( if (type.IsDefined(Globals.TypeOfCollectionDataContractAttribute, false)) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.IXmlSerializableCannotHaveCollectionDataContract, DataContract.GetClrTypeFullName(type)))); bool hasRoot; - XmlQualifiedName stableName; - SchemaExporter.GetXmlTypeInfo(type, out stableName, out _, out hasRoot); - this.StableName = stableName; - this.HasRoot = hasRoot; + XmlSchemaType? xsdType; + XmlQualifiedName xmlName; + SchemaExporter.GetXmlTypeInfo(type, out xmlName, out xsdType, out hasRoot); + XmlName = xmlName; + XsdType = xsdType; + HasRoot = hasRoot; XmlDictionary dictionary = new XmlDictionary(); - this.Name = dictionary.Add(StableName.Name); - this.Namespace = dictionary.Add(StableName.Namespace); + Name = dictionary.Add(XmlName.Name); + Namespace = dictionary.Add(XmlName.Namespace); object[]? xmlRootAttributes = UnderlyingType?.GetCustomAttributes(Globals.TypeOfXmlRootAttribute, false).ToArray(); if (xmlRootAttributes == null || xmlRootAttributes.Length == 0) { if (hasRoot) { _topLevelElementName = Name; - _topLevelElementNamespace = (this.StableName.Namespace == Globals.SchemaNamespace) ? DictionaryGlobals.EmptyString : Namespace; + _topLevelElementNamespace = (this.XmlName.Namespace == Globals.SchemaNamespace) ? DictionaryGlobals.EmptyString : Namespace; _isTopLevelElementNullable = true; } } @@ -186,6 +190,7 @@ internal override DataContractDictionary? KnownDataContracts Interlocked.MemoryBarrier(); _isKnownTypeAttributeChecked = true; } + _knownDataContracts ??= new DataContractDictionary(); } } return _knownDataContracts; @@ -197,47 +202,40 @@ internal override DataContractDictionary? KnownDataContracts internal XmlSchemaType? XsdType { - get { return _xsdType; } - set { _xsdType = value; } + get => _xsdType; + set => _xsdType = value; } internal bool IsAnonymous => _xsdType != null; - internal override bool HasRoot - { - get - { return _hasRoot; } - - set - { _hasRoot = value; } - } - internal override XmlDictionaryString? TopLevelElementName { - get - { return _topLevelElementName; } - set - { _topLevelElementName = value; } + get => _topLevelElementName; + set => _topLevelElementName = value; } internal override XmlDictionaryString? TopLevelElementNamespace { - get - { return _topLevelElementNamespace; } - set - { _topLevelElementNamespace = value; } + get => _topLevelElementNamespace; + set => _topLevelElementNamespace = value; } internal bool IsTopLevelElementNullable { - get { return _isTopLevelElementNullable; } - set { _isTopLevelElementNullable = value; } + get => _isTopLevelElementNullable; + set => _isTopLevelElementNullable = value; + } + + internal bool IsTypeDefinedOnImport + { + get => _isTypeDefinedOnImport; + set => _isTypeDefinedOnImport = value; } internal CreateXmlSerializableDelegate? CreateXmlSerializableDelegate { - get { return _createXmlSerializable; } - set { _createXmlSerializable = value; } + get => _createXmlSerializable; + set => _createXmlSerializable = value; } } @@ -253,6 +251,16 @@ internal CreateXmlSerializableDelegate? CreateXmlSerializableDelegate return ctor; } + internal void SetTopLevelElementName(XmlQualifiedName? elementName) + { + if (elementName != null) + { + XmlDictionary dictionary = new XmlDictionary(); + TopLevelElementName = dictionary.Add(elementName.Name); + TopLevelElementNamespace = dictionary.Add(elementName.Namespace); + } + } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal CreateXmlSerializableDelegate GenerateCreateXmlSerializableDelegate() { @@ -370,8 +378,30 @@ internal IXmlSerializable ReflectionCreateXmlSerializable(Type type) } } + internal override bool Equals(object? other, HashSet? checkedContracts) + { + if (IsEqualOrChecked(other, checkedContracts)) + return true; + + if (other is XmlDataContract dataContract) + { + if (this.HasRoot != dataContract.HasRoot) + return false; + + if (this.IsAnonymous) + { + return dataContract.IsAnonymous; + } + else + { + return (XmlName.Name == dataContract.XmlName.Name && XmlName.Namespace == dataContract.XmlName.Namespace); + } + } + return false; + } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { if (context == null) XmlObjectSerializerWriteContext.WriteRootIXmlSerializable(xmlWriter, obj); @@ -380,7 +410,7 @@ public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, Xml } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) { object? o; if (context == null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatGeneratorStatics.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatGeneratorStatics.cs index 6791ee9e45f586..ec6392e62e7c3b 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatGeneratorStatics.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatGeneratorStatics.cs @@ -1,11 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Reflection; -using System.Xml; using System.Collections; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.Serialization.DataContracts; +using System.Xml; namespace System.Runtime.Serialization { @@ -70,6 +71,9 @@ internal static MethodInfo WriteNamespaceDeclMethod private static PropertyInfo? s_extensionDataProperty; internal static PropertyInfo ExtensionDataProperty => s_extensionDataProperty ??= typeof(IExtensibleDataObject).GetProperty("ExtensionData")!; + private static MethodInfo? s_boxPointer; + internal static MethodInfo BoxPointer => s_boxPointer ??= typeof(Pointer).GetMethod("Box")!; + private static ConstructorInfo? s_dictionaryEnumeratorCtor; internal static ConstructorInfo DictionaryEnumeratorCtor { @@ -163,7 +167,7 @@ internal static MethodInfo GetUninitializedObjectMethod { if (s_getUninitializedObjectMethod == null) { - s_getUninitializedObjectMethod = typeof(XmlFormatReaderGenerator).GetMethod("UnsafeGetUninitializedObject", Globals.ScanAllMembers, new Type[] { typeof(int) }); + s_getUninitializedObjectMethod = typeof(XmlFormatReaderGenerator).GetMethod("UnsafeGetUninitializedObject", Globals.ScanAllMembers, new Type[] { typeof(int) })!; Debug.Assert(s_getUninitializedObjectMethod != null); } return s_getUninitializedObjectMethod; @@ -184,6 +188,9 @@ internal static MethodInfo OnDeserializationMethod } } + private static MethodInfo? s_unboxPointer; + internal static MethodInfo UnboxPointer => s_unboxPointer ??= typeof(Pointer).GetMethod("Unbox")!; + private static PropertyInfo? s_nodeTypeProperty; internal static PropertyInfo NodeTypeProperty { @@ -198,9 +205,11 @@ internal static PropertyInfo NodeTypeProperty } } + private static ConstructorInfo? s_serializationExceptionCtor; + internal static ConstructorInfo SerializationExceptionCtor => s_serializationExceptionCtor ??= typeof(SerializationException).GetConstructor(new Type[] { typeof(string) })!; + private static ConstructorInfo? s_extensionDataObjectCtor; - internal static ConstructorInfo ExtensionDataObjectCtor => s_extensionDataObjectCtor ??= - typeof(ExtensionDataObject).GetConstructor(Globals.ScanAllMembers, Type.EmptyTypes)!; + internal static ConstructorInfo ExtensionDataObjectCtor => s_extensionDataObjectCtor ??= typeof(ExtensionDataObject).GetConstructor(Globals.ScanAllMembers, Type.EmptyTypes)!; private static ConstructorInfo? s_hashtableCtor; internal static ConstructorInfo HashtableCtor @@ -529,9 +538,24 @@ internal static MethodInfo AddNewObjectWithIdMethod } } + private static MethodInfo? s_replaceDeserializedObjectMethod; + internal static MethodInfo ReplaceDeserializedObjectMethod + { + get + { + if (s_replaceDeserializedObjectMethod == null) + { + s_replaceDeserializedObjectMethod = typeof(XmlObjectSerializerReadContext).GetMethod("ReplaceDeserializedObject", Globals.ScanAllMembers); + Debug.Assert(s_replaceDeserializedObjectMethod != null); + } + return s_replaceDeserializedObjectMethod; + } + } + private static MethodInfo? s_getExistingObjectMethod; internal static MethodInfo GetExistingObjectMethod { + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] get { if (s_getExistingObjectMethod == null) @@ -613,20 +637,6 @@ internal static MethodInfo GetArrayLengthMethod } } - private static MethodInfo? s_createSerializationExceptionMethod; - internal static MethodInfo CreateSerializationExceptionMethod - { - get - { - if (s_createSerializationExceptionMethod == null) - { - s_createSerializationExceptionMethod = typeof(XmlObjectSerializerReadContext).GetMethod("CreateSerializationException", Globals.ScanAllMembers, new Type[] { typeof(string) }); - Debug.Assert(s_createSerializationExceptionMethod != null); - } - return s_createSerializationExceptionMethod; - } - } - private static MethodInfo? s_readSerializationInfoMethod; internal static MethodInfo ReadSerializationInfoMethod { @@ -821,21 +831,6 @@ internal static MethodInfo WriteISerializableMethod } } - - private static MethodInfo? s_isMemberTypeSameAsMemberValue; - internal static MethodInfo IsMemberTypeSameAsMemberValue - { - get - { - if (s_isMemberTypeSameAsMemberValue == null) - { - s_isMemberTypeSameAsMemberValue = typeof(XmlObjectSerializerWriteContext).GetMethod("IsMemberTypeSameAsMemberValue", Globals.ScanAllMembers, new Type[] { typeof(object), typeof(Type) }); - Debug.Assert(s_isMemberTypeSameAsMemberValue != null); - } - return s_isMemberTypeSameAsMemberValue; - } - } - private static MethodInfo? s_writeExtensionDataMethod; internal static MethodInfo WriteExtensionDataMethod { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatReaderGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatReaderGenerator.cs index 50ed91a7377f49..5144ee966b5edb 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatReaderGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatReaderGenerator.cs @@ -2,17 +2,18 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Xml; -using System.Xml.Schema; -using System.Reflection; -using System.Reflection.Emit; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Security; +using System.Reflection; +using System.Reflection.Emit; using System.Runtime.CompilerServices; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.DataContracts; +using System.Security; +using System.Xml; +using System.Xml.Schema; namespace System.Runtime.Serialization { @@ -83,7 +84,7 @@ public XmlFormatClassReaderDelegate GenerateClassReader(ClassDataContract classC bool memberAccessFlag = classContract.RequiresMemberAccessForRead(null); try { - _ilg.BeginMethod("Read" + classContract.StableName.Name + "FromXml", Globals.TypeOfXmlFormatClassReaderDelegate, memberAccessFlag); + _ilg.BeginMethod("Read" + classContract.XmlName.Name + "FromXml", Globals.TypeOfXmlFormatClassReaderDelegate, memberAccessFlag); } catch (SecurityException securityException) { @@ -118,14 +119,14 @@ public XmlFormatClassReaderDelegate GenerateClassReader(ClassDataContract classC ReadClass(classContract); } - _ = InvokeFactoryMethod(classContract, objectId); + bool isFactoryType = InvokeFactoryMethod(classContract, objectId); if (Globals.TypeOfIDeserializationCallback.IsAssignableFrom(classContract.UnderlyingType)) { _ilg.Call(_objectLocal, XmlFormatGeneratorStatics.OnDeserializationMethod, null); } InvokeOnDeserialized(classContract); - if (objectId == null) + if (objectId == null || !isFactoryType) { _ilg.Load(_objectLocal); @@ -145,12 +146,6 @@ public XmlFormatClassReaderDelegate GenerateClassReader(ClassDataContract classC _ilg.Call(XmlFormatGeneratorStatics.GetMemoryStreamMethod); _ilg.ConvertValue(Globals.TypeOfMemoryStream, _ilg.CurrentMethod.ReturnType); } - //Copy the KeyValuePairAdapter to a KeyValuePair. - else if (classContract.IsKeyValuePairAdapter) - { - _ilg.Call(classContract.GetKeyValuePairMethodInfo); - _ilg.ConvertValue(Globals.TypeOfKeyValuePair.MakeGenericType(classContract.KeyValuePairGenericArguments), _ilg.CurrentMethod.ReturnType); - } else { _ilg.ConvertValue(_objectLocal.LocalType, _ilg.CurrentMethod.ReturnType); @@ -212,11 +207,11 @@ private CodeGenerator GenerateCollectionReaderHelper(CollectionDataContract coll { if (isGetOnlyCollection) { - _ilg.BeginMethod("Read" + collectionContract.StableName.Name + "FromXml" + "IsGetOnly", Globals.TypeOfXmlFormatGetOnlyCollectionReaderDelegate, memberAccessFlag); + _ilg.BeginMethod("Read" + collectionContract.XmlName.Name + "FromXml" + "IsGetOnly", Globals.TypeOfXmlFormatGetOnlyCollectionReaderDelegate, memberAccessFlag); } else { - _ilg.BeginMethod("Read" + collectionContract.StableName.Name + "FromXml" + string.Empty, Globals.TypeOfXmlFormatCollectionReaderDelegate, memberAccessFlag); + _ilg.BeginMethod("Read" + collectionContract.XmlName.Name + "FromXml" + string.Empty, Globals.TypeOfXmlFormatCollectionReaderDelegate, memberAccessFlag); } } catch (SecurityException securityException) @@ -287,8 +282,8 @@ private void InvokeOnDeserializing(ClassDataContract classContract) Debug.Assert(_objectLocal != null); Debug.Assert(_objectType != null); - if (classContract.BaseContract != null) - InvokeOnDeserializing(classContract.BaseContract); + if (classContract.BaseClassContract != null) + InvokeOnDeserializing(classContract.BaseClassContract); if (classContract.OnDeserializing != null) { _ilg.LoadAddress(_objectLocal); @@ -304,8 +299,8 @@ private void InvokeOnDeserialized(ClassDataContract classContract) Debug.Assert(_objectLocal != null); Debug.Assert(_objectType != null); - if (classContract.BaseContract != null) - InvokeOnDeserialized(classContract.BaseContract); + if (classContract.BaseClassContract != null) + InvokeOnDeserialized(classContract.BaseClassContract); if (classContract.OnDeserialized != null) { _ilg.LoadAddress(_objectLocal); @@ -355,7 +350,7 @@ private void ReadClass(ClassDataContract classContract) MethodInfo? extensionDataSetMethod = currentContract.ExtensionDataSetMethod; if (extensionDataSetMethod != null) _ilg.Call(_objectLocal, extensionDataSetMethod, extensionDataLocal); - currentContract = currentContract.BaseContract; + currentContract = currentContract.BaseClassContract; } } else @@ -384,9 +379,16 @@ private void ReadMembers(ClassDataContract classContract, LocalBuilder? extensio _ilg.Call(_contextArg, XmlFormatGeneratorStatics.GetMemberIndexWithRequiredMembersMethod, _xmlReaderArg, _memberNamesArg, _memberNamespacesArg, memberIndexLocal, requiredIndexLocal, extensionDataLocal); else _ilg.Call(_contextArg, XmlFormatGeneratorStatics.GetMemberIndexMethod, _xmlReaderArg, _memberNamesArg, _memberNamespacesArg, memberIndexLocal, extensionDataLocal); - Label[] memberLabels = _ilg.Switch(memberCount); - ReadMembers(classContract, requiredMembers, memberLabels, memberIndexLocal, requiredIndexLocal); - _ilg.EndSwitch(); + if (memberCount > 0) + { + Label[] memberLabels = _ilg.Switch(memberCount); + ReadMembers(classContract, requiredMembers, memberLabels, memberIndexLocal, requiredIndexLocal); + _ilg.EndSwitch(); + } + else + { + _ilg.Pop(); + } _ilg.EndFor(); if (hasRequiredMembers) { @@ -402,7 +404,7 @@ private int ReadMembers(ClassDataContract classContract, bool[] requiredMembers, Debug.Assert(_objectLocal != null); Debug.Assert(_objectType != null); - int memberCount = (classContract.BaseContract == null) ? 0 : ReadMembers(classContract.BaseContract, requiredMembers, + int memberCount = (classContract.BaseClassContract == null) ? 0 : ReadMembers(classContract.BaseClassContract, requiredMembers, memberLabels, memberIndexLocal, requiredIndexLocal); for (int i = 0; i < classContract.Members!.Count; i++, memberCount++) @@ -428,12 +430,12 @@ private int ReadMembers(ClassDataContract classContract, bool[] requiredMembers, value = _ilg.DeclareLocal(memberType, dataMember.Name + "Value"); _ilg.Stloc(value); _ilg.Call(_contextArg, XmlFormatGeneratorStatics.StoreCollectionMemberInfoMethod, value); - ReadValue(memberType, dataMember.Name, classContract.StableName.Namespace); + ReadValue(memberType, dataMember.Name, classContract.XmlName.Namespace); } else { _ilg.Call(_contextArg, XmlFormatGeneratorStatics.ResetCollectionMemberInfoMethod); - value = ReadValue(memberType, dataMember.Name, classContract.StableName.Namespace); + value = ReadValue(memberType, dataMember.Name, classContract.XmlName.Namespace); _ilg.LoadAddress(_objectLocal); _ilg.ConvertAddress(_objectLocal.LocalType, _objectType); _ilg.Ldloc(value); @@ -460,7 +462,7 @@ private bool[] GetRequiredMembers(ClassDataContract contract, out int firstRequi private int GetRequiredMembers(ClassDataContract contract, bool[] requiredMembers) { - int memberCount = (contract.BaseContract == null) ? 0 : GetRequiredMembers(contract.BaseContract, requiredMembers); + int memberCount = (contract.BaseClassContract == null) ? 0 : GetRequiredMembers(contract.BaseClassContract, requiredMembers); List members = contract.Members!; for (int i = 0; i < members.Count; i++, memberCount++) { @@ -580,14 +582,18 @@ private void InternalDeserialize(LocalBuilder value, Type type, string name, str { _ilg.Load(_contextArg); _ilg.Load(_xmlReaderArg); - Type declaredType = type; + Type declaredType = type.IsPointer ? Globals.TypeOfReflectionPointer : type; _ilg.Load(DataContract.GetId(declaredType.TypeHandle)); _ilg.Ldtoken(declaredType); _ilg.Load(name); _ilg.Load(ns); _ilg.Call(XmlFormatGeneratorStatics.InternalDeserializeMethod); - _ilg.ConvertValue(Globals.TypeOfObject, type); + if (type.IsPointer) + _ilg.Call(XmlFormatGeneratorStatics.UnboxPointer); + else + _ilg.ConvertValue(Globals.TypeOfObject, type); + _ilg.Stloc(value); } @@ -640,7 +646,7 @@ private void ReadCollection(CollectionDataContract collectionContract) } } string itemName = collectionContract.ItemName; - string itemNs = collectionContract.StableName.Namespace; + string itemNs = collectionContract.XmlName.Namespace; _objectLocal = _ilg.DeclareLocal(type, "objectDeserialized"); if (!isArray) @@ -755,7 +761,7 @@ private void ReadGetOnlyCollection(CollectionDataContract collectionContract) Type itemType = collectionContract.ItemType; bool isArray = (collectionContract.Kind == CollectionKind.Array); string itemName = collectionContract.ItemName; - string itemNs = collectionContract.StableName.Namespace; + string itemNs = collectionContract.XmlName.Namespace; _objectLocal = _ilg.DeclareLocal(type, "objectDeserialized"); _ilg.Load(_contextArg); @@ -821,7 +827,7 @@ private bool TryReadPrimitiveArray(Type type, Type itemType, LocalBuilder size) return false; string? readArrayMethod = null; - switch (itemType.GetTypeCode()) + switch (Type.GetTypeCode(itemType)) { case TypeCode.Boolean: readArrayMethod = "TryReadBooleanArray"; @@ -953,19 +959,15 @@ private void ThrowUnexpectedStateException(XmlNodeType expectedState) _ilg.Throw(); } - private void ThrowValidationException(string msg, params object[] values) + private void ThrowValidationException(string msg) { - { - _ilg.Load(msg); - } + _ilg.Load(msg); ThrowValidationException(); } private void ThrowValidationException() { - //SerializationException is internal in SL and so cannot be directly invoked from DynamicMethod - //So use helper function to create SerializationException - _ilg.Call(XmlFormatGeneratorStatics.CreateSerializationExceptionMethod); + _ilg.New(XmlFormatGeneratorStatics.SerializationExceptionCtor); _ilg.Throw(); } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatWriterGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatWriterGenerator.cs index b510882ffec99d..2d49e4b023b475 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatWriterGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatWriterGenerator.cs @@ -2,17 +2,18 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Xml; -using System.Xml.Schema; -using System.Reflection; -using System.Reflection.Emit; using System.Collections; using System.Collections.Generic; -using System.Globalization; -using System.Security; -using System.Runtime.CompilerServices; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization.DataContracts; +using System.Security; +using System.Xml; +using System.Xml.Schema; namespace System.Runtime.Serialization { @@ -78,7 +79,7 @@ internal XmlFormatClassWriterDelegate GenerateClassWriter(ClassDataContract clas bool memberAccessFlag = classContract.RequiresMemberAccessForWrite(null); try { - _ilg.BeginMethod("Write" + classContract.StableName.Name + "ToXml", Globals.TypeOfXmlFormatClassWriterDelegate, memberAccessFlag); + _ilg.BeginMethod("Write" + classContract.XmlName.Name + "ToXml", Globals.TypeOfXmlFormatClassWriterDelegate, memberAccessFlag); } catch (SecurityException securityException) { @@ -92,6 +93,10 @@ internal XmlFormatClassWriterDelegate GenerateClassWriter(ClassDataContract clas } } InitArgs(classContract.UnderlyingType); + if (classContract.IsReadOnlyContract) + { + ThrowIfCannotSerializeReadOnlyTypes(classContract); + } WriteClass(classContract); return (XmlFormatClassWriterDelegate)_ilg.EndMethod(); } @@ -116,7 +121,7 @@ internal XmlFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionDa bool memberAccessFlag = collectionContract.RequiresMemberAccessForWrite(null); try { - _ilg.BeginMethod("Write" + collectionContract.StableName.Name + "ToXml", Globals.TypeOfXmlFormatCollectionWriterDelegate, memberAccessFlag); + _ilg.BeginMethod("Write" + collectionContract.XmlName.Name + "ToXml", Globals.TypeOfXmlFormatCollectionWriterDelegate, memberAccessFlag); } catch (SecurityException securityException) { @@ -130,6 +135,10 @@ internal XmlFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionDa } } InitArgs(collectionContract.UnderlyingType); + if (collectionContract.IsReadOnlyContract) + { + ThrowIfCannotSerializeReadOnlyTypes(collectionContract); + } WriteCollection(collectionContract); return (XmlFormatCollectionWriterDelegate)_ilg.EndMethod(); } @@ -160,13 +169,6 @@ private void InitArgs(Type objType) _ilg.ConvertValue(objectArg.ArgType, Globals.TypeOfMemoryStream); _ilg.Call(XmlFormatGeneratorStatics.GetMemoryStreamAdapterMethod); } - //Copy the KeyValuePair to a KeyValuePairAdapter. - else if (objType.IsGenericType && objType.GetGenericTypeDefinition() == Globals.TypeOfKeyValuePairAdapter) - { - ClassDataContract dc = (ClassDataContract)DataContract.GetDataContract(objType); - _ilg.ConvertValue(objectArg.ArgType, Globals.TypeOfKeyValuePair.MakeGenericType(dc.KeyValuePairGenericArguments!)); - _ilg.New(dc.KeyValuePairAdapterConstructorInfo!); - } else { _ilg.ConvertValue(objectArg.ArgType, objType); @@ -174,11 +176,32 @@ private void InitArgs(Type objType) _ilg.Stloc(_objectLocal); } + private void ThrowIfCannotSerializeReadOnlyTypes(ClassDataContract classContract) + { + ThrowIfCannotSerializeReadOnlyTypes(XmlFormatGeneratorStatics.ClassSerializationExceptionMessageProperty); + } + + private void ThrowIfCannotSerializeReadOnlyTypes(CollectionDataContract classContract) + { + ThrowIfCannotSerializeReadOnlyTypes(XmlFormatGeneratorStatics.CollectionSerializationExceptionMessageProperty); + } + + private void ThrowIfCannotSerializeReadOnlyTypes(PropertyInfo serializationExceptionMessageProperty) + { + _ilg.Load(_contextArg); + _ilg.LoadMember(XmlFormatGeneratorStatics.SerializeReadOnlyTypesProperty); + _ilg.IfNot(); + _ilg.Load(_dataContractArg); + _ilg.LoadMember(serializationExceptionMessageProperty); + _ilg.Load(null); + _ilg.Call(XmlFormatGeneratorStatics.ThrowInvalidDataContractExceptionMethod); + _ilg.EndIf(); + } private void InvokeOnSerializing(ClassDataContract classContract) { - if (classContract.BaseContract != null) - InvokeOnSerializing(classContract.BaseContract); + if (classContract.BaseClassContract != null) + InvokeOnSerializing(classContract.BaseClassContract); if (classContract.OnSerializing != null) { _ilg.LoadAddress(_objectLocal); @@ -190,8 +213,8 @@ private void InvokeOnSerializing(ClassDataContract classContract) private void InvokeOnSerialized(ClassDataContract classContract) { - if (classContract.BaseContract != null) - InvokeOnSerialized(classContract.BaseContract); + if (classContract.BaseClassContract != null) + InvokeOnSerialized(classContract.BaseClassContract); if (classContract.OnSerialized != null) { _ilg.LoadAddress(_objectLocal); @@ -257,8 +280,8 @@ private void WriteClass(ClassDataContract classContract) [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private int WriteMembers(ClassDataContract classContract, LocalBuilder? extensionDataLocal, ClassDataContract derivedMostClassContract) { - int memberCount = (classContract.BaseContract == null) ? 0 : - WriteMembers(classContract.BaseContract, extensionDataLocal, derivedMostClassContract); + int memberCount = (classContract.BaseClassContract == null) ? 0 : + WriteMembers(classContract.BaseClassContract, extensionDataLocal, derivedMostClassContract); LocalBuilder namespaceLocal = _ilg.DeclareLocal(typeof(XmlDictionaryString), "ns"); if (_contractNamespacesLocal == null) @@ -559,7 +582,7 @@ private bool TryWritePrimitiveArray(Type type, Type itemType, LocalBuilder value return false; string? writeArrayMethod = null; - switch (itemType.GetTypeCode()) + switch (Type.GetTypeCode(itemType)) { case TypeCode.Boolean: writeArrayMethod = "WriteBooleanArray"; @@ -601,6 +624,15 @@ private bool TryWritePrimitiveArray(Type type, Type itemType, LocalBuilder value private void WriteValue(LocalBuilder memberValue, bool writeXsiType) { Type memberType = memberValue.LocalType; + if (memberType.IsPointer) + { + _ilg.Load(memberValue); + _ilg.Load(memberType); + _ilg.Call(XmlFormatGeneratorStatics.BoxPointer); + memberType = Globals.TypeOfReflectionPointer; + memberValue = _ilg.DeclareLocal(memberType, "memberValueRefPointer"); + _ilg.Store(memberValue); + } bool isNullableOfT = (memberType.IsGenericType && memberType.GetGenericTypeDefinition() == Globals.TypeOfNullable); if (memberType.IsValueType && !isNullableOfT) @@ -670,10 +702,12 @@ private void InternalSerialize(MethodInfo methodInfo, LocalBuilder memberValue, _ilg.Load(_xmlWriterArg); _ilg.Load(memberValue); _ilg.ConvertValue(memberValue.LocalType, Globals.TypeOfObject); - //In SL GetTypeHandle throws MethodAccessException as its internal and extern. - //So as a workaround, call XmlObjectSerializerWriteContext.IsMemberTypeSameAsMemberValue that - //does the actual comparison and returns the bool value we care. - _ilg.Call(null, XmlFormatGeneratorStatics.IsMemberTypeSameAsMemberValue, memberValue, memberType); + LocalBuilder typeHandleValue = _ilg.DeclareLocal(typeof(RuntimeTypeHandle), "typeHandleValue"); + _ilg.Call(null, typeof(Type).GetMethod("GetTypeHandle")!, memberValue); + _ilg.Stloc(typeHandleValue); + _ilg.LoadAddress(typeHandleValue); + _ilg.Ldtoken(memberType); + _ilg.Call(typeof(RuntimeTypeHandle).GetMethod("Equals", new Type[] { typeof(RuntimeTypeHandle) })!); _ilg.Load(writeXsiType); _ilg.Load(DataContract.GetId(memberType.TypeHandle)); _ilg.Ldtoken(memberType); @@ -748,11 +782,11 @@ private static bool CheckIfMemberHasConflict(DataMember member, ClassDataContrac // Check for conflict with derived type members string name = member.Name; - string ns = classContract.StableName.Namespace; + string ns = classContract.XmlName.Namespace; ClassDataContract? currentContract = derivedMostClassContract; while (currentContract != null && currentContract != classContract) { - if (ns == currentContract.StableName.Namespace) + if (ns == currentContract.XmlName.Namespace) { List members = currentContract.Members!; for (int j = 0; j < members.Count; j++) @@ -761,7 +795,7 @@ private static bool CheckIfMemberHasConflict(DataMember member, ClassDataContrac return CheckIfConflictingMembersHaveDifferentTypes(members[j]); } } - currentContract = currentContract.BaseContract; + currentContract = currentContract.BaseClassContract; } return false; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializer.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializer.cs index 8ba00fd7041759..e656e9b259c83d 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializer.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializer.cs @@ -7,10 +7,12 @@ using System.Globalization; using System.IO; using System.Runtime.CompilerServices; +using System.Runtime.Serialization.DataContracts; using System.Security; using System.Text; using System.Xml; -using DataContractDictionary = System.Collections.Generic.Dictionary; + +using DataContractDictionary = System.Collections.Generic.Dictionary; namespace System.Runtime.Serialization { @@ -220,7 +222,7 @@ internal static bool CheckIfNeedsContractNsAtRoot(XmlDictionaryString? name, Xml if (name == null) return false; - if (contract.IsBuiltInDataContract || !contract.CanContainReferences) + if (contract.IsBuiltInDataContract || !contract.CanContainReferences || contract.IsISerializable) { return false; } @@ -378,12 +380,12 @@ internal static bool IsRootElement(XmlReaderDelegator reader, DataContract contr ClassDataContract? classContract = contract as ClassDataContract; if (classContract != null) - classContract = classContract.BaseContract; + classContract = classContract.BaseClassContract; while (classContract != null) { if (reader.IsStartElement(classContract.TopLevelElementName!, classContract.TopLevelElementNamespace!)) return true; - classContract = classContract.BaseContract; + classContract = classContract.BaseClassContract; } if (classContract == null) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerContext.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerContext.cs index 66ce7c6f8f8c06..57b3353cee2cb8 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerContext.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerContext.cs @@ -6,9 +6,11 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Runtime.Serialization.DataContracts; using System.Security; using System.Xml; -using DataContractDictionary = System.Collections.Generic.Dictionary; + +using DataContractDictionary = System.Collections.Generic.Dictionary; namespace System.Runtime.Serialization { @@ -46,19 +48,13 @@ internal XmlObjectSerializerContext(XmlObjectSerializer serializer, int maxItems internal XmlObjectSerializerContext(DataContractSerializer serializer, DataContract rootTypeDataContract, DataContractResolver? dataContractResolver) : this(serializer, serializer.MaxItemsInObjectGraph, - default(StreamingContext), + new StreamingContext(StreamingContextStates.All), serializer.IgnoreExtensionDataObject, dataContractResolver ) { this.rootTypeDataContract = rootTypeDataContract; - this.serializerKnownTypeList = serializer.knownTypeList; - } - - - internal virtual SerializationMode Mode - { - get { return SerializationMode.SharedContract; } + this.serializerKnownTypeList = serializer._knownTypeList; } internal virtual bool IsGetOnlyCollection @@ -67,7 +63,6 @@ internal virtual bool IsGetOnlyCollection set { } } - internal StreamingContext GetStreamingContext() { return _streamingContext; @@ -109,11 +104,11 @@ internal virtual DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type { if (IsGetOnlyCollection) { - return DataContract.GetGetOnlyCollectionDataContract(DataContract.GetId(typeHandle), typeHandle, type, Mode); + return DataContract.GetGetOnlyCollectionDataContract(DataContract.GetId(typeHandle), typeHandle, type); } else { - return DataContract.GetDataContract(typeHandle, type, Mode); + return DataContract.GetDataContract(typeHandle, type); } } @@ -135,11 +130,11 @@ internal virtual DataContract GetDataContract(int id, RuntimeTypeHandle typeHand { if (IsGetOnlyCollection) { - return DataContract.GetGetOnlyCollectionDataContract(id, typeHandle, null /*type*/, Mode); + return DataContract.GetGetOnlyCollectionDataContract(id, typeHandle, null /*type*/); } else { - return DataContract.GetDataContract(id, typeHandle, Mode); + return DataContract.GetDataContract(id, typeHandle); } } @@ -201,7 +196,7 @@ internal virtual DataContractDictionary? SerializerKnownDataContracts internal bool IsKnownType(DataContract dataContract, DataContractDictionary? knownDataContracts, Type? declaredType) { bool knownTypesAddedInCurrentScope = false; - if (knownDataContracts != null) + if (knownDataContracts?.Count > 0) { scopedKnownTypes.Push(knownDataContracts); knownTypesAddedInCurrentScope = true; @@ -219,7 +214,7 @@ internal bool IsKnownType(DataContract dataContract, DataContractDictionary? kno [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal bool IsKnownType(DataContract dataContract, Type? declaredType) { - DataContract? knownContract = ResolveDataContractFromKnownTypes(dataContract.StableName.Name, dataContract.StableName.Namespace, null /*memberTypeContract*/, declaredType); + DataContract? knownContract = ResolveDataContractFromKnownTypes(dataContract.XmlName.Name, dataContract.XmlName.Namespace, null /*memberTypeContract*/, declaredType); return knownContract != null && knownContract.UnderlyingType == dataContract.UnderlyingType; } @@ -227,7 +222,7 @@ internal bool IsKnownType(DataContract dataContract, Type? declaredType) internal Type? ResolveNameFromKnownTypes(XmlQualifiedName typeName) { DataContract? dataContract = ResolveDataContractFromKnownTypes(typeName); - return dataContract?.UnderlyingType; + return dataContract?.OriginalUnderlyingType; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -254,13 +249,13 @@ internal bool IsKnownType(DataContract dataContract, Type? declaredType) { if (memberTypeContract != null && !memberTypeContract.UnderlyingType.IsInterface - && memberTypeContract.StableName == qname) + && memberTypeContract.XmlName == qname) { dataContract = memberTypeContract; } if (dataContract == null && rootTypeDataContract != null) { - if (rootTypeDataContract.StableName == qname) + if (rootTypeDataContract.XmlName == qname) dataContract = rootTypeDataContract; else dataContract = ResolveDataContractFromRootDataContract(qname); @@ -276,7 +271,7 @@ internal bool IsKnownType(DataContract dataContract, Type? declaredType) while (collectionContract != null) { DataContract itemContract = GetDataContract(GetSurrogatedType(collectionContract.ItemType)); - if (itemContract.StableName == typeQName) + if (itemContract.XmlName == typeQName) { return itemContract; } @@ -284,23 +279,5 @@ internal bool IsKnownType(DataContract dataContract, Type? declaredType) } return null; } - - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal void PushKnownTypes(DataContract dc) - { - if (dc != null && dc.KnownDataContracts != null) - { - scopedKnownTypes.Push(dc.KnownDataContracts); - } - } - - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal void PopKnownTypes(DataContract dc) - { - if (dc != null && dc.KnownDataContracts != null) - { - scopedKnownTypes.Pop(); - } - } } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContext.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContext.cs index 09fb6e269353de..ed546bc93ee99a 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContext.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContext.cs @@ -6,11 +6,13 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.DataContracts; using System.Security; using System.Text; using System.Xml; using System.Xml.Serialization; -using DataContractDictionary = System.Collections.Generic.Dictionary; + +using DataContractDictionary = System.Collections.Generic.Dictionary; namespace System.Runtime.Serialization { @@ -21,6 +23,7 @@ internal class XmlObjectSerializerReadContext : XmlObjectSerializerContext private XmlSerializableReader? _xmlSerializableReader; private XmlDocument? _xmlDocument; private Attributes? _attributesInXmlData; + private XmlReaderDelegator? _extensionDataReader; private object? _getOnlyCollectionValue; private bool _isGetOnlyCollection; @@ -89,7 +92,7 @@ internal XmlObjectSerializerReadContext(DataContractSerializer serializer, DataC } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal virtual object? InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, string name, string ns) + internal virtual object? InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, string? name, string? ns) { DataContract dataContract = GetDataContract(declaredType); return InternalDeserialize(xmlReader, name, ns, declaredType, ref dataContract); @@ -102,6 +105,7 @@ internal XmlObjectSerializerReadContext(DataContractSerializer serializer, DataC return InternalDeserialize(xmlReader, name, ns, declaredType, ref dataContract); } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] protected bool TryHandleNullOrRef(XmlReaderDelegator reader, Type declaredType, string? name, string? ns, ref object? retObj) { ReadAttributes(reader); @@ -135,7 +139,7 @@ protected bool TryHandleNullOrRef(XmlReaderDelegator reader, Type declaredType, return retObj; bool knownTypesAddedInCurrentScope = false; - if (dataContract.KnownDataContracts != null) + if (dataContract.KnownDataContracts?.Count > 0) { scopedKnownTypes.Push(dataContract.KnownDataContracts); knownTypesAddedInCurrentScope = true; @@ -194,7 +198,7 @@ private bool ReplaceScopedKnownTypesTop(DataContractDictionary? knownDataContrac scopedKnownTypes.Pop(); knownTypesAddedInCurrentScope = false; } - if (knownDataContracts != null) + if (knownDataContracts?.Count > 0) { scopedKnownTypes.Push(knownDataContracts); knownTypesAddedInCurrentScope = true; @@ -331,6 +335,8 @@ internal void AddNewObjectWithId(string id, object? obj) { if (id != Globals.NewObjectId) DeserializedObjects.Add(id, obj); + if (_extensionDataReader?.UnderlyingExtensionDataReader != null) + _extensionDataReader.UnderlyingExtensionDataReader.SetDeserializedValue(obj); } public void ReplaceDeserializedObject(string id, object? oldObj, object? newObj) @@ -355,13 +361,22 @@ public void ReplaceDeserializedObject(string id, object? oldObj, object? newObj) DeserializedObjects.Remove(id); DeserializedObjects.Add(id, newObj); } + if (_extensionDataReader?.UnderlyingExtensionDataReader != null) + _extensionDataReader.UnderlyingExtensionDataReader.SetDeserializedValue(newObj); } - internal object GetExistingObject(string id, Type? type, string? name, string? ns) + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal object? GetExistingObject(string id, Type? type, string? name, string? ns) { object? retObj = DeserializedObjects.GetObject(id); + if (retObj == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.DeserializedObjectWithIdNotFound, id))); + + if (retObj is IDataNode dataNode) + { + retObj = (dataNode.Value != null && dataNode.IsFinalValue) ? dataNode.Value : DeserializeFromExtensionData(dataNode, type ?? dataNode.DataType, name, ns); + } return retObj; } @@ -392,6 +407,18 @@ public object GetRealObject(IObjectReference obj, string id) return realObj; } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private object? DeserializeFromExtensionData(IDataNode dataNode, Type type, string? name, string? ns) + { + // _extensionDataRead is only ever created here, so we know the casting property 'UnderlyingExtensionDataReader' won't be null. + _extensionDataReader ??= CreateReaderDelegatorForReader(new ExtensionDataReader(this)); + _extensionDataReader.UnderlyingExtensionDataReader!.SetDataNode(dataNode, name, ns); + object? retObj = InternalDeserialize(_extensionDataReader, type, name, ns); + dataNode.Clear(); + _extensionDataReader.UnderlyingExtensionDataReader!.Reset(); + return retObj; + } + internal static void Read(XmlReaderDelegator xmlReader) { if (!xmlReader.Read()) @@ -842,9 +869,11 @@ private ISerializableDataNode ReadUnknownISerializableData(XmlReaderDelegator xm private IDataNode ReadUnknownXmlData(XmlReaderDelegator xmlReader, string? dataContractName, string? dataContractNamespace) { - XmlDataNode dataNode = new XmlDataNode(); + XmlDataNode dataNode = new XmlDataNode() + { + OwnerDocument = Document + }; InitializeExtensionDataNode(dataNode, dataContractName, dataContractNamespace); - dataNode.OwnerDocument = Document; if (xmlReader.NodeType == XmlNodeType.EndElement) return dataNode; @@ -957,9 +986,11 @@ private IDataNode ReadAndResolveUnknownXmlData(XmlReaderDelegator xmlReader, IDi return ReadUnknownClassData(CreateReaderOverChildNodes(xmlAttributes, xmlChildNodes), dataContractName, dataContractNamespace); else { - XmlDataNode dataNode = new XmlDataNode(); + XmlDataNode dataNode = new XmlDataNode() + { + OwnerDocument = Document + }; InitializeExtensionDataNode(dataNode, dataContractName, dataContractNamespace); - dataNode.OwnerDocument = Document; dataNode.XmlChildNodes = xmlChildNodes; dataNode.XmlAttributes = xmlAttributes; return dataNode; @@ -983,14 +1014,14 @@ private static bool IsContentNode(XmlNodeType nodeType) internal XmlReaderDelegator CreateReaderOverChildNodes(IList? xmlAttributes, IList xmlChildNodes) { - XmlNode wrapperElement = CreateWrapperXmlElement(Document, xmlAttributes, xmlChildNodes, null, null, null); + XmlElement wrapperElement = CreateWrapperXmlElement(Document, xmlAttributes, xmlChildNodes, null, null, null); XmlReaderDelegator nodeReader = CreateReaderDelegatorForReader(new XmlNodeReader(wrapperElement)); nodeReader.MoveToContent(); Read(nodeReader); return nodeReader; } - internal static XmlNode CreateWrapperXmlElement(XmlDocument document, IList? xmlAttributes, IList xmlChildNodes, string? prefix, string? localName, string? ns) + internal static XmlElement CreateWrapperXmlElement(XmlDocument document, IList? xmlAttributes, IList? xmlChildNodes, string? prefix, string? localName, string? ns) { localName ??= "wrapper"; ns ??= string.Empty; @@ -1026,12 +1057,6 @@ internal static Exception CreateUnexpectedStateException(XmlNodeType expectedSta return XmlObjectSerializer.CreateSerializationExceptionWithReaderDetails(SR.Format(SR.ExpectingState, expectedState), xmlReader); } - //Silverlight only helper function to create SerializationException - internal static Exception CreateSerializationException(string message) - { - return XmlObjectSerializer.CreateSerializationException(message); - } - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] protected virtual object? ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContextComplex.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContextComplex.cs index 53f4021549ec10..7458857221d20a 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContextComplex.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContextComplex.cs @@ -1,22 +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.Collections; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Runtime.Serialization.DataContracts; namespace System.Runtime.Serialization { internal class XmlObjectSerializerReadContextComplex : XmlObjectSerializerReadContext { private readonly bool _preserveObjectReferences; - private readonly SerializationMode _mode; private readonly ISerializationSurrogateProvider? _serializationSurrogateProvider; internal XmlObjectSerializerReadContextComplex(DataContractSerializer serializer, DataContract rootTypeDataContract, DataContractResolver? dataContractResolver) : base(serializer, rootTypeDataContract, dataContractResolver) { - _mode = SerializationMode.SharedContract; _preserveObjectReferences = serializer.PreserveObjectReferences; _serializationSurrogateProvider = serializer.SerializationSurrogateProvider; } @@ -26,99 +26,31 @@ internal XmlObjectSerializerReadContextComplex(XmlObjectSerializer serializer, i { } - internal override SerializationMode Mode - { - get { return _mode; } - } - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal override object? InternalDeserialize(XmlReaderDelegator xmlReader, int declaredTypeID, RuntimeTypeHandle declaredTypeHandle, string name, string ns) { - if (_mode == SerializationMode.SharedContract) - { - if (_serializationSurrogateProvider == null) - return base.InternalDeserialize(xmlReader, declaredTypeID, declaredTypeHandle, name, ns); - else - return InternalDeserializeWithSurrogate(xmlReader, Type.GetTypeFromHandle(declaredTypeHandle)!, null /*surrogateDataContract*/, name, ns); - } + if (_serializationSurrogateProvider == null) + return base.InternalDeserialize(xmlReader, declaredTypeID, declaredTypeHandle, name, ns); else - { - return InternalDeserializeInSharedTypeMode(xmlReader, declaredTypeID, Type.GetTypeFromHandle(declaredTypeHandle)!, name, ns); - } + return InternalDeserializeWithSurrogate(xmlReader, Type.GetTypeFromHandle(declaredTypeHandle)!, null /*surrogateDataContract*/, name, ns); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal override object? InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, string name, string ns) + internal override object? InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, string? name, string? ns) { - if (_mode == SerializationMode.SharedContract) - { - if (_serializationSurrogateProvider == null) - return base.InternalDeserialize(xmlReader, declaredType, name, ns); - else - return InternalDeserializeWithSurrogate(xmlReader, declaredType, null /*surrogateDataContract*/, name, ns); - } + if (_serializationSurrogateProvider == null) + return base.InternalDeserialize(xmlReader, declaredType, name, ns); else - { - return InternalDeserializeInSharedTypeMode(xmlReader, -1, declaredType, name, ns); - } + return InternalDeserializeWithSurrogate(xmlReader, declaredType, null /*surrogateDataContract*/, name, ns); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal override object? InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract? dataContract, string? name, string? ns) { - if (_mode == SerializationMode.SharedContract) - { - if (_serializationSurrogateProvider == null) - return base.InternalDeserialize(xmlReader, declaredType, dataContract, name, ns); - else - return InternalDeserializeWithSurrogate(xmlReader, declaredType, dataContract, name, ns); - } - else - { - return InternalDeserializeInSharedTypeMode(xmlReader, -1, declaredType, name, ns); - } - } - - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private object? InternalDeserializeInSharedTypeMode(XmlReaderDelegator xmlReader, int declaredTypeID, Type declaredType, string? name, string? ns) - { - Debug.Assert(attributes != null); - - object? retObj = null; - if (TryHandleNullOrRef(xmlReader, declaredType, name, ns, ref retObj)) - return retObj; - - DataContract dataContract; - string? assemblyName = attributes.ClrAssembly; - string? typeName = attributes.ClrType; - if (assemblyName != null && typeName != null) - { - Assembly assembly; - DataContract? tempDataContract = ResolveDataContractInSharedTypeMode(assemblyName, typeName, out assembly, out _); - if (tempDataContract == null) - { - if (assembly == null) - throw XmlObjectSerializer.CreateSerializationException(SR.Format(SR.AssemblyNotFound, assemblyName)); - - throw XmlObjectSerializer.CreateSerializationException(SR.Format(SR.ClrTypeNotFound, assembly.FullName, typeName)); - } - - dataContract = tempDataContract; - //Array covariance is not supported in XSD. If declared type is array, data is sent in format of base array - if (declaredType != null && declaredType.IsArray) - dataContract = (declaredTypeID < 0) ? GetDataContract(declaredType) : GetDataContract(declaredTypeID, declaredType.TypeHandle); - } + if (_serializationSurrogateProvider == null) + return base.InternalDeserialize(xmlReader, declaredType, dataContract, name, ns); else - { - if (assemblyName != null) - throw XmlObjectSerializer.CreateSerializationException(XmlObjectSerializer.TryAddLineInfo(xmlReader, SR.Format(SR.AttributeNotFound, Globals.SerializationNamespace, Globals.ClrTypeLocalName, xmlReader.NodeType, xmlReader.NamespaceURI, xmlReader.LocalName))); - else if (typeName != null) - throw XmlObjectSerializer.CreateSerializationException(XmlObjectSerializer.TryAddLineInfo(xmlReader, SR.Format(SR.AttributeNotFound, Globals.SerializationNamespace, Globals.ClrAssemblyLocalName, xmlReader.NodeType, xmlReader.NamespaceURI, xmlReader.LocalName))); - else if (declaredType == null) - throw XmlObjectSerializer.CreateSerializationException(XmlObjectSerializer.TryAddLineInfo(xmlReader, SR.Format(SR.AttributeNotFound, Globals.SerializationNamespace, Globals.ClrTypeLocalName, xmlReader.NodeType, xmlReader.NamespaceURI, xmlReader.LocalName))); - dataContract = (declaredTypeID < 0) ? GetDataContract(declaredType) : GetDataContract(declaredTypeID, declaredType.TypeHandle); - } - return ReadDataContractValue(dataContract, xmlReader); + return InternalDeserializeWithSurrogate(xmlReader, declaredType, dataContract, name, ns); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -141,44 +73,6 @@ internal override SerializationMode Mode return obj; } - private static Type ResolveDataContractTypeInSharedTypeMode(string assemblyName, string typeName, out Assembly assembly) - { - // The method is used only when _mode == SerializationMode.SharedType. - // _mode is set to SerializationMode.SharedType only when the context is for NetDataContractSerializer. - throw new PlatformNotSupportedException(SR.PlatformNotSupported_NetDataContractSerializer); - } - - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private DataContract? ResolveDataContractInSharedTypeMode(string assemblyName, string typeName, out Assembly assembly, out Type type) - { - type = ResolveDataContractTypeInSharedTypeMode(assemblyName, typeName, out assembly); - if (type != null) - { - return GetDataContract(type); - } - - return null; - } - - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - protected override DataContract? ResolveDataContractFromTypeName() - { - Debug.Assert(attributes != null); - - if (_mode == SerializationMode.SharedContract) - { - return base.ResolveDataContractFromTypeName(); - } - else - { - if (attributes.ClrAssembly != null && attributes.ClrType != null) - { - return ResolveDataContractInSharedTypeMode(attributes.ClrAssembly, attributes.ClrType, out _, out _); - } - } - return null; - } - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal override void CheckIfTypeSerializable(Type memberType, bool isMemberTypeSerializable) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContext.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContext.cs index 00abd388dc0920..9abdced84b6e2e 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContext.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContext.cs @@ -3,18 +3,20 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization.DataContracts; +using System.Security; using System.Text; using System.Xml; -using System.Collections.Generic; using System.Xml.Serialization; -using System.Security; -using System.Runtime.CompilerServices; + using ExtensionDataObject = System.Object; -using System.Diagnostics.CodeAnalysis; namespace System.Runtime.Serialization { @@ -122,7 +124,7 @@ internal void SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelega { if (OnHandleIsReference(xmlWriter, dataContract, obj)) return; - if (dataContract.KnownDataContracts != null) + if (dataContract.KnownDataContracts?.Count > 0) { scopedKnownTypes.Push(dataContract.KnownDataContracts); WriteDataContractValue(dataContract, xmlWriter, obj, declaredTypeHandle); @@ -140,7 +142,7 @@ internal virtual void SerializeWithXsiTypeAtTopLevel(DataContract dataContract, Debug.Assert(rootTypeDataContract != null); bool verifyKnownType = false; - Type declaredType = rootTypeDataContract.UnderlyingType; + Type declaredType = rootTypeDataContract.OriginalUnderlyingType; if (declaredType.IsInterface && CollectionDataContract.IsCollectionInterface(declaredType)) { @@ -224,7 +226,7 @@ internal bool OnHandleIsReference(XmlWriterDelegator xmlWriter, DataContract con protected void SerializeAndVerifyType(DataContract dataContract, XmlWriterDelegator xmlWriter, object obj, bool verifyKnownType, RuntimeTypeHandle declaredTypeHandle, Type declaredType) { bool knownTypesAddedInCurrentScope = false; - if (dataContract.KnownDataContracts != null) + if (dataContract.KnownDataContracts?.Count > 0) { scopedKnownTypes.Push(dataContract.KnownDataContracts); knownTypesAddedInCurrentScope = true; @@ -234,10 +236,10 @@ protected void SerializeAndVerifyType(DataContract dataContract, XmlWriterDelega { if (!IsKnownType(dataContract, declaredType)) { - DataContract? knownContract = ResolveDataContractFromKnownTypes(dataContract.StableName.Name, dataContract.StableName.Namespace, null /*memberTypeContract*/, declaredType); + DataContract? knownContract = ResolveDataContractFromKnownTypes(dataContract.XmlName.Name, dataContract.XmlName.Namespace, null /*memberTypeContract*/, declaredType); if (knownContract == null || knownContract.UnderlyingType != dataContract.UnderlyingType) { - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.DcTypeNotFoundOnSerialize, DataContract.GetClrTypeFullName(dataContract.UnderlyingType), dataContract.StableName.Name, dataContract.StableName.Namespace))); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.DcTypeNotFoundOnSerialize, DataContract.GetClrTypeFullName(dataContract.UnderlyingType), dataContract.XmlName.Name, dataContract.XmlName.Namespace))); } } } @@ -501,11 +503,12 @@ public void WriteISerializable(XmlWriterDelegator xmlWriter, ISerializable obj) var serInfo = new SerializationInfo(objType, XmlObjectSerializer.FormatterConverter /*!UnsafeTypeForwardingEnabled is always false*/); GetObjectData(obj, serInfo, GetStreamingContext()); - if (!UnsafeTypeForwardingEnabled && serInfo.AssemblyName == Globals.MscorlibAssemblyName) - { - // Throw if a malicious type tries to set its assembly name to "0" to get deserialized in mscorlib - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.ISerializableAssemblyNameSetToZero, DataContract.GetClrTypeFullName(obj.GetType())))); - } + // (!UnsafeTypeForwardingEnabled) is always false + //if (!UnsafeTypeForwardingEnabled && serInfo.AssemblyName == Globals.MscorlibAssemblyName) + //{ + // // Throw if a malicious type tries to set its assembly name to "0" to get deserialized in mscorlib + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.ISerializableAssemblyNameSetToZero, DataContract.GetClrTypeFullName(obj.GetType())))); + //} WriteSerializationInfo(xmlWriter, objType, serInfo); } @@ -526,7 +529,7 @@ internal void WriteSerializationInfo(XmlWriterDelegator xmlWriter, Type objType, else { string typeName, typeNs; - DataContract.GetDefaultStableName(serInfo.FullTypeName, out typeName, out typeNs); + DataContract.GetDefaultXmlName(serInfo.FullTypeName, out typeName, out typeNs); xmlWriter.WriteAttributeQualifiedName(Globals.SerPrefix, DictionaryGlobals.ISerializableFactoryTypeLocalName, DictionaryGlobals.SerializationNamespace, DataContract.GetClrTypeString(typeName), DataContract.GetClrTypeString(typeNs)); } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContextComplex.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContextComplex.cs index b1abf32a8ee00f..1d9ebcb41ddd09 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContextComplex.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContextComplex.cs @@ -3,28 +3,27 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization.DataContracts; +using System.Security; using System.Text; using System.Xml; -using System.Collections.Generic; -using System.Security; -using System.Runtime.CompilerServices; -using System.Diagnostics.CodeAnalysis; namespace System.Runtime.Serialization { internal class XmlObjectSerializerWriteContextComplex : XmlObjectSerializerWriteContext { private readonly ISerializationSurrogateProvider? _serializationSurrogateProvider; - private readonly SerializationMode _mode; internal XmlObjectSerializerWriteContextComplex(DataContractSerializer serializer, DataContract rootTypeDataContract, DataContractResolver? dataContractResolver) : base(serializer, rootTypeDataContract, dataContractResolver) { - _mode = SerializationMode.SharedContract; this.preserveObjectReferences = serializer.PreserveObjectReferences; _serializationSurrogateProvider = serializer.SerializationSurrogateProvider; } @@ -34,11 +33,6 @@ internal XmlObjectSerializerWriteContextComplex(XmlObjectSerializer serializer, { } - internal override SerializationMode Mode - { - get { return _mode; } - } - internal override bool WriteClrTypeInfo(XmlWriterDelegator xmlWriter, DataContract dataContract) { return false; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlReaderDelegator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlReaderDelegator.cs index 716e568dda351e..597b48e63ac14b 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlReaderDelegator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlReaderDelegator.cs @@ -1,11 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Xml; -using System.Globalization; using System.Collections.Generic; -using System.Xml.Serialization; using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Runtime.Serialization.DataContracts; +using System.Xml; +using System.Xml.Serialization; namespace System.Runtime.Serialization { @@ -55,7 +56,8 @@ internal string GetAttribute(int i) return reader.GetAttribute(i); } - internal static bool IsEmptyElement + [SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Conceptually, this property describes this instance. Callers should expect to have an instance on hand to 'ask' about this 'emtpy' circumstance.")] + internal bool IsEmptyElement { get { return false; } } @@ -496,7 +498,7 @@ internal virtual DateTime ReadElementContentAsDateTime() if (isEndOfEmptyElement) ThrowNotAtElement(); - return XmlConvert.ToDateTime(reader.ReadElementContentAsString(), XmlDateTimeSerializationMode.RoundtripKind); + return reader.ReadElementContentAsDateTime(); } internal virtual DateTime ReadContentAsDateTime() @@ -783,9 +785,12 @@ private static void CheckExpectedArrayLength(XmlObjectSerializerReadContext cont context.IncrementItemCount(arrayLength); } - protected static int GetArrayLengthQuota(XmlObjectSerializerReadContext context) + protected int GetArrayLengthQuota(XmlObjectSerializerReadContext context) { - return Math.Min(context.RemainingItemCount, int.MaxValue); + if (dictionaryReader?.Quotas == null) + return context.RemainingItemCount; + + return Math.Min(context.RemainingItemCount, dictionaryReader.Quotas.MaxArrayLength); } private static void CheckActualArrayLength(int expectedLength, int actualLength, XmlDictionaryString itemName, XmlDictionaryString itemNamespace) @@ -1039,8 +1044,7 @@ internal bool Normalized { get { - XmlTextReader? xmlTextReader = reader as XmlTextReader; - if (xmlTextReader == null) + if (reader is not XmlTextReader xmlTextReader) { IXmlTextParser? xmlTextParser = reader as IXmlTextParser; return (xmlTextParser == null) ? false : xmlTextParser.Normalized; @@ -1050,11 +1054,9 @@ internal bool Normalized } set { - XmlTextReader? xmlTextReader = reader as XmlTextReader; - if (xmlTextReader == null) + if (reader is not XmlTextReader xmlTextReader) { - IXmlTextParser? xmlTextParser = reader as IXmlTextParser; - if (xmlTextParser != null) + if (reader is IXmlTextParser xmlTextParser) xmlTextParser.Normalized = value; } else @@ -1066,8 +1068,7 @@ internal WhitespaceHandling WhitespaceHandling { get { - XmlTextReader? xmlTextReader = reader as XmlTextReader; - if (xmlTextReader == null) + if (reader is not XmlTextReader xmlTextReader) { IXmlTextParser? xmlTextParser = reader as IXmlTextParser; return (xmlTextParser == null) ? WhitespaceHandling.None : xmlTextParser.WhitespaceHandling; @@ -1077,11 +1078,9 @@ internal WhitespaceHandling WhitespaceHandling } set { - XmlTextReader? xmlTextReader = reader as XmlTextReader; - if (xmlTextReader == null) + if (reader is not XmlTextReader xmlTextReader) { - IXmlTextParser? xmlTextParser = reader as IXmlTextParser; - if (xmlTextParser != null) + if (reader is IXmlTextParser xmlTextParser) xmlTextParser.WhitespaceHandling = value; } else diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlSerializableReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlSerializableReader.cs index a6a66b6c44acd3..7d01b25c192b58 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlSerializableReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlSerializableReader.cs @@ -10,7 +10,7 @@ namespace System.Runtime.Serialization { - internal sealed class XmlSerializableReader : XmlReader, IXmlLineInfo + internal sealed class XmlSerializableReader : XmlReader, IXmlLineInfo, IXmlTextParser // IXmlTextParser (Normalized, WhitespaceHandling) was added. Is it ever used? { private XmlReaderDelegator _xmlReader = null!; // initialized in BeginRead private int _startDepth; @@ -79,8 +79,10 @@ public override void Close() public override string BaseURI { get { return InnerReader.BaseURI; } } public override bool IsEmptyElement { get { return InnerReader.IsEmptyElement; } } public override bool IsDefault { get { return InnerReader.IsDefault; } } + public override char QuoteChar { get { return InnerReader.QuoteChar; } } public override XmlSpace XmlSpace { get { return InnerReader.XmlSpace; } } public override string XmlLang { get { return InnerReader.XmlLang; } } + public override IXmlSchemaInfo? SchemaInfo { get { return InnerReader.SchemaInfo; } } public override Type ValueType { get { return InnerReader.ValueType; } } public override int AttributeCount { get { return InnerReader.AttributeCount; } } public override string this[int i] { get { return InnerReader[i]; } } @@ -122,6 +124,41 @@ public override void Close() public override int ReadContentAsBase64(byte[] buffer, int index, int count) { return InnerReader.ReadContentAsBase64(buffer, index, count); } public override int ReadContentAsBinHex(byte[] buffer, int index, int count) { return InnerReader.ReadContentAsBinHex(buffer, index, count); } public override int ReadValueChunk(char[] buffer, int index, int count) { return InnerReader.ReadValueChunk(buffer, index, count); } + public override string ReadString() { return InnerReader.ReadString(); } + + // IXmlTextParser members + bool IXmlTextParser.Normalized + { + get + { + IXmlTextParser? xmlTextParser = InnerReader as IXmlTextParser; + return (xmlTextParser == null) ? _xmlReader.Normalized : xmlTextParser.Normalized; + } + set + { + if (InnerReader is not IXmlTextParser xmlTextParser) + _xmlReader.Normalized = value; + else + xmlTextParser.Normalized = value; + } + } + + WhitespaceHandling IXmlTextParser.WhitespaceHandling + { + get + { + IXmlTextParser? xmlTextParser = InnerReader as IXmlTextParser; + return (xmlTextParser == null) ? _xmlReader.WhitespaceHandling : xmlTextParser.WhitespaceHandling; + } + set + { + if (InnerReader is not IXmlTextParser xmlTextParser) + _xmlReader.WhitespaceHandling = value; + else + xmlTextParser.WhitespaceHandling = value; + } + } + // IXmlLineInfo members bool IXmlLineInfo.HasLineInfo() { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlSerializableWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlSerializableWriter.cs index 8dc67936bf496c..1b9c39249c6ba8 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlSerializableWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlSerializableWriter.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Runtime.Serialization.DataContracts; using System.Xml; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlWriterDelegator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlWriterDelegator.cs index d9366ebf510cd2..9873c19c40abb1 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlWriterDelegator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlWriterDelegator.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Xml; +using System.Runtime.Serialization.DataContracts; using System.Globalization; @@ -281,7 +282,7 @@ internal void WriteAnyType(object value) internal void WriteAnyType(object value, Type valueType) { bool handled = true; - switch (valueType.GetTypeCode()) + switch (Type.GetTypeCode(valueType)) { case TypeCode.Boolean: WriteBoolean((bool)value); @@ -329,6 +330,7 @@ internal void WriteAnyType(object value, Type valueType) WriteUnsignedLong((ulong)value); break; case TypeCode.Empty: + case TypeCode.DBNull: case TypeCode.Object: default: if (valueType == Globals.TypeOfByteArray) @@ -453,7 +455,7 @@ internal void WriteBoolean(bool value, XmlDictionaryString name, XmlDictionarySt internal virtual void WriteDateTime(DateTime value) { - WriteString(XmlConvert.ToString(value, XmlDateTimeSerializationMode.RoundtripKind)); + writer.WriteValue(value); } internal void WriteDateTime(DateTime value, XmlDictionaryString name, XmlDictionaryString? ns) @@ -618,13 +620,6 @@ internal void WriteTimeSpan(TimeSpan value) writer.WriteRaw(XmlConvert.ToString(value)); } - internal void WriteTimeSpan(char value, XmlDictionaryString name, XmlDictionaryString? ns) - { - WriteStartElementPrimitive(name, ns); - writer.WriteRaw(XmlConvert.ToString(value)); - WriteEndElementPrimitive(); - } - internal void WriteTimeSpan(TimeSpan value, XmlDictionaryString name, XmlDictionaryString? ns) { WriteStartElementPrimitive(name, ns); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XsdDataContractExporter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XsdDataContractExporter.cs index ad147ba1af8fd7..3a59e19a558679 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XsdDataContractExporter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XsdDataContractExporter.cs @@ -1,42 +1,73 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Xml; -using System.Xml.Schema; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Reflection; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.Serialization.DataContracts; +using System.Xml; +using System.Xml.Schema; namespace System.Runtime.Serialization { + /// + /// Allows the transformation of a set of .NET types that are used in data contracts into an XML schema file (.xsd). + /// + /// + /// Use the class when you have created a Web service that incorporates data represented by + /// runtime types and when you need to export XML schemas for each type to be consumed by other Web services. + /// That is, transforms a set of runtime types into XML schemas. The schemas can then be exposed + /// through a Web Services Description Language (WSDL) document for use by others who need to interoperate with your service. + /// + /// Conversely, if you are creating a Web service that must interoperate with an existing Web service, use the XsdDataContractImporter + /// to transform XML schemas and create the runtime types that represent the data in a selected programming language. + /// + /// The generates an object that contains the collection of schemas. + /// Access the set of schemas through the property. + /// public class XsdDataContractExporter { private ExportOptions? _options; private XmlSchemaSet? _schemas; private DataContractSet? _dataContractSet; + /// + /// Initializes a new instance of the class. + /// public XsdDataContractExporter() { } + /// + /// Initializes a new instance of the class with the specified set of schemas. + /// + /// An that contains the schemas to be exported. public XsdDataContractExporter(XmlSchemaSet? schemas) { - this._schemas = schemas; + _schemas = schemas; } + /// + /// Gets or sets an that contains options that can be set for the export operation. + /// public ExportOptions? Options { get { return _options; } set { _options = value; } } + /// + /// Gets the collection of exported XML schemas. + /// public XmlSchemaSet Schemas { get { - throw new PlatformNotSupportedException(SR.PlatformNotSupported_SchemaImporter); + XmlSchemaSet schemaSet = GetSchemaSet(); + SchemaImporter.CompileSchemaSet(schemaSet); + return schemaSet; } } @@ -50,17 +81,28 @@ private XmlSchemaSet GetSchemaSet() return _schemas; } - private static DataContractSet DataContractSet + private DataContractSet DataContractSet { get { - // On .NET Framework , we set _dataContractSet = Options.GetSurrogate()); - // But Options.GetSurrogate() is not available on NetCore because IDataContractSurrogate - // is not in NetStandard. - throw new PlatformNotSupportedException(SR.PlatformNotSupported_IDataContractSurrogate); + if (_dataContractSet == null) + { + _dataContractSet = new DataContractSet(Options?.DataContractSurrogate, null, null); + } + return _dataContractSet; } } + private static void EnsureTypeNotGeneric(Type type) + { + if (type.ContainsGenericParameters) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.GenericTypeNotExportable, type))); + } + + /// + /// Transforms the types contained in the specified collection of assemblies. + /// + /// A (of ) that contains the types to export. [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public void Export(ICollection assemblies) { @@ -81,13 +123,20 @@ public void Export(ICollection assemblies) Export(); } - catch + catch (Exception ex) { + if (Fx.IsFatal(ex)) + throw; + _dataContractSet = oldValue; throw; } } + /// + /// Transforms the types contained in the passed to this method. + /// + /// A (of ) that contains the types to export. [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public void Export(ICollection types) { @@ -105,13 +154,20 @@ public void Export(ICollection types) Export(); } - catch + catch (Exception ex) { + if (Fx.IsFatal(ex)) + throw; + _dataContractSet = oldValue; throw; } } + /// + /// Transforms the specified .NET Framework type into an XML schema definition language (XSD) schema. + /// + /// The to transform into an XML schema. [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public void Export(Type type) { @@ -123,13 +179,21 @@ public void Export(Type type) AddType(type); Export(); } - catch + catch (Exception ex) { + if (Fx.IsFatal(ex)) + throw; + _dataContractSet = oldValue; throw; } } + /// + /// Returns the contract name and contract namespace for the . + /// + /// The that was exported. + /// An that represents the contract name of the type and its namespace. [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public XmlQualifiedName GetSchemaTypeName(Type type) { @@ -137,13 +201,17 @@ public XmlQualifiedName GetSchemaTypeName(Type type) type = GetSurrogatedType(type); DataContract dataContract = DataContract.GetDataContract(type); - DataContractSet.EnsureTypeNotGeneric(dataContract.UnderlyingType); - XmlDataContract? xmlDataContract = dataContract as XmlDataContract; - if (xmlDataContract != null && xmlDataContract.IsAnonymous) + EnsureTypeNotGeneric(dataContract.UnderlyingType); + if (dataContract is XmlDataContract xmlDataContract && xmlDataContract.IsAnonymous) return XmlQualifiedName.Empty; - return dataContract.StableName; + return dataContract.XmlName; } + /// + /// Returns the XML schema type for the specified type. + /// + /// The type to return a schema for. + /// An that contains the XML schema. [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public XmlSchemaType? GetSchemaType(Type type) { @@ -151,13 +219,17 @@ public XmlQualifiedName GetSchemaTypeName(Type type) type = GetSurrogatedType(type); DataContract dataContract = DataContract.GetDataContract(type); - DataContractSet.EnsureTypeNotGeneric(dataContract.UnderlyingType); - XmlDataContract? xmlDataContract = dataContract as XmlDataContract; - if (xmlDataContract != null && xmlDataContract.IsAnonymous) + EnsureTypeNotGeneric(dataContract.UnderlyingType); + if (dataContract is XmlDataContract xmlDataContract && xmlDataContract.IsAnonymous) return xmlDataContract.XsdType; return null; } + /// + /// Returns the top-level name and namespace for the . + /// + /// The to query. + /// The that represents the top-level name and namespace for this Type, which is written to the stream when writing this object. [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public XmlQualifiedName? GetRootElementName(Type type) { @@ -165,8 +237,8 @@ public XmlQualifiedName GetSchemaTypeName(Type type) type = GetSurrogatedType(type); DataContract dataContract = DataContract.GetDataContract(type); - DataContractSet.EnsureTypeNotGeneric(dataContract.UnderlyingType); - if (dataContract.HasRoot) + EnsureTypeNotGeneric(dataContract.UnderlyingType); + if (dataContract is not XmlDataContract xdc || xdc.HasRoot) // All non-XmlDataContracts "have root". { return new XmlQualifiedName(dataContract.TopLevelElementName!.Value, dataContract.TopLevelElementNamespace!.Value); } @@ -176,18 +248,17 @@ public XmlQualifiedName GetSchemaTypeName(Type type) } } - private static Type GetSurrogatedType(Type type) + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private Type GetSurrogatedType(Type type) { -#if SUPPORT_SURROGATE - IDataContractSurrogate dataContractSurrogate; - if (options != null && (dataContractSurrogate = Options.GetSurrogate()) != null) - type = DataContractSurrogateCaller.GetDataContractType(dataContractSurrogate, type); -#endif + ISerializationSurrogateProvider? surrogate = Options?.DataContractSurrogate; + if (surrogate != null) + type = DataContractSurrogateCaller.GetDataContractType(surrogate, type); return type; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private static void CheckAndAddType(Type type) + private void CheckAndAddType(Type type) { type = GetSurrogatedType(type); if (!type.ContainsGenericParameters && DataContract.IsTypeSerializable(type)) @@ -195,7 +266,7 @@ private static void CheckAndAddType(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private static void AddType(Type type) + private void AddType(Type type) { DataContractSet.Add(type); } @@ -204,8 +275,8 @@ private static void AddType(Type type) private void Export() { AddKnownTypes(); - SchemaExporter schemaExporter = new SchemaExporter(GetSchemaSet(), DataContractSet); - schemaExporter.Export(); + SchemaExporter exporter = new SchemaExporter(GetSchemaSet(), DataContractSet); + exporter.Export(); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -228,6 +299,11 @@ private void AddKnownTypes() } } + /// + /// Gets a value that indicates whether the set of runtime types contained in a set of assemblies can be exported. + /// + /// A of that contains the assemblies with the types to export. + /// true if the types can be exported; otherwise, false. [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public bool CanExport(ICollection assemblies) { @@ -253,13 +329,21 @@ public bool CanExport(ICollection assemblies) _dataContractSet = oldValue; return false; } - catch + catch (Exception ex) { + if (Fx.IsFatal(ex)) + throw; + _dataContractSet = oldValue; throw; } } + /// + /// Gets a value that indicates whether the set of runtime types contained in a can be exported. + /// + /// A that contains the specified types to export. + /// true if the types can be exported; otherwise, false. [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public bool CanExport(ICollection types) { @@ -282,13 +366,21 @@ public bool CanExport(ICollection types) _dataContractSet = oldValue; return false; } - catch + catch (Exception ex) { + if (Fx.IsFatal(ex)) + throw; + _dataContractSet = oldValue; throw; } } + /// + /// Gets a value that indicates whether the specified runtime type can be exported. + /// + /// The to export. + /// true if the type can be exported; otherwise, false. [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public bool CanExport(Type type) { @@ -306,8 +398,11 @@ public bool CanExport(Type type) _dataContractSet = oldValue; return false; } - catch + catch (Exception ex) { + if (Fx.IsFatal(ex)) + throw; + _dataContractSet = oldValue; throw; } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/ArrayHelper.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/ArrayHelper.cs index 3baa246fd2a4ef..b343623355ec30 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/ArrayHelper.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/ArrayHelper.cs @@ -17,6 +17,9 @@ public TArray[] ReadArray(XmlDictionaryReader reader, TArgument localName, TArgu int count; if (reader.TryGetArrayLength(out count)) { + if (count > maxArrayLength) + XmlExceptionHelper.ThrowMaxArrayLengthOrMaxItemsQuotaExceeded(reader, maxArrayLength); + if (count > XmlDictionaryReader.MaxInitialArrayLength) count = XmlDictionaryReader.MaxInitialArrayLength; } @@ -35,6 +38,8 @@ public TArray[] ReadArray(XmlDictionaryReader reader, TArgument localName, TArgu break; read += actual; } + if (totalRead > maxArrayLength - read) + XmlExceptionHelper.ThrowMaxArrayLengthOrMaxItemsQuotaExceeded(reader, maxArrayLength); totalRead += read; if (read < array.Length || reader.NodeType == XmlNodeType.EndElement) break; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/EncodingStreamWrapper.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/EncodingStreamWrapper.cs index 0d9437d5443d0e..9b88c45d8561db 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/EncodingStreamWrapper.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/EncodingStreamWrapper.cs @@ -556,15 +556,15 @@ public override long Position } } - protected override void Dispose(bool disposing) + public override void Close() { if (_stream.CanWrite) { Flush(); } + base.Close(); _stream.Dispose(); - base.Dispose(disposing); } public override void Flush() @@ -726,8 +726,4 @@ public override void SetLength(long value) throw new NotSupportedException(); } } - - // Add format exceptions - // Do we need to modify the stream position/Seek to account for the buffer? - // ASSUMPTION (Microsoft): This class will only be used for EITHER reading OR writing. } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/StringHandle.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/StringHandle.cs index eacd50236de1e9..50dce6014220d3 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/StringHandle.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/StringHandle.cs @@ -111,9 +111,11 @@ public string GetString(XmlNameTable nameTable) return _bufferReader.GetString(_offset, _length, nameTable); if (type == StringHandleType.Dictionary) return nameTable.Add(_bufferReader.GetDictionaryString(_key).Value); - DiagnosticUtility.DebugAssert(type == StringHandleType.ConstString, "Should be ConstString"); - //If not Utf8 then the StringHandleType is ConstString - return nameTable.Add(s_constStrings[_key]); + if (type == StringHandleType.ConstString) + return nameTable.Add(s_constStrings[_key]); + // If none of the above, must be StringHandleType.EscapedUTF8 + DiagnosticUtility.DebugAssert(type == StringHandleType.EscapedUTF8, "Should be EscapedUTF8"); + return _bufferReader.GetEscapedString(_offset, _length, nameTable); } public string GetString() @@ -123,9 +125,11 @@ public string GetString() return _bufferReader.GetString(_offset, _length); if (type == StringHandleType.Dictionary) return _bufferReader.GetDictionaryString(_key).Value; - DiagnosticUtility.DebugAssert(type == StringHandleType.ConstString, "Should be ConstString"); - //If not Utf8 then the StringHandleType is ConstString - return s_constStrings[_key]; + if (type == StringHandleType.ConstString) + return s_constStrings[_key]; + // If none of the above, must be StringHandleType.EscapedUTF8 + DiagnosticUtility.DebugAssert(type == StringHandleType.EscapedUTF8, "Should be EscapedUTF8"); + return _bufferReader.GetEscapedString(_offset, _length); } public byte[] GetString(out int offset, out int length) @@ -177,6 +181,7 @@ public bool TryGetDictionaryString([NotNullWhen(true)] out XmlDictionaryString? value = null; return false; } + public override string ToString() { return GetString(); @@ -211,7 +216,7 @@ private bool Equals2(string s2) return _bufferReader.GetDictionaryString(_key).Value == s2; if (type == StringHandleType.UTF8) return _bufferReader.Equals2(_offset, _length, s2); - DiagnosticUtility.DebugAssert(type == StringHandleType.ConstString, ""); + DiagnosticUtility.DebugAssert(type == StringHandleType.EscapedUTF8 || type == StringHandleType.ConstString, ""); return GetString() == s2; } @@ -248,6 +253,7 @@ public bool Equals([NotNullWhen(true)] StringHandle? other) { return !s1.Equals2(xmlString2); } + public static bool operator ==(StringHandle s1, string s2) { return s1.Equals2(s2); @@ -275,6 +281,7 @@ public int CompareTo(StringHandle that) else return string.Compare(this.GetString(), that.GetString(), StringComparison.Ordinal); } + public override bool Equals([NotNullWhen(true)] object? obj) { return Equals(obj as StringHandle); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/ValueHandle.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/ValueHandle.cs index f09a06baa1572e..9a1195317eb103 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/ValueHandle.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/ValueHandle.cs @@ -114,6 +114,7 @@ public void SetQNameValue(int prefix, int key) { SetValue(ValueHandleType.QName, key, prefix); } + public void SetValue(ValueHandleType type, int offset, int length) { _type = type; @@ -414,6 +415,7 @@ public Guid ToGuid() return XmlConverter.ToGuid(_bufferReader.Buffer, _offset, _length); return XmlConverter.ToGuid(GetString()); } + public override string ToString() { return GetString(); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseReader.cs index 158e62374292d7..d8a3f8f6291f62 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseReader.cs @@ -127,6 +127,7 @@ protected void MoveToInitial(XmlDictionaryReaderQuotas quotas) _attributeIndex = -1; _rootElement = false; _readingElement = false; + _signing = false; MoveToNode(s_initialNode); } @@ -191,6 +192,7 @@ private bool CheckDeclAttribute(int index, string localName, string? value, bool return true; } + protected XmlCommentNode MoveToComment() { _commentNode ??= new XmlCommentNode(_bufferReader); @@ -229,6 +231,7 @@ protected XmlTextNode MoveToWhitespaceText() MoveToNode(_whitespaceTextNode); return _whitespaceTextNode; } + protected XmlElementNode ElementNode { get @@ -341,6 +344,7 @@ protected XmlAttributeNode AddXmlAttribute() { return AddAttribute(QNameType.Normal, true); } + protected XmlAttributeNode AddXmlnsAttribute(Namespace ns) { if (!ns.Prefix.IsEmpty && ns.Uri.IsEmpty) @@ -385,6 +389,7 @@ protected void FixXmlAttribute(XmlAttributeNode attributeNode) } } } + protected bool OutsideRootElement { get @@ -392,6 +397,7 @@ protected bool OutsideRootElement return _depth == 0; } } + public override bool CanReadBinaryContent { get { return true; } @@ -610,7 +616,6 @@ private XmlAttributeNode GetAttributeNode(int index) return null; } - public override string GetAttribute(int index) { return GetAttributeNode(index).ValueAsString; @@ -639,6 +644,7 @@ public override string GetAttribute(int index) return null; return attributeNode.ValueAsString; } + public sealed override bool IsEmptyElement { get @@ -781,17 +787,19 @@ private void CheckAttributes(XmlAttributeNode[] attributeNodes, int attributeCou } } - public override void MoveToAttribute(int index) { MoveToNode(GetAttributeNode(index)); + _attributeIndex = index; } + public override bool MoveToAttribute(string name) { XmlNode? attributeNode = GetAttributeNode(name); if (attributeNode == null) return false; MoveToNode(attributeNode); + _attributeIndex = _attributeStart; return true; } @@ -801,6 +809,7 @@ public override bool MoveToAttribute(string localName, string? namespaceUri) if (attributeNode == null) return false; MoveToNode(attributeNode); + _attributeIndex = _attributeStart; return true; } @@ -902,7 +911,7 @@ public override XmlNameTable NameTable { if (_nameTable == null) { - _nameTable = new NameTable(); + _nameTable = new QuotaNameTable(this, _quotas.MaxNameTableCharCount); _nameTable.Add(xml); _nameTable.Add(xmlns); _nameTable.Add(xmlnsNamespace); @@ -953,6 +962,13 @@ public override string Prefix } } + public override char QuoteChar + { + get + { + return _node.QuoteChar; + } + } public override bool IsLocalName(string localName) { @@ -1192,6 +1208,36 @@ public override string ReadElementContentAsString() return s; } } + + public override string ReadElementString() + { + MoveToStartElement(); + if (IsEmptyElement) + { + Read(); + return string.Empty; + } + else + { + Read(); + string s = ReadString(); + ReadEndElement(); + return s; + } + } + + public override string ReadElementString(string name) + { + MoveToStartElement(name); + return ReadElementString(); + } + + public override string ReadElementString(string localName, string namespaceUri) + { + MoveToStartElement(localName, namespaceUri); + return ReadElementString(); + } + public override void ReadStartElement() { if (_node.NodeType != XmlNodeType.Element) @@ -2065,7 +2111,6 @@ protected XmlNode(XmlNodeType nodeType, _depthDelta = depthDelta; _isEmptyElement = false; _quoteChar = '"'; - _qnameType = QNameType.Normal; } @@ -2249,6 +2294,7 @@ public bool IsLocalNameAndNamespaceUri(XmlDictionaryString localName, XmlDiction return this.Namespace.Prefix == localName && ns.Value == xmlnsNamespace; } } + public bool IsPrefixAndLocalName(string prefix, string localName) { if (_qnameType == QNameType.Normal) @@ -2539,6 +2585,7 @@ public XmlCommentNode(XmlBufferReader bufferReader) { } } + protected sealed class XmlEndOfFileNode : XmlNode { public XmlEndOfFileNode(XmlBufferReader bufferReader) @@ -2911,6 +2958,7 @@ public Namespace AddNamespace() return XmlNamespace; return null; } + public Namespace? LookupNamespace(string prefix) { PrefixHandleType shortPrefix; @@ -3060,6 +3108,7 @@ public bool IsUri(XmlDictionaryString s) } return false; } + public StringHandle Uri { get @@ -3080,5 +3129,56 @@ public Namespace? OuterUri } } } + + private sealed class QuotaNameTable : XmlNameTable + { + private readonly XmlDictionaryReader _reader; + private readonly XmlNameTable _nameTable; + private readonly int _maxCharCount; + private int _charCount; + + public QuotaNameTable(XmlDictionaryReader reader, int maxCharCount) + { + _reader = reader; + _nameTable = new NameTable(); + _maxCharCount = maxCharCount; + _charCount = 0; + } + + public override string? Get(char[] chars, int offset, int count) + { + return _nameTable.Get(chars, offset, count); + } + + public override string? Get(string value) + { + return _nameTable.Get(value); + } + + private void Add(int charCount) + { + if (charCount > _maxCharCount - _charCount) + XmlExceptionHelper.ThrowMaxNameTableCharCountExceeded(_reader, _maxCharCount); + _charCount += charCount; + } + + public override string Add(char[] chars, int offset, int count) + { + string? s = _nameTable.Get(chars, offset, count); + if (s != null) + return s; + Add(count); + return _nameTable.Add(chars, offset, count); + } + + public override string Add(string value) + { + string? s = _nameTable.Get(value); + if (s != null) + return s; + Add(value.Length); + return _nameTable.Add(value); + } + } } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseWriter.cs index 4ddcff94559aa9..d197af209fd8e4 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseWriter.cs @@ -13,7 +13,7 @@ namespace System.Xml { - internal abstract class XmlBaseWriter : XmlDictionaryWriter + internal abstract class XmlBaseWriter : XmlDictionaryWriter, IFragmentCapableXmlDictionaryWriter { private XmlNodeWriter _writer = null!; // initialized in SetOutput private readonly NamespaceManager _nsMgr; @@ -29,6 +29,10 @@ internal abstract class XmlBaseWriter : XmlDictionaryWriter private int _trailByteCount; private XmlStreamNodeWriter _nodeWriter = null!; // initialized in SetOutput private XmlSigningNodeWriter? _signingWriter; + private XmlUTF8NodeWriter? _textFragmentWriter; + private XmlNodeWriter? _oldWriter; + private Stream? _oldStream; + private int _oldNamespaceBoundary; private bool _inList; private const string xmlnsNamespace = "http://www.w3.org/2000/xmlns/"; private const string xmlNamespace = "http://www.w3.org/XML/1998/namespace"; @@ -57,6 +61,8 @@ protected void SetOutput(XmlStreamNodeWriter writer) } _attributeLocalName = null; _attributeValue = null; + _oldWriter = null; + _oldStream = null; } public override void Flush() @@ -98,6 +104,9 @@ public override void Close() _attributeLocalName = null; _nodeWriter.Close(); _signingWriter?.Close(); + _textFragmentWriter?.Close(); + _oldWriter = null; + _oldStream = null; } } @@ -431,7 +440,7 @@ public override void WriteComment(string? text) { text = string.Empty; } - else if (text.Contains("--") || text.StartsWith('-')) + else if (text.Contains("--") || text.EndsWith('-')) { throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.XmlInvalidCommentChars, nameof(text))); } @@ -926,6 +935,18 @@ public override void WriteEndDocument() _documentState = DocumentState.End; } + protected int NamespaceBoundary + { + get + { + return _nsMgr.NamespaceBoundary; + } + set + { + _nsMgr.NamespaceBoundary = value; + } + } + public override void WriteEntityRef(string name) { throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.XmlMethodNotSupported, "WriteEntityRef"))); @@ -1149,6 +1170,10 @@ public override void WriteValue(object value) { WriteValue((Array)value); } + else if (value is IStreamProvider) + { + WriteValue((IStreamProvider)value); + } else { WritePrimitiveValue(value); @@ -1426,11 +1451,27 @@ public override void WriteValue(TimeSpan value) } } + private static void EnsureBufferBounds(byte[] buffer, int offset, int count) + { + ArgumentNullException.ThrowIfNull(buffer); + + // Not checking upper bound because it will be caught by "count". This is what XmlTextWriter does. + if (offset < 0) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(offset), SR.ValueMustBeNonNegative)); + + if (count < 0) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(count), SR.ValueMustBeNonNegative)); + if (count > buffer.Length - offset) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(count), SR.Format(SR.SizeExceedsRemainingBufferSpace, buffer.Length - offset))); + } + public override void WriteBinHex(byte[] buffer, int offset, int count) { if (IsClosed) ThrowClosed(); + EnsureBufferBounds(buffer, offset, count); + WriteRaw(BinHexEncoding.GetString(buffer, offset, count)); } @@ -1439,16 +1480,7 @@ public override void WriteBase64(byte[] buffer, int offset, int count) if (IsClosed) ThrowClosed(); - ArgumentNullException.ThrowIfNull(buffer); - - // Not checking upper bound because it will be caught by "count". This is what XmlTextWriter does. - if (offset < 0) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(offset), SR.ValueMustBeNonNegative)); - - if (count < 0) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(count), SR.ValueMustBeNonNegative)); - if (count > buffer.Length - offset) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(count), SR.Format(SR.SizeExceedsRemainingBufferSpace, buffer.Length - offset))); + EnsureBufferBounds(buffer, offset, count); if (count > 0) { @@ -1473,6 +1505,7 @@ public override void WriteBase64(byte[] buffer, int offset, int count) WriteAttributeText(XmlConverter.Base64Encoding.GetString(_trailBytes, 0, _trailByteCount)); WriteAttributeText(XmlConverter.Base64Encoding.GetString(buffer, offset, actualByteCount - _trailByteCount)); } + if (!_isXmlnsAttribute) { StartContent(); @@ -1480,6 +1513,7 @@ public override void WriteBase64(byte[] buffer, int offset, int count) EndContent(); } _trailByteCount = (totalByteCount - actualByteCount); + if (_trailByteCount > 0) { int trailOffset = offset + count - _trailByteCount; @@ -1500,16 +1534,7 @@ public override Task WriteBase64Async(byte[] buffer, int offset, int count) if (IsClosed) ThrowClosed(); - ArgumentNullException.ThrowIfNull(buffer); - - // Not checking upper bound because it will be caught by "count". This is what XmlTextWriter does. - if (offset < 0) - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(offset), SR.ValueMustBeNonNegative)); - - if (count < 0) - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(count), SR.ValueMustBeNonNegative)); - if (count > buffer.Length - offset) - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(count), SR.Format(SR.SizeExceedsRemainingBufferSpace, buffer.Length - offset))); + EnsureBufferBounds(buffer, offset, count); return WriteBase64AsyncImpl(buffer, offset, count); } @@ -1603,6 +1628,125 @@ public override void EndCanonicalization() protected abstract XmlSigningNodeWriter CreateSigningNodeWriter(); + public virtual bool CanFragment + { + get + { + return true; + } + } + + public void StartFragment(Stream stream, bool generateSelfContainedTextFragment) + { + if (!CanFragment) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException()); + + if (IsClosed) + ThrowClosed(); + + ArgumentNullException.ThrowIfNull(stream); + + if (_oldStream != null || _oldWriter != null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException()); + if (WriteState == WriteState.Attribute) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.XmlInvalidWriteState, "StartFragment", WriteState.ToString()))); + + FlushElement(); + _writer.Flush(); + + _oldNamespaceBoundary = NamespaceBoundary; + + XmlStreamNodeWriter? fragmentWriter = null; + if (generateSelfContainedTextFragment) + { + this.NamespaceBoundary = _depth + 1; + _textFragmentWriter ??= new XmlUTF8NodeWriter(); + _textFragmentWriter.SetOutput(stream, false, Encoding.UTF8); + fragmentWriter = _textFragmentWriter; + } + + if (Signing) + { + if (fragmentWriter != null) + { + _oldWriter = _signingWriter!.NodeWriter; + _signingWriter.NodeWriter = fragmentWriter; + } + else + { + _oldStream = ((XmlStreamNodeWriter)_signingWriter!.NodeWriter).OutputStream; + ((XmlStreamNodeWriter)_signingWriter.NodeWriter).OutputStream = stream; + } + } + else + { + if (fragmentWriter != null) + { + _oldWriter = _writer; + _writer = fragmentWriter; + } + else + { + _oldStream = _nodeWriter.OutputStream; + _nodeWriter.OutputStream = stream; + } + } + } + + public void EndFragment() + { + if (IsClosed) + ThrowClosed(); + + if (_oldStream == null && _oldWriter == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException()); + if (WriteState == WriteState.Attribute) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.XmlInvalidWriteState, "EndFragment", WriteState.ToString()))); + + FlushElement(); + _writer.Flush(); + + if (Signing) + { + if (_oldWriter != null) + _signingWriter!.NodeWriter = _oldWriter; + else + ((XmlStreamNodeWriter)_signingWriter!.NodeWriter).OutputStream = _oldStream!; // Checked above. _oldStream can't be null if _oldWriter is. + } + else + { + if (_oldWriter != null) + _writer = _oldWriter; + else + _nodeWriter.OutputStream = _oldStream!; // Checked above. _oldStream can't be null if _oldWriter is. + } + NamespaceBoundary = _oldNamespaceBoundary; + _oldWriter = null; + _oldStream = null; + } + + public void WriteFragment(byte[] buffer, int offset, int count) + { + if (!CanFragment) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException()); + + if (IsClosed) + ThrowClosed(); + + EnsureBufferBounds(buffer, offset, count); + + if (WriteState == WriteState.Attribute) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.XmlInvalidWriteState, "WriteFragment", WriteState.ToString()))); + + if (_writer != _nodeWriter) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException()); + + FlushElement(); + FlushBase64(); + _nodeWriter.Flush(); + _nodeWriter.OutputStream.Write(buffer, offset, count); + } + private void FlushBase64() { if (_trailByteCount > 0) @@ -1802,6 +1946,7 @@ private sealed class NamespaceManager private int _attributeCount; private XmlSpace _space; private string? _lang; + private int _namespaceBoundary; private int _nsTop; private readonly Namespace _defaultNamespace; @@ -1843,9 +1988,29 @@ public void Clear() _attributeCount = 0; _space = XmlSpace.None; _lang = null; + _namespaceBoundary = 0; _lastNameSpace = null; } + public int NamespaceBoundary + { + get + { + return _namespaceBoundary; + } + set + { + int i; + for (i = 0; i < _nsCount; i++) + if (_namespaces![i].Depth >= value) + break; + + _nsTop = i; + _namespaceBoundary = value; + _lastNameSpace = null; + } + } + public void Close() { if (_depth == 0) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryReader.cs index 9b6911441951b2..07765d45883b7d 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryReader.cs @@ -29,6 +29,7 @@ internal sealed class XmlBinaryReader : XmlBaseReader, IXmlBinaryReaderInitializ private int _arrayCount; private int _maxBytesPerRead; private XmlBinaryNodeType _arrayNodeType; + private OnXmlDictionaryReaderClose? _onClose; public XmlBinaryReader() { @@ -50,7 +51,7 @@ public void SetInput(byte[] buffer, int offset, int count, throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(count), SR.ValueMustBeNonNegative)); if (count > buffer.Length - offset) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(count), SR.Format(SR.SizeExceedsRemainingBufferSpace, buffer.Length - offset))); - MoveToInitial(quotas, session, null); + MoveToInitial(quotas, session, onClose); BufferReader.SetBuffer(buffer, offset, count, dictionary, session); _buffered = true; } @@ -63,7 +64,7 @@ public void SetInput(Stream stream, { ArgumentNullException.ThrowIfNull(stream); - MoveToInitial(quotas, session, null); + MoveToInitial(quotas, session, onClose); BufferReader.SetBuffer(stream, dictionary, session); _buffered = false; } @@ -73,12 +74,26 @@ private void MoveToInitial(XmlDictionaryReaderQuotas quotas, XmlBinaryReaderSess MoveToInitial(quotas); _maxBytesPerRead = quotas.MaxBytesPerRead; _arrayState = ArrayState.None; + _onClose = onClose; _isTextWithEndElement = false; } public override void Close() { base.Close(); + OnXmlDictionaryReaderClose? onClose = _onClose; + _onClose = null; + if (onClose != null) + { + try + { + onClose(this); + } + catch (Exception e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); + } + } } public override string ReadElementContentAsString() @@ -710,8 +725,10 @@ private void ReadAttributes() private void ReadAttributes2() { + int startOffset = 0; + if (_buffered) - _ = BufferReader.Offset; + startOffset = BufferReader.Offset; while (true) { @@ -845,6 +862,8 @@ private void ReadAttributes2() ReadAttributeText(attributeNode.AttributeText!); break; default: + if (_buffered && (BufferReader.Offset - startOffset) > _maxBytesPerRead) + XmlExceptionHelper.ThrowMaxBytesPerReadExceeded(this, _maxBytesPerRead); ProcessAttributes(); return; } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryWriter.cs index ef401fd8031821..be47e110a151bd 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryWriter.cs @@ -807,28 +807,8 @@ public override unsafe void WriteDecimalText(decimal d) public override void WriteDateTimeText(DateTime dt) { - WriteTextNodeWithInt64(XmlBinaryNodeType.DateTimeText, ToBinary(dt)); + WriteTextNodeWithInt64(XmlBinaryNodeType.DateTimeText, dt.ToBinary()); } - private static long ToBinary(DateTime dt) - { - long temp = 0; - switch (dt.Kind) - { - case DateTimeKind.Local: - temp |= -9223372036854775808L; // 0x8000000000000000 - temp |= dt.ToUniversalTime().Ticks; - break; - case DateTimeKind.Utc: - temp |= 0x4000000000000000L; - temp |= dt.Ticks; - break; - case DateTimeKind.Unspecified: - temp = dt.Ticks; - break; - } - return temp; - } - public override void WriteUniqueIdText(UniqueId value) { @@ -906,7 +886,7 @@ public void WriteDateTimeArray(DateTime[] array, int offset, int count) WriteArrayInfo(XmlBinaryNodeType.DateTimeTextWithEndElement, count); for (int i = 0; i < count; i++) { - WriteInt64(ToBinary(array[offset + i])); + WriteInt64(array[offset + i].ToBinary()); } } @@ -1185,7 +1165,6 @@ private unsafe void UnsafeWriteArray(string? prefix, string localName, string? n { WriteStartArray(prefix, localName, namespaceUri, count); _writer.UnsafeWriteArray(nodeType, count, array, arrayMax); - // WriteEndArray(); } private unsafe void UnsafeWriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, @@ -1193,7 +1172,6 @@ private unsafe void UnsafeWriteArray(string? prefix, XmlDictionaryString localNa { WriteStartArray(prefix, localName, namespaceUri, count); _writer.UnsafeWriteArray(nodeType, count, array, arrayMax); - // WriteEndArray(); } private static void CheckArray(Array array, int offset, int count) @@ -1212,6 +1190,11 @@ private static void CheckArray(Array array, int offset, int count) public override unsafe void WriteArray(string? prefix, string localName, string? namespaceUri, bool[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1226,6 +1209,11 @@ public override unsafe void WriteArray(string? prefix, string localName, string? public override unsafe void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, bool[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1240,6 +1228,11 @@ public override unsafe void WriteArray(string? prefix, XmlDictionaryString local public override unsafe void WriteArray(string? prefix, string localName, string? namespaceUri, short[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1254,6 +1247,11 @@ public override unsafe void WriteArray(string? prefix, string localName, string? public override unsafe void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, short[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1268,6 +1266,11 @@ public override unsafe void WriteArray(string? prefix, XmlDictionaryString local public override unsafe void WriteArray(string? prefix, string localName, string? namespaceUri, int[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1282,6 +1285,11 @@ public override unsafe void WriteArray(string? prefix, string localName, string? public override unsafe void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, int[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1296,6 +1304,11 @@ public override unsafe void WriteArray(string? prefix, XmlDictionaryString local public override unsafe void WriteArray(string? prefix, string localName, string? namespaceUri, long[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1310,6 +1323,11 @@ public override unsafe void WriteArray(string? prefix, string localName, string? public override unsafe void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, long[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1324,6 +1342,11 @@ public override unsafe void WriteArray(string? prefix, XmlDictionaryString local public override unsafe void WriteArray(string? prefix, string localName, string? namespaceUri, float[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1338,6 +1361,11 @@ public override unsafe void WriteArray(string? prefix, string localName, string? public override unsafe void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, float[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1352,6 +1380,11 @@ public override unsafe void WriteArray(string? prefix, XmlDictionaryString local public override unsafe void WriteArray(string? prefix, string localName, string? namespaceUri, double[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1366,6 +1399,11 @@ public override unsafe void WriteArray(string? prefix, string localName, string? public override unsafe void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, double[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1380,6 +1418,11 @@ public override unsafe void WriteArray(string? prefix, XmlDictionaryString local public override unsafe void WriteArray(string? prefix, string localName, string? namespaceUri, decimal[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1394,6 +1437,11 @@ public override unsafe void WriteArray(string? prefix, string localName, string? public override unsafe void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, decimal[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1409,26 +1457,34 @@ public override unsafe void WriteArray(string? prefix, XmlDictionaryString local // DateTime public override void WriteArray(string? prefix, string localName, string? namespaceUri, DateTime[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) { WriteStartArray(prefix, localName, namespaceUri, count); _writer.WriteDateTimeArray(array, offset, count); - // WriteEndArray(); } } } public override void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, DateTime[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) { WriteStartArray(prefix, localName, namespaceUri, count); _writer.WriteDateTimeArray(array, offset, count); - // WriteEndArray(); } } } @@ -1436,26 +1492,34 @@ public override void WriteArray(string? prefix, XmlDictionaryString localName, X // Guid public override void WriteArray(string? prefix, string localName, string? namespaceUri, Guid[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) { WriteStartArray(prefix, localName, namespaceUri, count); _writer.WriteGuidArray(array, offset, count); - // WriteEndArray(); } } } public override void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, Guid[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) { WriteStartArray(prefix, localName, namespaceUri, count); _writer.WriteGuidArray(array, offset, count); - // WriteEndArray(); } } } @@ -1463,26 +1527,34 @@ public override void WriteArray(string? prefix, XmlDictionaryString localName, X // TimeSpan public override void WriteArray(string? prefix, string localName, string? namespaceUri, TimeSpan[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) { WriteStartArray(prefix, localName, namespaceUri, count); _writer.WriteTimeSpanArray(array, offset, count); - // WriteEndArray(); } } } public override void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, TimeSpan[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) { WriteStartArray(prefix, localName, namespaceUri, count); _writer.WriteTimeSpanArray(array, offset, count); - // WriteEndArray(); } } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBufferReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBufferReader.cs index fab4fb8531c1ca..2ad5b6cdcabc91 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBufferReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBufferReader.cs @@ -12,6 +12,7 @@ using System.Runtime.Serialization; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Buffers.Binary; namespace System.Xml { @@ -210,13 +211,14 @@ private bool TryEnsureBytes(int count) if (_stream == null) return false; + DiagnosticUtility.DebugAssert(_offset <= int.MaxValue - count, ""); + // The data could be coming from an untrusted source, so we use a standard // "multiply by 2" growth algorithm to avoid overly large memory utilization. // Constant value of 256 comes from MemoryStream implementation. do { - DiagnosticUtility.DebugAssert(_offset <= int.MaxValue - count, ""); int newOffsetMax = _offset + count; if (newOffsetMax <= _offsetMax) return true; @@ -427,16 +429,19 @@ public unsafe double ReadDouble() return value; } - public unsafe decimal ReadDecimal() + public decimal ReadDecimal() { - int offset; - byte[] buffer = GetBuffer(ValueHandleLength.Decimal, out offset); - decimal value; - byte* pb = (byte*)&value; - for (int i = 0; i < sizeof(decimal); i++) - pb[i] = buffer[offset + i]; - Advance(ValueHandleLength.Decimal); - return value; + byte[] buffer = GetBuffer(ValueHandleLength.Decimal, out int offset); + ReadOnlySpan bytes = buffer.AsSpan(offset, sizeof(decimal)); + ReadOnlySpan span = stackalloc int[4] + { + BinaryPrimitives.ReadInt32LittleEndian(bytes), + BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(4)), + BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(8)), + BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(12)) + }; + + return new decimal(span); } public UniqueId ReadUniqueId() @@ -680,6 +685,13 @@ public string GetEscapedString(int offset, int length) return new string(chars, 0, charCount); } + public string GetEscapedString(int offset, int length, XmlNameTable nameTable) + { + char[] chars = GetCharBuffer(length); + int charCount = GetEscapedChars(offset, length, chars); + return nameTable.Add(chars, 0, charCount); + } + private int GetLessThanCharEntity(int offset, int length) { byte[] buffer = _buffer; @@ -1059,14 +1071,18 @@ public unsafe double GetDouble(int offset) return value; } - public unsafe decimal GetDecimal(int offset) + public decimal GetDecimal(int offset) { - byte[] buffer = _buffer; - decimal value; - byte* pb = (byte*)&value; - for (int i = 0; i < sizeof(decimal); i++) - pb[i] = buffer[offset + i]; - return value; + ReadOnlySpan bytes = _buffer.AsSpan(offset, sizeof(decimal)); + ReadOnlySpan span = stackalloc int[4] + { + BinaryPrimitives.ReadInt32LittleEndian(bytes), + BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(4)), + BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(8)), + BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(12)) + }; + + return new decimal(span); } public UniqueId GetUniqueId(int offset) @@ -1132,9 +1148,11 @@ public XmlDictionaryString GetDictionaryString(int key) { keyDictionary = _dictionary!; } + XmlDictionaryString? s; if (!keyDictionary.TryLookup(key >> 1, out s)) XmlExceptionHelper.ThrowInvalidBinaryFormat(_reader); + return s; } @@ -1165,6 +1183,7 @@ public int ReadDictionaryKey() XmlExceptionHelper.ThrowXmlDictionaryStringIDUndefinedStatic(_reader, staticKey); } } + return key; } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlCanonicalWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlCanonicalWriter.cs index 951bd780443cb9..4b94ed46ce2d74 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlCanonicalWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlCanonicalWriter.cs @@ -127,7 +127,8 @@ public void Close() _inclusivePrefixes = null; } - public static void WriteDeclaration() + [SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "This class is should roughly mirror the XmlNodeWriter API where this is an instance method.")] + public void WriteDeclaration() { } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlConverter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlConverter.cs index e50ec3865375ec..0a856e04e7f1cd 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlConverter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlConverter.cs @@ -247,7 +247,7 @@ public static UniqueId ToUniqueId(string value) { try { - return new UniqueId(Trim(value)); + return new UniqueId(value.Trim()); } catch (ArgumentException exception) { @@ -293,7 +293,7 @@ public static Guid ToGuid(string value) { try { - return new Guid(Trim(value)); + return new Guid(value.Trim()); } catch (FormatException exception) { @@ -318,7 +318,7 @@ public static ulong ToUInt64(string value) { try { - return ulong.Parse(value, NumberStyles.Any, NumberFormatInfo.InvariantInfo); + return ulong.Parse(value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo); } catch (ArgumentException exception) { @@ -395,7 +395,6 @@ public static int ToChars(byte[] buffer, int offset, int count, char[] chars, in public static string ToString(double value) { return XmlConvert.ToString(value); } public static string ToString(decimal value) { return XmlConvert.ToString(value); } public static string ToString(TimeSpan value) { return XmlConvert.ToString(value); } - public static string ToString(UniqueId value) { return value.ToString(); } public static string ToString(Guid value) { return value.ToString(); } public static string ToString(ulong value) { return value.ToString(NumberFormatInfo.InvariantInfo); } @@ -459,14 +458,14 @@ public static void ToQualifiedName(string qname, out string prefix, out string l if (index < 0) { prefix = string.Empty; - localName = Trim(qname); + localName = qname.Trim(); } else { if (index == qname.Length - 1) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.XmlInvalidQualifiedName, qname))); - prefix = Trim(qname.Substring(0, index)); - localName = Trim(qname.Substring(index + 1)); + prefix = qname.Substring(0, index).Trim(); + localName = qname.Substring(index + 1).Trim(); } } @@ -795,7 +794,7 @@ private static int ToAsciiChars(string s, byte[] buffer, int offset) { for (int i = 0; i < s.Length; i++) { - Fx.Assert(s[i] < 128, ""); + DiagnosticUtility.DebugAssert(s[i] < 128, ""); buffer[offset++] = (byte)s[i]; } return s.Length; @@ -1135,21 +1134,5 @@ public static string StripWhitespace(string s) } }); } - - private static string Trim(string s) - { - int i; - for (i = 0; i < s.Length && IsWhitespace(s[i]); i++) - ; - int j; - for (j = s.Length; j > 0 && IsWhitespace(s[j - 1]); j--) - ; - if (i == 0 && j == s.Length) - return s; - else if (j == 0) - return string.Empty; - else - return s.Substring(i, j - i); - } } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryReader.cs index 92b5bb6c960d29..d065ef92957b4b 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryReader.cs @@ -358,6 +358,9 @@ internal byte[] ReadContentAsBase64(int maxByteArrayContentLength, int maxInitia int length; if (TryGetBase64ContentLength(out length)) { + if (length > maxByteArrayContentLength) + XmlExceptionHelper.ThrowMaxArrayLengthExceeded(this, maxByteArrayContentLength); + if (length <= maxInitialCount) { byte[] buffer = new byte[length]; @@ -522,6 +525,8 @@ private byte[] ReadContentAsBytes(bool base64, int maxByteArrayContentLength) break; read += actual; } + if (totalRead > maxByteArrayContentLength - read) + XmlExceptionHelper.ThrowMaxArrayLengthExceeded(this, maxByteArrayContentLength); totalRead += read; if (read < buffer.Length) break; @@ -1497,6 +1502,13 @@ public override string Prefix } } + public override char QuoteChar + { + get + { + return _reader.QuoteChar; + } + } public override bool Read() { @@ -1508,6 +1520,15 @@ public override bool ReadAttributeValue() return _reader.ReadAttributeValue(); } + public override string ReadElementString(string name) + { + return _reader.ReadElementString(name); + } + + public override string ReadElementString(string localName, string namespaceUri) + { + return _reader.ReadElementString(localName, namespaceUri); + } public override string ReadInnerXml() { @@ -1534,6 +1555,11 @@ public override void ReadEndElement() _reader.ReadEndElement(); } + public override string ReadString() + { + return _reader.ReadString(); + } + public override ReadState ReadState { get @@ -1772,9 +1798,7 @@ public override object ReadContentAs(Type type, IXmlNamespaceResolver? namespace public bool HasLineInfo() { - IXmlLineInfo? lineInfo = _reader as IXmlLineInfo; - - if (lineInfo == null) + if (_reader is not IXmlLineInfo lineInfo) return false; return lineInfo.HasLineInfo(); @@ -1784,9 +1808,7 @@ public int LineNumber { get { - IXmlLineInfo? lineInfo = _reader as IXmlLineInfo; - - if (lineInfo == null) + if (_reader is not IXmlLineInfo lineInfo) return 1; return lineInfo.LineNumber; @@ -1797,9 +1819,7 @@ public int LinePosition { get { - IXmlLineInfo? lineInfo = _reader as IXmlLineInfo; - - if (lineInfo == null) + if (_reader is not IXmlLineInfo lineInfo) return 1; return lineInfo.LinePosition; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryWriter.cs index b39afc2bea8ab5..938eefb42fd038 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryWriter.cs @@ -251,7 +251,7 @@ private void WriteElementNode(XmlDictionaryReader reader, bool defattr) { WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI); } - if (defattr || !reader.IsDefault) + if (defattr || (!reader.IsDefault && (reader.SchemaInfo == null || !reader.SchemaInfo.IsDefault))) { if (reader.MoveToFirstAttribute()) { @@ -375,8 +375,7 @@ protected virtual void WriteTextNode(XmlDictionaryReader reader, bool isAttribut public override void WriteNode(XmlReader reader, bool defattr) { - XmlDictionaryReader? dictionaryReader = reader as XmlDictionaryReader; - if (dictionaryReader != null) + if (reader is XmlDictionaryReader dictionaryReader) WriteNode(dictionaryReader, defattr); else base.WriteNode(reader, defattr); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlExceptionHelper.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlExceptionHelper.cs index 3d6cc5cb606550..8e0a9399709989 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlExceptionHelper.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlExceptionHelper.cs @@ -31,8 +31,7 @@ private static void ThrowXmlException(XmlDictionaryReader reader, string res, st private static void ThrowXmlException(XmlDictionaryReader reader, string res, string? arg1, string? arg2, string? arg3) { string s = SR.Format(res, arg1, arg2, arg3); - IXmlLineInfo? lineInfo = reader as IXmlLineInfo; - if (lineInfo != null && lineInfo.HasLineInfo()) + if (reader is IXmlLineInfo lineInfo && lineInfo.HasLineInfo()) { s += " " + SR.Format(SR.XmlLineInfo, lineInfo.LineNumber, lineInfo.LinePosition); } @@ -44,8 +43,7 @@ private static void ThrowXmlException(XmlDictionaryReader reader, string res, st public static void ThrowXmlException(XmlDictionaryReader reader, XmlException exception) { string s = exception.Message; - IXmlLineInfo? lineInfo = reader as IXmlLineInfo; - if (lineInfo != null && lineInfo.HasLineInfo()) + if (reader is IXmlLineInfo lineInfo && lineInfo.HasLineInfo()) { s += " " + SR.Format(SR.XmlLineInfo, lineInfo.LineNumber, lineInfo.LinePosition); } @@ -142,6 +140,12 @@ public static void ThrowMaxArrayLengthExceeded(XmlDictionaryReader reader, int m ThrowXmlException(reader, SR.XmlMaxArrayLengthExceeded, maxArrayLength.ToString(NumberFormatInfo.CurrentInfo)); } + [DoesNotReturn] + public static void ThrowMaxArrayLengthOrMaxItemsQuotaExceeded(XmlDictionaryReader reader, int maxQuota) + { + ThrowXmlException(reader, SR.XmlMaxArrayLengthOrMaxItemsQuotaExceeded, maxQuota.ToString(NumberFormatInfo.CurrentInfo)); + } + [DoesNotReturn] public static void ThrowMaxBytesPerReadExceeded(XmlDictionaryReader reader, int maxBytesPerRead) { @@ -160,6 +164,12 @@ public static void ThrowMaxStringContentLengthExceeded(XmlDictionaryReader reade ThrowXmlException(reader, SR.XmlMaxStringContentLengthExceeded, maxStringContentLength.ToString(NumberFormatInfo.CurrentInfo)); } + [DoesNotReturn] + public static void ThrowMaxNameTableCharCountExceeded(XmlDictionaryReader reader, int maxNameTableCharCount) + { + ThrowXmlException(reader, SR.XmlMaxNameTableCharCountExceeded, maxNameTableCharCount.ToString(NumberFormatInfo.CurrentInfo)); + } + [DoesNotReturn] public static void ThrowBase64DataExpected(XmlDictionaryReader reader) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlSigningNodeWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlSigningNodeWriter.cs index 7fb89ae6c825d4..9f24a0fc8f7568 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlSigningNodeWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlSigningNodeWriter.cs @@ -64,7 +64,7 @@ public override void Close() public override void WriteDeclaration() { _writer.WriteDeclaration(); - XmlCanonicalWriter.WriteDeclaration(); + _signingWriter.WriteDeclaration(); } public override void WriteComment(string text) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlStreamNodeWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlStreamNodeWriter.cs index 6075d0a9c608e6..f5c218aa755c89 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlStreamNodeWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlStreamNodeWriter.cs @@ -10,7 +10,6 @@ namespace System.Xml { internal abstract class XmlStreamNodeWriter : XmlNodeWriter { - private Stream _stream = null!; // initialized by SetOutput private readonly byte[] _buffer; private int _offset; private bool _ownsStream; @@ -22,16 +21,22 @@ internal abstract class XmlStreamNodeWriter : XmlNodeWriter protected XmlStreamNodeWriter() { _buffer = new byte[bufferLength]; + OutputStream = null!; // Always initialized by SetOutput() } protected void SetOutput(Stream stream, bool ownsStream, Encoding? encoding) { - _stream = stream; + OutputStream = stream; _ownsStream = ownsStream; _offset = 0; - _encoding = encoding; + + if (encoding != null) + _encoding = encoding; } + // Getting/Setting the Stream exists for fragmenting + public Stream OutputStream { get; set; } + // StreamBuffer/BufferOffset exists only for the BinaryWriter to fix up nodes public byte[] StreamBuffer { @@ -52,19 +57,7 @@ public int Position { get { - return (int)_stream.Position + _offset; - } - } - - private int GetByteCount(char[] chars) - { - if (_encoding == null) - { - return s_UTF8Encoding.GetByteCount(chars); - } - else - { - return _encoding.GetByteCount(chars); + return (int)OutputStream.Position + _offset; } } @@ -225,7 +218,7 @@ public void WriteBytes(byte[] byteBuffer, int byteOffset, int byteCount) else { FlushBuffer(); - _stream.Write(byteBuffer, byteOffset, byteCount); + OutputStream.Write(byteBuffer, byteOffset, byteCount); } } @@ -237,14 +230,14 @@ protected unsafe void UnsafeWriteBytes(byte* bytes, int byteCount) { for (int i = 0; i < bufferLength; i++) buffer[i] = bytes[i]; - _stream.Write(buffer, 0, bufferLength); + OutputStream.Write(buffer, 0, bufferLength); bytes += bufferLength; byteCount -= bufferLength; } { for (int i = 0; i < byteCount; i++) buffer[i] = bytes[i]; - _stream.Write(buffer, 0, byteCount); + OutputStream.Write(buffer, 0, byteCount); } } @@ -282,7 +275,7 @@ protected void WriteUTF8Chars(byte[] chars, int charOffset, int charCount) else { FlushBuffer(); - _stream.Write(chars, charOffset, charCount); + OutputStream.Write(chars, charOffset, charCount); } } @@ -369,12 +362,7 @@ protected unsafe int UnsafeGetUTF8Length(char* chars, int charCount) if (chars == charsMax) return charCount; - char[] chArray = new char[charsMax - chars]; - for (int i = 0; i < chArray.Length; i++) - { - chArray[i] = chars[i]; - } - return (int)(chars - (charsMax - charCount)) + GetByteCount(chArray); + return (int)(chars - (charsMax - charCount)) + (_encoding ?? s_UTF8Encoding).GetByteCount(chars, (int)(charsMax - chars)); } protected unsafe int UnsafeGetUTF8Chars(char* chars, int charCount, byte[] buffer, int offset) @@ -425,7 +413,7 @@ protected virtual void FlushBuffer() { if (_offset != 0) { - _stream.Write(_buffer, 0, _offset); + OutputStream.Write(_buffer, 0, _offset); _offset = 0; } } @@ -434,7 +422,7 @@ protected virtual Task FlushBufferAsync() { if (_offset != 0) { - var task = _stream.WriteAsync(_buffer, 0, _offset); + var task = OutputStream.WriteAsync(_buffer, 0, _offset); _offset = 0; return task; } @@ -445,24 +433,24 @@ protected virtual Task FlushBufferAsync() public override void Flush() { FlushBuffer(); - _stream.Flush(); + OutputStream.Flush(); } public override async Task FlushAsync() { await FlushBufferAsync().ConfigureAwait(false); - await _stream.FlushAsync().ConfigureAwait(false); + await OutputStream.FlushAsync().ConfigureAwait(false); } public override void Close() { - if (_stream != null) + if (OutputStream != null) { if (_ownsStream) { - _stream.Dispose(); + OutputStream.Dispose(); } - _stream = null!; + OutputStream = null!; } } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlUTF8TextWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlUTF8TextWriter.cs index 2a1314d1a8792a..16b9b3d61bd7f6 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlUTF8TextWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlUTF8TextWriter.cs @@ -14,7 +14,7 @@ public interface IXmlTextWriterInitializer internal sealed class XmlUTF8TextWriter : XmlBaseWriter, IXmlTextWriterInitializer { - private XmlUTF8NodeWriter? _writer; + private XmlUTF8NodeWriter _writer = null!; // initialized in SetOutput public void SetOutput(Stream stream, Encoding encoding, bool ownsStream) { @@ -31,6 +31,15 @@ public void SetOutput(Stream stream, Encoding encoding, bool ownsStream) SetOutput(_writer); } + public override bool CanFragment + { + get + { + // Fragmenting only works for utf8 + return _writer.Encoding == null; + } + } + protected override XmlSigningNodeWriter CreateSigningNodeWriter() { return new XmlSigningNodeWriter(true); @@ -94,6 +103,14 @@ public XmlUTF8NodeWriter(bool[] isEscapedAttributeChar, bool[] isEscapedElementC _inAttribute = false; } + public Encoding? Encoding + { + get + { + return _encoding; + } + } + private byte[] GetCharEntityBuffer() => _entityChars ??= new byte[maxEntityLength]; private char[] GetCharBuffer(int charCount) @@ -686,7 +703,7 @@ public override void WriteUInt64Text(ulong value) { int offset; byte[] buffer = GetBuffer(XmlConverter.MaxUInt64Chars, out offset); - Advance(XmlConverter.ToChars((double)value, buffer, offset)); + Advance(XmlConverter.ToChars(value, buffer, offset)); } public override void WriteGuidText(Guid value) diff --git a/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.cs b/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.cs index 014c498f6eace0..668170766c98cc 100644 --- a/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.cs +++ b/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.cs @@ -46,6 +46,8 @@ public DataContractJsonSerializer(System.Type type, System.Xml.XmlDictionaryStri public int MaxItemsInObjectGraph { get { throw null; } } public bool SerializeReadOnlyTypes { get { throw null; } } public bool UseSimpleDictionaryFormat { get { throw null; } } + public System.Runtime.Serialization.ISerializationSurrogateProvider? GetSerializationSurrogateProvider() { throw null; } + public void SetSerializationSurrogateProvider(System.Runtime.Serialization.ISerializationSurrogateProvider? provider) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] public override bool IsStartObject(System.Xml.XmlDictionaryReader reader) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] diff --git a/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.csproj b/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.csproj index 927772e02ceb86..350ad3eebfc99d 100644 --- a/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.csproj +++ b/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.csproj @@ -2,12 +2,14 @@ $(NetCoreAppCurrent) + + - - - + + + diff --git a/src/libraries/System.Runtime.Serialization.Primitives/ref/System.Runtime.Serialization.Primitives.cs b/src/libraries/System.Runtime.Serialization.Primitives/ref/System.Runtime.Serialization.Primitives.cs index 92749bc455abec..63f1abbba95ae9 100644 --- a/src/libraries/System.Runtime.Serialization.Primitives/ref/System.Runtime.Serialization.Primitives.cs +++ b/src/libraries/System.Runtime.Serialization.Primitives/ref/System.Runtime.Serialization.Primitives.cs @@ -76,6 +76,13 @@ public partial interface ISerializationSurrogateProvider object GetObjectToSerialize(object obj, System.Type targetType); System.Type GetSurrogateType(System.Type type); } + public interface ISerializationSurrogateProvider2 : ISerializationSurrogateProvider + { + object? GetCustomDataToExport(Reflection.MemberInfo memberInfo, Type dataContractType); + object? GetCustomDataToExport(Type runtimeType, Type dataContractType); + void GetKnownCustomDataTypes(Collections.ObjectModel.Collection customDataTypes); + Type? GetReferencedTypeOnImport(string typeName, string typeNamespace, object? customData); + } [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Struct, Inherited=true, AllowMultiple=true)] public sealed partial class KnownTypeAttribute : System.Attribute { diff --git a/src/libraries/System.Runtime.Serialization.Primitives/src/System.Runtime.Serialization.Primitives.csproj b/src/libraries/System.Runtime.Serialization.Primitives/src/System.Runtime.Serialization.Primitives.csproj index 13aff766d38dd4..bd7267ae26b97c 100644 --- a/src/libraries/System.Runtime.Serialization.Primitives/src/System.Runtime.Serialization.Primitives.csproj +++ b/src/libraries/System.Runtime.Serialization.Primitives/src/System.Runtime.Serialization.Primitives.csproj @@ -11,6 +11,7 @@ + diff --git a/src/libraries/System.Runtime.Serialization.Primitives/src/System/Runtime/Serialization/ISerializationSurrogateProvider.cs b/src/libraries/System.Runtime.Serialization.Primitives/src/System/Runtime/Serialization/ISerializationSurrogateProvider.cs index 6ff2e2c8994515..571fc45de92d1d 100644 --- a/src/libraries/System.Runtime.Serialization.Primitives/src/System/Runtime/Serialization/ISerializationSurrogateProvider.cs +++ b/src/libraries/System.Runtime.Serialization.Primitives/src/System/Runtime/Serialization/ISerializationSurrogateProvider.cs @@ -3,10 +3,39 @@ namespace System.Runtime.Serialization { + /// + /// Provides the methods needed to substitute one type for another by DataContractSerializer during serialization + /// and deserialization. This interface together with (and + /// `System.Runtime.Serialization.Schema.ISerializationCodeDomSurrogateProvider`) replace + /// the `IDataContractSurrogate` from .Net 4.8. + /// public interface ISerializationSurrogateProvider { + /// + /// During serialization, deserialization, and schema import and export, returns a data contract type that substitutes the specified type. + /// (Formerly known as `GetDataContractType` on the .Net 4.8 `IDataContractSurrogate` interface.) + /// + /// The runtime type to substitute. + /// The to substitute for the type value. This type must be serializable by the DataContractSerializer. For example, + /// it must be marked with the DataContractAttribute attribute or other mechanisms that the serializer recognizes. Type GetSurrogateType(Type type); + + /// + /// During serialization, returns an object that substitutes the specified object. + /// + /// The object to substitute. + /// The that the substituted object should be assigned to. + /// The substituted object that will be serialized. The object must be serializable by the DataContractSerializer. For example, + /// it must be marked with the attribute or other mechanisms that the serializer recognizes. object GetObjectToSerialize(object obj, Type targetType); + + /// + /// During deserialization, returns an object that is a substitute for the specified object. + /// + /// The deserialized object to be substituted. + /// The that the substituted object should be assigned to. + /// The substituted deserialized object. This object must be of a type that is serializable by the DataContractSerializer. For example, + /// it must be marked with the attribute or other mechanisms that the serializer recognizes. object GetDeserializedObject(object obj, Type targetType); } } diff --git a/src/libraries/System.Runtime.Serialization.Primitives/src/System/Runtime/Serialization/ISerializationSurrogateProvider2.cs b/src/libraries/System.Runtime.Serialization.Primitives/src/System/Runtime/Serialization/ISerializationSurrogateProvider2.cs new file mode 100644 index 00000000000000..0194c0f111a6ae --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Primitives/src/System/Runtime/Serialization/ISerializationSurrogateProvider2.cs @@ -0,0 +1,48 @@ +// 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.ObjectModel; +using System.Reflection; + +namespace System.Runtime.Serialization +{ + /// + /// Provides the methods needed to substitute one type for another by DataContractSerializer during export + /// and import of XML schema documents (XSD). This interface builds upon . + /// Together (along with `System.Runtime.Serialization.Schema.ISerializationCodeDomSurrogateProvider`), these + /// interfaces replace the `IDataContractSurrogate` from .Net 4.8. + /// + public interface ISerializationSurrogateProvider2 : ISerializationSurrogateProvider + { + /// + /// During schema export operations, inserts annotations into the schema for non-null return values. + /// + /// A that describes the member. + /// The data contract type to be annotated. + /// An object that represents the annotation to be inserted into the XML schema definition. + object? GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType); + + /// + /// During schema export operations, inserts annotations into the schema for non-null return values. + /// + /// The runtime type to be replaced. + /// The data contract type to be annotated. + /// An object that represents the annotation to be inserted into the XML schema definition. + object? GetCustomDataToExport(Type runtimeType, Type dataContractType); + + /// + /// Sets the collection of known types to use for serialization and deserialization of the custom data objects. + /// + /// A of to add known types to. + void GetKnownCustomDataTypes(Collection customDataTypes); + + /// + /// During schema import, returns the type referenced by the schema. + /// + /// The name of the type in schema. + /// The namespace of the type in schema. + /// The object that represents the annotation inserted into the XML schema definition, which is data that can be used for finding the referenced type. + /// The to use for the referenced type. + Type? GetReferencedTypeOnImport(string typeName, string typeNamespace, object? customData); + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/Directory.Build.props b/src/libraries/System.Runtime.Serialization.Schema/Directory.Build.props new file mode 100644 index 00000000000000..4a2bc78c16e30a --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/Directory.Build.props @@ -0,0 +1,8 @@ + + + + true + + browser;ios;tvos;maccatalyst + + \ No newline at end of file diff --git a/src/libraries/System.Runtime.Serialization.Schema/System.Runtime.Serialization.Schema.sln b/src/libraries/System.Runtime.Serialization.Schema/System.Runtime.Serialization.Schema.sln new file mode 100644 index 00000000000000..b273f4e5bf6169 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/System.Runtime.Serialization.Schema.sln @@ -0,0 +1,51 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{A83DE19F-4E19-4FEE-A3D7-FBA9E065B186}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.Serialization.Schema", "ref\System.Runtime.Serialization.Schema.csproj", "{74AE27CF-E940-4EEB-9A19-0968689B627E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.Serialization.Schema", "src\System.Runtime.Serialization.Schema.csproj", "{14D5A803-D5BF-44E5-B2B5-0B0BC297748E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.Serialization.Schema.Tests", "tests\System.Runtime.Serialization.Schema.Tests.csproj", "{627FFEFC-A317-4AD1-809F-B26CA7F475BD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{2D7D470B-B092-45BC-900A-CDB20CA94BE7}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{54321C0F-1323-4962-A01C-AC07028C3FA8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{6591788E-0894-4655-AE2F-602407C4F766}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A83DE19F-4E19-4FEE-A3D7-FBA9E065B186}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A83DE19F-4E19-4FEE-A3D7-FBA9E065B186}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A83DE19F-4E19-4FEE-A3D7-FBA9E065B186}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A83DE19F-4E19-4FEE-A3D7-FBA9E065B186}.Release|Any CPU.Build.0 = Release|Any CPU + {74AE27CF-E940-4EEB-9A19-0968689B627E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {74AE27CF-E940-4EEB-9A19-0968689B627E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {74AE27CF-E940-4EEB-9A19-0968689B627E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {74AE27CF-E940-4EEB-9A19-0968689B627E}.Release|Any CPU.Build.0 = Release|Any CPU + {14D5A803-D5BF-44E5-B2B5-0B0BC297748E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {14D5A803-D5BF-44E5-B2B5-0B0BC297748E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {14D5A803-D5BF-44E5-B2B5-0B0BC297748E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {14D5A803-D5BF-44E5-B2B5-0B0BC297748E}.Release|Any CPU.Build.0 = Release|Any CPU + {627FFEFC-A317-4AD1-809F-B26CA7F475BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {627FFEFC-A317-4AD1-809F-B26CA7F475BD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {627FFEFC-A317-4AD1-809F-B26CA7F475BD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {627FFEFC-A317-4AD1-809F-B26CA7F475BD}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {A83DE19F-4E19-4FEE-A3D7-FBA9E065B186} = {2D7D470B-B092-45BC-900A-CDB20CA94BE7} + {627FFEFC-A317-4AD1-809F-B26CA7F475BD} = {2D7D470B-B092-45BC-900A-CDB20CA94BE7} + {74AE27CF-E940-4EEB-9A19-0968689B627E} = {54321C0F-1323-4962-A01C-AC07028C3FA8} + {14D5A803-D5BF-44E5-B2B5-0B0BC297748E} = {6591788E-0894-4655-AE2F-602407C4F766} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {70EC5780-3C80-4D52-93B0-7FBF64E29572} + EndGlobalSection +EndGlobal diff --git a/src/libraries/System.Runtime.Serialization.Schema/ref/System.Runtime.Serialization.Schema.cs b/src/libraries/System.Runtime.Serialization.Schema/ref/System.Runtime.Serialization.Schema.cs new file mode 100644 index 00000000000000..5f1c074aaf6dd0 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/ref/System.Runtime.Serialization.Schema.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ------------------------------------------------------------------------------ +// Changes to this file must follow the https://aka.ms/api-review process. +// ------------------------------------------------------------------------------ + +namespace System.Runtime.Serialization +{ + public partial interface ISerializationCodeDomSurrogateProvider + { + System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit); + } + public partial class ImportOptions + { + public System.CodeDom.Compiler.CodeDomProvider? CodeProvider { get { throw null; } set { throw null; } } + public bool EnableDataBinding { get { throw null; } set { throw null; } } + public ISerializationSurrogateProvider? DataContractSurrogate { get { throw null; } set { throw null; } } + public bool GenerateInternal { get { throw null; } set { throw null; } } + public bool GenerateSerializable { get { throw null; } set { throw null; } } + public bool ImportXmlType { get { throw null; } set { throw null; } } + public System.Collections.Generic.IDictionary Namespaces { get { throw null; } } + public System.Collections.Generic.ICollection ReferencedCollectionTypes { get { throw null; } } + public System.Collections.Generic.ICollection ReferencedTypes { get { throw null; } } + } + public partial class XsdDataContractImporter + { + public XsdDataContractImporter() { throw null; } + public XsdDataContractImporter(System.CodeDom.CodeCompileUnit codeCompileUnit) { throw null; } + + public System.CodeDom.CodeCompileUnit CodeCompileUnit { get { throw null; } } + public ImportOptions? Options { get { throw null; } set { throw null; } } + + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public bool CanImport(System.Xml.Schema.XmlSchemaSet schemas) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public bool CanImport(System.Xml.Schema.XmlSchemaSet schemas, System.Collections.Generic.ICollection typeNames) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public bool CanImport(System.Xml.Schema.XmlSchemaSet schemas, System.Xml.XmlQualifiedName typeName) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public bool CanImport(System.Xml.Schema.XmlSchemaSet schemas, System.Xml.Schema.XmlSchemaElement element) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public System.CodeDom.CodeTypeReference GetCodeTypeReference(System.Xml.XmlQualifiedName typeName) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public System.CodeDom.CodeTypeReference GetCodeTypeReference(System.Xml.XmlQualifiedName typeName, System.Xml.Schema.XmlSchemaElement element) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public System.Collections.Generic.ICollection? GetKnownTypeReferences(System.Xml.XmlQualifiedName typeName) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public void Import(System.Xml.Schema.XmlSchemaSet schemas) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public void Import(System.Xml.Schema.XmlSchemaSet schemas, System.Collections.Generic.ICollection typeNames) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public void Import(System.Xml.Schema.XmlSchemaSet schemas, System.Xml.XmlQualifiedName typeName) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public System.Xml.XmlQualifiedName? Import(System.Xml.Schema.XmlSchemaSet schemas, System.Xml.Schema.XmlSchemaElement element) { throw null; } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/ref/System.Runtime.Serialization.Schema.csproj b/src/libraries/System.Runtime.Serialization.Schema/ref/System.Runtime.Serialization.Schema.csproj new file mode 100644 index 00000000000000..1e7317def2573f --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/ref/System.Runtime.Serialization.Schema.csproj @@ -0,0 +1,13 @@ + + + $(NetCoreAppCurrent) + + + + + + + + + + \ No newline at end of file diff --git a/src/libraries/System.Runtime.Serialization.Schema/src/Resources/Strings.resx b/src/libraries/System.Runtime.Serialization.Schema/src/Resources/Strings.resx new file mode 100644 index 00000000000000..54cb0b117a74e4 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/src/Resources/Strings.resx @@ -0,0 +1,181 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Invalid '{0}' annotation in type '{1}' from namespace '{2}'. Attribute '{3}' not present. + + + Cannot export null assembly provided via '{0}' parameter. + + + Cannot export null type provided via KnownTypesCollection. + + + Cannot export null type provided via '{0}' parameter. + + + Existing type '{0}' specified via the referenced types collection has been referenced in the generated code. Members cannot be added for this type since it cannot be modified. + + + Existing type '{0}' specified via the referenced types collection has been referenced in the generated code. Cannot set namespace for this type since it cannot be modified. + + + Type '{0}' cannot be exported as a schema type because it is an open generic type. You can only export a generic type if all its generic parameter types are actual types. + + + Type '{0}' from namespace '{1}' has not been imported from schema. Consider first importing this type by calling one of the Import methods on XsdDataContractImporter. + + + + A unique name cannot be computed for '{0}' because there are already Int32.MaxValue types of with the same name. + + + Type with data contract name '{0}' in namespace '{1}' cannot be imported. Cannot derive from sealed referenced type '{2}'. + + + Collection type cannot be generated for type '{0}' from namespace '{1}'. Cannot use a generic list type as a base type because the language does not support generic type references. + + + It contains a circular reference for type '{0}' from namespace '{1}'. + + + CLR namespace '{2}' has already been mapped to data contract namespace '{0}'. It cannot be mapped to another data contract namespace '{1}'. + + + ISerializable type with data contract name '{0}' in namespace '{1}' cannot be imported. The data contract name cannot be customized for ISerializable type and the generated name '{2}' does not match the expected name '{0}'. Check if the required name has been mapped to a different type or if it is an invalid CLR name which cannot be generated or if the type requires an outer type which is not present. + + + ISerializable type with data contract name '{0}' in namespace '{1}' cannot be imported. The data contract namespace cannot be customized for ISerializable types and the generated namespace '{3}' does not match the required CLR namespace '{2}'. Check if the required namespace has been mapped to a different data contract namespace and consider mapping it explicitly using the namespaces collection. + + + Collection type cannot be generated for type '{0}' from namespace '{1}'. Rename the type to '{2}' in namespace '{3}' or reference an existing collection type that implements '{4}' or '{5}' which can be used as a base type for the generated collection. + + + Referenced type '{0}' with data contract name '{1}' in namespace '{2}' cannot be used since it does not match imported DataContract. Need to exclude this type from referenced types. + + + Type '{0}' in namespace '{1}' cannot be imported. {2} + + + Schema type '{2}' in namespace '{3}' must be imported as an XML type. Type '{0}' cannot be mapped to this schema type because it does not implement '{1}'. Consider not adding type '{0}' to the list of referenced types or changing it to implement '{1}'. + + + An internal error has occurred. Unexpected contract type '{0}' for type '{1}' encountered. + + diff --git a/src/libraries/System.Runtime.Serialization.Schema/src/System.Runtime.Serialization.Schema.csproj b/src/libraries/System.Runtime.Serialization.Schema/src/System.Runtime.Serialization.Schema.csproj new file mode 100644 index 00000000000000..d1d56008051099 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/src/System.Runtime.Serialization.Schema.csproj @@ -0,0 +1,28 @@ + + + $(NetCoreAppCurrent) + Microsoft + + true + Provides support for importing and exporting xsd schemas for DataContractSerializer. + +Commonly Used Types: +System.Runtime.Serialization.Schema.XsdDataContractExporter +System.Runtime.Serialization.Schema.XsdDataContractImporter + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/CodeExporter.cs b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/CodeExporter.cs new file mode 100644 index 00000000000000..45476be4423f10 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/CodeExporter.cs @@ -0,0 +1,2033 @@ +// 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.CodeDom; +using System.CodeDom.Compiler; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Reflection; +using System.Runtime.Serialization.DataContracts; +using System.Security; +using System.Text; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; + +using DataContractDictionary = System.Collections.Generic.Dictionary; +using ExceptionUtil = System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility; + +namespace System.Runtime.Serialization +{ + internal sealed class CodeExporter + { + private const string WildcardNamespaceMapping = "*"; + private const string TypeNameFieldName = "typeName"; + private const int MaxIdentifierLength = 511; + + private static readonly object s_codeUserDataActualTypeKey = new object(); + private static readonly object s_surrogateDataKey = typeof(ISerializationSurrogateProvider2); + + private DataContractSet _dataContractSet; + private CodeCompileUnit _codeCompileUnit; + private ImportOptions? _options; + private Dictionary _namespaces; + private Dictionary _clrNamespaces; + + internal CodeExporter(DataContractSet dataContractSet, ImportOptions? options, CodeCompileUnit codeCompileUnit) + { + _dataContractSet = dataContractSet; + _codeCompileUnit = codeCompileUnit; + AddReferencedAssembly(Assembly.GetExecutingAssembly()); + _options = options; + _namespaces = new Dictionary(); + _clrNamespaces = new Dictionary(StringComparer.OrdinalIgnoreCase); + + // Update namespace tables for DataContract(s) that are already processed + foreach (KeyValuePair pair in dataContractSet.Contracts) + { + DataContract dataContract = pair.Value; + if (!(dataContract.IsBuiltInDataContract || dataContract.Is(DataContractType.ClassDataContract))) + { + ContractCodeDomInfo contractCodeDomInfo = GetContractCodeDomInfo(dataContract); + if (contractCodeDomInfo.IsProcessed && !contractCodeDomInfo.UsesWildcardNamespace) + { + string? clrNamespace = contractCodeDomInfo.ClrNamespace; + if (clrNamespace != null && !_clrNamespaces.ContainsKey(clrNamespace)) + { + _clrNamespaces.Add(clrNamespace, dataContract.XmlName.Namespace); + _namespaces.Add(dataContract.XmlName.Namespace, clrNamespace); + } + } + } + } + + // Copy options.Namespaces to namespace tables + if (_options != null) + { + foreach (KeyValuePair pair in _options.Namespaces) + { + string dataContractNamespace = pair.Key; + string clrNamespace = pair.Value; + if (clrNamespace == null) + clrNamespace = string.Empty; + + string? currentDataContractNamespace; + if (_clrNamespaces.TryGetValue(clrNamespace, out currentDataContractNamespace)) + { + if (dataContractNamespace != currentDataContractNamespace) + throw ExceptionUtil.ThrowHelperError(new InvalidOperationException(SR.Format(SR.CLRNamespaceMappedMultipleTimes, currentDataContractNamespace, dataContractNamespace, clrNamespace))); + } + else + _clrNamespaces.Add(clrNamespace, dataContractNamespace); + + string? currentClrNamespace; + if (_namespaces.TryGetValue(dataContractNamespace, out currentClrNamespace)) + { + if (clrNamespace != currentClrNamespace) + { + _namespaces.Remove(dataContractNamespace); + _namespaces.Add(dataContractNamespace, clrNamespace); + } + } + else + _namespaces.Add(dataContractNamespace, clrNamespace); + } + } + + // Update namespace tables for pre-existing namespaces in CodeCompileUnit + foreach (CodeNamespace codeNS in codeCompileUnit.Namespaces) + { + string ns = codeNS.Name ?? string.Empty; + if (!_clrNamespaces.ContainsKey(ns)) + { + _clrNamespaces.Add(ns, null); + } + if (ns.Length == 0) + { + foreach (CodeTypeDeclaration codeTypeDecl in codeNS.Types) + { + AddGlobalTypeName(codeTypeDecl.Name); + } + } + } + + } + + private void AddReferencedAssembly(Assembly assembly) + { + bool alreadyExisting = false; +#pragma warning disable IL3000 // Avoid accessing Assembly file path when publishing as a single file + string assemblyName = System.IO.Path.GetFileName(assembly.Location); + if (string.IsNullOrWhiteSpace(assemblyName)) + assemblyName = $"[{assembly.FullName}]"; +#pragma warning restore IL3000 // Avoid accessing Assembly file path when publishing as a single file + + foreach (string? existingName in _codeCompileUnit.ReferencedAssemblies) + { + if (string.Equals(existingName, assemblyName, StringComparison.OrdinalIgnoreCase)) + { + alreadyExisting = true; + break; + } + } + if (!alreadyExisting) + _codeCompileUnit.ReferencedAssemblies.Add(assemblyName); + + } + + private bool GenerateSerializableTypes + { + get { return (_options == null) ? false : _options.GenerateSerializable; } + } + + private bool GenerateInternalTypes + { + get { return (_options == null) ? false : _options.GenerateInternal; } + } + + private bool EnableDataBinding + { + get { return (_options == null) ? false : _options.EnableDataBinding; } + } + + private CodeDomProvider? CodeProvider + { + get { return _options?.CodeProvider; } + } + + private bool SupportsDeclareEvents + { + get { return (CodeProvider == null) ? true : CodeProvider.Supports(GeneratorSupport.DeclareEvents); } + } + + private bool SupportsDeclareValueTypes + { + get { return (CodeProvider == null) ? true : CodeProvider.Supports(GeneratorSupport.DeclareValueTypes); } + } + + private bool SupportsGenericTypeReference + { + get { return (CodeProvider == null) ? true : CodeProvider.Supports(GeneratorSupport.GenericTypeReference); } + } + + private bool SupportsAssemblyAttributes + { + get { return (CodeProvider == null) ? true : CodeProvider.Supports(GeneratorSupport.AssemblyAttributes); } + } + + private bool SupportsPartialTypes + { + get { return (CodeProvider == null) ? true : CodeProvider.Supports(GeneratorSupport.PartialTypes); } + } + + private bool SupportsNestedTypes + { + get { return (CodeProvider == null) ? true : CodeProvider.Supports(GeneratorSupport.NestedTypes); } + } + + private string FileExtension + { + get { return (CodeProvider == null) ? string.Empty : CodeProvider.FileExtension; } + } + + private Dictionary Namespaces + { + get { return _namespaces; } + } + + private Dictionary ClrNamespaces + { + get { return _clrNamespaces; } + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + internal void Export() + { + try + { + foreach (KeyValuePair pair in _dataContractSet.Contracts) + { + DataContract dataContract = pair.Value; + if (dataContract.IsBuiltInDataContract) + continue; + + ContractCodeDomInfo contractCodeDomInfo = GetContractCodeDomInfo(dataContract); + if (!contractCodeDomInfo.IsProcessed) + { + switch (dataContract.GetContractType()) + { + case DataContractType.ClassDataContract: + if (dataContract.IsISerializable) + ExportISerializableDataContract(dataContract, contractCodeDomInfo); + else + ExportClassDataContractHierarchy(dataContract.XmlName, dataContract, contractCodeDomInfo, new Dictionary()); + break; + case DataContractType.CollectionDataContract: + ExportCollectionDataContract(dataContract, contractCodeDomInfo); + break; + case DataContractType.EnumDataContract: + ExportEnumDataContract(dataContract, contractCodeDomInfo); + break; + default: + if (dataContract is XmlDataContract xmlDataContract) + ExportXmlDataContract(xmlDataContract, contractCodeDomInfo); + else + throw ExceptionUtil.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, GetClrTypeFullName(dataContract.GetType()), GetClrTypeFullName(dataContract.UnderlyingType)))); + break; + }; + contractCodeDomInfo.IsProcessed = true; + } + } + + if (_options?.DataContractSurrogate is ISerializationCodeDomSurrogateProvider cdSurrogateProvider) + { + CodeNamespace[] namespaces = new CodeNamespace[_codeCompileUnit.Namespaces.Count]; + _codeCompileUnit.Namespaces.CopyTo(namespaces, 0); + foreach (CodeNamespace codeNamespace in namespaces) + InvokeProcessImportedType(codeNamespace.Types, cdSurrogateProvider); + } + } + finally + { + CodeGenerator.ValidateIdentifiers(_codeCompileUnit); + } + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private void ExportClassDataContractHierarchy(XmlQualifiedName typeName, DataContract classContract, ContractCodeDomInfo contractCodeDomInfo, Dictionary contractNamesInHierarchy) + { + Debug.Assert(classContract.Is(DataContractType.ClassDataContract)); + + if (contractNamesInHierarchy.ContainsKey(classContract.XmlName)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.TypeCannotBeImported, typeName.Name, typeName.Namespace, SR.Format(SR.CircularTypeReference, classContract.XmlName.Name, classContract.XmlName.Namespace)))); + contractNamesInHierarchy.Add(classContract.XmlName, null); + + DataContract? baseContract = classContract.BaseContract; + if (baseContract != null) + { + ContractCodeDomInfo baseContractCodeDomInfo = GetContractCodeDomInfo(baseContract); + if (!baseContractCodeDomInfo.IsProcessed) + { + ExportClassDataContractHierarchy(typeName, baseContract, baseContractCodeDomInfo, contractNamesInHierarchy); + baseContractCodeDomInfo.IsProcessed = true; + } + } + ExportClassDataContract(classContract, contractCodeDomInfo); + } + + private void InvokeProcessImportedType(CollectionBase collection, ISerializationCodeDomSurrogateProvider surrogateProvider) + { + object[] objects = new object[collection.Count]; + ((ICollection)collection).CopyTo(objects, 0); + + foreach (object obj in objects) + { + if (obj is CodeTypeDeclaration codeTypeDeclaration) + { + CodeTypeDeclaration? newCodeTypeDeclaration = surrogateProvider.ProcessImportedType(codeTypeDeclaration, _codeCompileUnit); + + if (newCodeTypeDeclaration != codeTypeDeclaration) + { + ((IList)collection).Remove(codeTypeDeclaration); + if (newCodeTypeDeclaration != null) + ((IList)collection).Add(newCodeTypeDeclaration); + } + if (newCodeTypeDeclaration != null) + InvokeProcessImportedType(newCodeTypeDeclaration.Members, surrogateProvider); + } + } + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + internal CodeTypeReference GetCodeTypeReference(DataContract dataContract) + { + if (dataContract.IsBuiltInDataContract) + return GetCodeTypeReference(dataContract.UnderlyingType); + + ContractCodeDomInfo contractCodeDomInfo = GetContractCodeDomInfo(dataContract); + GenerateType(dataContract, contractCodeDomInfo); + + // This is supposed to be set by GenerateType. If it wasn't, there is a problem. + Debug.Assert(contractCodeDomInfo.TypeReference != null); + return contractCodeDomInfo.TypeReference!; + } + + private CodeTypeReference GetCodeTypeReference(Type type) + { + AddReferencedAssembly(type.Assembly); + return new CodeTypeReference(type); + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private CodeTypeReference? GetCodeTypeReference(Type type, IList? parameters) + { + CodeTypeReference codeTypeReference = GetCodeTypeReference(type); + + if (parameters != null) + { + foreach (var param in parameters) + { + CodeTypeReference? paramTypeReference = null; + bool isParamValueType = true; // Default not important. It either gets set, or paramTypeReference stays null and we short circuit before referencing this value. + + if (param is DataContract paramContract) + { + paramTypeReference = GetCodeTypeReference(paramContract); + isParamValueType = paramContract.IsValueType; + } + else if (param is Tuple typeParameters) + { + paramTypeReference = GetCodeTypeReference(typeParameters.Item1, typeParameters.Item2); + isParamValueType = (paramTypeReference != null && paramTypeReference.ArrayRank == 0); // only value type information we can get from CodeTypeReference + } + + if (paramTypeReference == null) + return null; + if (type == typeof(Nullable<>) && !isParamValueType) + return paramTypeReference; + else + codeTypeReference.TypeArguments.Add(paramTypeReference); + } + } + + return codeTypeReference; + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + internal CodeTypeReference GetElementTypeReference(DataContract dataContract, bool isElementTypeNullable) + { + CodeTypeReference elementTypeReference = GetCodeTypeReference(dataContract); + if (dataContract.IsValueType && isElementTypeNullable) + elementTypeReference = WrapNullable(elementTypeReference); + return elementTypeReference; + } + + [SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Part of a logical set of properties, some of which are not static. May not remain static with future implementation updates.")] + private XmlQualifiedName GenericListName + { + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + get { return DataContract.GetXmlName(typeof(List<>)); } + } + + private DataContract GenericListContract + { + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + get { return _dataContractSet.GetDataContract(typeof(List<>)); } + } + + [SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Part of a logical set of properties, some of which are not static. May not remain static with future implementation updates.")] + private XmlQualifiedName GenericDictionaryName + { + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + get { return DataContract.GetXmlName(typeof(Dictionary<,>)); } + } + + private DataContract GenericDictionaryContract + { + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + get { return _dataContractSet.GetDataContract(typeof(Dictionary<,>)); } + } + + private ContractCodeDomInfo GetContractCodeDomInfo(DataContract dataContract) + { + ContractCodeDomInfo? contractCodeDomInfo = null; + if (_dataContractSet.ProcessedContracts.TryGetValue(dataContract, out object? info)) + contractCodeDomInfo = info as ContractCodeDomInfo; + if (contractCodeDomInfo == null) + { + contractCodeDomInfo = new ContractCodeDomInfo(); + _dataContractSet.ProcessedContracts.Add(dataContract, contractCodeDomInfo); + } + return contractCodeDomInfo; + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private void GenerateType(DataContract dataContract, ContractCodeDomInfo contractCodeDomInfo) + { + if (!contractCodeDomInfo.IsProcessed) + { + CodeTypeReference? referencedType = GetReferencedType(dataContract); + if (referencedType != null) + { + contractCodeDomInfo.TypeReference = referencedType; + contractCodeDomInfo.ReferencedTypeExists = true; + } + else + { + CodeTypeDeclaration? type = contractCodeDomInfo.TypeDeclaration; + if (type == null) + { + string clrNamespace = GetClrNamespace(dataContract, contractCodeDomInfo); + CodeNamespace ns = GetCodeNamespace(clrNamespace, dataContract.XmlName.Namespace, contractCodeDomInfo); + type = GetNestedType(dataContract, contractCodeDomInfo); + if (type == null) + { + string typeName = XmlConvert.DecodeName(dataContract.XmlName.Name); + typeName = GetClrIdentifier(typeName, ImportGlobals.DefaultTypeName); + if (NamespaceContainsType(ns, typeName) || GlobalTypeNameConflicts(clrNamespace, typeName)) + { + for (int i = 1;; i++) + { + string uniqueName = AppendToValidClrIdentifier(typeName, i.ToString(NumberFormatInfo.InvariantInfo)); + if (!NamespaceContainsType(ns, uniqueName) && !GlobalTypeNameConflicts(clrNamespace, uniqueName)) + { + typeName = uniqueName; + break; + } + if (i == int.MaxValue) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.CannotComputeUniqueName, typeName))); + } + } + + type = CreateTypeDeclaration(typeName, dataContract); + ns.Types.Add(type); + if (string.IsNullOrEmpty(clrNamespace)) + { + AddGlobalTypeName(typeName); + } + contractCodeDomInfo.TypeReference = new CodeTypeReference((clrNamespace == null || clrNamespace.Length == 0) ? typeName : clrNamespace + "." + typeName); + + if (GenerateInternalTypes) + type.TypeAttributes = TypeAttributes.NotPublic; + else + type.TypeAttributes = TypeAttributes.Public; + } + + if (_options?.DataContractSurrogate != null) + type.UserData.Add(s_surrogateDataKey, _dataContractSet.SurrogateData[dataContract]); + + contractCodeDomInfo.TypeDeclaration = type; + } + } + } + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private CodeTypeDeclaration? GetNestedType(DataContract dataContract, ContractCodeDomInfo contractCodeDomInfo) + { + if (!SupportsNestedTypes) + return null; + string originalName = dataContract.XmlName.Name; + int nestedTypeIndex = originalName.LastIndexOf('.'); + if (nestedTypeIndex <= 0) + return null; + string containingTypeName = originalName.Substring(0, nestedTypeIndex); + DataContract? containingDataContract = _dataContractSet.GetDataContract(new XmlQualifiedName(containingTypeName, dataContract.XmlName.Namespace)); + if (containingDataContract == null) + return null; + string nestedTypeName = XmlConvert.DecodeName(originalName.Substring(nestedTypeIndex + 1)); + nestedTypeName = GetClrIdentifier(nestedTypeName, ImportGlobals.DefaultTypeName); + + ContractCodeDomInfo containingContractCodeDomInfo = GetContractCodeDomInfo(containingDataContract); + GenerateType(containingDataContract, containingContractCodeDomInfo); + if (containingContractCodeDomInfo.ReferencedTypeExists) + return null; + + CodeTypeDeclaration containingType = containingContractCodeDomInfo.TypeDeclaration!; // Nested types by definition have containing types. + if (TypeContainsNestedType(containingType, nestedTypeName)) + { + for (int i = 1;; i++) + { + string uniqueName = AppendToValidClrIdentifier(nestedTypeName, i.ToString(NumberFormatInfo.InvariantInfo)); + if (!TypeContainsNestedType(containingType, uniqueName)) + { + nestedTypeName = uniqueName; + break; + } + } + } + + CodeTypeDeclaration type = CreateTypeDeclaration(nestedTypeName, dataContract); + containingType.Members.Add(type); + contractCodeDomInfo.TypeReference = new CodeTypeReference(containingContractCodeDomInfo.TypeReference!.BaseType + "+" + nestedTypeName); // Again, nested types by definition have containing types. + + if (GenerateInternalTypes) + type.TypeAttributes = TypeAttributes.NestedAssembly; + else + type.TypeAttributes = TypeAttributes.NestedPublic; + return type; + } + + private static CodeTypeDeclaration CreateTypeDeclaration(string typeName, DataContract dataContract) + { + CodeTypeDeclaration typeDecl = new CodeTypeDeclaration(typeName); + CodeAttributeDeclaration debuggerStepThroughAttribute = new CodeAttributeDeclaration(typeof(System.Diagnostics.DebuggerStepThroughAttribute).FullName!); + CodeAttributeDeclaration generatedCodeAttribute = new CodeAttributeDeclaration(typeof(GeneratedCodeAttribute).FullName!); + + AssemblyName assemblyName = Assembly.GetExecutingAssembly().GetName(); + generatedCodeAttribute.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(assemblyName.Name))); + generatedCodeAttribute.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(assemblyName.Version?.ToString()))); + + // System.Diagnostics.DebuggerStepThroughAttribute not allowed on enums + // ensure that the attribute is only generated on types that are not enums + if (!dataContract.Is(DataContractType.EnumDataContract)) + { + typeDecl.CustomAttributes.Add(debuggerStepThroughAttribute); + } + typeDecl.CustomAttributes.Add(generatedCodeAttribute); + return typeDecl; + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private CodeTypeReference? GetReferencedType(DataContract dataContract) + { + CodeTypeReference? typeReference = GetSurrogatedTypeReference(dataContract); + if (typeReference != null) + return typeReference; + + Type? type = _dataContractSet.GetReferencedType(dataContract.XmlName, dataContract, out DataContract? referencedContract, out object[]? parameters, SupportsGenericTypeReference); + if (type != null && !type.IsGenericTypeDefinition && !type.ContainsGenericParameters) + { + if (dataContract is XmlDataContract xmlContract) + { + if (typeof(IXmlSerializable).IsAssignableFrom(type)) + { + if (xmlContract.IsTypeDefinedOnImport) + { + if (!xmlContract.Equals(_dataContractSet.GetDataContract(type))) + throw ExceptionUtil.ThrowHelperError(new InvalidOperationException(SR.Format(SR.ReferencedTypeDoesNotMatch, type.AssemblyQualifiedName, dataContract.XmlName.Name, dataContract.XmlName.Namespace))); + } + else + { + xmlContract.IsValueType = type.IsValueType; + xmlContract.IsTypeDefinedOnImport = true; + } + return GetCodeTypeReference(type); + } + throw ExceptionUtil.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.TypeMustBeIXmlSerializable, GetClrTypeFullName(type), GetClrTypeFullName(typeof(IXmlSerializable)), dataContract.XmlName.Name, dataContract.XmlName.Namespace))); + } + referencedContract = _dataContractSet.GetDataContract(type); + if (referencedContract.Equals(dataContract)) + { + typeReference = GetCodeTypeReference(type); + typeReference.UserData.Add(s_codeUserDataActualTypeKey, type); + return typeReference; + } + throw ExceptionUtil.ThrowHelperError(new InvalidOperationException(SR.Format(SR.ReferencedTypeDoesNotMatch, type.AssemblyQualifiedName, dataContract.XmlName.Name, dataContract.XmlName.Namespace))); + } + else if (type != null) + { + typeReference = GetCodeTypeReference(type, parameters); + + if (referencedContract != null && !referencedContract.Equals(dataContract)) + { + Debug.Assert(typeReference != null); + type = (Type?)typeReference.UserData[s_codeUserDataActualTypeKey]; + throw ExceptionUtil.ThrowHelperError(new InvalidOperationException(SR.Format(SR.ReferencedTypeDoesNotMatch, + type?.AssemblyQualifiedName, + referencedContract.XmlName.Name, + referencedContract.XmlName.Namespace))); + } + + return typeReference; + } + else if (referencedContract != null) + { + typeReference = GetCodeTypeReference(referencedContract); + return typeReference; + } + + return GetReferencedCollectionType(dataContract.As(DataContractType.CollectionDataContract)); + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private CodeTypeReference? GetReferencedCollectionType(DataContract? collectionContract) + { + if (collectionContract == null) + return null; + + Debug.Assert(collectionContract.Is(DataContractType.CollectionDataContract)); + + if (HasDefaultCollectionNames(collectionContract)) + { + CodeTypeReference? typeReference; + if (!TryGetReferencedDictionaryType(collectionContract, out typeReference)) + { + // ItemContract - aka BaseContract - is never null for CollectionDataContract + DataContract itemContract = collectionContract.BaseContract!; + if (collectionContract.IsDictionaryLike(out _, out _, out _)) + { + GenerateKeyValueType(itemContract.As(DataContractType.ClassDataContract)); + } + bool isItemTypeNullable = collectionContract.IsItemTypeNullable(); + if (!TryGetReferencedListType(itemContract, isItemTypeNullable, out typeReference)) + { + CodeTypeReference? elementTypeReference = GetElementTypeReference(itemContract, isItemTypeNullable); + if (elementTypeReference != null) + typeReference = new CodeTypeReference(elementTypeReference, 1); + } + } + return typeReference; + } + return null; + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private static bool HasDefaultCollectionNames(DataContract collectionContract) + { + Debug.Assert(collectionContract.Is(DataContractType.CollectionDataContract)); + + // ItemContract - aka BaseContract - is never null for CollectionDataContract + DataContract itemContract = collectionContract.BaseContract!; + bool isDictionary = collectionContract.IsDictionaryLike(out string? keyName, out string? valueName, out string? itemName); + if (itemName != itemContract.XmlName.Name) + return false; + + if (isDictionary && (keyName != ImportGlobals.KeyLocalName || valueName != ImportGlobals.ValueLocalName)) + return false; + + XmlQualifiedName expectedType = itemContract.GetArrayTypeName(collectionContract.IsItemTypeNullable()); + return (collectionContract.XmlName.Name == expectedType.Name && collectionContract.XmlName.Namespace == expectedType.Namespace); + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private bool TryGetReferencedDictionaryType(DataContract collectionContract, [NotNullWhen(true)] out CodeTypeReference? typeReference) + { + Debug.Assert(collectionContract.Is(DataContractType.CollectionDataContract)); + + // Check if it is a dictionary and use referenced dictionary type if present + if (collectionContract.IsDictionaryLike(out _, out _, out _) + && SupportsGenericTypeReference) + { + Type? type = _dataContractSet.GetReferencedType(GenericDictionaryName, GenericDictionaryContract, out DataContract? _, out object[]? _) ?? typeof(Dictionary<,>); + + // ItemContract - aka BaseContract - is never null for CollectionDataContract + DataContract? itemContract = collectionContract.BaseContract!.As(DataContractType.ClassDataContract); + + // A dictionary should have a Key/Value item contract that has at least two members: key and value. + Debug.Assert(itemContract != null); + Debug.Assert(itemContract.DataMembers.Count >= 2); + + DataMember keyMember = itemContract.DataMembers[0]; + DataMember valueMember = itemContract.DataMembers[1]; + CodeTypeReference? keyTypeReference = GetElementTypeReference(keyMember.MemberTypeContract, keyMember.IsNullable); + CodeTypeReference? valueTypeReference = GetElementTypeReference(valueMember.MemberTypeContract, valueMember.IsNullable); + if (keyTypeReference != null && valueTypeReference != null) + { + typeReference = GetCodeTypeReference(type); + typeReference.TypeArguments.Add(keyTypeReference); + typeReference.TypeArguments.Add(valueTypeReference); + return true; + } + } + typeReference = null; + return false; + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private bool TryGetReferencedListType(DataContract itemContract, bool isItemTypeNullable, out CodeTypeReference? typeReference) + { + if (SupportsGenericTypeReference) + { + Type? type = _dataContractSet.GetReferencedType(GenericListName, GenericListContract, out DataContract? _, out object[]? _); + if (type != null) + { + typeReference = GetCodeTypeReference(type); + typeReference.TypeArguments.Add(GetElementTypeReference(itemContract, isItemTypeNullable)!); // Lists have an item type + return true; + } + } + typeReference = null; + return false; + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private CodeTypeReference? GetSurrogatedTypeReference(DataContract dataContract) + { + Type? type = GetReferencedTypeOnImport(dataContract); + if (type != null) + { + CodeTypeReference typeReference = GetCodeTypeReference(type); + typeReference.UserData.Add(s_codeUserDataActualTypeKey, type); + return typeReference; + } + return null; + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private Type? GetReferencedTypeOnImport(DataContract dataContract) + { + Type? type = null; + if (_options?.DataContractSurrogate is ISerializationSurrogateProvider2 surrogateProvider) + { + if (DataContract.GetBuiltInDataContract(dataContract.XmlName.Name, dataContract.XmlName.Namespace) == null) + type = surrogateProvider.GetReferencedTypeOnImport(dataContract.XmlName.Name, dataContract.XmlName.Namespace, _dataContractSet.SurrogateData[dataContract]); + } + return type; + } + + private static bool NamespaceContainsType(CodeNamespace ns, string typeName) + { + foreach (CodeTypeDeclaration type in ns.Types) + { + if (string.Equals(typeName, type.Name, StringComparison.OrdinalIgnoreCase)) + return true; + } + return false; + } + + private bool GlobalTypeNameConflicts(string clrNamespace, string typeName) + { + return (string.IsNullOrEmpty(clrNamespace) && _clrNamespaces.ContainsKey(typeName)); + } + + private void AddGlobalTypeName(string typeName) + { + if (!_clrNamespaces.ContainsKey(typeName)) + { + _clrNamespaces.Add(typeName, null); + } + } + + private static bool TypeContainsNestedType(CodeTypeDeclaration containingType, string typeName) + { + foreach (CodeTypeMember member in containingType.Members) + { + if (member is CodeTypeDeclaration declaration) + { + if (string.Equals(typeName, declaration.Name, StringComparison.OrdinalIgnoreCase)) + return true; + } + } + return false; + } + + private static string GetNameForAttribute(string name) + { + string decodedName = XmlConvert.DecodeName(name); + if (string.CompareOrdinal(name, decodedName) == 0) + return name; + string reencodedName = SchemaImportHelper.EncodeLocalName(decodedName); + return (string.CompareOrdinal(name, reencodedName) == 0) ? decodedName : name; + } + + private void AddSerializableAttribute(bool generateSerializable, CodeTypeDeclaration type, ContractCodeDomInfo contractCodeDomInfo) + { + if (generateSerializable) + { + type.CustomAttributes.Add(SerializableAttribute); + AddImportStatement(typeof(SerializableAttribute).Namespace, contractCodeDomInfo.CodeNamespace); + } + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private void ExportClassDataContract(DataContract classDataContract, ContractCodeDomInfo contractCodeDomInfo) + { + Debug.Assert(classDataContract.Is(DataContractType.ClassDataContract)); + + GenerateType(classDataContract, contractCodeDomInfo); + if (contractCodeDomInfo.ReferencedTypeExists) + return; + + // This is supposed to be set by GenerateType. If it wasn't, there is a problem. + Debug.Assert(contractCodeDomInfo.TypeDeclaration != null); + + CodeTypeDeclaration type = contractCodeDomInfo.TypeDeclaration; + if (SupportsPartialTypes) + type.IsPartial = true; + if (classDataContract.IsValueType && SupportsDeclareValueTypes) + type.IsStruct = true; + else + type.IsClass = true; + + string dataContractName = GetNameForAttribute(classDataContract.XmlName.Name); + CodeAttributeDeclaration dataContractAttribute = new CodeAttributeDeclaration(GetClrTypeFullName(typeof(DataContractAttribute))); + dataContractAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.NameProperty, new CodePrimitiveExpression(dataContractName))); + dataContractAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.NamespaceProperty, new CodePrimitiveExpression(classDataContract.XmlName.Namespace))); + if (classDataContract.IsReference != ImportGlobals.DefaultIsReference) + dataContractAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.IsReferenceProperty, new CodePrimitiveExpression(classDataContract.IsReference))); + type.CustomAttributes.Add(dataContractAttribute); + AddImportStatement(typeof(DataContractAttribute).Namespace, contractCodeDomInfo.CodeNamespace); + + AddSerializableAttribute(GenerateSerializableTypes, type, contractCodeDomInfo); + + AddKnownTypes(classDataContract, contractCodeDomInfo); + + bool raisePropertyChanged = EnableDataBinding && SupportsDeclareEvents; + if (classDataContract.BaseContract == null) + { + if (!type.IsStruct) + type.BaseTypes.Add(typeof(object)); + AddExtensionData(contractCodeDomInfo); + AddPropertyChangedNotifier(contractCodeDomInfo, type.IsStruct); + } + else + { + ContractCodeDomInfo baseContractCodeDomInfo = GetContractCodeDomInfo(classDataContract.BaseContract); + Debug.Assert(baseContractCodeDomInfo.IsProcessed, "Cannot generate code for type if code for base type has not been generated"); + type.BaseTypes.Add(baseContractCodeDomInfo.TypeReference); + AddBaseMemberNames(baseContractCodeDomInfo, contractCodeDomInfo); + if (baseContractCodeDomInfo.ReferencedTypeExists) + { + Type? actualType = (Type?)baseContractCodeDomInfo.TypeReference?.UserData[s_codeUserDataActualTypeKey]; + Debug.Assert(actualType != null); // If we're in this if condition, then we should be able to get a Type + ThrowIfReferencedBaseTypeSealed(actualType, classDataContract); + if (!typeof(IExtensibleDataObject).IsAssignableFrom(actualType)) + AddExtensionData(contractCodeDomInfo); + if (!typeof(INotifyPropertyChanged).IsAssignableFrom(actualType)) + { + AddPropertyChangedNotifier(contractCodeDomInfo, type.IsStruct); + } + else + { + raisePropertyChanged = false; + } + } + } + + for (int i = 0; i < classDataContract.DataMembers.Count; i++) + { + DataMember dataMember = classDataContract.DataMembers[i]; + + CodeTypeReference memberType = GetElementTypeReference(dataMember.MemberTypeContract, + (dataMember.IsNullable && dataMember.MemberTypeContract.IsValueType)); + + string dataMemberName = GetNameForAttribute(dataMember.Name); + string propertyName = GetMemberName(dataMemberName, contractCodeDomInfo); + string fieldName = GetMemberName(AppendToValidClrIdentifier(propertyName, ImportGlobals.DefaultFieldSuffix), contractCodeDomInfo); + + CodeMemberField field = new CodeMemberField(); + field.Type = memberType; + field.Name = fieldName; + field.Attributes = MemberAttributes.Private; + + CodeMemberProperty property = CreateProperty(memberType, propertyName, fieldName, dataMember.MemberTypeContract.IsValueType && SupportsDeclareValueTypes, raisePropertyChanged); + object? surrogateData = _dataContractSet.SurrogateData[dataMember]; + if (surrogateData != null) + property.UserData.Add(s_surrogateDataKey, surrogateData); + + CodeAttributeDeclaration dataMemberAttribute = new CodeAttributeDeclaration(GetClrTypeFullName(typeof(DataMemberAttribute))); + if (dataMemberName != property.Name) + dataMemberAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.NameProperty, new CodePrimitiveExpression(dataMemberName))); + if (dataMember.IsRequired != ImportGlobals.DefaultIsRequired) + dataMemberAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.IsRequiredProperty, new CodePrimitiveExpression(dataMember.IsRequired))); + if (dataMember.EmitDefaultValue != ImportGlobals.DefaultEmitDefaultValue) + dataMemberAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.EmitDefaultValueProperty, new CodePrimitiveExpression(dataMember.EmitDefaultValue))); + if (dataMember.Order != ImportGlobals.DefaultOrder) + dataMemberAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.OrderProperty, new CodePrimitiveExpression(dataMember.Order))); + property.CustomAttributes.Add(dataMemberAttribute); + + if (GenerateSerializableTypes && !dataMember.IsRequired) + { + CodeAttributeDeclaration optionalFieldAttribute = new CodeAttributeDeclaration(GetClrTypeFullName(typeof(OptionalFieldAttribute))); + field.CustomAttributes.Add(optionalFieldAttribute); + } + + type.Members.Add(field); + type.Members.Add(property); + } + } + + private bool CanDeclareAssemblyAttribute(ContractCodeDomInfo contractCodeDomInfo) + { + return SupportsAssemblyAttributes && !contractCodeDomInfo.UsesWildcardNamespace; + } + + private static bool NeedsExplicitNamespace(string dataContractNamespace, string clrNamespace) + { + return (SchemaImportHelper.GetDefaultXmlNamespace(clrNamespace) != dataContractNamespace); + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + internal ICollection? GetKnownTypeReferences(DataContract dataContract) + { + DataContractDictionary? knownTypeDictionary = GetKnownTypeContracts(dataContract); + if (knownTypeDictionary == null) + return null; + + ICollection? knownTypeContracts = knownTypeDictionary.Values; + if (knownTypeContracts == null || knownTypeContracts.Count == 0) + return null; + + List knownTypeReferences = new List(); + foreach (DataContract knownTypeContract in knownTypeContracts) + { + knownTypeReferences.Add(GetCodeTypeReference(knownTypeContract)); + } + return knownTypeReferences; + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private DataContractDictionary? GetKnownTypeContracts(DataContract dataContract) + { + if (_dataContractSet.KnownTypesForObject != null && IsObjectContract(dataContract)) + { + return _dataContractSet.KnownTypesForObject; + } + else if (dataContract.Is(DataContractType.ClassDataContract)) + { + ContractCodeDomInfo contractCodeDomInfo = GetContractCodeDomInfo(dataContract); + if (!contractCodeDomInfo.IsProcessed) + GenerateType(dataContract, contractCodeDomInfo); + if (contractCodeDomInfo.ReferencedTypeExists) + return GetKnownTypeContracts(dataContract, new Dictionary()); + } + return null; + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private DataContractDictionary? GetKnownTypeContracts(DataContract classDataContract, Dictionary handledContracts) + { + Debug.Assert(classDataContract.Is(DataContractType.ClassDataContract)); + + if (handledContracts.ContainsKey(classDataContract)) + return classDataContract.KnownDataContracts; + + handledContracts.Add(classDataContract, null); + bool objectMemberHandled = false; + foreach (DataMember dataMember in classDataContract.DataMembers) + { + DataContract memberContract = dataMember.MemberTypeContract; + if (!objectMemberHandled && _dataContractSet.KnownTypesForObject != null && IsObjectContract(memberContract)) + { + AddKnownTypeContracts(classDataContract, _dataContractSet.KnownTypesForObject); + objectMemberHandled = true; + } + else if (memberContract.Is(DataContractType.ClassDataContract)) + { + ContractCodeDomInfo memberCodeDomInfo = GetContractCodeDomInfo(memberContract); + if (!memberCodeDomInfo.IsProcessed) + GenerateType(memberContract, memberCodeDomInfo); + if (memberCodeDomInfo.ReferencedTypeExists) + { + AddKnownTypeContracts(classDataContract, GetKnownTypeContracts(memberContract, handledContracts)); + } + } + } + + return classDataContract.KnownDataContracts; + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private static void AddKnownTypeContracts(DataContract classDataContract, DataContractDictionary? knownContracts) + { + if (knownContracts == null || knownContracts.Count == 0) + return; + + // This is a ClassDataContract. As such, it's KnownDataContracts collection is always non-null. + Debug.Assert(classDataContract.Is(DataContractType.ClassDataContract)); + Debug.Assert(classDataContract.KnownDataContracts != null); + + foreach (KeyValuePair pair in knownContracts) + { + if (classDataContract.XmlName != pair.Key && !classDataContract.KnownDataContracts.ContainsKey(pair.Key) && !pair.Value.IsBuiltInDataContract) + classDataContract.KnownDataContracts.Add(pair.Key, pair.Value); + } + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private void AddKnownTypes(DataContract classDataContract, ContractCodeDomInfo contractCodeDomInfo) + { + Debug.Assert(classDataContract.Is(DataContractType.ClassDataContract)); + + DataContractDictionary? knownContractDictionary = GetKnownTypeContracts(classDataContract, new Dictionary()); + if (knownContractDictionary == null || knownContractDictionary.Count == 0) + return; + + ICollection knownTypeContracts = knownContractDictionary.Values; + foreach (DataContract knownTypeContract in knownTypeContracts) + { + // This is only called from methods that first call GenerateType to fill in this info. + Debug.Assert(contractCodeDomInfo.TypeDeclaration != null); + + CodeAttributeDeclaration knownTypeAttribute = new CodeAttributeDeclaration(GetClrTypeFullName(typeof(KnownTypeAttribute))); + knownTypeAttribute.Arguments.Add(new CodeAttributeArgument(new CodeTypeOfExpression(GetCodeTypeReference(knownTypeContract)))); + contractCodeDomInfo.TypeDeclaration.CustomAttributes.Add(knownTypeAttribute); + } + AddImportStatement(typeof(KnownTypeAttribute).Namespace, contractCodeDomInfo.CodeNamespace); + } + + private CodeTypeReference WrapNullable(CodeTypeReference memberType) + { + if (!SupportsGenericTypeReference) + return memberType; + + CodeTypeReference nullableOfMemberType = GetCodeTypeReference(typeof(Nullable<>)); + nullableOfMemberType.TypeArguments.Add(memberType); + return nullableOfMemberType; + } + + private void AddExtensionData(ContractCodeDomInfo contractCodeDomInfo) + { + if (contractCodeDomInfo.TypeDeclaration != null) + { + CodeTypeDeclaration type = contractCodeDomInfo.TypeDeclaration; + type.BaseTypes.Add(GetClrTypeFullName(typeof(IExtensibleDataObject))); + CodeMemberField extensionDataObjectField = ExtensionDataObjectField; + if (GenerateSerializableTypes) + { + CodeAttributeDeclaration nonSerializedAttribute = new CodeAttributeDeclaration(GetClrTypeFullName(typeof(NonSerializedAttribute))); + extensionDataObjectField.CustomAttributes.Add(nonSerializedAttribute); + } + type.Members.Add(extensionDataObjectField); + contractCodeDomInfo.AddMemberName(extensionDataObjectField.Name); + CodeMemberProperty extensionDataObjectProperty = ExtensionDataObjectProperty; + type.Members.Add(extensionDataObjectProperty); + contractCodeDomInfo.AddMemberName(extensionDataObjectProperty.Name); + } + } + + private void AddPropertyChangedNotifier(ContractCodeDomInfo contractCodeDomInfo, bool isValueType) + { + if (EnableDataBinding && SupportsDeclareEvents && contractCodeDomInfo.TypeDeclaration != null) + { + CodeTypeDeclaration codeTypeDeclaration = contractCodeDomInfo.TypeDeclaration; + codeTypeDeclaration.BaseTypes.Add(CodeTypeIPropertyChange); + CodeMemberEvent memberEvent = PropertyChangedEvent; + codeTypeDeclaration.Members.Add(memberEvent); + CodeMemberMethod raisePropertyChangedEventMethod = RaisePropertyChangedEventMethod; + if (!isValueType) + raisePropertyChangedEventMethod.Attributes |= MemberAttributes.Family; + codeTypeDeclaration.Members.Add(raisePropertyChangedEventMethod); + contractCodeDomInfo.AddMemberName(memberEvent.Name); + contractCodeDomInfo.AddMemberName(raisePropertyChangedEventMethod.Name); + } + } + + private static void ThrowIfReferencedBaseTypeSealed(Type baseType, DataContract dataContract) + { + if (baseType.IsSealed) + throw ExceptionUtil.ThrowHelperError(new InvalidOperationException(SR.Format(SR.CannotDeriveFromSealedReferenceType, dataContract.XmlName.Name, dataContract.XmlName.Namespace, GetClrTypeFullName(baseType)))); + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private void ExportEnumDataContract(DataContract enumDataContract, ContractCodeDomInfo contractCodeDomInfo) + { + Debug.Assert(enumDataContract.Is(DataContractType.EnumDataContract)); + + GenerateType(enumDataContract, contractCodeDomInfo); + if (contractCodeDomInfo.ReferencedTypeExists) + return; + + // This is supposed to be set by GenerateType. If it wasn't, there is a problem. + Debug.Assert(contractCodeDomInfo.TypeDeclaration != null); + + CodeTypeDeclaration type = contractCodeDomInfo.TypeDeclaration; + // BaseContract is never null for EnumDataContract + Type baseType = enumDataContract.BaseContract!.UnderlyingType; + type.IsEnum = true; + type.BaseTypes.Add(baseType); + if (baseType.IsDefined(typeof(FlagsAttribute), false)) + { + type.CustomAttributes.Add(new CodeAttributeDeclaration(GetClrTypeFullName(typeof(FlagsAttribute)))); + AddImportStatement(typeof(FlagsAttribute).Namespace, contractCodeDomInfo.CodeNamespace); + } + + string dataContractName = GetNameForAttribute(enumDataContract.XmlName.Name); + CodeAttributeDeclaration dataContractAttribute = new CodeAttributeDeclaration(GetClrTypeFullName(typeof(DataContractAttribute))); + dataContractAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.NameProperty, new CodePrimitiveExpression(dataContractName))); + dataContractAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.NamespaceProperty, new CodePrimitiveExpression(enumDataContract.XmlName.Namespace))); + type.CustomAttributes.Add(dataContractAttribute); + AddImportStatement(typeof(DataContractAttribute).Namespace, contractCodeDomInfo.CodeNamespace); + + for (int i = 0; i < enumDataContract.DataMembers.Count; i++) + { + string stringValue = enumDataContract.DataMembers[i].Name; + long longValue = enumDataContract.DataMembers[i].Order; // Members[] and Values[] go hand in hand. + + CodeMemberField enumMember = new CodeMemberField(); + if (baseType == typeof(ulong)) + enumMember.InitExpression = new CodeSnippetExpression(XmlConvert.ToString((ulong)longValue)); + else + enumMember.InitExpression = new CodePrimitiveExpression(longValue); + enumMember.Name = GetMemberName(stringValue, contractCodeDomInfo); + CodeAttributeDeclaration enumMemberAttribute = new CodeAttributeDeclaration(GetClrTypeFullName(typeof(EnumMemberAttribute))); + if (enumMember.Name != stringValue) + enumMemberAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.ValueProperty, new CodePrimitiveExpression(stringValue))); + enumMember.CustomAttributes.Add(enumMemberAttribute); + type.Members.Add(enumMember); + } + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private void ExportISerializableDataContract(DataContract classDataContract, ContractCodeDomInfo contractCodeDomInfo) + { + Debug.Assert(classDataContract.Is(DataContractType.ClassDataContract)); + + GenerateType(classDataContract, contractCodeDomInfo); + if (contractCodeDomInfo.ReferencedTypeExists) + return; + + if (SchemaImportHelper.GetDefaultXmlNamespace(contractCodeDomInfo.ClrNamespace) != classDataContract.XmlName.Namespace) + throw ExceptionUtil.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.InvalidClrNamespaceGeneratedForISerializable, classDataContract.XmlName.Name, classDataContract.XmlName.Namespace, SchemaImportHelper.GetDataContractNamespaceFromUri(classDataContract.XmlName.Namespace), contractCodeDomInfo.ClrNamespace))); + + string dataContractName = GetNameForAttribute(classDataContract.XmlName.Name); + int nestedTypeIndex = dataContractName.LastIndexOf('.'); + string expectedName = (nestedTypeIndex <= 0 || nestedTypeIndex == dataContractName.Length - 1) ? dataContractName : dataContractName.Substring(nestedTypeIndex + 1); + + // This is supposed to be set by GenerateType. If it wasn't, there is a problem. + Debug.Assert(contractCodeDomInfo.TypeDeclaration != null); + + if (contractCodeDomInfo.TypeDeclaration.Name != expectedName) + throw ExceptionUtil.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.InvalidClrNameGeneratedForISerializable, classDataContract.XmlName.Name, classDataContract.XmlName.Namespace, contractCodeDomInfo.TypeDeclaration.Name))); + + CodeTypeDeclaration type = contractCodeDomInfo.TypeDeclaration; + if (SupportsPartialTypes) + type.IsPartial = true; + if (classDataContract.IsValueType && SupportsDeclareValueTypes) + type.IsStruct = true; + else + type.IsClass = true; + + AddSerializableAttribute(true /*generateSerializable*/, type, contractCodeDomInfo); + + AddKnownTypes(classDataContract, contractCodeDomInfo); + + if (classDataContract.BaseContract == null) + { + if (!type.IsStruct) + type.BaseTypes.Add(typeof(object)); + type.BaseTypes.Add(GetClrTypeFullName(typeof(ISerializable))); + type.Members.Add(ISerializableBaseConstructor); + type.Members.Add(SerializationInfoField); + type.Members.Add(SerializationInfoProperty); + type.Members.Add(GetObjectDataMethod); + AddPropertyChangedNotifier(contractCodeDomInfo, type.IsStruct); + } + else + { + ContractCodeDomInfo baseContractCodeDomInfo = GetContractCodeDomInfo(classDataContract.BaseContract); + GenerateType(classDataContract.BaseContract, baseContractCodeDomInfo); + type.BaseTypes.Add(baseContractCodeDomInfo.TypeReference); + if (baseContractCodeDomInfo.ReferencedTypeExists) + { + Type? actualType = (Type?)baseContractCodeDomInfo.TypeReference?.UserData[s_codeUserDataActualTypeKey]; + Debug.Assert(actualType != null); // If we're in this if condition, then we should be able to get a Type + ThrowIfReferencedBaseTypeSealed(actualType, classDataContract); + } + type.Members.Add(ISerializableDerivedConstructor); + } + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private void GenerateKeyValueType(DataContract? keyValueContract) + { + Debug.Assert(keyValueContract == null || keyValueContract.Is(DataContractType.ClassDataContract)); + + // Add code for KeyValue item type in the case where its usage is limited to dictionary + // and dictionary is not found in referenced types + if (keyValueContract != null && _dataContractSet.GetDataContract(keyValueContract.XmlName) == null) + { + ContractCodeDomInfo? contractCodeDomInfo = null; + if (_dataContractSet.ProcessedContracts.TryGetValue(keyValueContract, out object? info)) + contractCodeDomInfo = info as ContractCodeDomInfo; + if (contractCodeDomInfo == null) + { + contractCodeDomInfo = new ContractCodeDomInfo(); + _dataContractSet.ProcessedContracts.Add(keyValueContract, contractCodeDomInfo); + ExportClassDataContract(keyValueContract, contractCodeDomInfo); + contractCodeDomInfo.IsProcessed = true; + } + } + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private void ExportCollectionDataContract(DataContract collectionContract, ContractCodeDomInfo contractCodeDomInfo) + { + Debug.Assert(collectionContract.Is(DataContractType.CollectionDataContract)); + + GenerateType(collectionContract, contractCodeDomInfo); + if (contractCodeDomInfo.ReferencedTypeExists) + return; + + string dataContractName = GetNameForAttribute(collectionContract.XmlName.Name); + + // If type name is not expected, generate collection type that derives from referenced list type and uses [CollectionDataContract] + if (!SupportsGenericTypeReference) + throw ExceptionUtil.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.CannotUseGenericTypeAsBase, dataContractName, + collectionContract.XmlName.Namespace))); + + // ItemContract - aka BaseContract - is never null for CollectionDataContract + DataContract itemContract = collectionContract.BaseContract!; + bool isItemTypeNullable = collectionContract.IsItemTypeNullable(); + bool isDictionary = collectionContract.IsDictionaryLike(out string? keyName, out string? valueName, out string? itemName); + + CodeTypeReference? baseTypeReference; + bool foundDictionaryBase = TryGetReferencedDictionaryType(collectionContract, out baseTypeReference); + if (!foundDictionaryBase) + { + if (isDictionary) + { + GenerateKeyValueType(itemContract.As(DataContractType.ClassDataContract)); + } + if (!TryGetReferencedListType(itemContract, isItemTypeNullable, out baseTypeReference)) + { + if (SupportsGenericTypeReference) + { + baseTypeReference = GetCodeTypeReference(typeof(List<>)); + baseTypeReference.TypeArguments.Add(GetElementTypeReference(itemContract, isItemTypeNullable)); + } + else + { + string expectedTypeName = ImportGlobals.ArrayPrefix + itemContract.XmlName.Name; + string expectedTypeNs = SchemaImportHelper.GetCollectionNamespace(itemContract.XmlName.Namespace); + throw ExceptionUtil.ThrowHelperError(new InvalidOperationException(SR.Format(SR.ReferencedBaseTypeDoesNotExist, + dataContractName, collectionContract.XmlName.Namespace, + expectedTypeName, expectedTypeNs, GetClrTypeFullName(typeof(IList<>)), GetClrTypeFullName(typeof(ICollection<>))))); + } + } + } + + // This is supposed to be set by GenerateType. If it wasn't, there is a problem. + Debug.Assert(contractCodeDomInfo.TypeDeclaration != null); + + CodeTypeDeclaration generatedType = contractCodeDomInfo.TypeDeclaration; + generatedType.BaseTypes.Add(baseTypeReference); + CodeAttributeDeclaration collectionContractAttribute = new CodeAttributeDeclaration(GetClrTypeFullName(typeof(CollectionDataContractAttribute))); + collectionContractAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.NameProperty, new CodePrimitiveExpression(dataContractName))); + collectionContractAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.NamespaceProperty, new CodePrimitiveExpression(collectionContract.XmlName.Namespace))); + if (collectionContract.IsReference != ImportGlobals.DefaultIsReference) + collectionContractAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.IsReferenceProperty, new CodePrimitiveExpression(collectionContract.IsReference))); + collectionContractAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.ItemNameProperty, new CodePrimitiveExpression(GetNameForAttribute(itemName!)))); // ItemName is never null for Collection contracts. + if (foundDictionaryBase) + { + // These are not null if we are working with a dictionary. See CollectionDataContract.IsDictionary + Debug.Assert(keyName != null); + Debug.Assert(valueName != null); + collectionContractAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.KeyNameProperty, new CodePrimitiveExpression(GetNameForAttribute(keyName)))); + collectionContractAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.ValueNameProperty, new CodePrimitiveExpression(GetNameForAttribute(valueName)))); + } + generatedType.CustomAttributes.Add(collectionContractAttribute); + AddImportStatement(typeof(CollectionDataContractAttribute).Namespace, contractCodeDomInfo.CodeNamespace); + AddSerializableAttribute(GenerateSerializableTypes, generatedType, contractCodeDomInfo); + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private void ExportXmlDataContract(XmlDataContract xmlDataContract, ContractCodeDomInfo contractCodeDomInfo) + { + GenerateType(xmlDataContract, contractCodeDomInfo); + if (contractCodeDomInfo.ReferencedTypeExists) + return; + + // This is supposed to be set by GenerateType. If it wasn't, there is a problem. + Debug.Assert(contractCodeDomInfo.TypeDeclaration != null); + + CodeTypeDeclaration type = contractCodeDomInfo.TypeDeclaration; + if (SupportsPartialTypes) + type.IsPartial = true; + if (xmlDataContract.IsValueType) + type.IsStruct = true; + else + { + type.IsClass = true; + type.BaseTypes.Add(typeof(object)); + } + AddSerializableAttribute(GenerateSerializableTypes, type, contractCodeDomInfo); + + type.BaseTypes.Add(GetClrTypeFullName(typeof(IXmlSerializable))); + + type.Members.Add(NodeArrayField); + type.Members.Add(NodeArrayProperty); + type.Members.Add(ReadXmlMethod); + type.Members.Add(WriteXmlMethod); + type.Members.Add(GetSchemaMethod); + if (xmlDataContract.IsAnonymous && !xmlDataContract.HasRoot) + { + type.CustomAttributes.Add(new CodeAttributeDeclaration( + GetClrTypeFullName(ImportGlobals.TypeOfXmlSchemaProviderAttribute), + new CodeAttributeArgument(NullReference), + new CodeAttributeArgument(ImportGlobals.IsAnyProperty, new CodePrimitiveExpression(true))) + ); + } + else + { + type.CustomAttributes.Add(new CodeAttributeDeclaration( + GetClrTypeFullName(ImportGlobals.TypeOfXmlSchemaProviderAttribute), + new CodeAttributeArgument(new CodePrimitiveExpression(ImportGlobals.ExportSchemaMethod))) + ); + + CodeMemberField typeNameField = new CodeMemberField(ImportGlobals.TypeOfXmlQualifiedName, TypeNameFieldName); + typeNameField.Attributes |= MemberAttributes.Static | MemberAttributes.Private; + XmlQualifiedName typeName = xmlDataContract.IsAnonymous + ? XsdDataContractImporter.ImportActualType(xmlDataContract.XsdType?.Annotation, xmlDataContract.XmlName, xmlDataContract.XmlName) + : xmlDataContract.XmlName; + typeNameField.InitExpression = new CodeObjectCreateExpression(ImportGlobals.TypeOfXmlQualifiedName, new CodePrimitiveExpression(typeName.Name), new CodePrimitiveExpression(typeName.Namespace)); + type.Members.Add(typeNameField); + + type.Members.Add(GetSchemaStaticMethod); + + bool isElementNameDifferent = + (xmlDataContract.TopLevelElementName != null && xmlDataContract.TopLevelElementName.Value != xmlDataContract.XmlName.Name) || + (xmlDataContract.TopLevelElementNamespace != null && xmlDataContract.TopLevelElementNamespace.Value != xmlDataContract.XmlName.Namespace); + if (isElementNameDifferent || xmlDataContract.IsTopLevelElementNullable == false) + { + CodeAttributeDeclaration xmlRootAttribute = new CodeAttributeDeclaration(GetClrTypeFullName(typeof(XmlRootAttribute))); + if (isElementNameDifferent) + { + if (xmlDataContract.TopLevelElementName != null) + { + xmlRootAttribute.Arguments.Add(new CodeAttributeArgument("ElementName", new CodePrimitiveExpression(xmlDataContract.TopLevelElementName.Value))); + } + if (xmlDataContract.TopLevelElementNamespace != null) + { + xmlRootAttribute.Arguments.Add(new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(xmlDataContract.TopLevelElementNamespace.Value))); + } + } + if (xmlDataContract.IsTopLevelElementNullable == false) + xmlRootAttribute.Arguments.Add(new CodeAttributeArgument("IsNullable", new CodePrimitiveExpression(false))); + type.CustomAttributes.Add(xmlRootAttribute); + } + } + AddPropertyChangedNotifier(contractCodeDomInfo, type.IsStruct); + } + + private CodeNamespace GetCodeNamespace(string clrNamespace, string dataContractNamespace, ContractCodeDomInfo contractCodeDomInfo) + { + if (contractCodeDomInfo.CodeNamespace != null) + return contractCodeDomInfo.CodeNamespace; + + CodeNamespaceCollection codeNamespaceCollection = _codeCompileUnit.Namespaces; + foreach (CodeNamespace ns in codeNamespaceCollection) + { + if (ns.Name == clrNamespace) + { + contractCodeDomInfo.CodeNamespace = ns; + return ns; + } + } + + CodeNamespace codeNamespace = new CodeNamespace(clrNamespace); + codeNamespaceCollection.Add(codeNamespace); + + if (CanDeclareAssemblyAttribute(contractCodeDomInfo) + && NeedsExplicitNamespace(dataContractNamespace, clrNamespace)) + { + CodeAttributeDeclaration namespaceAttribute = new CodeAttributeDeclaration(GetClrTypeFullName(typeof(ContractNamespaceAttribute))); + namespaceAttribute.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(dataContractNamespace))); + namespaceAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.ClrNamespaceProperty, new CodePrimitiveExpression(clrNamespace))); + _codeCompileUnit.AssemblyCustomAttributes.Add(namespaceAttribute); + } + contractCodeDomInfo.CodeNamespace = codeNamespace; + return codeNamespace; + } + + private static string GetMemberName(string memberName, ContractCodeDomInfo contractCodeDomInfo) + { + memberName = GetClrIdentifier(memberName, ImportGlobals.DefaultGeneratedMember); + + // This is only called from Export* methods which have already called GenerateType to fill in this info. + Debug.Assert(contractCodeDomInfo.TypeDeclaration != null); + + if (memberName == contractCodeDomInfo.TypeDeclaration.Name) + memberName = AppendToValidClrIdentifier(memberName, ImportGlobals.DefaultMemberSuffix); + + if (contractCodeDomInfo.GetMemberNames().Contains(memberName)) + { + string uniqueMemberName; + for (int i = 1;; i++) + { + uniqueMemberName = AppendToValidClrIdentifier(memberName, i.ToString(NumberFormatInfo.InvariantInfo)); + if (!contractCodeDomInfo.GetMemberNames().Contains(uniqueMemberName)) + { + memberName = uniqueMemberName; + break; + } + } + } + + contractCodeDomInfo.AddMemberName(memberName); + return memberName; + } + + private static void AddBaseMemberNames(ContractCodeDomInfo baseContractCodeDomInfo, ContractCodeDomInfo contractCodeDomInfo) + { + if (!baseContractCodeDomInfo.ReferencedTypeExists) + { + HashSet baseMemberNames = baseContractCodeDomInfo.GetMemberNames(); + HashSet memberNames = contractCodeDomInfo.GetMemberNames(); + foreach (string name in baseMemberNames) + { + memberNames.Add(name); + } + } + } + + private static string GetClrIdentifier(string identifier, string defaultIdentifier) + { + if (identifier.Length <= MaxIdentifierLength && CodeGenerator.IsValidLanguageIndependentIdentifier(identifier)) + return identifier; + + bool isStart = true; + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < identifier.Length && builder.Length < MaxIdentifierLength; i++) + { + char c = identifier[i]; + if (IsValid(c)) + { + if (isStart && !IsValidStart(c)) + builder.Append('_'); + builder.Append(c); + isStart = false; + } + } + if (builder.Length == 0) + return defaultIdentifier; + + return builder.ToString(); + } + + internal static string GetClrTypeFullName(Type type) + { + return !type.IsGenericTypeDefinition && type.ContainsGenericParameters ? type.Namespace + "." + type.Name : type.FullName!; + } + + private static string AppendToValidClrIdentifier(string identifier, string appendString) + { + int availableLength = MaxIdentifierLength - identifier.Length; + int requiredLength = appendString.Length; + if (availableLength < requiredLength) + identifier = identifier.Substring(0, MaxIdentifierLength - requiredLength); + identifier += appendString; + return identifier; + } + + private string GetClrNamespace(DataContract dataContract, ContractCodeDomInfo contractCodeDomInfo) + { + string? clrNamespace = contractCodeDomInfo.ClrNamespace; + bool usesWildcardNamespace = false; + if (clrNamespace == null) + { + if (!Namespaces.TryGetValue(dataContract.XmlName.Namespace, out clrNamespace)) + { + if (Namespaces.TryGetValue(WildcardNamespaceMapping, out clrNamespace)) + { + usesWildcardNamespace = true; + } + else + { + clrNamespace = GetClrNamespace(dataContract.XmlName.Namespace); + if (ClrNamespaces.ContainsKey(clrNamespace)) + { + string uniqueNamespace; + for (int i = 1;; i++) + { + uniqueNamespace = ((clrNamespace.Length == 0) ? ImportGlobals.DefaultClrNamespace : clrNamespace) + i.ToString(NumberFormatInfo.InvariantInfo); + if (!ClrNamespaces.ContainsKey(uniqueNamespace)) + { + clrNamespace = uniqueNamespace; + break; + } + } + } + AddNamespacePair(dataContract.XmlName.Namespace, clrNamespace); + } + } + contractCodeDomInfo.ClrNamespace = clrNamespace; + contractCodeDomInfo.UsesWildcardNamespace = usesWildcardNamespace; + } + return clrNamespace; + } + + private void AddNamespacePair(string dataContractNamespace, string clrNamespace) + { + Namespaces.Add(dataContractNamespace, clrNamespace); + ClrNamespaces.Add(clrNamespace, dataContractNamespace); + } + + private static void AddImportStatement(string? clrNamespace, CodeNamespace? codeNamespace) + { + // We don't expect these to be null when passed in, but they are usually properties on larger classes which declare their types as nullable and we can't control, so we allow nullable parameters. + Debug.Assert(clrNamespace != null); + Debug.Assert(codeNamespace != null); + + if (clrNamespace == codeNamespace.Name) + return; + + CodeNamespaceImportCollection importCollection = codeNamespace.Imports; + foreach (CodeNamespaceImport import in importCollection) + { + if (import.Namespace == clrNamespace) + return; + } + + importCollection.Add(new CodeNamespaceImport(clrNamespace)); + } + + private static string GetClrNamespace(string? dataContractNamespace) + { + if (dataContractNamespace == null || dataContractNamespace.Length == 0) + return string.Empty; + + StringBuilder builder = new StringBuilder(); + if (Uri.TryCreate(dataContractNamespace, UriKind.RelativeOrAbsolute, out Uri? uri)) + { + Dictionary fragments = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (!uri.IsAbsoluteUri) + AddToNamespace(builder, uri.OriginalString, fragments); + else + { + string uriString = uri.AbsoluteUri; + if (uriString.StartsWith(ImportGlobals.DataContractXsdBaseNamespace, StringComparison.Ordinal)) + AddToNamespace(builder, uriString.Substring(ImportGlobals.DataContractXsdBaseNamespace.Length), fragments); + else + { + string host = uri.Host; + if (host != null) + AddToNamespace(builder, host, fragments); + string path = uri.PathAndQuery; + if (path != null) + AddToNamespace(builder, path, fragments); + } + } + } + + if (builder.Length == 0) + return string.Empty; + + int length = builder.Length; + if (builder[builder.Length - 1] == '.') + length--; + length = Math.Min(MaxIdentifierLength, length); + + return builder.ToString(0, length); + } + + private static void AddToNamespace(StringBuilder builder, string? fragment, Dictionary fragments) + { + if (fragment == null) + return; + bool isStart = true; + int fragmentOffset = builder.Length; + int fragmentLength = 0; + + for (int i = 0; i < fragment.Length && builder.Length < MaxIdentifierLength; i++) + { + char c = fragment[i]; + + if (IsValid(c)) + { + if (isStart && !IsValidStart(c)) + builder.Append('_'); + builder.Append(c); + fragmentLength++; + isStart = false; + } + else if ((c == '.' || c == '/' || c == ':') && (builder.Length == 1 + || (builder.Length > 1 && builder[builder.Length - 1] != '.'))) + { + AddNamespaceFragment(builder, fragmentOffset, fragmentLength, fragments); + builder.Append('.'); + fragmentOffset = builder.Length; + fragmentLength = 0; + isStart = true; + } + } + AddNamespaceFragment(builder, fragmentOffset, fragmentLength, fragments); + } + + private static void AddNamespaceFragment(StringBuilder builder, int fragmentOffset, + int fragmentLength, Dictionary fragments) + { + if (fragmentLength == 0) + return; + + string nsFragment = builder.ToString(fragmentOffset, fragmentLength); + if (fragments.ContainsKey(nsFragment)) + { + for (int i = 1;; i++) + { + string uniquifier = i.ToString(NumberFormatInfo.InvariantInfo); + string uniqueNsFragment = AppendToValidClrIdentifier(nsFragment, uniquifier); + if (!fragments.ContainsKey(uniqueNsFragment)) + { + builder.Append(uniquifier); + nsFragment = uniqueNsFragment; + break; + } + if (i == int.MaxValue) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.CannotComputeUniqueName, nsFragment))); + } + } + fragments.Add(nsFragment, null); + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + internal static bool IsObjectContract(DataContract? dataContract) + { + Dictionary previousCollectionTypes = new Dictionary(); + while (dataContract != null && dataContract.Is(DataContractType.CollectionDataContract)) + { + if (dataContract.OriginalUnderlyingType == null) + { + dataContract = dataContract.BaseContract; + continue; + } + + if (!previousCollectionTypes.ContainsKey(dataContract.OriginalUnderlyingType)) + { + previousCollectionTypes.Add(dataContract.OriginalUnderlyingType, dataContract.OriginalUnderlyingType); + dataContract = dataContract.BaseContract; + } + else + { + break; + } + } + + return dataContract != null && dataContract.Is(DataContractType.PrimitiveDataContract) && dataContract.UnderlyingType == typeof(object); + } + + private static bool IsValidStart(char c) + { + return (char.GetUnicodeCategory(c) != UnicodeCategory.DecimalDigitNumber); + } + + private static bool IsValid(char c) + { + UnicodeCategory uc = char.GetUnicodeCategory(c); + + // each char must be Lu, Ll, Lt, Lm, Lo, Nd, Mn, Mc, Pc + + switch (uc) + { + case UnicodeCategory.UppercaseLetter: // Lu + case UnicodeCategory.LowercaseLetter: // Ll + case UnicodeCategory.TitlecaseLetter: // Lt + case UnicodeCategory.ModifierLetter: // Lm + case UnicodeCategory.OtherLetter: // Lo + case UnicodeCategory.DecimalDigitNumber: // Nd + case UnicodeCategory.NonSpacingMark: // Mn + case UnicodeCategory.SpacingCombiningMark: // Mc + case UnicodeCategory.ConnectorPunctuation: // Pc + return true; + default: + return false; + } + } + + private CodeTypeReference CodeTypeIPropertyChange + { + get { return GetCodeTypeReference(typeof(System.ComponentModel.INotifyPropertyChanged)); } + } + + private static CodeThisReferenceExpression ThisReference + { + get { return new CodeThisReferenceExpression(); } + } + + private static CodePrimitiveExpression NullReference + { + get { return new CodePrimitiveExpression(null); } + } + + private CodeParameterDeclarationExpression SerializationInfoParameter + { + get { return new CodeParameterDeclarationExpression(GetCodeTypeReference(typeof(SerializationInfo)), ImportGlobals.SerializationInfoFieldName); } + } + + private CodeParameterDeclarationExpression StreamingContextParameter + { + get { return new CodeParameterDeclarationExpression(GetCodeTypeReference(typeof(StreamingContext)), ImportGlobals.ContextFieldName); } + } + + private CodeAttributeDeclaration SerializableAttribute + { + get { return new CodeAttributeDeclaration(GetCodeTypeReference(typeof(SerializableAttribute))); } + } + + private CodeMemberProperty NodeArrayProperty + { + get + { + return CreateProperty(GetCodeTypeReference(ImportGlobals.TypeOfXmlNodeArray), ImportGlobals.NodeArrayPropertyName, ImportGlobals.NodeArrayFieldName, false/*isValueType*/); + } + } + + private CodeMemberField NodeArrayField + { + get + { + CodeMemberField nodeArrayField = new CodeMemberField(); + nodeArrayField.Type = GetCodeTypeReference(ImportGlobals.TypeOfXmlNodeArray); + nodeArrayField.Name = ImportGlobals.NodeArrayFieldName; + nodeArrayField.Attributes = MemberAttributes.Private; + return nodeArrayField; + } + } + + private CodeMemberMethod ReadXmlMethod + { + get + { + CodeMemberMethod readXmlMethod = new CodeMemberMethod(); + readXmlMethod.Name = "ReadXml"; + CodeParameterDeclarationExpression readerArg = new CodeParameterDeclarationExpression(typeof(XmlReader), "reader"); + readXmlMethod.Parameters.Add(readerArg); + readXmlMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final; + readXmlMethod.ImplementationTypes.Add(typeof(IXmlSerializable)); + CodeAssignStatement setNode = new CodeAssignStatement(); + setNode.Left = new CodeFieldReferenceExpression(ThisReference, ImportGlobals.NodeArrayFieldName); + setNode.Right = new CodeMethodInvokeExpression( + new CodeTypeReferenceExpression(GetCodeTypeReference(typeof(XmlSerializableServices))), + nameof(XmlSerializableServices.ReadNodes), + new CodeArgumentReferenceExpression(readerArg.Name) + ); + readXmlMethod.Statements.Add(setNode); + return readXmlMethod; + } + } + + private CodeMemberMethod WriteXmlMethod + { + get + { + CodeMemberMethod writeXmlMethod = new CodeMemberMethod(); + writeXmlMethod.Name = "WriteXml"; + CodeParameterDeclarationExpression writerArg = new CodeParameterDeclarationExpression(typeof(XmlWriter), "writer"); + writeXmlMethod.Parameters.Add(writerArg); + writeXmlMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final; + writeXmlMethod.ImplementationTypes.Add(typeof(IXmlSerializable)); + writeXmlMethod.Statements.Add( + new CodeMethodInvokeExpression( + new CodeTypeReferenceExpression(GetCodeTypeReference(typeof(XmlSerializableServices))), + nameof(XmlSerializableServices.WriteNodes), + new CodeArgumentReferenceExpression(writerArg.Name), + new CodePropertyReferenceExpression(ThisReference, ImportGlobals.NodeArrayPropertyName) + ) + ); + return writeXmlMethod; + } + } + + private CodeMemberMethod GetSchemaMethod + { + get + { + CodeMemberMethod getSchemaMethod = new CodeMemberMethod(); + getSchemaMethod.Name = "GetSchema"; + getSchemaMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final; + getSchemaMethod.ImplementationTypes.Add(typeof(IXmlSerializable)); + getSchemaMethod.ReturnType = GetCodeTypeReference(typeof(XmlSchema)); + getSchemaMethod.Statements.Add(new CodeMethodReturnStatement(NullReference)); + return getSchemaMethod; + } + } + + private CodeMemberMethod GetSchemaStaticMethod + { + get + { + CodeMemberMethod getSchemaStaticMethod = new CodeMemberMethod(); + getSchemaStaticMethod.Name = ImportGlobals.ExportSchemaMethod; + getSchemaStaticMethod.ReturnType = GetCodeTypeReference(ImportGlobals.TypeOfXmlQualifiedName); + CodeParameterDeclarationExpression paramDeclaration = new CodeParameterDeclarationExpression(typeof(XmlSchemaSet), "schemas"); + getSchemaStaticMethod.Parameters.Add(paramDeclaration); + getSchemaStaticMethod.Attributes = MemberAttributes.Static | MemberAttributes.Public; + getSchemaStaticMethod.Statements.Add( + new CodeMethodInvokeExpression( + new CodeTypeReferenceExpression(GetCodeTypeReference(typeof(XmlSerializableServices))), + nameof(XmlSerializableServices.AddDefaultSchema), + new CodeArgumentReferenceExpression(paramDeclaration.Name), + new CodeFieldReferenceExpression(null, TypeNameFieldName) + ) + ); + getSchemaStaticMethod.Statements.Add( + new CodeMethodReturnStatement( + new CodeFieldReferenceExpression(null, TypeNameFieldName) + ) + ); + return getSchemaStaticMethod; + } + } + + private CodeConstructor ISerializableBaseConstructor + { + get + { + CodeConstructor baseConstructor = new CodeConstructor(); + baseConstructor.Attributes = MemberAttributes.Public; + baseConstructor.Parameters.Add(SerializationInfoParameter); + baseConstructor.Parameters.Add(StreamingContextParameter); + CodeAssignStatement setObjectData = new CodeAssignStatement(); + setObjectData.Left = new CodePropertyReferenceExpression(ThisReference, ImportGlobals.SerializationInfoFieldName); + setObjectData.Right = new CodeArgumentReferenceExpression(ImportGlobals.SerializationInfoFieldName); + baseConstructor.Statements.Add(setObjectData); + // Special-cased check for vb here since CodeGeneratorOptions does not provide information indicating that VB cannot initialize event member + if (EnableDataBinding && SupportsDeclareEvents && string.CompareOrdinal(FileExtension, "vb") != 0) + { + baseConstructor.Statements.Add(new CodeAssignStatement(new CodePropertyReferenceExpression(ThisReference, PropertyChangedEvent.Name), NullReference)); + } + return baseConstructor; + } + } + + private CodeConstructor ISerializableDerivedConstructor + { + get + { + CodeConstructor derivedConstructor = new CodeConstructor(); + derivedConstructor.Attributes = MemberAttributes.Public; + derivedConstructor.Parameters.Add(SerializationInfoParameter); + derivedConstructor.Parameters.Add(StreamingContextParameter); + derivedConstructor.BaseConstructorArgs.Add(new CodeVariableReferenceExpression(ImportGlobals.SerializationInfoFieldName)); + derivedConstructor.BaseConstructorArgs.Add(new CodeVariableReferenceExpression(ImportGlobals.ContextFieldName)); + return derivedConstructor; + } + } + + private CodeMemberField SerializationInfoField + { + get + { + CodeMemberField serializationInfoField = new CodeMemberField(); + serializationInfoField.Type = GetCodeTypeReference(typeof(SerializationInfo)); + serializationInfoField.Name = ImportGlobals.SerializationInfoFieldName; + serializationInfoField.Attributes = MemberAttributes.Private; + return serializationInfoField; + } + } + + private CodeMemberProperty SerializationInfoProperty + { + get + { + return CreateProperty(GetCodeTypeReference(typeof(SerializationInfo)), ImportGlobals.SerializationInfoPropertyName, ImportGlobals.SerializationInfoFieldName, false/*isValueType*/); + } + } + + private CodeMemberMethod GetObjectDataMethod + { + get + { + CodeMemberMethod getObjectDataMethod = new CodeMemberMethod(); + getObjectDataMethod.Name = ImportGlobals.GetObjectDataMethodName; + getObjectDataMethod.Parameters.Add(SerializationInfoParameter); + getObjectDataMethod.Parameters.Add(StreamingContextParameter); + getObjectDataMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final; + getObjectDataMethod.ImplementationTypes.Add(typeof(ISerializable)); + + // Generates: if (this.SerializationInfo == null) return; + CodeConditionStatement returnIfNull = new CodeConditionStatement(); + returnIfNull.Condition = new CodeBinaryOperatorExpression( + new CodePropertyReferenceExpression(ThisReference, ImportGlobals.SerializationInfoPropertyName), + CodeBinaryOperatorType.IdentityEquality, + NullReference); + returnIfNull.TrueStatements.Add(new CodeMethodReturnStatement()); + + // Generates: SerializationInfoEnumerator enumerator = this.SerializationInfo.GetEnumerator(); + CodeVariableDeclarationStatement getEnumerator = new CodeVariableDeclarationStatement(); + getEnumerator.Type = GetCodeTypeReference(typeof(SerializationInfoEnumerator)); + getEnumerator.Name = ImportGlobals.EnumeratorFieldName; + getEnumerator.InitExpression = new CodeMethodInvokeExpression( + new CodePropertyReferenceExpression(ThisReference, ImportGlobals.SerializationInfoPropertyName), + ImportGlobals.GetEnumeratorMethodName); + + //Generates: SerializationEntry entry = enumerator.Current; + CodeVariableDeclarationStatement getCurrent = new CodeVariableDeclarationStatement(); + getCurrent.Type = GetCodeTypeReference(typeof(SerializationEntry)); + getCurrent.Name = ImportGlobals.SerializationEntryFieldName; + getCurrent.InitExpression = new CodePropertyReferenceExpression( + new CodeVariableReferenceExpression(ImportGlobals.EnumeratorFieldName), + ImportGlobals.CurrentPropertyName); + + //Generates: info.AddValue(entry.Name, entry.Value); + CodeExpressionStatement addValue = new CodeExpressionStatement(); + CodePropertyReferenceExpression getCurrentName = new CodePropertyReferenceExpression( + new CodeVariableReferenceExpression(ImportGlobals.SerializationEntryFieldName), + ImportGlobals.NameProperty); + CodePropertyReferenceExpression getCurrentValue = new CodePropertyReferenceExpression( + new CodeVariableReferenceExpression(ImportGlobals.SerializationEntryFieldName), + ImportGlobals.ValueProperty); + addValue.Expression = new CodeMethodInvokeExpression( + new CodeArgumentReferenceExpression(ImportGlobals.SerializationInfoFieldName), + ImportGlobals.AddValueMethodName, + new CodeExpression[] { getCurrentName, getCurrentValue }); + + //Generates: for (; enumerator.MoveNext(); ) + CodeIterationStatement loop = new CodeIterationStatement(); + loop.TestExpression = new CodeMethodInvokeExpression( + new CodeVariableReferenceExpression(ImportGlobals.EnumeratorFieldName), + ImportGlobals.MoveNextMethodName); + loop.InitStatement = loop.IncrementStatement = new CodeSnippetStatement(string.Empty); + loop.Statements.Add(getCurrent); + loop.Statements.Add(addValue); + + getObjectDataMethod.Statements.Add(returnIfNull); + getObjectDataMethod.Statements.Add(getEnumerator); + getObjectDataMethod.Statements.Add(loop); + + return getObjectDataMethod; + } + } + + private CodeMemberField ExtensionDataObjectField + { + get + { + CodeMemberField extensionDataObjectField = new CodeMemberField(); + extensionDataObjectField.Type = GetCodeTypeReference(typeof(ExtensionDataObject)); + extensionDataObjectField.Name = ImportGlobals.ExtensionDataObjectFieldName; + extensionDataObjectField.Attributes = MemberAttributes.Private; + return extensionDataObjectField; + } + } + + private CodeMemberProperty ExtensionDataObjectProperty + { + get + { + CodeMemberProperty extensionDataObjectProperty = new CodeMemberProperty(); + extensionDataObjectProperty.Type = GetCodeTypeReference(typeof(ExtensionDataObject)); + extensionDataObjectProperty.Name = ImportGlobals.ExtensionDataObjectPropertyName; + extensionDataObjectProperty.Attributes = MemberAttributes.Public | MemberAttributes.Final; + extensionDataObjectProperty.ImplementationTypes.Add(typeof(IExtensibleDataObject)); + + CodeMethodReturnStatement propertyGet = new CodeMethodReturnStatement(); + propertyGet.Expression = new CodeFieldReferenceExpression(ThisReference, ImportGlobals.ExtensionDataObjectFieldName); + extensionDataObjectProperty.GetStatements.Add(propertyGet); + + CodeAssignStatement propertySet = new CodeAssignStatement(); + propertySet.Left = new CodeFieldReferenceExpression(ThisReference, ImportGlobals.ExtensionDataObjectFieldName); + propertySet.Right = new CodePropertySetValueReferenceExpression(); + extensionDataObjectProperty.SetStatements.Add(propertySet); + + return extensionDataObjectProperty; + } + } + + private CodeMemberMethod RaisePropertyChangedEventMethod + { + get + { + CodeMemberMethod raisePropertyChangedEventMethod = new CodeMemberMethod(); + raisePropertyChangedEventMethod.Name = "RaisePropertyChanged"; + raisePropertyChangedEventMethod.Attributes = MemberAttributes.Final; + CodeArgumentReferenceExpression propertyName = new CodeArgumentReferenceExpression("propertyName"); + raisePropertyChangedEventMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), propertyName.ParameterName)); + CodeVariableReferenceExpression propertyChanged = new CodeVariableReferenceExpression("propertyChanged"); + raisePropertyChangedEventMethod.Statements.Add(new CodeVariableDeclarationStatement(typeof(PropertyChangedEventHandler), propertyChanged.VariableName, new CodeEventReferenceExpression(ThisReference, PropertyChangedEvent.Name))); + CodeConditionStatement ifStatement = new CodeConditionStatement(new CodeBinaryOperatorExpression(propertyChanged, CodeBinaryOperatorType.IdentityInequality, NullReference)); + raisePropertyChangedEventMethod.Statements.Add(ifStatement); + ifStatement.TrueStatements.Add(new CodeDelegateInvokeExpression(propertyChanged, ThisReference, new CodeObjectCreateExpression(typeof(PropertyChangedEventArgs), propertyName))); + return raisePropertyChangedEventMethod; + } + } + + private CodeMemberEvent PropertyChangedEvent + { + get + { + CodeMemberEvent propertyChangedEvent = new CodeMemberEvent(); + propertyChangedEvent.Attributes = MemberAttributes.Public; + propertyChangedEvent.Name = "PropertyChanged"; + propertyChangedEvent.Type = GetCodeTypeReference(typeof(PropertyChangedEventHandler)); + propertyChangedEvent.ImplementationTypes.Add(typeof(INotifyPropertyChanged)); + return propertyChangedEvent; + } + } + + private CodeMemberProperty CreateProperty(CodeTypeReference type, string propertyName, string fieldName, bool isValueType) + { + return CreateProperty(type, propertyName, fieldName, isValueType, EnableDataBinding && SupportsDeclareEvents); + } + + private CodeMemberProperty CreateProperty(CodeTypeReference type, string propertyName, string fieldName, bool isValueType, bool raisePropertyChanged) + { + CodeMemberProperty property = new CodeMemberProperty(); + property.Type = type; + property.Name = propertyName; + property.Attributes = MemberAttributes.Final; + if (GenerateInternalTypes) + property.Attributes |= MemberAttributes.Assembly; + else + property.Attributes |= MemberAttributes.Public; + + CodeMethodReturnStatement propertyGet = new CodeMethodReturnStatement(); + propertyGet.Expression = new CodeFieldReferenceExpression(ThisReference, fieldName); + property.GetStatements.Add(propertyGet); + + CodeAssignStatement propertySet = new CodeAssignStatement(); + propertySet.Left = new CodeFieldReferenceExpression(ThisReference, fieldName); + propertySet.Right = new CodePropertySetValueReferenceExpression(); + if (raisePropertyChanged) + { + CodeConditionStatement ifStatement = new CodeConditionStatement(); + CodeExpression left = new CodeFieldReferenceExpression(ThisReference, fieldName); + CodeExpression right = new CodePropertySetValueReferenceExpression(); + if (!isValueType) + { + left = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(typeof(object)), + "ReferenceEquals", new CodeExpression[] { left, right }); + } + else + { + left = new CodeMethodInvokeExpression(left, "Equals", new CodeExpression[] { right }); + } + right = new CodePrimitiveExpression(true); + ifStatement.Condition = new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.IdentityInequality, right); + ifStatement.TrueStatements.Add(propertySet); + ifStatement.TrueStatements.Add(new CodeMethodInvokeExpression(ThisReference, RaisePropertyChangedEventMethod.Name, new CodePrimitiveExpression(propertyName))); + property.SetStatements.Add(ifStatement); + } + else + property.SetStatements.Add(propertySet); + return property; + } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ContractCodeDomInfo.cs b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ContractCodeDomInfo.cs new file mode 100644 index 00000000000000..809978d33b29b1 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ContractCodeDomInfo.cs @@ -0,0 +1,64 @@ +// 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.CodeDom; +using System.Collections.Generic; +using System.Diagnostics; +using System.Xml; +using System.Xml.Schema; + +using ExceptionUtil = System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility; + +namespace System.Runtime.Serialization +{ + internal sealed class ContractCodeDomInfo + { + private string? _clrNamespace; + // NOTE TODO smolloy - This was a Dictionary previously, so adding a duplicate entry would throw an exception. + // HashSet does not allow duplicates either, but it just returns false instead of throwing. I think it's safe to not + // throw in that case here, so long as we don't add duplicates. It's just a string list. + private HashSet? _memberNames; + + internal string? ClrNamespace + { + get { return ReferencedTypeExists ? null : _clrNamespace; } + set + { + if (ReferencedTypeExists) + throw ExceptionUtil.ThrowHelperError(new InvalidOperationException(SR.Format(SR.CannotSetNamespaceForReferencedType, TypeReference?.BaseType))); + else + _clrNamespace = value; + } + } + + internal CodeNamespace? CodeNamespace { get; set; } + + internal bool IsProcessed { get; set; } + + internal bool ReferencedTypeExists { get; set; } + + internal CodeTypeDeclaration? TypeDeclaration { get; set; } + + internal CodeTypeReference? TypeReference { get; set; } + + internal bool UsesWildcardNamespace { get; set; } + + internal HashSet GetMemberNames() + { + if (ReferencedTypeExists) + throw ExceptionUtil.ThrowHelperError(new InvalidOperationException(SR.Format(SR.CannotSetMembersForReferencedType, TypeReference?.BaseType))); + else + return _memberNames ??= new HashSet(StringComparer.OrdinalIgnoreCase); + } + + internal bool AddMemberName(string memberName) + { + HashSet names = GetMemberNames(); + + // If the name already exists, 4.8 threw an exception because the backing collection type was Dictionary + Debug.Assert(!names.Contains(memberName)); + return names.Add(memberName); + } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/DiagnosticUtility.cs b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/DiagnosticUtility.cs new file mode 100644 index 00000000000000..46b0dde85c686b --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/DiagnosticUtility.cs @@ -0,0 +1,100 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Threading; + +namespace System.Runtime.Serialization +{ + internal static class Fx + { + public static bool IsFatal(Exception exception) + { + while (exception != null) + { + // NetFx checked for FatalException and FatalInternalException as well, which were ServiceModel constructs. + if ((exception is OutOfMemoryException && !(exception is InsufficientMemoryException)) || + exception is ThreadAbortException) + { + return true; + } + + // These exceptions aren't themselves fatal, but since the CLR uses them to wrap other exceptions, + // we want to check to see whether they've been used to wrap a fatal exception. If so, then they + // count as fatal. + if (exception is TypeInitializationException || + exception is TargetInvocationException) + { + exception = exception.InnerException!; + } + else if (exception is AggregateException) + { + // AggregateExceptions have a collection of inner exceptions, which may themselves be other + // wrapping exceptions (including nested AggregateExceptions). Recursively walk this + // hierarchy. The (singular) InnerException is included in the collection. + var innerExceptions = ((AggregateException)exception).InnerExceptions; + foreach (Exception innerException in innerExceptions) + { + if (IsFatal(innerException)) + { + return true; + } + } + + break; + } + else + { + break; + } + } + + return false; + } + } + + internal static class DiagnosticUtility + { + [Conditional("DEBUG")] + [DoesNotReturn] + public static void DebugAssert(string message) + { + DebugAssert(false, message); + } + + [Conditional("DEBUG")] + public static void DebugAssert([DoesNotReturnIf(false)] bool condition, string message) + { + Debug.Assert(condition, message); + } + + internal static class ExceptionUtility + { + public static Exception ThrowHelperArgumentNull(string message) + { + return new ArgumentNullException(message); + } + + public static Exception ThrowHelperError(Exception e) + { + return e; + } + + public static Exception ThrowHelperArgument(string message) + { + return new ArgumentException(message); + } + + internal static Exception ThrowHelperFatal(string message, Exception innerException) + { + return ThrowHelperError(new Exception(message, innerException)); + } + internal static Exception ThrowHelperCallback(Exception e) + { + return ThrowHelperError(e); + } + } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ISerializationCodeDomSurrogateProvider.cs b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ISerializationCodeDomSurrogateProvider.cs new file mode 100644 index 00000000000000..a69a18711d0b4c --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ISerializationCodeDomSurrogateProvider.cs @@ -0,0 +1,24 @@ +// 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.CodeDom; +using System.CodeDom.Compiler; +using System.Collections.Generic; + +namespace System.Runtime.Serialization +{ + /// + /// Represents a DataContract surrogate provider that is capable of modifying generated type code using . + /// + public interface ISerializationCodeDomSurrogateProvider + { + /// + /// Processes the type that has been generated from the imported schema. + /// + /// A to process that represents the type declaration generated during schema import. + /// The that contains the other code generated during schema import. + /// A that contains the processed type. + CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit); + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ImportGlobals.cs b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ImportGlobals.cs new file mode 100644 index 00000000000000..59392aed8b04f1 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ImportGlobals.cs @@ -0,0 +1,156 @@ +// 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.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; + +namespace System.Runtime.Serialization +{ + internal static class ImportGlobals + { + internal const string SerializerTrimmerWarning = "Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the " + + "required types are preserved."; + + public const string ActualTypeLocalName = "ActualType"; + public const string ActualTypeNameAttribute = "Name"; + public const string ActualTypeNamespaceAttribute = "Namespace"; + public const string AddValueMethodName = "AddValue"; + public const string AnyTypeLocalName = "anyType"; + public const string ArrayPrefix = "ArrayOf"; + public const string ClrNamespaceProperty = "ClrNamespace"; + public const string CollectionsNamespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays"; + public const string ContextFieldName = "context"; + public const string CurrentPropertyName = "Current"; + public const string DataContractXsdBaseNamespace = "http://schemas.datacontract.org/2004/07/"; + public const string DefaultClrNamespace = "GeneratedNamespace"; + public const bool DefaultEmitDefaultValue = true; + public const string DefaultGeneratedMember = "GeneratedMember"; + public const string DefaultFieldSuffix = "Field"; + public const bool DefaultIsReference = false; + public const bool DefaultIsRequired = false; + public const string DefaultMemberSuffix = "Member"; + public const int DefaultOrder = 0; + public const string DefaultTypeName = "GeneratedType"; + public const string DefaultValueLocalName = "DefaultValue"; + public const string EmitDefaultValueAttribute = "EmitDefaultValue"; + public const string EmitDefaultValueProperty = "EmitDefaultValue"; + public const string EnumerationValueLocalName = "EnumerationValue"; + public const string EnumeratorFieldName = "enumerator"; + public const string ExportSchemaMethod = "ExportSchema"; + public const string ExtensionDataObjectFieldName = "extensionDataField"; + public const string ExtensionDataObjectPropertyName = "ExtensionData"; + public const string False = "false"; + public const string GenericNameAttribute = "Name"; + public const string GenericNamespaceAttribute = "Namespace"; + public const string GenericParameterLocalName = "GenericParameter"; + public const string GenericParameterNestedLevelAttribute = "NestedLevel"; + public const string GenericTypeLocalName = "GenericType"; + public const string GetEnumeratorMethodName = "GetEnumerator"; + public const string GetObjectDataMethodName = "GetObjectData"; + public const string IdLocalName = "Id"; + public const string IntLocalName = "int"; + public const string IsAnyProperty = "IsAny"; + public const string IsDictionaryLocalName = "IsDictionary"; + public const string ISerializableFactoryTypeLocalName = "FactoryType"; + public const string IsReferenceProperty = "IsReference"; + public const string IsRequiredProperty = "IsRequired"; + public const string IsValueTypeLocalName = "IsValueType"; + public const string ItemNameProperty = "ItemName"; + public const string KeyLocalName = "Key"; + public const string KeyNameProperty = "KeyName"; + public const string MoveNextMethodName = "MoveNext"; + public const string NameProperty = "Name"; + public const string NamespaceProperty = "Namespace"; + public const string NodeArrayFieldName = "nodesField"; + public const string NodeArrayPropertyName = "Nodes"; + public const string OccursUnbounded = "unbounded"; + public const string OrderProperty = "Order"; + public const string RefLocalName = "Ref"; + public const string SchemaInstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance"; + public const string SchemaLocalName = "schema"; + public const string SchemaNamespace = "http://www.w3.org/2001/XMLSchema"; + public const string SerializationEntryFieldName = "entry"; + public const string SerializationInfoFieldName = "info"; + public const string SerializationInfoPropertyName = "SerializationInfo"; + public const string SerializationNamespace = "http://schemas.microsoft.com/2003/10/Serialization/"; + public const string SerPrefixForSchema = "ser"; + public const string StringLocalName = "string"; + public const string SurrogateDataLocalName = "Surrogate"; + public const string TnsPrefix = "tns"; + public const string True = "true"; + public const string ValueLocalName = "Value"; + public const string ValueNameProperty = "ValueName"; + public const string ValueProperty = "Value"; + + private static Uri? s_dataContractXsdBaseNamespaceUri; + internal static Uri DataContractXsdBaseNamespaceUri => s_dataContractXsdBaseNamespaceUri ??= new Uri(DataContractXsdBaseNamespace); + + private static XmlQualifiedName? s_idQualifiedName; + internal static XmlQualifiedName IdQualifiedName => s_idQualifiedName ??= new XmlQualifiedName(ImportGlobals.IdLocalName, ImportGlobals.SerializationNamespace); + + private static XmlQualifiedName? s_refQualifiedName; + internal static XmlQualifiedName RefQualifiedName => s_refQualifiedName ??= new XmlQualifiedName(ImportGlobals.RefLocalName, ImportGlobals.SerializationNamespace); + + private static Type? s_typeOfXmlElement; + internal static Type TypeOfXmlElement => s_typeOfXmlElement ??= typeof(XmlElement); + + private static Type? s_typeOfXmlNodeArray; + internal static Type TypeOfXmlNodeArray => s_typeOfXmlNodeArray ??= typeof(XmlNode[]); + + private static Type? s_typeOfXmlQualifiedName; + internal static Type TypeOfXmlQualifiedName => s_typeOfXmlQualifiedName ??= typeof(XmlQualifiedName); + + private static Type? s_typeOfXmlSchemaProviderAttribute; + internal static Type TypeOfXmlSchemaProviderAttribute => s_typeOfXmlSchemaProviderAttribute ??= typeof(XmlSchemaProviderAttribute); + + private static Type? s_typeOfXmlSchemaType; + internal static Type TypeOfXmlSchemaType => s_typeOfXmlSchemaType ??= typeof(XmlSchemaType); + + + public const string SerializationSchema = @" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"; + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ImportOptions.cs b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ImportOptions.cs new file mode 100644 index 00000000000000..0e8fab9d0df5b9 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ImportOptions.cs @@ -0,0 +1,72 @@ +// 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.CodeDom; +using System.CodeDom.Compiler; +using System.Collections.Generic; + +namespace System.Runtime.Serialization +{ + /// + /// Represents the options that can be set on an . + /// + /// + /// The is used to generate code from XML schema using the .NET CodeDOM. To generate an XML schema from an assembly, use the . + /// + public class ImportOptions + { + private ICollection? _referencedTypes; + private ICollection? _referencedCollectionTypes; + private IDictionary? _namespaces; + + /// + /// Gets or sets a instance that provides the means to check whether particular options for a target language are supported. + /// + public CodeDomProvider? CodeProvider { get; set; } + + /// + /// Gets or sets a value that specifies whether types in generated code should implement the interface. + /// + public bool EnableDataBinding { get; set; } + + /// + /// Gets or sets a data contract surrogate provider that can be used to modify the code generated during an import operation. + /// + /// + /// The interface type for this option is ISerializationSurrogateProvider, but to take full advantage of the imported code modification + /// abilities, using an ISerializationSurrogateProvider2 that also implements is recommended. + /// + public ISerializationSurrogateProvider? DataContractSurrogate { get; set; } + + /// + /// Gets or sets a value that specifies whether generated code will be marked internal or public. + /// + public bool GenerateInternal { get; set; } + + /// + /// Gets or sets a value that specifies whether generated data contract classes will be marked with the attribute in addition to the attribute. + /// + public bool GenerateSerializable { get; set; } + + /// + /// Gets or sets a value that determines whether all XML schema types, even those that do not conform to a data contract schema, will be imported. + /// + public bool ImportXmlType { get; set; } + + /// + /// Gets a dictionary that contains the mapping of data contract namespaces to the CLR namespaces that must be used to generate code during an import operation. + /// + public IDictionary Namespaces => _namespaces ??= new Dictionary(); + + /// + /// Gets a collection of types that represents data contract collections that should be referenced when generating code for collections, such as lists or dictionaries of items. + /// + public ICollection ReferencedCollectionTypes => _referencedCollectionTypes ??= new List(); + + /// + /// Gets a containing types referenced in generated code. + /// + public ICollection ReferencedTypes => _referencedTypes ??= new List(); + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/SchemaImportHelper.cs b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/SchemaImportHelper.cs new file mode 100644 index 00000000000000..a268f8ef792663 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/SchemaImportHelper.cs @@ -0,0 +1,127 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.DataContracts; +using System.Xml; + +namespace System.Runtime.Serialization +{ + internal enum DataContractType + { + ClassDataContract, + CollectionDataContract, + EnumDataContract, + PrimitiveDataContract, + XmlDataContract, + Unknown = -1 + } + + internal static class DataContractExtensions + { + internal static DataContractType GetContractType(this DataContract dataContract) => dataContract.ContractType switch + { + "ClassDataContract" => DataContractType.ClassDataContract, + "CollectionDataContract" => DataContractType.CollectionDataContract, + "EnumDataContract" => DataContractType.EnumDataContract, + "PrimitiveDataContract" => DataContractType.PrimitiveDataContract, + "XmlDataContract" => DataContractType.XmlDataContract, + _ => DataContractType.Unknown + }; + + internal static bool Is(this DataContract dataContract, DataContractType dcType) + { + return (dataContract.GetContractType() == dcType); + } + + internal static DataContract? As(this DataContract dataContract, DataContractType dcType) + { + if (dataContract.GetContractType() == dcType) + return dataContract; + return null; + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + internal static bool IsItemTypeNullable(this DataContract collectionDataContract) + { + if (collectionDataContract.GetContractType() == DataContractType.CollectionDataContract) + { + // ItemContract - aka BaseContract - is never null for CollectionDataContract + return SchemaImportHelper.IsTypeNullable(collectionDataContract.BaseContract!.UnderlyingType); + } + + return false; + } + } + + internal static class SchemaImportHelper + { + internal static bool IsTypeNullable(Type type) + { + return !type.IsValueType || + (type.IsGenericType && + type.GetGenericTypeDefinition() == typeof(Nullable<>)); + } + + internal static string GetCollectionNamespace(string elementNs) + { + return IsBuiltInNamespace(elementNs) ? ImportGlobals.CollectionsNamespace : elementNs; + } + + internal static string GetDataContractNamespaceFromUri(string uriString) + { + return uriString.StartsWith(ImportGlobals.DataContractXsdBaseNamespace, StringComparison.Ordinal) ? uriString.Substring(ImportGlobals.DataContractXsdBaseNamespace.Length) : uriString; + } + + internal static string GetDefaultXmlNamespace(string? clrNs) + { + if (clrNs == null) clrNs = string.Empty; + return new Uri(ImportGlobals.DataContractXsdBaseNamespaceUri, clrNs).AbsoluteUri; + } + + internal static bool IsBuiltInNamespace(string ns) + { + return (ns == ImportGlobals.SchemaNamespace || ns == ImportGlobals.SerializationNamespace); + } + + // This should match the behavior of DataContract.EncodeLocalName + internal static string EncodeLocalName(string localName) + { + if (IsAsciiLocalName(localName)) + return localName; + + if (IsValidNCName(localName)) + return localName; + + return XmlConvert.EncodeLocalName(localName); + } + + private static bool IsAsciiLocalName(string localName) + { + if (localName.Length == 0) + return false; + if (!char.IsAsciiLetter(localName[0])) + return false; + for (int i = 1; i < localName.Length; i++) + { + char ch = localName[i]; + if (!char.IsAsciiLetterOrDigit(ch)) + return false; + } + return true; + } + + private static bool IsValidNCName(string name) + { + try + { + XmlConvert.VerifyNCName(name); + return true; + } + catch (XmlException) + { + return false; + } + } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/XsdDataContractImporter.cs b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/XsdDataContractImporter.cs new file mode 100644 index 00000000000000..00f54a684a0595 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/XsdDataContractImporter.cs @@ -0,0 +1,406 @@ +// 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.CodeDom; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.DataContracts; +using System.Xml; +using System.Xml.Schema; + +using ExceptionUtil = System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility; + +namespace System.Runtime.Serialization +{ + /// + /// Allows the transformation of a set of XML schema files (.xsd) into common language runtime (CLR) types. + /// + /// + /// Use the if you are creating a Web service that must interoperate with an existing + /// Web service, or to create data contract types from XML schemas. will transform a + /// set of XML schemas and create the .NET Framework types that represent the data contract in a selected programming language. + /// To create the code, use the classes in the namespace. + /// + /// Conversely, use the class when you have created a Web service that incorporates + /// data represented by CLR types and when you need to export XML schemas for each data type to be consumed by other Web + /// services.That is, transforms a set of CLR types into a set of XML schemas. + /// + public class XsdDataContractImporter + { + private CodeCompileUnit _codeCompileUnit = null!; // Not directly referenced. Always lazy initialized by property getter. + private DataContractSet? _dataContractSet; + + private static readonly XmlQualifiedName[] s_emptyTypeNameArray = Array.Empty(); + private XmlQualifiedName[] _singleTypeNameArray = null!; // Not directly referenced. Always lazy initialized by property getter. + private XmlSchemaElement[] _singleElementArray = null!; // Not directly referenced. Always lazy initialized by property getter. + + /// + /// Initializes a new instance of the class. + /// + public XsdDataContractImporter() + { + } + + /// + /// Initializes a new instance of the class with the that will be used to generate CLR code. + /// + /// The that will be used to store the code. + public XsdDataContractImporter(CodeCompileUnit codeCompileUnit) + { + _codeCompileUnit = codeCompileUnit; + } + + /// + /// Gets or sets an that contains settable options for the import operation. + /// + public ImportOptions? Options { get; set; } + + /// + /// Gets a used for storing the CLR types generated. + /// + public CodeCompileUnit CodeCompileUnit => _codeCompileUnit ??= new CodeCompileUnit(); + + private DataContractSet DataContractSet + { + get + { + if (_dataContractSet == null) + { + _dataContractSet = Options == null ? new DataContractSet(null, null, null) : + new DataContractSet(Options.DataContractSurrogate, Options.ReferencedTypes, Options.ReferencedCollectionTypes); + } + return _dataContractSet; + } + } + + /// + /// Transforms the specified set of XML schemas contained in an into a . + /// + /// A that contains the schema representations to generate CLR types for. + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + public void Import(XmlSchemaSet schemas) + { + if (schemas == null) + throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(schemas))); + + InternalImport(schemas, null, null); + } + + /// + /// Transforms the specified set of schema types contained in an into CLR types generated into a . + /// + /// A that contains the schema representations. + /// A (of ) that represents the set of schema types to import. + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + public void Import(XmlSchemaSet schemas, ICollection typeNames) + { + if (schemas == null) + throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(schemas))); + + if (typeNames == null) + throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(typeNames))); + + InternalImport(schemas, typeNames, null); + } + + /// + /// Transforms the specified XML schema type contained in an into a . + /// + /// A that contains the schema representations. + /// A that represents a specific schema type to import. + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + public void Import(XmlSchemaSet schemas, XmlQualifiedName typeName) + { + if (schemas == null) + throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(schemas))); + + if (typeName == null) + throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(typeName))); + + SingleTypeNameArray[0] = typeName; + InternalImport(schemas, SingleTypeNameArray, null); + } + + /// + /// Transforms the specified schema element in the set of specified XML schemas into a and + /// returns an that represents the data contract name for the specified element. + /// + /// An that contains the schemas to transform. + /// An that represents the specific schema element to transform. + /// An that represents the specified element. + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + public XmlQualifiedName? Import(XmlSchemaSet schemas, XmlSchemaElement element) + { + if (schemas == null) + throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(schemas))); + + if (element == null) + throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(element))); + + SingleElementArray[0] = element; + IList? elementNames = InternalImport(schemas, s_emptyTypeNameArray, SingleElementArray); + Debug.Assert(elementNames != null && elementNames.Count > 0); + return elementNames[0]; + } + + /// + /// Gets a value that indicates whether the schemas contained in an can be transformed into a . + /// + /// A that contains the schemas to transform. + /// true if the schemas can be transformed to data contract types; otherwise, false. + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + public bool CanImport(XmlSchemaSet schemas) + { + if (schemas == null) + throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(schemas))); + + return InternalCanImport(schemas, null, null); + } + + /// + /// Gets a value that indicates whether the specified set of types contained in an can be transformed into CLR types generated into a . + /// + /// A that contains the schemas to transform. + /// An of that represents the set of schema types to import. + /// true if the schemas can be transformed; otherwise, false. + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + public bool CanImport(XmlSchemaSet schemas, ICollection typeNames) + { + if (schemas == null) + throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(schemas))); + + if (typeNames == null) + throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(typeNames))); + + return InternalCanImport(schemas, typeNames, null); + } + + /// + /// Gets a value that indicates whether the schemas contained in an can be transformed into a . + /// + /// A that contains the schema representations. + /// An that specifies the names of the schema types that need to be imported from the . + /// true if the schemas can be transformed to data contract types; otherwise, false. + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + public bool CanImport(XmlSchemaSet schemas, XmlQualifiedName typeName) + { + if (schemas == null) + throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(schemas))); + + if (typeName == null) + throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(typeName))); + + return InternalCanImport(schemas, new XmlQualifiedName[] { typeName }, null); + } + + /// + /// Gets a value that indicates whether a specific schema element contained in an can be imported. + /// + /// An to import. + /// A specific to check in the set of schemas. + /// true if the element can be imported; otherwise, false. + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + public bool CanImport(XmlSchemaSet schemas, XmlSchemaElement element) + { + if (schemas == null) + throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(schemas))); + + if (element == null) + throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(element))); + + SingleElementArray[0] = element; + return InternalCanImport(schemas, s_emptyTypeNameArray, SingleElementArray); + } + + /// + /// Returns a to the CLR type generated for the schema type with the specified . + /// + /// The that specifies the schema type to look up. + /// A reference to the CLR type generated for the schema type with the typeName specified. + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + public CodeTypeReference GetCodeTypeReference(XmlQualifiedName typeName) + { + DataContract dataContract = FindDataContract(typeName); + CodeExporter codeExporter = new CodeExporter(DataContractSet, Options, CodeCompileUnit); + return codeExporter.GetCodeTypeReference(dataContract); + } + + /// + /// Returns a for the specified XML qualified element and schema element. + /// + /// An that specifies the XML qualified name of the schema type to look up. + /// An that specifies an element in an XML schema. + /// A that represents the type that was generated for the specified schema type. + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + public CodeTypeReference GetCodeTypeReference(XmlQualifiedName typeName, XmlSchemaElement element) + { + if (element == null) + throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(element))); + if (typeName == null) + throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(typeName))); + DataContract dataContract = FindDataContract(typeName); + CodeExporter codeExporter = new CodeExporter(DataContractSet, Options, CodeCompileUnit); + return codeExporter.GetElementTypeReference(dataContract, element.IsNillable); + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + internal DataContract FindDataContract(XmlQualifiedName typeName) + { + if (typeName == null) + throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(typeName))); + + DataContract? dataContract = DataContract.GetBuiltInDataContract(typeName.Name, typeName.Namespace); + if (dataContract == null) + { + dataContract = DataContractSet.GetDataContract(typeName); + if (dataContract == null) + throw ExceptionUtil.ThrowHelperError(new InvalidOperationException(SR.Format(SR.TypeHasNotBeenImported, typeName.Name, typeName.Namespace))); + } + return dataContract; + } + + /// + /// Returns a list of objects that represents the known types generated when generating code for the specified schema type. + /// + /// An that represents the schema type to look up known types for. + /// A collection of type . + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + public ICollection? GetKnownTypeReferences(XmlQualifiedName typeName) + { + if (typeName == null) + throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(typeName))); + + DataContract? dataContract = DataContract.GetBuiltInDataContract(typeName.Name, typeName.Namespace); + if (dataContract == null) + { + dataContract = DataContractSet.GetDataContract(typeName); + if (dataContract == null) + throw ExceptionUtil.ThrowHelperError(new InvalidOperationException(SR.Format(SR.TypeHasNotBeenImported, typeName.Name, typeName.Namespace))); + } + + CodeExporter codeExporter = new CodeExporter(DataContractSet, Options, CodeCompileUnit); + return codeExporter.GetKnownTypeReferences(dataContract); + } + + private XmlQualifiedName[] SingleTypeNameArray + { + get + { + if (_singleTypeNameArray == null) + _singleTypeNameArray = new XmlQualifiedName[1]; + return _singleTypeNameArray; + } + } + + private XmlSchemaElement[] SingleElementArray + { + get + { + if (_singleElementArray == null) + _singleElementArray = new XmlSchemaElement[1]; + return _singleElementArray; + } + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private IList? InternalImport(XmlSchemaSet schemas, ICollection? typeNames, ICollection? elements) + { + DataContractSet? oldValue = (_dataContractSet == null) ? null : new DataContractSet(_dataContractSet); + IList? elementTypeNames = null; + try + { + if (elements != null) + elementTypeNames = DataContractSet.ImportSchemaSet(schemas, elements, ImportXmlDataType); + else + DataContractSet.ImportSchemaSet(schemas, typeNames, ImportXmlDataType); + + CodeExporter codeExporter = new CodeExporter(DataContractSet, Options, CodeCompileUnit); + codeExporter.Export(); + + return elementTypeNames; + } + catch (Exception ex) + { + if (Fx.IsFatal(ex)) + throw; + + _dataContractSet = oldValue; + throw; + } + } + + private bool ImportXmlDataType + { + get + { + return Options == null ? false : Options.ImportXmlType; + } + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private bool InternalCanImport(XmlSchemaSet schemas, ICollection? typeNames, ICollection? elements) + { + DataContractSet? oldValue = (_dataContractSet == null) ? null : new DataContractSet(_dataContractSet); + try + { + if (elements != null) + DataContractSet.ImportSchemaSet(schemas, elements, ImportXmlDataType); + else + DataContractSet.ImportSchemaSet(schemas, typeNames, ImportXmlDataType); + return true; + } + catch (InvalidDataContractException) + { + _dataContractSet = oldValue; + return false; + } + catch (Exception ex) + { + if (Fx.IsFatal(ex)) + throw; + + _dataContractSet = oldValue; + throw; + } + } + + private static XmlQualifiedName? s_actualTypeAnnotationName; + internal static XmlQualifiedName ActualTypeAnnotationName => s_actualTypeAnnotationName ??= new XmlQualifiedName(ImportGlobals.ActualTypeLocalName, ImportGlobals.SerializationNamespace); + + internal static XmlQualifiedName ImportActualType(XmlSchemaAnnotation? annotation, XmlQualifiedName defaultTypeName, XmlQualifiedName typeName) + { + XmlElement? actualTypeElement = ImportAnnotation(annotation, ActualTypeAnnotationName); + if (actualTypeElement == null) + return defaultTypeName; + + XmlNode? nameAttribute = actualTypeElement.Attributes.GetNamedItem(ImportGlobals.ActualTypeNameAttribute); + if (nameAttribute?.Value == null) + throw ExceptionUtil.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.AnnotationAttributeNotFound, ActualTypeAnnotationName.Name, typeName.Name, typeName.Namespace, ImportGlobals.ActualTypeNameAttribute))); + XmlNode? nsAttribute = actualTypeElement.Attributes.GetNamedItem(ImportGlobals.ActualTypeNamespaceAttribute); + if (nsAttribute?.Value == null) + throw ExceptionUtil.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.AnnotationAttributeNotFound, ActualTypeAnnotationName.Name, typeName.Name, typeName.Namespace, ImportGlobals.ActualTypeNamespaceAttribute))); + return new XmlQualifiedName(nameAttribute.Value, nsAttribute.Value); + } + + private static XmlElement? ImportAnnotation(XmlSchemaAnnotation? annotation, XmlQualifiedName annotationQualifiedName) + { + if (annotation != null && annotation.Items != null && annotation.Items.Count > 0 && annotation.Items[0] is XmlSchemaAppInfo) + { + XmlSchemaAppInfo appInfo = (XmlSchemaAppInfo)annotation.Items[0]; + XmlNode?[]? markup = appInfo.Markup; + if (markup != null) + { + for (int i = 0; i < markup.Length; i++) + { + XmlElement? annotationElement = markup[i] as XmlElement; + if (annotationElement != null && annotationElement.LocalName == annotationQualifiedName.Name && annotationElement.NamespaceURI == annotationQualifiedName.Namespace) + return annotationElement; + } + } + } + return null; + } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/tests/System.Runtime.Serialization.Schema.Tests.csproj b/src/libraries/System.Runtime.Serialization.Schema/tests/System.Runtime.Serialization.Schema.Tests.csproj new file mode 100644 index 00000000000000..6515ccf3936470 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/tests/System.Runtime.Serialization.Schema.Tests.csproj @@ -0,0 +1,19 @@ + + + $(NetCoreAppCurrent) + true + + + + + + + + + + + + + + + diff --git a/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/DataContracts.cs b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/DataContracts.cs new file mode 100644 index 00000000000000..77d12cf9e6d4ce --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/DataContracts.cs @@ -0,0 +1,160 @@ +using System.Runtime.Serialization; +using System.Xml.Serialization; + +[assembly: ContractNamespace("http://special1.tempuri.org", ClrNamespace = "System.Runtime.Serialization.Schema.Tests.DataContracts")] + +namespace System.Runtime.Serialization.Schema.Tests.DataContracts +{ + [DataContract(Namespace = "http://basic")] + public class Point + { + [DataMember] + public int X = 42; + [DataMember] + public int Y = 43; + } + + [DataContract(Namespace = "http://shapes")] + public class Circle + { + [DataMember] + public Point Center = new Point(); + [DataMember] + public int Radius = 5; + } + + [DataContract(Namespace = "http://shapes")] + public class Square + { + [DataMember] + public Point BottomLeft = new Point(); + [DataMember] + public int Side = 5; + } + + public struct NonAttributedPersonStruct + { + public string firstName; + public string lastName; + } + + public class NonAttributedPersonClass + { + public string firstName = "John"; + public string lastName = "Smith"; + + internal NonAttributedPersonClass() + { + } + } + + public class ExtendedSquare : Square + { + public string lineColor = "black"; + } + + [DataContract(Name = "AnotherValidType", Namespace = "http://schemas.datacontract.org/2004/07/barNs")] + public class AnotherValidType + { + [DataMember] + public string member; + } + + [DataContract(Name = "AnotherValidType", Namespace = "http://schemas.datacontract.org/2004/07/barNs")] + public class ConflictingAnotherValidType + { + [DataMember] + public string member; + } + + public class NonAttributedType + { + public NonAttributedSquare Length; + } + + public class NonAttributedSquare + { + public int Length; + } + + [DataContract(IsReference = true)] + public class RefType1 + { + } + + [DataContract] + public class NonRefType + { + } + + [Serializable] + public class ISerializableFormatClass : ISerializable + { + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + } + } + +#pragma warning disable CS0169, IDE0051, IDE1006 + #region SurrogateTests + [XmlRoot("XmlSerializerPersonElement")] + public class XmlSerializerPerson + { + public XmlSerializerPerson() { } + [XmlAttribute] + public string Name; + [XmlAttribute] + public int Age; + } + + [DataContract] + public class CircleContainer + { + [DataMember] + public SerializableCircle Circle { get { return null; } set { } } + [DataMember] + SerializableCircle[] circles; + } + + [Serializable] + public class SerializableCircle + { + public int Radius; + } + + [Serializable] + public class SerializableSquare + { + public int Side; + } + + public class Node + { + Node next; + } + + [Serializable] + public class SerializableNode + { + SerializableNode next; + } + #endregion + + [DataContract] + public class SerializableClass + { + [DataMember] + string member; + + [DataMember(Order = 3)] + string v3member; + } + + [DataContract] + public class DerivedClass : SerializableClass + { + [DataMember] + SerializableClass[] derivedMember; + } +#pragma warning restore CS0169, IDE0051, IDE1006 +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/Import/ImportOptionsTests.cs b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/Import/ImportOptionsTests.cs new file mode 100644 index 00000000000000..49867cc07ba88b --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/Import/ImportOptionsTests.cs @@ -0,0 +1,160 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Runtime.Serialization.Schema; +using System.Runtime.Serialization.Schema.Tests.DataContracts; +using System.Xml; +using System.Xml.Schema; +using Xunit; +using Xunit.Abstractions; + +namespace System.Runtime.Serialization.Schema.Tests +{ + public class ImportOptionsTests + { + private readonly ITestOutputHelper _output; + + public ImportOptionsTests(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void DefaultOptions() + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + Assert.NotNull(importer); + Assert.NotNull(importer.Options); + Assert.False(importer.Options.EnableDataBinding); + Assert.False(importer.Options.GenerateInternal); + Assert.False(importer.Options.GenerateSerializable); + Assert.False(importer.Options.ImportXmlType); + Assert.Null(importer.Options.CodeProvider); + Assert.NotNull(importer.Options.Namespaces); + Assert.Empty(importer.Options.Namespaces); + Assert.NotNull(importer.Options.ReferencedCollectionTypes); + Assert.Empty(importer.Options.ReferencedCollectionTypes); + Assert.NotNull(importer.Options.ReferencedTypes); + Assert.Empty(importer.Options.ReferencedTypes); + Assert.Null(importer.Options.DataContractSurrogate); + } + + [Fact] + public void GetImportOptions() + { + XsdDataContractImporter importer = new XsdDataContractImporter(); + importer.Options = new ImportOptions(); + Assert.NotNull(importer.Options); + } + + [Fact] + public void SetImportOptions() + { + XsdDataContractImporter e = new XsdDataContractImporter(); + e.Options = new ImportOptions(); + e.Options.Namespaces.Add("Test", "http://schemas.datacontract.org/2004/07/fooNs"); + Assert.Single(e.Options.Namespaces); + } + + [Fact] + public void GenerateInternal() + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + importer.Options.GenerateInternal = true; + importer.Import(SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[0]); + _output.WriteLine(SchemaUtils.DumpCode(importer.CodeCompileUnit)); + Assert.True(importer.Options.GenerateInternal); + } + + [Fact] + public void EnableDataBinding() + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + importer.Options.EnableDataBinding = true; + importer.Import(SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[0]); + _output.WriteLine(SchemaUtils.DumpCode(importer.CodeCompileUnit)); + Assert.True(importer.Options.EnableDataBinding); + } + + [Fact] + public void GenerateSerializable() + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + importer.Options.GenerateSerializable = true; + importer.Import(SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[0]); + _output.WriteLine(SchemaUtils.DumpCode(importer.CodeCompileUnit)); + Assert.True(importer.Options.GenerateSerializable); + } + + [Fact] + public void ImportXmlType() + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + importer.Options.ImportXmlType = true; + importer.Import(SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[0]); + _output.WriteLine(SchemaUtils.DumpCode(importer.CodeCompileUnit)); + Assert.True(importer.Options.ImportXmlType); + } + + [Fact] + public void CodeProvider() + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + CodeDomProvider codeProvider = CodeDomProvider.CreateProvider("csharp"); + importer.Options.CodeProvider = codeProvider; + Console.WriteLine(importer.Options.CodeProvider.GetType().FullName); + importer.Import(SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[0]); + _output.WriteLine(SchemaUtils.DumpCode(importer.CodeCompileUnit)); + Assert.Equal(codeProvider, importer.Options.CodeProvider); + } + + [Theory] + [InlineData("http://schemas.datacontract.org/2004/07/fooNs", "customizedNamespace")] + [InlineData("*", "customizedNamespace")] + [InlineData("null", "customizedNamespace")] + public void Namespaces(string dcns, string clrns) + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + Assert.NotNull(importer.Options.Namespaces); + Assert.Empty(importer.Options.Namespaces); + + importer.Options.Namespaces.Add(dcns, clrns); + importer.Import(SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[0]); + _output.WriteLine(SchemaUtils.DumpCode(importer.CodeCompileUnit)); + } + + [Theory] + [MemberData(nameof(ReferencedTypes_MemberData))] + public void ReferencedTypes(XmlSchemaSet schemas, XmlQualifiedName qname, Type[] referencedTypes, Type expectedExceptionType = null, string msg = null) + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + for (int i = 0; i < referencedTypes.Length; i++) + importer.Options.ReferencedTypes.Add(referencedTypes[i]); + + if (expectedExceptionType == null) + { + importer.Import(schemas, qname); + _output.WriteLine(SchemaUtils.DumpCode(importer.CodeCompileUnit)); + } + else + { + var ex = Assert.Throws(expectedExceptionType, () => importer.Import(schemas, qname)); + + if (!string.IsNullOrEmpty(msg)) + Assert.StartsWith(msg, ex.Message); + } + } + public static IEnumerable ReferencedTypes_MemberData() + { + yield return new object[] { SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[1], new Type[] { typeof(AnotherValidType) } }; + yield return new object[] { SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[2], new Type[] { typeof(NonAttributedSquare) } }; + yield return new object[] { SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[1], new Type[] { typeof(AnotherValidType), typeof(ConflictingAnotherValidType) }, + typeof(InvalidOperationException), @"List of referenced types contains more than one type with data contract name 'AnotherValidType' in namespace 'http://schemas.datacontract.org/2004/07/barNs'. Need to exclude all but one of the following types. Only matching types can be valid references:"}; + // These last two are described as "negative" in the original NetFx XsdDCImporterApi test code... but they don't fail here or there. + yield return new object[] { SchemaUtils.IsReferenceSchemas, SchemaUtils.ValidTypeNames[3], new Type[] { typeof(NonRefType) } }; + yield return new object[] { SchemaUtils.IsReferenceSchemas, SchemaUtils.ValidTypeNames[4], new Type[] { typeof(RefType1) } }; + } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/Import/ImporterTests.cs b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/Import/ImporterTests.cs new file mode 100644 index 00000000000000..79d9c1f65078e9 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/Import/ImporterTests.cs @@ -0,0 +1,417 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.CodeDom; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Reflection; +using System.Runtime.Serialization.Schema; +using System.Runtime.Serialization.Schema.Tests.DataContracts; +using System.Xml; +using System.Xml.Schema; +using Xunit; +using Xunit.Abstractions; + +namespace System.Runtime.Serialization.Schema.Tests +{ + public class ImporterTests + { + private readonly ITestOutputHelper _output; + public ImporterTests(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void Ctor_Default() + { + XsdDataContractImporter xci = new XsdDataContractImporter(); + Assert.NotNull(xci); + Assert.Null(xci.Options); + } + + [Fact] + public void Ctor_CCU() + { + CodeCompileUnit ccu = new CodeCompileUnit(); + XsdDataContractImporter xci = new XsdDataContractImporter(ccu); + Assert.NotNull(xci); + Assert.Equal(ccu, xci.CodeCompileUnit); + } + + [Theory] + [MemberData(nameof(CanImport_MemberData))] + public void CanImport(bool expectedResult, Func canImport, Type expectedExceptionType = null, string msg = null) + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + if (expectedExceptionType == null) + { + Assert.Equal(expectedResult, canImport(importer)); + } + else + { + var ex = Assert.Throws(expectedExceptionType, () => canImport(importer)); + + if (!string.IsNullOrEmpty(msg)) + Assert.Equal(msg, ex.Message); + } + } + public static IEnumerable CanImport_MemberData() + { + // CanImport(XmlSchemaSet) + yield return new object[] { true, (XsdDataContractImporter imp) => imp.CanImport(SchemaUtils.PositiveSchemas) }; + yield return new object[] { false, (XsdDataContractImporter imp) => imp.CanImport(null), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'schemas')" }; + yield return new object[] { false, (XsdDataContractImporter imp) => imp.CanImport(SchemaUtils.MixedSchemas) }; + + // CanImport(XmlSchemaSet, ICollection) + yield return new object[] { true, (XsdDataContractImporter imp) => imp.CanImport(SchemaUtils.PositiveSchemas, new XmlQualifiedName[] { SchemaUtils.ValidTypeNames[0] }) }; + yield return new object[] { false, (XsdDataContractImporter imp) => imp.CanImport(null, SchemaUtils.InvalidTypeNames), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'schemas')" }; + yield return new object[] { false, (XsdDataContractImporter imp) => imp.CanImport(SchemaUtils.MixedSchemas, (ICollection)null), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'typeNames')" }; + yield return new object[] { false, (XsdDataContractImporter imp) => imp.CanImport(SchemaUtils.MixedSchemas, new XmlQualifiedName[] { null }), typeof(ArgumentException), @"Cannot import type for null XmlQualifiedName specified via parameter." }; + yield return new object[] { false, (XsdDataContractImporter imp) => imp.CanImport(SchemaUtils.MixedSchemas, SchemaUtils.InvalidTypeNames) }; + + // CanImport(XmlSchemaSet, XmlQualifiedName) + yield return new object[] { true, (XsdDataContractImporter imp) => imp.CanImport(SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[0]) }; + yield return new object[] { false, (XsdDataContractImporter imp) => imp.CanImport(null, SchemaUtils.InvalidTypeNames[0]), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'schemas')" }; + yield return new object[] { false, (XsdDataContractImporter imp) => imp.CanImport(SchemaUtils.MixedSchemas, (XmlQualifiedName)null), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'typeName')" }; + yield return new object[] { false, (XsdDataContractImporter imp) => imp.CanImport(SchemaUtils.MixedSchemas, SchemaUtils.InvalidTypeNames[0]) }; + + // CanImport(XmlSchemaSet, XmlSchemaElement) + // TODO + + // CanImportTests.cs + foreach (var citArgs in SchemaUtils.CanImportTests) + { + XmlSchemaSet schemaSet = SchemaUtils.ReadStringsIntoSchemaSet(citArgs.schemaString); + if (citArgs.qnames == null) + yield return new object[] { citArgs.expectedResult, (XsdDataContractImporter imp) => imp.CanImport(schemaSet) }; + else if (citArgs.qnames.Length == 1 && citArgs.isElement) + yield return new object[] { citArgs.expectedResult, (XsdDataContractImporter imp) => imp.CanImport(schemaSet, SchemaUtils.GetSchemaElement(schemaSet, citArgs.qnames[0])) }; + else if (citArgs.qnames.Length == 1) + yield return new object[] { citArgs.expectedResult, (XsdDataContractImporter imp) => imp.CanImport(schemaSet, citArgs.qnames[0]) }; + else + yield return new object[] { citArgs.expectedResult, (XsdDataContractImporter imp) => imp.CanImport(schemaSet, citArgs.qnames) }; + } + } + + [Theory] + [MemberData(nameof(Import_MemberData))] + public void Import(Action import, int codeLength = -1) + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + import(importer); + string code = SchemaUtils.DumpCode(importer.CodeCompileUnit); + _output.WriteLine(code); + if (codeLength >= 0) + Assert.Equal(codeLength, code.Length); + } + public static IEnumerable Import_MemberData() + { + int newlineSize = Environment.NewLine.Length; + + // Import(XmlSchemaSet) + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.PositiveSchemas), 5060 + (168 * newlineSize) }; // 168 lines + + // Import(XmlSchemaSet, ICollection) + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.PositiveSchemas, new XmlQualifiedName[] { SchemaUtils.ValidTypeNames[0] }), 1515 + (50 * newlineSize) }; // 50 lines + + // Import(XmlSchemaSet, XmlQualifiedName) + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[0]), 1515 + (50 * newlineSize) }; // 50 lines + + // Import(XmlSchemaSet, XmlSchemaElement) + // TODO + + // From CanImportTests.cs + foreach (var citArgs in SchemaUtils.CanImportTests) + { + if (citArgs.expectedResult) + { + XmlSchemaSet schemaSet = SchemaUtils.ReadStringsIntoSchemaSet(citArgs.schemaString); + if (citArgs.qnames == null) + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(schemaSet) }; + else if (citArgs.qnames.Length == 1 && citArgs.isElement) + yield return new object[] { (XsdDataContractImporter imp) => { imp.Import(schemaSet, SchemaUtils.GetSchemaElement(schemaSet, citArgs.qnames[0])); } }; + else if (citArgs.qnames.Length == 1 && !citArgs.isElement) + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(schemaSet, citArgs.qnames[0]) }; + else + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(schemaSet, citArgs.qnames) }; + } + } + + // From FormatVersioning.cs : Positive tests + (string msg, Type type, string xpath, string xmlFrag)[] formatVersioningArgs = new (string, Type, string, string)[] { + ("Optional Serialization Attribute in class", + typeof(SerializableFormatClass), @"//xs:schema/xs:complexType[@name='ImporterTests.SerializableFormatClass']/xs:sequence", @""), + ("Optional Serialization Attribute in ISerializable", + typeof(ISerializableFormatClass), @"//xs:schema/xs:complexType[@name='ISerializableFormatClass']/xs:sequence", @""), + ("Optional Serialization Attribute in Array", + typeof(SerializableFormatClass), @"//xs:schema/xs:complexType[@name='ArrayOfImporterTests.SerializableFormatClass']/xs:sequence", @""), + ("Optional Serialization Element in class", + typeof(SerializableFormatClass), @"//xs:schema/xs:complexType[@name='ImporterTests.SerializableFormatClass']/xs:sequence/xs:element", @""), + ("Optional Serialization Element in ISerializable", + typeof(ISerializableFormatClass), @"//xs:schema/xs:complexType[@name='ISerializableFormatClass']/xs:sequence/xs:any", @""), + ("Optional Serialization Element in Array", + typeof(SerializableFormatClass), @"//xs:schema/xs:complexType[@name='ArrayOfImporterTests.SerializableFormatClass']/xs:sequence/xs:element", @""), + }; + foreach (var fvArg in formatVersioningArgs) + { + (XmlSchemaSet schemaSet, XmlQualifiedName typeName) = PrepareFormatVersioningTest(fvArg.type, fvArg.xpath, fvArg.xmlFrag); + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(schemaSet, typeName) }; + } + } + static (XmlSchemaSet, XmlQualifiedName) PrepareFormatVersioningTest(Type type, string xpath, string xmlFrag) + { + XsdDataContractExporter exporter = new XsdDataContractExporter(); + exporter.Export(type); + XmlSchemaSet schemaSet = exporter.Schemas; + XmlQualifiedName typeName = exporter.GetSchemaTypeName(type); + string schemaString = SchemaUtils.GetSchemaString(schemaSet, typeName.Namespace); + schemaString = SchemaUtils.InsertElement(schemaString, xpath, xmlFrag, true); + schemaString = SchemaUtils.InsertElement(schemaString, @"//xs:schema/xs:complexType", @"", false); + schemaString = SchemaUtils.InsertElement(schemaString, @"//xs:schema/xs:complexType", @"", false); + schemaString = SchemaUtils.InsertAttribute(schemaString, @"//xs:schema", "xmlns", @"ser", @"http://www.w3.org/2000/xmlns/", "http://schemas.microsoft.com/2003/10/Serialization/"); + SchemaUtils.SetSchemaString(schemaSet, typeName.Namespace, schemaString); + schemaSet.Add(XmlSchema.Read(new StringReader(SchemaUtils.GlobalSchema), null)); + + XmlSchema v2SerializationSchema = SchemaUtils.GetSchema(schemaSet, "http://schemas.microsoft.com/2003/10/Serialization/"); + XmlSchemaElement v2Element = new XmlSchemaElement(); + v2Element.Name = "V2Element"; + v2SerializationSchema.Items.Add(v2Element); + XmlSchemaAttribute v2Attribute = new XmlSchemaAttribute(); + v2Attribute.Name = "V2Attribute"; + v2SerializationSchema.Items.Add(v2Attribute); + schemaSet.Reprocess(v2SerializationSchema); + + return (schemaSet, typeName); + } + + [Theory] + [MemberData(nameof(Import_NegativeCases_MemberData))] + public void Import_NegativeCases(Action import, Type expectedExceptionType, string msg = null) + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + var ex = Assert.Throws(expectedExceptionType, () => import(importer)); + + if (!string.IsNullOrEmpty(msg)) + Assert.Equal(msg, ex.Message); + } + public static IEnumerable Import_NegativeCases_MemberData() + { + // Import(XmlSchemaSet) + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(null), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'schemas')" }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.MixedSchemas), typeof(InvalidDataContractException), @"Type 'InvalidType' in namespace 'http://schemas.datacontract.org/2004/07/fooNs' cannot be imported. The root particle must be a sequence. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + + // Import(XmlSchemaSet, ICollection) + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(null, SchemaUtils.InvalidTypeNames), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'schemas')" }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.MixedSchemas, (ICollection)null), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'typeNames')" }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.MixedSchemas, new XmlQualifiedName[] { null }), typeof(ArgumentException), @"Cannot import type for null XmlQualifiedName specified via parameter." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.MixedSchemas, SchemaUtils.InvalidTypeNames), typeof(InvalidDataContractException), @"Type 'InvalidType' in namespace 'http://schemas.datacontract.org/2004/07/fooNs' cannot be imported. The root particle must be a sequence. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + + // Import(XmlSchemaSet, XmlQualifiedName) + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(null, SchemaUtils.InvalidTypeNames[0]), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'schemas')" }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.MixedSchemas, (XmlQualifiedName)null), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'typeName')" }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.MixedSchemas, SchemaUtils.InvalidTypeNames[0]), typeof(InvalidDataContractException), @"Type 'InvalidType' in namespace 'http://schemas.datacontract.org/2004/07/fooNs' cannot be imported. The root particle must be a sequence. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + + // Import(XmlSchemaSet, XmlSchemaElement) + // TODO + + // NegativeTests.cs, part 1 : Bad schema + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[1]), SchemaUtils.NegativeTypeNames[1]), + typeof(InvalidDataContractException), @"Invalid type specified. Type with name 'FooType' not found in schema with namespace 'http://EmptySchema'." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[2]), SchemaUtils.NegativeTypeNames[2]), + typeof(InvalidDataContractException), @"Invalid type specified. Type with name 'FooType' not found in schema with namespace 'http://NonExistantSchema'." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[3])), + typeof(InvalidDataContractException), @"Type 'InvalidTopLevelElementType' in namespace 'http://schemas.datacontract.org/2004/07/foo' cannot be imported. The global element found in the schema with same name references a different type 'int' in namespace 'http://www.w3.org/2001/XMLSchema'. Data contract types must have the same name as their root element name. Consider removing the global element or changing its type. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[4])), + typeof(InvalidDataContractException), @"Type 'ExtraAttributesType' in namespace 'http://schemas.datacontract.org/2004/07/foo' cannot be imported. Attributes must be optional and from namespace 'http://schemas.microsoft.com/2003/10/Serialization/'. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[5]), SchemaUtils.NegativeTypeNames[5]), + typeof(InvalidDataContractException), @"Type 'ExtraAttributeWildcardType' in namespace 'http://schemas.datacontract.org/2004/07/foo' cannot be imported. 'anyAttribute' is not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[6])), + typeof(InvalidDataContractException), @"Type 'InvalidRootParticleType' in namespace 'http://schemas.datacontract.org/2004/07/foo' cannot be imported. The root particle must be a sequence. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[7])), + typeof(InvalidDataContractException), @"Type 'InvalidTopLevelElement' in namespace 'http://schemas.datacontract.org/2004/07/foo' cannot be imported. The global element found in the schema with same name references a different type 'string' in namespace 'http://www.w3.org/2001/XMLSchema'. Data contract types must have the same name as their root element name. Consider removing the global element or changing its type. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[8])), + typeof(InvalidDataContractException), @"Type 'TypeWithElementsOfSameName' in namespace 'http://schemas.datacontract.org/2004/07/foo' cannot be imported. The type contains two elements with the same name 'DuplicatedName'. Multiple elements with the same name in one type are not supported because members marked with DataMemberAttribute attribute must have unique names. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[9])), + typeof(InvalidDataContractException), @"Type 'SimpleTypeUnion' in namespace 'http://schemas.datacontract.org/2004/07/foo' cannot be imported. Simple types with content are not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[10])), + typeof(InvalidDataContractException), @"Enum type 'EnumOnlyList' in namespace 'http://schemas.datacontract.org/2004/07/foo' cannot be imported. Simple type list must contain an anonymous type specifying enumeration facets. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[11])), + typeof(InvalidDataContractException), @"Enum type 'EnumNonStringBaseType' in namespace 'http://schemas.datacontract.org/2004/07/foo' cannot be imported. Anonymous type with cannot be used to create Flags enumeration because it is not a valid enum type. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[12])), + typeof(InvalidDataContractException), @"Type 'ComplexTypeWithSimpleContent' in namespace 'http://schemas.datacontract.org/2004/07/foo' cannot be imported. Complex types with simple content extension are not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[13])), + typeof(InvalidDataContractException), @"Array type 'ArrayOfBar' in namespace 'http://schemas.datacontract.org/2004/07/foo' cannot be imported. Form for element 'Bar' must be qualified. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[14])), + typeof(InvalidDataContractException), @"Type 'DataSet.datasetType' in namespace 'http://tempuri.org/' cannot be imported. The root sequence must contain only local elements. Group ref, choice, any and nested sequences are not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + + // NegativeTests.cs, part 2 : Bad attribute + (Type type, string xpath, string prefix, string localName, string ns, string value, string exMsg)[] badAttributeCases = new (Type, string, string, string, string, string, string)[] { + (typeof(SerializableClass), @"//xs:schema/xs:complexType[@name='SerializableClass']", "", @"abstract", "", @"true", @"Type 'SerializableClass' in namespace 'http://special1.tempuri.org' cannot be imported. The type cannot have 'abstract' set to 'true'. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + (typeof(SerializableClass), @"//xs:schema/xs:complexType[@name='SerializableClass']", null, @"mixed", "", @"true", @"Type 'SerializableClass' in namespace 'http://special1.tempuri.org' cannot be imported. Complex type with mixed content is not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + (typeof(SerializableClass), @"//xs:schema/xs:complexType[@name='SerializableClass']/xs:sequence/xs:element[@name='member']", "", @"fixed", "", @"xxx", @"Type 'SerializableClass' in namespace 'http://special1.tempuri.org' cannot be imported. Fixed value on element 'member' is not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + (typeof(SerializableClass), @"//xs:schema/xs:complexType[@name='SerializableClass']/xs:sequence/xs:element[@name='member']", "", @"default", "", @"yyy", @"Type 'SerializableClass' in namespace 'http://special1.tempuri.org' cannot be imported. Default value on element 'member' is not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + (typeof(SerializableClass), @"//xs:schema/xs:element[@name='SerializableClass']", "", @"abstract", "", @"true", @"Type 'SerializableClass' in namespace 'http://special1.tempuri.org' cannot be imported. The element cannot have 'abstract' set to 'true'. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + (typeof(SerializableClass), @"//xs:schema/xs:element[@name='SerializableClass']", "", @"substitutionGroup", "", @"tns:Head", @"Type 'SerializableClass' in namespace 'http://special1.tempuri.org' cannot be imported. Substitution group on element 'SerializableClass' is not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + }; + foreach (var bac in badAttributeCases) + { + XsdDataContractExporter exporter = new XsdDataContractExporter(); + exporter.Export(bac.type); + XmlSchemaSet schemaSet = exporter.Schemas; + XmlQualifiedName typeName = exporter.GetSchemaTypeName(bac.type); + string schemaString = SchemaUtils.GetSchemaString(schemaSet, typeName.Namespace); + schemaString = SchemaUtils.InsertElement(schemaString, @"//xs:schema/xs:element[@name='SerializableClass']", @"", true); + schemaString = SchemaUtils.InsertAttribute(schemaString, bac.xpath, bac.prefix, bac.localName, bac.ns, bac.value); + XmlSchemaSet schema = SchemaUtils.ReadStringsIntoSchemaSet(schemaString); + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(schema), typeof(InvalidDataContractException), bac.exMsg }; + } + + // NegativeTests.cs, part 3 : Bad element + (Type type, string xpath, string xmlFrag, bool insertAfter, string exMsg)[] badElementCases = new(Type, string, string, bool, string)[] { + (typeof(SerializableClass), @"//xs:schema/xs:complexType[@name='SerializableClass']/xs:sequence/xs:element", @"", true, @"Type 'SerializableClass' in namespace 'http://special1.tempuri.org' cannot be imported. Ref to element 'SerializableClass' in 'http://special1.tempuri.org' namespace is not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + (typeof(DerivedClass), @"//xs:schema/xs:complexType[@name='DerivedClass']/xs:complexContent/xs:extension/xs:sequence/xs:element", @"", false, @"Type 'DerivedClass' in namespace 'http://special1.tempuri.org' cannot be imported. The root sequence must contain only local elements. Group ref, choice, any and nested sequences are not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + (typeof(DerivedClass), @"//xs:schema/xs:complexType[@name='DerivedClass']/xs:complexContent/xs:extension/xs:sequence/xs:element", @"", false, @"Type 'DerivedClass' in namespace 'http://special1.tempuri.org' cannot be imported. Ref to element 'int' in 'http://schemas.microsoft.com/2003/10/Serialization/' namespace is not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + }; + foreach (var bec in badElementCases) + { + XsdDataContractExporter exporter = new XsdDataContractExporter(); + exporter.Export(bec.type); + XmlSchemaSet schemaSet = exporter.Schemas; + XmlQualifiedName typeName = exporter.GetSchemaTypeName(bec.type); + string schemaString = SchemaUtils.GetSchemaString(schemaSet, typeName.Namespace); + schemaString = SchemaUtils.InsertElement(schemaString, bec.xpath, bec.xmlFrag, bec.insertAfter); + XmlSchemaSet schema = SchemaUtils.ReadStringsIntoSchemaSet(schemaString); + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(schema), typeof(InvalidDataContractException), bec.exMsg }; + } + + // FormatVersioning.cs : Negative tests + (string msg, Type type, string xpath, string xmlFrag, string exMsg)[] formatVersioningNegativeArgs = new (string, Type, string, string, string)[] { + ("Required Serialization Attribute in class", + typeof(SerializableFormatClass), @"//xs:schema/xs:complexType[@name='ImporterTests.SerializableFormatClass']/xs:sequence", @"", + @"Type 'ImporterTests.SerializableFormatClass' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests' cannot be imported. Attributes must be optional and from namespace 'http://schemas.microsoft.com/2003/10/Serialization/'. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + ("Required Serialization Attribute in ISerializable", + typeof(ISerializableFormatClass), @"//xs:schema/xs:complexType[@name='ISerializableFormatClass']/xs:sequence", @"", + @"Type 'ISerializableFormatClass' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests.DataContracts' cannot be imported. Attributes must be optional and from namespace 'http://schemas.microsoft.com/2003/10/Serialization/'. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + ("Required Serialization Attribute in Array", + typeof(SerializableFormatClass), @"//xs:schema/xs:complexType[@name='ArrayOfImporterTests.SerializableFormatClass']/xs:sequence", @"", + @"Type 'ArrayOfImporterTests.SerializableFormatClass' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests' cannot be imported. Attributes must be optional and from namespace 'http://schemas.microsoft.com/2003/10/Serialization/'. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + ("Required Serialization Element in class", + typeof(SerializableFormatClass), @"//xs:schema/xs:complexType[@name='ImporterTests.SerializableFormatClass']/xs:sequence/xs:element", @"", + @"Type 'ImporterTests.SerializableFormatClass' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests' cannot be imported. Ref to element 'V2Element' in 'http://schemas.microsoft.com/2003/10/Serialization/' namespace is not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + ("Required Serialization Element in ISerializable", + typeof(ISerializableFormatClass), @"//xs:schema/xs:complexType[@name='ISerializableFormatClass']/xs:sequence/xs:any", @"", + @"Type 'ISerializableFormatClass' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests.DataContracts' cannot be imported. The root sequence must contain only local elements. Group ref, choice, any and nested sequences are not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + ("Required Serialization Element in Array", + typeof(SerializableFormatClass), @"//xs:schema/xs:complexType[@name='ArrayOfImporterTests.SerializableFormatClass']/xs:sequence/xs:element", @"", + @"Type 'ArrayOfImporterTests.SerializableFormatClass' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests' cannot be imported. 'maxOccurs' on element 'ImporterTests.SerializableFormatClass' must be 1. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + ("Optional Global Attribute in class", + typeof(SerializableFormatClass), @"//xs:schema/xs:complexType[@name='ImporterTests.SerializableFormatClass']/xs:sequence", @"", + @"Type 'ImporterTests.SerializableFormatClass' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests' cannot be imported. Attributes must be optional and from namespace 'http://schemas.microsoft.com/2003/10/Serialization/'. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + ("Optional Global Attribute in ISerializable", + typeof(ISerializableFormatClass), @"//xs:schema/xs:complexType[@name='ISerializableFormatClass']/xs:sequence", @"", + @"Type 'ISerializableFormatClass' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests.DataContracts' cannot be imported. Attributes must be optional and from namespace 'http://schemas.microsoft.com/2003/10/Serialization/'. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + ("Optional Global Attribute in Array", + typeof(SerializableFormatClass), @"//xs:schema/xs:complexType[@name='ArrayOfImporterTests.SerializableFormatClass']/xs:sequence", @"", + @"Type 'ArrayOfImporterTests.SerializableFormatClass' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests' cannot be imported. Attributes must be optional and from namespace 'http://schemas.microsoft.com/2003/10/Serialization/'. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + }; + foreach (var fvArg in formatVersioningNegativeArgs) + { + (XmlSchemaSet schemaSet, XmlQualifiedName typeName) = PrepareFormatVersioningTest(fvArg.type, fvArg.xpath, fvArg.xmlFrag); + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(schemaSet, typeName), typeof(InvalidDataContractException), fvArg.exMsg }; + } + } +#pragma warning disable CS0169, IDE0051, IDE1006 + [Serializable] + public class SerializableFormatClass + { + SerializableFormatClass[] array; + } +#pragma warning restore CS0169, IDE0051, IDE1006 + + [Theory] + [MemberData(nameof(GetCodeTypeReference_MemberData))] + public void GetCodeTypeReference(XmlSchemaSet schemas, XmlQualifiedName qname, string exptectedType, Type expectedExceptionType = null, string msg = null) + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + + if (schemas != null) + importer.Import(schemas); + + if (expectedExceptionType == null) + { + CodeTypeReference ctr = importer.GetCodeTypeReference(qname); + Assert.NotNull(ctr); + + string typeString = SchemaUtils.GetString(ctr); + _output.WriteLine(typeString); + Assert.Equal(exptectedType, typeString); + } + else + { + var ex = Assert.Throws(expectedExceptionType, () => importer.GetCodeTypeReference(qname)); + + if (!string.IsNullOrEmpty(msg)) + Assert.Equal(msg, ex.Message); + } + } + public static IEnumerable GetCodeTypeReference_MemberData() + { + // GetCodeTypeReference(XmlQualifiedName) + yield return new object[] { SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[0], "fooNs.ValidType" }; + yield return new object[] { SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[2], "Suites.SchemaImport.NonAttributedType" }; + yield return new object[] { null, null, null, typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'typeName')" }; + yield return new object[] { null, SchemaUtils.InvalidTypeNames[0], null, typeof(InvalidOperationException), @"Type 'InvalidType' from namespace 'http://schemas.datacontract.org/2004/07/fooNs' has not been imported from schema. Consider first importing this type by calling one of the Import methods on XsdDataContractImporter." }; + yield return new object[] { SchemaUtils.PositiveSchemas, SchemaUtils.InvalidTypeNames[0], null, typeof(InvalidOperationException), @"Type 'InvalidType' from namespace 'http://schemas.datacontract.org/2004/07/fooNs' has not been imported from schema. Consider first importing this type by calling one of the Import methods on XsdDataContractImporter." }; + + // GetCodeTypeReference(XmlQualifiedName, XmlSchemaElement) + // TODO + } + + + [Theory] + [MemberData(nameof(GetKnownTypeReferences_MemberData))] + public void GetKnownTypeReferences(XmlSchemaSet schemas, XmlQualifiedName qname, int expectedRefCount, Type expectedExceptionType = null, string msg = null) + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + + if (schemas != null) + importer.Import(schemas); + + if (expectedExceptionType == null) + { + ICollection knownTypeReferences = importer.GetKnownTypeReferences(qname); + + if (knownTypeReferences == null) + { + _output.WriteLine("KnownType count: null"); + Assert.Equal(0, expectedRefCount); + } + else + { + _output.WriteLine("KnownType count: {0}", knownTypeReferences.Count); + foreach (CodeTypeReference knownTypeReference in knownTypeReferences) + _output.WriteLine(SchemaUtils.GetString(knownTypeReference)); + Assert.Equal(expectedRefCount, knownTypeReferences.Count); + } + } + else + { + var ex = Assert.Throws(expectedExceptionType, () => importer.GetKnownTypeReferences(qname)); + + if (!string.IsNullOrEmpty(msg)) + Assert.Equal(msg, ex.Message); + } + } + public static IEnumerable GetKnownTypeReferences_MemberData() + { + // GetKnownTypeReferences(XmlQualifiedName) + yield return new object[] { SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[0], 0 }; + yield return new object[] { null, null, -1, typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'typeName')" }; + yield return new object[] { null, SchemaUtils.ValidTypeNames[0], -1, typeof(InvalidOperationException), @"Type 'ValidType' from namespace 'http://schemas.datacontract.org/2004/07/fooNs' has not been imported from schema. Consider first importing this type by calling one of the Import methods on XsdDataContractImporter." }; + // TODO - a positive case with non-zero ref count. + } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/Import/SurrogateTests.cs b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/Import/SurrogateTests.cs new file mode 100644 index 00000000000000..35d64bde333ba5 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/Import/SurrogateTests.cs @@ -0,0 +1,220 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.CodeDom; +using System.Collections.ObjectModel; +using System.Reflection; +using System.Runtime.Serialization.Schema; +using System.Runtime.Serialization.Schema.Tests.DataContracts; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using Xunit; +using Xunit.Abstractions; + +namespace System.Runtime.Serialization.Schema.Tests +{ + // TODO - Add a test covering 'ISerializationCodeDomSurrogateProvider'/ProcessImportedType - There was nothing in NetFx test suites for this. + public class SurrogateTests + { + static Type[] testTypes = new Type[] + { + typeof(CircleContainer), + typeof(Node), + typeof(XmlSerializerPerson), + }; + + private readonly ITestOutputHelper _output; + public SurrogateTests(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void DefaultScenario() + { + XsdDataContractExporter exporter = new XsdDataContractExporter(); + exporter.Options = new ExportOptions(); + exporter.Options.DataContractSurrogate = new SurrogateProvider(false); + for (int i = 0; i < testTypes.Length; i++) + exporter.Export((Type)testTypes[i]); + + XsdDataContractImporter importer = new XsdDataContractImporter(); + importer.Options = new ImportOptions(); + importer.Options.DataContractSurrogate = exporter.Options.DataContractSurrogate; + importer.Options.ImportXmlType = true; + importer.Import(exporter.Schemas); + + string code = SchemaUtils.DumpCode(importer.CodeCompileUnit); + _output.WriteLine(code); + + Assert.Contains(@"[assembly: System.Runtime.Serialization.ContractNamespaceAttribute(""http://special1.tempuri.org"", ClrNamespace=""special1.tempuri.org"")]", code); + Assert.Contains(@"[assembly: System.Runtime.Serialization.ContractNamespaceAttribute("""", ClrNamespace="""")]", code); + + Assert.Contains(@"namespace special1.tempuri.org", code); + Assert.Matches(@"\[System.Runtime.Serialization.DataContractAttribute\(Name=""CircleContainer"", Namespace=""http://special1.tempuri.org""\)\]\s*public partial class CircleContainer : object, System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Contains(@"private System.Runtime.Serialization.Schema.Tests.DataContracts.SerializableSquare[] circlesField;", code); + Assert.Contains(@"private System.Runtime.Serialization.Schema.Tests.DataContracts.SerializableSquare CircleField;", code); + Assert.Matches(@"\[System.Runtime.Serialization.DataMemberAttribute\(\)\]\s*public System.Runtime.Serialization.Schema.Tests.DataContracts.SerializableSquare Circle", code); + Assert.Contains(@"public partial class SerializableSquare : object, System.Runtime.Serialization.IExtensibleDataObject", code); + + Assert.Contains(@"namespace System.Runtime.Serialization.Schema.Tests.DataContracts", code); + Assert.Matches(@"\[System.Runtime.Serialization.DataContractAttribute\(Name\s*=\s*""SerializableNode"", Namespace\s*=\s*""http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests""\s*\+\s*"".DataContracts""\)\]\s*public partial class SerializableNode : object, System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Matches(@"\[System.Xml.Serialization.XmlSchemaProviderAttribute\(""ExportSchema""\)\]\s*\[System.Xml.Serialization.XmlRootAttribute\(ElementName\s*=\s*""XmlSerializerPersonElement"", Namespace\s*=\s*""""\)\]\s*public partial class XmlSerializerPerson : object, System.Xml.Serialization.IXmlSerializable", code); + } + + [Fact] + public void WithReferencedType() + { + XsdDataContractExporter exporter = new XsdDataContractExporter(); + exporter.Options = new ExportOptions(); + exporter.Options.DataContractSurrogate = new SurrogateProvider(false); + for (int i = 0; i < testTypes.Length; i++) + exporter.Export((Type)testTypes[i]); + + XsdDataContractImporter importer = new XsdDataContractImporter(); + importer.Options = new ImportOptions(); + importer.Options.DataContractSurrogate = exporter.Options.DataContractSurrogate; + importer.Options.ImportXmlType = true; + importer.Options.ReferencedTypes.Add(typeof(SerializableCircle)); + importer.Import(exporter.Schemas); + + string code = SchemaUtils.DumpCode(importer.CodeCompileUnit); + _output.WriteLine(code); + + Assert.Contains(@"[assembly: System.Runtime.Serialization.ContractNamespaceAttribute(""http://special1.tempuri.org"", ClrNamespace=""special1.tempuri.org"")]", code); + Assert.Contains(@"[assembly: System.Runtime.Serialization.ContractNamespaceAttribute("""", ClrNamespace="""")]", code); + + Assert.Contains(@"namespace special1.tempuri.org", code); + Assert.Matches(@"\[System.Runtime.Serialization.DataContractAttribute\(Name=""CircleContainer"", Namespace=""http://special1.tempuri.org""\)\]\s*public partial class CircleContainer : object, System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Contains(@"private System.Runtime.Serialization.Schema.Tests.DataContracts.SerializableSquare[] circlesField;", code); + Assert.Contains(@"private System.Runtime.Serialization.Schema.Tests.DataContracts.SerializableCircle CircleField;", code); + Assert.Matches(@"\[System.Runtime.Serialization.DataMemberAttribute\(\)\]\s*public System.Runtime.Serialization.Schema.Tests.DataContracts.SerializableCircle Circle", code); + + Assert.Contains(@"namespace System.Runtime.Serialization.Schema.Tests.DataContracts", code); + Assert.Matches(@"\[System.Runtime.Serialization.DataContractAttribute\(Name\s*=\s*""SerializableNode"", Namespace\s*=\s*""http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests""\s*\+\s*"".DataContracts""\)\]\s*public partial class SerializableNode : object, System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Matches(@"\[System.Xml.Serialization.XmlSchemaProviderAttribute\(""ExportSchema""\)\]\s*\[System.Xml.Serialization.XmlRootAttribute\(ElementName\s*=\s*""XmlSerializerPersonElement"", Namespace\s*=\s*""""\)\]\s*public partial class XmlSerializerPerson : object, System.Xml.Serialization.IXmlSerializable", code); + Assert.DoesNotContain(@"public partial class SerializableSquare : object, System.Runtime.Serialization.IExtensibleDataObject", code); + } + + [Fact] + public void WithSurrogateBinding() + { + XsdDataContractExporter exporter = new XsdDataContractExporter(); + exporter.Options = new ExportOptions(); + exporter.Options.DataContractSurrogate = new SurrogateProvider(true); + for (int i = 0; i < testTypes.Length; i++) + exporter.Export((Type)testTypes[i]); + + XsdDataContractImporter importer = new XsdDataContractImporter(); + importer = new XsdDataContractImporter(); + importer.Options = new ImportOptions(); + importer.Options.DataContractSurrogate = exporter.Options.DataContractSurrogate; + importer.Options.ImportXmlType = true; + importer.Options.ReferencedTypes.Add(typeof(Circle)); + importer.Import(exporter.Schemas); + + string code = SchemaUtils.DumpCode(importer.CodeCompileUnit); + _output.WriteLine(code); + + Assert.Contains(@"[assembly: System.Runtime.Serialization.ContractNamespaceAttribute(""http://special1.tempuri.org"", ClrNamespace=""special1.tempuri.org"")]", code); + Assert.DoesNotContain(@"[assembly: System.Runtime.Serialization.ContractNamespaceAttribute("""", ClrNamespace="""")]", code); + + Assert.Contains(@"namespace special1.tempuri.org", code); + Assert.Matches(@"\[System.Runtime.Serialization.DataContractAttribute\(Name=""CircleContainer"", Namespace=""http://special1.tempuri.org""\)\]\s*public partial class CircleContainer : object, System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Contains(@"private System.Runtime.Serialization.Schema.Tests.DataContracts.SerializableCircle[] circlesField;", code); + Assert.Contains(@"private System.Runtime.Serialization.Schema.Tests.DataContracts.SerializableCircle CircleField;", code); + Assert.Matches(@"\[System.Runtime.Serialization.DataMemberAttribute\(\)\]\s*public System.Runtime.Serialization.Schema.Tests.DataContracts.SerializableCircle Circle", code); + + Assert.DoesNotContain(@"namespace System.Runtime.Serialization.Schema.Tests.DataContracts", code); + Assert.DoesNotContain(@"class SerializableSquare", code); + Assert.DoesNotContain(@"class SerializableNode", code); + Assert.DoesNotContain(@"class XmlSerializerPerson", code); + } + } + + internal class SurrogateProvider : ISerializationSurrogateProvider2 + { + static XmlQualifiedName s_circleList = new XsdDataContractExporter().GetSchemaTypeName(typeof(SerializableCircle[])); + static XmlQualifiedName s_square = new XsdDataContractExporter().GetSchemaTypeName(typeof(SerializableSquare)); + static XmlQualifiedName s_serializableNode = new XsdDataContractExporter().GetSchemaTypeName(typeof(SerializableNode)); + static XmlQualifiedName s_xmlSerializerPersonAdapter = new XsdDataContractExporter().GetSchemaTypeName(typeof(XmlSerializerAdapter)); + + bool _surrogateBinding; + public SurrogateProvider(bool surrogateBinding) { _surrogateBinding = surrogateBinding; } + + public object? GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType) => memberInfo.MemberType.ToString(); + public object? GetCustomDataToExport(Type runtimeType, Type dataContractType) => runtimeType.Name; + public object GetDeserializedObject(object obj, Type targetType) => throw new NotImplementedException(); + public void GetKnownCustomDataTypes(Collection customDataTypes) { } + public object GetObjectToSerialize(object obj, Type targetType) => throw new NotImplementedException(); + public Type GetSurrogateType(Type type) // Formerly Known As GetDataContractType(Type)... but this was in the first interface, so we can't change this name. + { + if (type == typeof(Node)) + return typeof(SerializableNode); + if (type == typeof(SerializableCircle)) + return typeof(SerializableSquare); + if (type == typeof(XmlSerializerPerson)) + return typeof(XmlSerializerAdapter); + return type; + } + public Type? GetReferencedTypeOnImport(string name, string ns, object? customData) + { + if (!_surrogateBinding) + { + // Collection item and type name mismatch must be handled by surrogate to avoid exception + return (name == s_circleList.Name && ns == s_circleList.Namespace) ? typeof(SerializableSquare[]) : null; + } + + if (name == s_square.Name && ns == s_square.Namespace) + return typeof(SerializableCircle); + if (name == s_circleList.Name && ns == s_circleList.Namespace) + return typeof(SerializableCircle[]); + if (name == s_serializableNode.Name && ns == s_serializableNode.Namespace) + return typeof(Node); + if (name == s_xmlSerializerPersonAdapter.Name && ns == s_xmlSerializerPersonAdapter.Namespace) + return typeof(XmlSerializerPerson); + return null; + } + + [XmlSchemaProvider("StaticGetSchema")] + public class XmlSerializerAdapter : IXmlSerializable + { + public XmlSchema GetSchema() + { + throw new NotImplementedException(); + } + + public void ReadXml(XmlReader reader) + { + throw new NotImplementedException(); + } + + public void WriteXml(XmlWriter writer) + { + throw new NotImplementedException(); + } + + static XmlQualifiedName StaticGetSchema(XmlSchemaSet schemaSet) + { + XmlReflectionImporter importer = new XmlReflectionImporter(); + XmlTypeMapping xmlTypeMapping = importer.ImportTypeMapping(typeof(T)); + XmlSchemas schemas = new XmlSchemas(); + XmlSchemaExporter exporter = new XmlSchemaExporter(schemas); + exporter.ExportTypeMapping(xmlTypeMapping); + schemas.Compile(new ValidationEventHandler(ValidationCallbackWithErrorCode), true); + for (int i = 0; i < schemas.Count; i++) + { + XmlSchema schema = schemas[i]; + schemaSet.Add(schema); + } + return new XmlQualifiedName(xmlTypeMapping.TypeName, xmlTypeMapping.Namespace); + } + + private static void ValidationCallbackWithErrorCode(object sender, ValidationEventArgs args) + { + Console.WriteLine("Schema warning: " + args.Message); + } + } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/RoundTripTest.cs b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/RoundTripTest.cs new file mode 100644 index 00000000000000..67c31622ace958 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/RoundTripTest.cs @@ -0,0 +1,349 @@ +// 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.Data; +using System.Runtime.Serialization.Schema; +using System.Xml; +using Xunit; +using Xunit.Abstractions; + +namespace System.Runtime.Serialization.Schema.Tests +{ + public class RoundTripTest + { + private readonly ITestOutputHelper _output; + + public RoundTripTest(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void RountTripTest() + { + // AppContext SetSwitch seems to be unreliable in the unit test case. So let's not rely on it + // for test coverage. But let's do look at the app switch to get our verification correct. + AppContext.TryGetSwitch("Switch.System.Runtime.Serialization.DataContracts.Auto_Import_KVP", out bool autoImportKVP); + + XsdDataContractExporter exporter = new XsdDataContractExporter(); + exporter.Export(typeof(RootClass)); + XsdDataContractImporter importer = new XsdDataContractImporter(); + importer.Options = new ImportOptions(); + importer.Options.ImportXmlType = true; + importer.Options.ReferencedTypes.Add(typeof(DBNull)); + importer.Options.ReferencedTypes.Add(typeof(DateTimeOffset)); + importer.Import(exporter.Schemas); + + string code = SchemaUtils.DumpCode(importer.CodeCompileUnit); + _output.WriteLine(code); + + Assert.Contains(@"This code was generated by a tool.", code); + Assert.Contains(@"namespace System.Runtime.Serialization.Schema.Tests", code); + Assert.Contains(@"public partial class RoundTripTestRootClass : object, System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Contains(@"[System.Xml.Serialization.XmlRootAttribute(ElementName=""SchemaDefinedType"", Namespace=""http://schemas.datacontract.org/2004/07/System.Runtime.Serialization"")]", code); + Assert.Contains(@"public partial class dataSetType : object, System.Xml.Serialization.IXmlSerializable", code); + Assert.Contains(@"[System.Runtime.Serialization.DataContractAttribute(Name=""RoundTripTest.DataContractClass"", Namespace=""http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests""", code); + Assert.Contains(@"public partial class RoundTripTestDataContractClass : object, System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Contains(@"[System.Runtime.Serialization.DataContractAttribute(Name=""RoundTripTest.DataContractStruct"", Namespace=""http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests""", code); + Assert.Contains(@"public partial struct RoundTripTestDataContractStruct : System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Contains(@"[System.Runtime.Serialization.DataContractAttribute(Name=""RoundTripTest.EmitDefaultClass"", Namespace=""http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests""", code); + Assert.Contains(@"public partial class RoundTripTestEmitDefaultClass : object, System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Contains(@"public System.Nullable NullableDataContractStruct2", code); + Assert.Contains(@"[System.Runtime.Serialization.DataContractAttribute(Name=""RoundTripTest.EncodingMismatchClass"", Namespace=""http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests""", code); + Assert.Contains(@"public partial class RoundTripTestEncodingMismatchClass : object, System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Contains(@"public enum RoundTripTestMyEnum : int", code); + Assert.Contains(@"TwoHundred = 200", code); + Assert.Contains(@"public enum RoundTripTestMyFlagsEnum : int", code); + Assert.Contains(@"Four = 4,", code); + Assert.Contains(@"public class ArrayOfNullableOfRoundTripTestMyEnumho3BZmza : System.Collections.Generic.List", code); + Assert.Contains(@"namespace schemas.microsoft.com._2003._10.Serialization.Arrays", code); + Assert.Contains(@"public partial class ArrayOfKeyValueOfintArrayOfstringty7Ep6D1 : object, System.Xml.Serialization.IXmlSerializable", code); + Assert.Contains(@"private static System.Xml.XmlQualifiedName typeName = new System.Xml.XmlQualifiedName(""ArrayOfKeyValueOfintArrayOfstringty7Ep6D1"", ""http://schemas.microsoft.com/2003/10/Serialization/Arrays"");", code); + Assert.Contains(@"public partial class ArrayOfKeyValueOfNullableOfunsignedByteNullableOfunsignedByte_ShTDFhl_P : object, System.Xml.Serialization.IXmlSerializable", code); + + if (autoImportKVP) + { + Assert.Contains(@"public partial struct KeyValuePairOfintArrayOfstringty7Ep6D1 : System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Contains(@"public partial struct KeyValuePairOfNullableOfunsignedByteNullableOfunsignedByte_ShTDFhl_P : System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Contains(@"[System.Runtime.Serialization.DataContractAttribute(Name=""KeyValuePairOfstringNullableOfintU6ho3Bhd"", Namespace=""http://schemas.datacontract.org/2004/07/System.Collections.Generic"")]", code); + } + else + { + Assert.DoesNotContain(@"public partial struct KeyValuePairOfintArrayOfstringty7Ep6D1 : System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.DoesNotContain(@"public partial struct KeyValuePairOfNullableOfunsignedByteNullableOfunsignedByte_ShTDFhl_P : System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.DoesNotContain(@"[System.Runtime.Serialization.DataContractAttribute(Name=""KeyValuePairOfstringNullableOfintU6ho3Bhd"", Namespace=""http://schemas.datacontract.org/2004/07/System.Collections.Generic"")]", code); + } + } + + [Fact] + public void IsReferenceType() + { + XsdDataContractExporter exporter = new XsdDataContractExporter(); + exporter.Export(typeof(RootIsReferenceContainer)); + XsdDataContractImporter importer = new XsdDataContractImporter(); + importer.Options = new ImportOptions(); + importer.Options.ImportXmlType = true; + importer.Import(exporter.Schemas); + string code = SchemaUtils.DumpCode(importer.CodeCompileUnit); + _output.WriteLine(code); + + Assert.True(code.Length > 616); + } + + +#pragma warning disable CS0169, CS0414, IDE0051, IDE1006 + #region RoundTripTest DataContracts + [DataContract] + public class RootClass + { + [DataMember] MyEnum myEnum; + [DataMember] MyEnum[] arrayOfMyEnum; + [DataMember] MyEnum? nullableOfMyEnum; + [DataMember] MyEnum?[] arrayOfNullableOfMyEnum; + [DataMember] MyFlagsEnum myFlagsEnum; + [DataMember] XmlNode[] xmlNodes; + [DataMember] XmlElement xmlElement; + [DataMember] DataContractClass dataContractClass; + [DataMember] DataContractClass[] arrayOfDataContractClass; + [DataMember] DataContractStruct dataContractStruct; + [DataMember] DataContractStruct[] arrayOfDataContractStruct; + [DataMember] DataContractStruct? nullableOfDataContractStruct; + [DataMember] DataContractStruct?[] arrayOfNullableOfDataContractStruct; + [DataMember] DataSet dataSet; + [DataMember] IList> intLists; + [DataMember] IList>> dictionaries; + [DataMember] IDictionary nullableValues; + [DataMember] IDictionary nullableKeyAndValues; + [DataMember] EmitDefaultClass emitDefaultClass; + [DataMember] EncodingMismatchClass encodingMismatchClass; + [DataMember] DBNull dbnull; + } + + public enum MyEnum { Hundred = 100, TwoHundred = 200 }; + [Flags] + public enum MyFlagsEnum { Four = 4, Eight = 8 }; + + [DataContract] + public class DataContractClass + { + [DataMember] public int IntValue; + [DataMember] public Guid GuidValue; + [DataMember] TimeSpan timeSpanValue; + } + + [DataContract] + public struct DataContractStruct + { + [DataMember] public char CharValue; + [DataMember] Decimal decimalValue; + [DataMember] XmlQualifiedName qname; + } + + + [DataContract] + public class EmitDefaultClass + { + [DataMember(EmitDefaultValue = false)] + public string Name1; + [DataMember(EmitDefaultValue = false)] + public int Age1; + [DataMember(EmitDefaultValue = false)] + public int? Salary1; + [DataMember(EmitDefaultValue = false)] + public DataContractStruct DataContractStruct1; + [DataMember(EmitDefaultValue = false)] + public DataContractStruct? NullableDataContractStruct1; + [DataMember(EmitDefaultValue = false)] + public DateTime DateTime1; + [DataMember(EmitDefaultValue = false)] + public DateTimeOffset DateTimeOffset1; + [DataMember(EmitDefaultValue = false)] + public Guid Guid1; + [DataMember(EmitDefaultValue = false)] + public Decimal Decimal1; + [DataMember(EmitDefaultValue = false)] + public TimeSpan TimeSpan1; + + [DataMember(IsRequired = true, EmitDefaultValue = false)] + public string Name2; + [DataMember(IsRequired = true, EmitDefaultValue = false)] + public int Age2; + [DataMember(IsRequired = true, EmitDefaultValue = false)] + public int? Salary2; + [DataMember(IsRequired = true, EmitDefaultValue = false)] + public DataContractStruct DataContractStruct2; + [DataMember(IsRequired = true, EmitDefaultValue = false)] + public DataContractStruct? NullableDataContractStruct2; + [DataMember(IsRequired = true, EmitDefaultValue = false)] + public DateTime DateTime2; + [DataMember(IsRequired = true, EmitDefaultValue = false)] + public DateTimeOffset DateTimeOffset2; + [DataMember(IsRequired = true, EmitDefaultValue = false)] + public Guid Guid2; + [DataMember(IsRequired = true, EmitDefaultValue = false)] + public Decimal Decimal2; + [DataMember(IsRequired = true, EmitDefaultValue = false)] + public TimeSpan TimeSpan2; + + } + + [DataContract] + public class EncodingMismatchClass + { + [DataMember(Name = "a:b")] + public int a; + [DataMember(Name = "a_x003A_bc_x003C__x003B_")] + public int b; + [DataMember(Name = "a_x003a_bc_x003b__x003c_")] + public int c; + } + #endregion + + #region IsReferenceType DataContracts + [DataContract] + class RootIsReferenceContainer + { + [DataMember] + RefEdibleItem r = new RefEdibleItem(); + [DataMember] + Fruit w = new Fruit(); + [DataMember] + RefApple x = new RefApple(); + [DataMember] + public RefCustomer customer = RefCustomer.CreateInstance(); + [DataMember] + RefGrades grades = new RefGrades(); + [DataMember] + CircularLinkedList_ContainsBackpointingRef clcb = new CircularLinkedList_ContainsBackpointingRef(); + [DataMember] + RefCircularLinks_ContainsBackpointer rccb = new RefCircularLinks_ContainsBackpointer(); + [DataMember] + RefCircularNodeA_ContainsRefWithBackpointer rcnacr = new RefCircularNodeA_ContainsRefWithBackpointer(); + } + + [DataContract(IsReference = true)] + class RefEdibleItem + { + } + + [DataContract] + class Fruit : RefEdibleItem + { + } + + [DataContract(IsReference = true)] + class RefApple : Fruit + { + } + + [CollectionDataContract(IsReference = true)] + public class RefGrades : List + { + } + + [DataContract(IsReference = true)] + class RefCustomer + { + [DataMember] + string Name; + [DataMember] + int ZipCode; + + internal static RefCustomer CreateInstance() + { + RefCustomer x = new RefCustomer(); + x.Name = "Bill Gates"; + x.ZipCode = 98052; + return x; + } + } + + + [DataContract] + public class CircularLinkedList_ContainsBackpointingRef + { + [DataMember] + RefNode start; + + [DataMember] + int numberOfNodes; + + public CircularLinkedList_ContainsBackpointingRef() + { + numberOfNodes = 4; + RefNode currentNode = null, prevNode = null; + start = null; + for (int i = 0; i < numberOfNodes; i++) + { + currentNode = new RefNode(i, "Hello World"); + if (i == 0) + start = currentNode; + if (prevNode != null) + prevNode.Next = currentNode; + prevNode = currentNode; + } + currentNode.Next = start; + } + } + + [DataContract(IsReference = true)] + public class RefNode + { + [DataMember] + public RefNode Next; + + [DataMember] + int id; + + [DataMember] + string name; + + public RefNode(int id, string name) + { + this.id = id; + this.name = name; + } + } + + + [DataContract(IsReference = true)] + public class RefCircularLinks_ContainsBackpointer + { + [DataMember] + RefCircularLinks_ContainsBackpointer link; + + public RefCircularLinks_ContainsBackpointer() + { + link = this; + } + } + + + [DataContract(IsReference = true)] + public class RefCircularNodeA_ContainsRefWithBackpointer + { + [DataMember] + RefCircularNodeB_ContainsRefWithBackpointer linkToB; + + public RefCircularNodeA_ContainsRefWithBackpointer() + { + linkToB = new RefCircularNodeB_ContainsRefWithBackpointer(this); + } + } + + [DataContract(IsReference = true)] + public class RefCircularNodeB_ContainsRefWithBackpointer + { + [DataMember] + RefCircularNodeA_ContainsRefWithBackpointer linkToA; + + public RefCircularNodeB_ContainsRefWithBackpointer(RefCircularNodeA_ContainsRefWithBackpointer nodeA) + { + linkToA = nodeA; + } + } + #endregion +#pragma warning restore CS0169, CS0414, IDE0051, IDE1006 + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/SchemaUtils.cs b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/SchemaUtils.cs new file mode 100644 index 00000000000000..cea8d7333ccb7c --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/SchemaUtils.cs @@ -0,0 +1,370 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.CodeDom; +using System.CodeDom.Compiler; +using System.IO; +using System.Xml; +using System.Xml.Schema; + +namespace System.Runtime.Serialization.Schema.Tests +{ + internal class SchemaUtils + { + static XmlWriterSettings writerSettings = new XmlWriterSettings() { Indent = true }; + + #region Test Data + internal static XmlSchemaSet PositiveSchemas = SchemaUtils.ReadStringsIntoSchemaSet( + new string[] { + @" + + + ", + @" + + + ", + @" + + + + + ", + }); + + internal static XmlSchemaSet IsReferenceSchemas = SchemaUtils.ReadStringsIntoSchemaSet( + new string[] { + @" + + + + + + + + + ", + @" + + + + + + + + ", + }); + + internal static XmlSchemaSet MixedSchemas = SchemaUtils.ReadStringsIntoSchemaSet( + new string[] { + @" + + + + + ", + @" + + + ", + }); + + internal static string[] NegativeSchemaStrings = + new string[] { + @"", // null + @" + ", // new XmlQualifiedName("FooType", "http://EmptySchema"), + @" + ", // new XmlQualifiedName("FooType", "http://NonExistantSchema"), + @" + + + ", // null + @" + + + ", // null + @" + + + ", // new XmlQualifiedName("ExtraAttributeWildcardType", "http://schemas.datacontract.org/2004/07/foo"), + @" + + + ", // null + @" + + + ", // null + @" + + + ", // null + @" + + + ", // null + @" + + + ", // null + @" + + + ", // null + @" + + + ", // null + @" + + + + + ", // null + @" + + ", // null + }; + + internal static (bool expectedResult, bool isElement, XmlQualifiedName[] qnames, string schemaString)[] CanImportTests = new (bool, bool, XmlQualifiedName[], string)[] { + (false, false, new XmlQualifiedName[] { new XmlQualifiedName("InvalidTopLevelElementType", "http://schemas.datacontract.org/2004/07/foo") }, + @" + + + + "), + (true, false, new XmlQualifiedName[] { new XmlQualifiedName("ValidType", "http://schemas.datacontract.org/2004/07/foo") }, + @" + + + + "), + (true, false, new XmlQualifiedName[] { + new XmlQualifiedName("Address", "http://schemas.datacontract.org/2004/07/Suites.SchemaImport.Classes"), + new XmlQualifiedName("Person", "http://schemas.datacontract.org/2004/07/Suites.SchemaImport.Classes"), + new XmlQualifiedName("ArrayOfAddress", "http://schemas.datacontract.org/2004/07/Suites.SchemaImport.Classes"), + new XmlQualifiedName("ArrayOfArrayOfAddress", "http://schemas.datacontract.org/2004/07/Suites.SchemaImport.Classes"), + }, + @" + + + + + + + + + "), + (true, false, null, + @" + + + + "), + (true, false, null, + @" + + + "), + (false, false, new XmlQualifiedName[] { new XmlQualifiedName("TypeWithExtraAttributes", "http://schemas.datacontract.org/2004/07/foo") }, + @" + + + "), + (true, true, new XmlQualifiedName[] { new XmlQualifiedName("Address", "http://schemas.datacontract.org/2004/07/Suites.SchemaImport.Classes") }, + @" + + + + + + + + "), + }; + + internal static XmlQualifiedName[] ValidTypeNames = new XmlQualifiedName[] { + new XmlQualifiedName("ValidType", "http://schemas.datacontract.org/2004/07/fooNs"), + new XmlQualifiedName("AnotherValidType", "http://schemas.datacontract.org/2004/07/barNs"), + new XmlQualifiedName("NonAttributedType", "http://schemas.datacontract.org/2004/07/Suites.SchemaImport"), + new XmlQualifiedName("NonRefType", "http://schemas.datacontract.org/2004/07/Suites.SchemaImport.ReferencedTypes"), + new XmlQualifiedName("RefType1", "http://schemas.datacontract.org/2004/07/Suites.SchemaImport.ReferencedTypes"), + }; + + internal static XmlQualifiedName[] InvalidTypeNames = new XmlQualifiedName[] { + new XmlQualifiedName("InvalidType", "http://schemas.datacontract.org/2004/07/fooNs"), + }; + + // These correspond with the set in 'NegativeSchemaStrings' + internal static XmlQualifiedName[] NegativeTypeNames = new XmlQualifiedName[] { + null, + new XmlQualifiedName("FooType", "http://EmptySchema"), + new XmlQualifiedName("FooType", "http://NonExistantSchema"), + null, + null, + new XmlQualifiedName("ExtraAttributeWildcardType", "http://schemas.datacontract.org/2004/07/foo"), + null, + null, + null, + null, + null, + null, + null, + null, + null, + }; + + internal static string GlobalSchema = @" + + + "; + #endregion + + + internal static XsdDataContractImporter CreateImporterWithDefaultOptions() + { + XsdDataContractImporter importer = new XsdDataContractImporter(); + importer.Options = new ImportOptions(); + return importer; + } + + internal static string DumpCode(CodeCompileUnit ccu, CodeDomProvider provider = null) + { + provider ??= CodeDomProvider.CreateProvider("csharp"); + + CodeGeneratorOptions options = new CodeGeneratorOptions() + { + BlankLinesBetweenMembers = true, + BracingStyle = "C", + }; + + StringWriter sw = new StringWriter(); + provider.GenerateCodeFromCompileUnit(ccu, sw, options); + return sw.ToString(); + } + + internal static XmlSchema GetSchema(XmlSchemaSet schemaSet, string targetNs) + { + XmlSchema schema = null; + foreach (XmlSchema ctSchema in schemaSet.Schemas()) + { + if (ctSchema.TargetNamespace == targetNs) + { + schema = ctSchema; + break; + } + } + return schema; + } + + internal static XmlSchemaElement GetSchemaElement(XmlSchemaSet schemaSet, XmlQualifiedName qname) + { + foreach (XmlSchema schema in schemaSet.Schemas(qname.Namespace)) + { + XmlSchemaElement schemaElement = (XmlSchemaElement)schema.Elements[qname]; + if (schemaElement != null) + return schemaElement; + } + throw new Exception(String.Format("Element {0} is not found", qname)); + } + + internal static string GetSchemaString(XmlSchemaSet schemaSet, string targetNs) + { + XmlSchema schema = GetSchema(schemaSet, targetNs); + StringWriter stringWriter = new StringWriter(); + XmlWriter xmlWriter = XmlWriter.Create(stringWriter, writerSettings); + schema.Write(xmlWriter); + xmlWriter.Flush(); + return stringWriter.ToString(); + } + + internal static void SetSchemaString(XmlSchemaSet schemaSet, string targetNs, string schemaString) + { + XmlSchema schema = null; + foreach (XmlSchema ctSchema in schemaSet.Schemas()) + { + if (ctSchema.TargetNamespace == targetNs) + { + schema = ctSchema; + break; + } + } + schemaSet.Remove(schema); + schema = XmlSchema.Read(new StringReader(schemaString), null); + schemaSet.Add(schema); + } + + internal static string GetString(CodeTypeReference typeReference) + { + if (typeReference.ArrayRank > 0) + { + CodeTypeReference arrayType = typeReference; + string arrayString = String.Empty; + for (; ; ) + { + int rank = typeReference.ArrayRank; + arrayString += "["; + for (int r = 1; r < rank; r++) + arrayString += ","; + arrayString += "]"; + + typeReference = typeReference.ArrayElementType; + if (typeReference.ArrayRank == 0) + break; + } + return String.Format("Array of {0}{1}", typeReference.BaseType, arrayString); + } + else + return typeReference.BaseType; + } + + internal static string InsertAttribute(string xml, string xpath, string prefix, string localName, string ns, string value) + { + XmlDocument xmlDoc = new XmlDocument(); + xmlDoc.Load(XmlReader.Create(new StringReader(xml))); + XmlNamespaceManager nsMgr = new XmlNamespaceManager(xmlDoc.NameTable); + nsMgr.AddNamespace("xs", XmlSchema.Namespace); + XmlElement xmlElement = (XmlElement)xmlDoc.SelectSingleNode(xpath, nsMgr); + XmlAttribute xmlAttribute = xmlDoc.CreateAttribute(prefix, localName, ns); + xmlAttribute.Value = value; + xmlElement.Attributes.Append(xmlAttribute); + + StringWriter stringWriter = new StringWriter(); + xmlDoc.Save(XmlWriter.Create(stringWriter, writerSettings)); + return stringWriter.ToString(); + } + + internal static string InsertElement(string xml, string xpath, string xmlFrag, bool insertAfter) + { + XmlDocument xmlDoc = new XmlDocument(); + xmlDoc.Load(XmlReader.Create(new StringReader(xml))); + XmlNamespaceManager nsMgr = new XmlNamespaceManager(xmlDoc.NameTable); + nsMgr.AddNamespace("xs", XmlSchema.Namespace); + XmlNode xmlNode = xmlDoc.SelectSingleNode(xpath, nsMgr); + if (insertAfter) + xmlNode.ParentNode.InsertAfter(xmlDoc.ReadNode(XmlReader.Create(new StringReader(xmlFrag))), xmlNode); + else + xmlNode.ParentNode.InsertBefore(xmlDoc.ReadNode(XmlReader.Create(new StringReader(xmlFrag))), xmlNode); + + StringWriter stringWriter = new StringWriter(); + xmlDoc.Save(XmlWriter.Create(stringWriter, writerSettings)); + return stringWriter.ToString(); + } + + + internal static XmlSchemaSet ReadStringsIntoSchemaSet(params string[] schemaStrings) + { + XmlSchemaSet schemaSet = new XmlSchemaSet(); + foreach (string schemaString in schemaStrings) + { + StringReader reader = new StringReader(schemaString); + XmlSchema schema = XmlSchema.Read(reader, null); + if (schema == null) + throw new Exception("Could not read schema"); + schemaSet.Add(schema); + } + return schemaSet; + } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Xml/ref/System.Runtime.Serialization.Xml.cs b/src/libraries/System.Runtime.Serialization.Xml/ref/System.Runtime.Serialization.Xml.cs index 6a3d406d810269..e1f9618e94dc61 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/ref/System.Runtime.Serialization.Xml.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/ref/System.Runtime.Serialization.Xml.cs @@ -79,6 +79,7 @@ public partial class ExportOptions { public ExportOptions() { } public System.Collections.ObjectModel.Collection KnownTypes { get { throw null; } } + public ISerializationSurrogateProvider? DataContractSurrogate { get { throw null; } set { throw null; } } } public sealed partial class ExtensionDataObject { @@ -470,3 +471,87 @@ public virtual void WriteXmlnsAttribute(string? prefix, string namespaceUri) { } public virtual void WriteXmlnsAttribute(string? prefix, System.Xml.XmlDictionaryString namespaceUri) { } } } +namespace System.Runtime.Serialization.DataContracts +{ + public abstract partial class DataContract + { + internal const System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes DataContractPreserveMemberTypes = + System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods | + System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicMethods | + System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors | + System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicConstructors | + System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | + System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties; + + internal DataContract(DataContractCriticalHelper helper) { } + + public virtual DataContract? BaseContract { [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] get { throw null; } } + public virtual string? ContractType { get { throw null; } } + public virtual bool IsBuiltInDataContract { get { throw null; } } + public virtual bool IsISerializable { get { throw null; } } + public virtual bool IsReference { get { throw null; } } + public virtual bool IsValueType { get { throw null; } } + public virtual System.Collections.Generic.Dictionary? KnownDataContracts { [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] get { throw null; } } + public virtual System.Collections.ObjectModel.ReadOnlyCollection DataMembers { get { throw null; } } + public virtual Type OriginalUnderlyingType { get { throw null; } } + public virtual System.Xml.XmlQualifiedName XmlName { get { throw null; } } + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(DataContract.DataContractPreserveMemberTypes)] + public virtual Type UnderlyingType { get { throw null; } } + public virtual System.Xml.XmlDictionaryString? TopLevelElementName { get { throw null; } } + public virtual System.Xml.XmlDictionaryString? TopLevelElementNamespace { get { throw null; } } + + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public static DataContract? GetBuiltInDataContract(string name, string ns) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public static System.Xml.XmlQualifiedName GetXmlName(Type type) { throw null; } + + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public virtual System.Xml.XmlQualifiedName GetArrayTypeName(bool isNullable) { throw null; } + public virtual bool IsDictionaryLike([Diagnostics.CodeAnalysis.NotNullWhen(true)] out string? keyName, [Diagnostics.CodeAnalysis.NotNullWhen(true)] out string? valueName, [Diagnostics.CodeAnalysis.NotNullWhen(true)] out string? itemName) { throw null; } + } + internal abstract partial class DataContractCriticalHelper { } + public sealed partial class DataContractSet + { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public DataContractSet(DataContractSet dataContractSet) { throw null; } + public DataContractSet(ISerializationSurrogateProvider? dataContractSurrogate, System.Collections.Generic.IEnumerable? referencedTypes, System.Collections.Generic.IEnumerable? referencedCollectionTypes) { throw null; } + + public System.Collections.Generic.Dictionary Contracts { get { throw null; } } + public System.Collections.Generic.Dictionary? KnownTypesForObject { get { throw null; } } + public System.Collections.Generic.Dictionary ProcessedContracts { get { throw null; } } + public System.Collections.Hashtable SurrogateData { get { throw null; } } + + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public DataContract GetDataContract(Type type) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public DataContract? GetDataContract(System.Xml.XmlQualifiedName key) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public Type? GetReferencedType(System.Xml.XmlQualifiedName xmlName, DataContract dataContract, out DataContract? referencedContract, out object[]? genericParameters, bool? supportGenericTypes = null) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public void ImportSchemaSet(System.Xml.Schema.XmlSchemaSet schemaSet, System.Collections.Generic.IEnumerable? typeNames, bool importXmlDataType) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public System.Collections.Generic.List ImportSchemaSet(System.Xml.Schema.XmlSchemaSet schemaSet, System.Collections.Generic.IEnumerable elements, bool importXmlDataType) { throw null; } + } + public sealed partial class DataMember + { + internal DataMember() { } + + public bool EmitDefaultValue { get { throw null; } } + public bool IsNullable { get { throw null; } } + public bool IsRequired { get { throw null; } } + public DataContract MemberTypeContract { [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] get { throw null; } } + public string Name { get { throw null; } } + public long Order { get { throw null; } } + } + public sealed partial class XmlDataContract : DataContract + { + internal XmlDataContract(Type type) : base(default) { } + + public bool HasRoot { get { throw null; } } + public bool IsAnonymous { get { throw null; } } + public bool IsTopLevelElementNullable { get { throw null; } } + public bool IsTypeDefinedOnImport { get { throw null; } set { throw null; } } + public new bool IsValueType { get { throw null; } set { throw null; } } + public System.Xml.Schema.XmlSchemaType? XsdType { get { throw null; } } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs index f88b0e308e53f3..9b0f071947d335 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs @@ -112,6 +112,18 @@ public static void DCS_DateTimeAsRoot() Assert.StrictEqual(DataContractSerializerHelper.SerializeAndDeserialize(DateTime.SpecifyKind(DateTime.MaxValue, DateTimeKind.Utc), @"9999-12-31T23:59:59.9999999Z"), DateTime.SpecifyKind(DateTime.MaxValue, DateTimeKind.Utc)); } + [Fact] + public static void DCS_BinarySerializationOfDateTime() + { + DateTime dateTime = DateTime.Parse("2021-01-01"); + MemoryStream ms = new(); + DataContractSerializer dcs = new(dateTime.GetType()); + using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(ms, null, null, ownsStream: true)) + dcs.WriteObject(writer, dateTime); + var serializedBytes = ms.ToArray(); + Assert.Equal(72, serializedBytes.Length); + } + [Fact] public static void DCS_DecimalAsRoot() { @@ -2727,14 +2739,6 @@ static string GenerateaAndGetXPath(Type t, MemberInfo[] mi) t, mi, out xname); } - [Fact] - public static void XsdDataContractExporterTest() - { - XsdDataContractExporter exporter = new XsdDataContractExporter(); - Assert.Throws(() => exporter.CanExport(typeof(Employee))); - Assert.Throws(() => exporter.Export(typeof(Employee))); - } - [Fact] public static void DCS_MyISerializableType() { @@ -3867,6 +3871,135 @@ public static void DCS_BasicPerSerializerRoundTripAndCompare_EnumStruct_NotNetFr #endregion + [Fact] + public static void DCS_KnownSerializableTypes_KeyValuePair_2() + { + KeyValuePair kvp = new KeyValuePair("the_key", 42); + Assert.StrictEqual(kvp, DataContractSerializerHelper.SerializeAndDeserialize>(kvp, "the_key42")); + } + + [Fact] + public static void DCS_KnownSerializableTypes_Queue_1() + { + Queue q = new Queue(); + q.Enqueue("first"); + q.Enqueue("second"); + Queue q2 = DataContractSerializerHelper.SerializeAndDeserialize>(q, "<_array xmlns:a=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\">firstsecond<_head>0<_size>2<_tail>2<_version>3"); + Assert.Equal(q, q2); + Assert.StrictEqual(q.Count, q2.Count); + Assert.Equal(q.Dequeue(), q2.Dequeue()); + Assert.Equal(q.Dequeue(), q2.Dequeue()); + } + + [Fact] + public static void DCS_KnownSerializableTypes_Stack_1() + { + Stack stk = new Stack(); + stk.Push("first"); + stk.Push("last"); + Stack result = DataContractSerializerHelper.SerializeAndDeserialize>(stk, "<_array xmlns:a=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\">firstlast<_size>2<_version>2"); + Assert.Equal(stk, result); + Assert.StrictEqual(stk.Count, result.Count); + Assert.Equal(stk.Pop(), result.Pop()); + Assert.Equal(stk.Pop(), result.Pop()); + } + + [Fact] + public static void DCS_KnownSerializableTypes_ReadOnlyCollection_1() + { + ReadOnlyCollection roc = new ReadOnlyCollection(new string[] { "one", "two", "three", "four" }); + ReadOnlyCollection result = DataContractSerializerHelper.SerializeAndDeserialize>(roc, "onetwothreefour"); + Assert.Equal(roc, result); + Assert.StrictEqual(roc.Count, result.Count); + + for (int i = 0; i < roc.Count; i++) + Assert.Equal(roc[i], result[i]); + } + + [Fact] + public static void DCS_KnownSerializableTypes_ReadOnlyDictionary_2() + { + ReadOnlyDictionary rod = new ReadOnlyDictionary(new Dictionary { { "one", 1 }, { "two", 22 }, { "three", 333 }, { "four", 4444 } }); + ReadOnlyDictionary result = DataContractSerializerHelper.SerializeAndDeserialize>(rod, "one1two22three333four4444"); + Assert.Equal(rod, result); + Assert.StrictEqual(rod.Count, result.Count); + + foreach (var kvp in rod) + { + Assert.True(result.ContainsKey(kvp.Key)); + Assert.Equal(kvp.Value, result[kvp.Key]); + } + } + + [Fact] + public static void DCS_KnownSerializableTypes_Queue() + { + Queue q = new Queue(); + q.Enqueue("first"); + q.Enqueue("second"); + Queue q2 = DataContractSerializerHelper.SerializeAndDeserialize(q, "<_array xmlns:a=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\">firstsecond<_growFactor>200<_head>0<_size>2<_tail>2<_version>2"); + Assert.Equal(q, q2); + Assert.StrictEqual(q.Count, q2.Count); + Assert.StrictEqual(q.Dequeue(), q2.Dequeue()); + Assert.StrictEqual(q.Dequeue(), q2.Dequeue()); + } + + [Fact] + public static void DCS_KnownSerializableTypes_Stack() + { + Stack stk = new Stack(); + stk.Push("first"); + stk.Push("last"); + Stack result = DataContractSerializerHelper.SerializeAndDeserialize(stk, "<_array xmlns:a=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\">firstlast<_size>2<_version>2"); + Assert.Equal(stk, result); + Assert.StrictEqual(stk.Count, result.Count); + Assert.StrictEqual(stk.Pop(), result.Pop()); + Assert.StrictEqual(stk.Pop(), result.Pop()); + } + + [Fact] + [ActiveIssue("No issue filed yet. Turns out, CultureInfo is not serialzable, even if it is included in s_knownSerializableTypeInfos")] + public static void DCS_KnownSerializableTypes_CultureInfo() + { + CultureInfo ci = new CultureInfo("pl"); + Assert.StrictEqual(ci, DataContractSerializerHelper.SerializeAndDeserialize(ci, "", null, null, true)); + } + + [Fact] + public static void DCS_KnownSerializableTypes_Version() + { + Version ver = new Version(5, 4, 3); + Assert.StrictEqual(ver, DataContractSerializerHelper.SerializeAndDeserialize(ver, "<_Build>3<_Major>5<_Minor>4<_Revision>-1")); + } + + [Fact] + public static void DCS_KnownSerializableTypes_Tuples() + { + Tuple t1 = new Tuple("first"); + Assert.StrictEqual(t1, DataContractSerializerHelper.SerializeAndDeserialize>(t1, "first")); + + Tuple t2 = new Tuple("first", "second"); + Assert.StrictEqual(t2, DataContractSerializerHelper.SerializeAndDeserialize>(t2, "firstsecond")); + + Tuple t3 = new Tuple("first", "second", "third"); + Assert.StrictEqual(t3, DataContractSerializerHelper.SerializeAndDeserialize>(t3, "firstsecondthird")); + + Tuple t4 = new Tuple("first", "second", "third", "fourth"); + Assert.StrictEqual(t4, DataContractSerializerHelper.SerializeAndDeserialize>(t4, "firstsecondthirdfourth")); + + Tuple t5 = new Tuple("first", "second", "third", "fourth", "fifth"); + Assert.StrictEqual(t5, DataContractSerializerHelper.SerializeAndDeserialize>(t5, "firstsecondthirdfourthfifth")); + + Tuple t6 = new Tuple("first", "second", "third", "fourth", "fifth", "sixth"); + Assert.StrictEqual(t6, DataContractSerializerHelper.SerializeAndDeserialize>(t6, "firstsecondthirdfourthfifthsixth")); + + Tuple t7 = new Tuple("first", "second", "third", "fourth", "fifth", "sixth", "seventh"); + Assert.StrictEqual(t7, DataContractSerializerHelper.SerializeAndDeserialize>(t7, "firstsecondthirdfourthfifthsixthseventh")); + + Tuple> t8 = new Tuple>("first", "second", "third", "fourth", "fifth", "sixth", "seventh", new Tuple(8, 9, "tenth")); + Assert.StrictEqual(t8, DataContractSerializerHelper.SerializeAndDeserialize>>(t8, "firstsecondthirdfourthfifthsixthseventh89tenth")); + } + [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/60462", TestPlatforms.iOS | TestPlatforms.tvOS)] public static void DCS_TypeWithVirtualGenericProperty() diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/System.Runtime.Serialization.Xml.Tests.csproj b/src/libraries/System.Runtime.Serialization.Xml/tests/System.Runtime.Serialization.Xml.Tests.csproj index 88c8927cea01e0..ce77b694a88335 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/System.Runtime.Serialization.Xml.Tests.csproj +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/System.Runtime.Serialization.Xml.Tests.csproj @@ -28,10 +28,29 @@ + + + + + + + + + + + + + + + + + + + - + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/ExportOptionsTests.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/ExportOptionsTests.cs new file mode 100644 index 00000000000000..7f33e82eb3b26f --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/ExportOptionsTests.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; +using Xunit.Abstractions; + +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +{ + public class ExportOptionsTests + { + private readonly ITestOutputHelper _output; + + public ExportOptionsTests(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void DefaultOptions() + { + ExportOptions options = new ExportOptions(); + Assert.NotNull(options); + Assert.Null(options.DataContractSurrogate); + Assert.NotNull(options.KnownTypes); + Assert.Empty(options.KnownTypes); + } + + [Fact] + public void GetImportOptions() + { + XsdDataContractExporter exporter = new XsdDataContractExporter(); + var options = new ExportOptions(); + exporter.Options = options; + Assert.NotNull(exporter.Options); + Assert.Equal(options, exporter.Options); + } + + [Fact] + public void SetImportOptions() + { + XsdDataContractExporter e = new XsdDataContractExporter(); + e.Options = new ExportOptions(); + Assert.Empty(e.Options.KnownTypes); + e.Options.KnownTypes.Add(typeof(Types.Point)); + Assert.Single(e.Options.KnownTypes); + } + + [Fact] + public void KnownTypes_Negative() + { + XsdDataContractExporter e = new XsdDataContractExporter(); + e.Options = new ExportOptions(); + e.Options.KnownTypes.Add(null); + var ex = Assert.Throws(() => e.Export(typeof(Types.Point))); + Assert.Equal(@"Cannot export null type provided via KnownTypesCollection.", ex.Message); + } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/ExporterApiTests.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/ExporterApiTests.cs new file mode 100644 index 00000000000000..03e2c5f5a918bb --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/ExporterApiTests.cs @@ -0,0 +1,336 @@ +// 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.Reflection; +using System.Xml; +using System.Xml.Schema; +using Xunit; +using Xunit.Abstractions; + +using SerializableTypes.XsdDataContractExporterTests; + +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +{ + public class ExporterApiTests + { + private readonly ITestOutputHelper _output; + public ExporterApiTests(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void Ctor_Default() + { + XsdDataContractExporter xce = new XsdDataContractExporter(); + Assert.NotNull(xce); + Assert.Null(xce.Options); + } + + [Fact] + public void Ctor_Schemas() + { + XmlSchemaSet schemaSet = new XmlSchemaSet(); + XsdDataContractExporter xce = new XsdDataContractExporter(schemaSet); + Assert.NotNull(xce); + Assert.Null(xce.Options); + Assert.Equal(schemaSet, xce.Schemas); + } + + [Theory] + [MemberData(nameof(CanExport_MemberData))] + public void CanExport(bool expectedResult, string testname, Func canExport, Type expectedExceptionType = null, string msg = null) + { + _output.WriteLine($"=============== {testname} ==============="); + XsdDataContractExporter importer = new XsdDataContractExporter(); + if (expectedExceptionType == null) + { + Assert.Equal(expectedResult, canExport(importer)); + } + else + { + var ex = Assert.Throws(expectedExceptionType, () => canExport(importer)); + + if (!string.IsNullOrEmpty(msg)) + Assert.Equal(msg, ex.Message); + } + } + public static IEnumerable CanExport_MemberData() + { + //yield return new object[] { true, "", (XsdDataContractExporter exp) => exp.CanExport() }; + //yield return new object[] { false, "", (XsdDataContractExporter exp) => exp.CanExport(), typeof(), @"" }; + + // CanExport(Type) + yield return new object[] { true, "t1+", (XsdDataContractExporter exp) => exp.CanExport(typeof(Types.Point)) }; + yield return new object[] { false, "t2-", (XsdDataContractExporter exp) => exp.CanExport((Type)null), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'type')" }; + yield return new object[] { false, "t3-", (XsdDataContractExporter exp) => exp.CanExport(typeof(Types.NonSerializableSquare)) }; + yield return new object[] { true, "t4+", (XsdDataContractExporter exp) => exp.CanExport(typeof(Types.NonAttributedPersonStruct)) }; + yield return new object[] { true, "t5+", (XsdDataContractExporter exp) => exp.CanExport(typeof(Types.NonAttributedPersonClass)) }; + yield return new object[] { true, "t6+", (XsdDataContractExporter exp) => exp.CanExport(typeof(Types.ExtendedSquare)) }; + yield return new object[] { false, "t7-", (XsdDataContractExporter exp) => exp.CanExport(typeof(Types.RecursiveCollection1)) }; + yield return new object[] { false, "t8-", (XsdDataContractExporter exp) => exp.CanExport(typeof(Types.RecursiveCollection2)) }; + yield return new object[] { false, "t9-", (XsdDataContractExporter exp) => exp.CanExport(typeof(Types.RecursiveCollection3)) }; + yield return new object[] { false, "t10-", (XsdDataContractExporter exp) => exp.CanExport(typeof(Types.RecursiveCollection4)) }; + + // CanExport(ICollection) + yield return new object[] { true, "ca1+", (XsdDataContractExporter exp) => exp.CanExport(new Assembly[] { typeof(DataContractTypes).Assembly }) }; + yield return new object[] { false, "ca2-", (XsdDataContractExporter exp) => exp.CanExport((ICollection)null), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'assemblies')" }; + yield return new object[] { false, "ca3-", (XsdDataContractExporter exp) => exp.CanExport(new Assembly[] { null }), typeof(ArgumentException), @"Cannot export null assembly provided via 'assemblies' parameter." }; + yield return new object[] { false, "ca4-", (XsdDataContractExporter exp) => exp.CanExport(new Assembly[] { typeof(ExporterApiTests).Assembly }) }; + + // CanExport(ICollection) + yield return new object[] { true, "ct1+", (XsdDataContractExporter exp) => exp.CanExport(new Type[] { typeof(Types.Point), typeof(Types.Circle) }) }; + yield return new object[] { false, "ct2-", (XsdDataContractExporter exp) => exp.CanExport((ICollection)null), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'types')" }; + yield return new object[] { false, "ct3-", (XsdDataContractExporter exp) => exp.CanExport(new Type[] { null }), typeof(ArgumentException), @"Cannot export null type provided via 'types' parameter." }; + yield return new object[] { false, "ct4-", (XsdDataContractExporter exp) => exp.CanExport(new Type[] { typeof(Types.Point), typeof(Types.NonSerializableSquare) }) }; + } + + [Theory] + [MemberData(nameof(Export_MemberData))] + public void Export(string testname, Action export, Action schemaCheck = null) + { + _output.WriteLine($"=============== {testname} ==============="); + XsdDataContractExporter exporter = new XsdDataContractExporter(); + export(exporter); + + string schemas = SchemaUtils.DumpSchema(exporter.Schemas); + _output.WriteLine("Count = " + exporter.Schemas.Count); + _output.WriteLine(schemas); + + // When checking schema count, be sure to include the "Serialization" schema - which is omitted from 'DumpSchema' - as + // well as the XmlSchema, both of which are the base from which all further schemas build. + if (schemaCheck != null) + schemaCheck(schemas, exporter.Schemas); + + Assert.True(schemas.Length > 0); + } + public static IEnumerable Export_MemberData() + { + // Export(Type) + yield return new object[] { "Exp1", (XsdDataContractExporter exp) => exp.Export(typeof(Types.Point)), (string s, XmlSchemaSet ss) => { + Assert.Equal(3, ss.Count); + // *basic* + // Point + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + } }; + + // Export(ICollection) + // AppContext SetSwitch seems to be unreliable in the unit test case. So let's not rely on it + // for test coverage. But let's do look at the app switch to get our verification correct. + AppContext.TryGetSwitch("Switch.System.Runtime.Serialization.DataContracts.Auto_Import_KVP", out bool autoImportKVP); + yield return new object[] { "Exp2", (XsdDataContractExporter exp) => exp.Export(new Assembly[] { typeof(DataContractTypes).Assembly }), (string s, XmlSchemaSet ss) => { + Assert.Equal(autoImportKVP ? 21 : 20, ss.Count); + Assert.Equal(autoImportKVP ? 171 : 163, ss.GlobalTypes.Count); + Assert.Equal(autoImportKVP ? 204 : 196, ss.GlobalElements.Count); + } }; + + // Export(ICollection) + yield return new object[] { "Exp3", (XsdDataContractExporter exp) => exp.Export(new Type[] { typeof(Types.Point), typeof(Types.Circle) }), (string s, XmlSchemaSet ss) => { + Assert.Equal(4, ss.Count); + // *basic* + // Point + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + // *shapes* + // Circle + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + } }; + yield return new object[] { "Exp4", (XsdDataContractExporter exp) => exp.Export(new Type[] { typeof(Types.NonAttributedPersonStruct), typeof(Types.NonAttributedPersonClass), typeof(Types.ExtendedSquare) }), (string s, XmlSchemaSet ss) => { + Assert.Equal(5, ss.Count); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + // *Types* + // NonAttributedPersonStruct + SchemaUtils.OrderedContains(@"", ref s); + Assert.Matches(@"\s*true", s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + // NonAttributedPersonClass + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + // ExtendedSquare + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + // *shapes* + // Square + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + // *basic* + // Point + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + } }; + + // EnumsTest - from Enums.cs + yield return new object[] { "ExpEnum", (XsdDataContractExporter exp) => exp.Export(new Type[] { typeof(System.Reflection.TypeAttributes) }), (string s, XmlSchemaSet ss) => { + Assert.Equal(3, ss.Count); + //Assert.Equal(3, ss.GlobalAttributes.Count); + Assert.Equal(5, ss.GlobalTypes.Count); + Assert.Equal(23, ss.GlobalElements.Count); + } }; + } + + [Theory] + [MemberData(nameof(Export_NegativeCases_MemberData))] + public void Export_NegativeCases(string testname, Action export, Type expectedExceptionType, string exMsg = null) + { + _output.WriteLine($"=============== {testname} ==============="); + XsdDataContractExporter exporter = new XsdDataContractExporter(); + var ex = Assert.Throws(expectedExceptionType, () => export(exporter)); + _output.WriteLine(ex.Message); + + if (exMsg != null) + Assert.Equal(exMsg, ex.Message); + } + public static IEnumerable Export_NegativeCases_MemberData() + { + // Export(Type) + yield return new object[] { "tn", (XsdDataContractExporter exp) => exp.Export((Type)null), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'type')" }; + yield return new object[] { "tinv", (XsdDataContractExporter exp) => exp.Export(typeof(Types.NonSerializableSquare)), typeof(InvalidDataContractException), @"Type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.Types.NonSerializableSquare' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required." }; + + // Export(ICollection) + yield return new object[] { "can", (XsdDataContractExporter exp) => exp.Export((ICollection)null), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'assemblies')" }; + yield return new object[] { "canv", (XsdDataContractExporter exp) => exp.Export(new Assembly[] { null }), typeof(ArgumentException), @"Cannot export null assembly provided via 'assemblies' parameter." }; + // This exception message might change with updates to this test assembly. Right now, 'NonSerializablePerson' is the non-serializable type that gets found first. If this becomes an issue, consider not verifying the exception message. + yield return new object[] { "cainv", (XsdDataContractExporter exp) => exp.Export(new Assembly[] { typeof(ExporterApiTests).Assembly }), typeof(InvalidDataContractException), @"Type 'NonSerializablePerson' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required." }; + + // Export(ICollection) + yield return new object[] { "ctn", (XsdDataContractExporter exp) => exp.Export((ICollection)null), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'types')" }; + yield return new object[] { "ctnv", (XsdDataContractExporter exp) => exp.Export(new Type[] { null }), typeof(ArgumentException), @"Cannot export null type provided via 'types' parameter." }; + yield return new object[] { "ctinv", (XsdDataContractExporter exp) => exp.Export(new Type[] { typeof(Types.Point), typeof(Types.NonSerializableSquare) }), typeof(InvalidDataContractException), @"Type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.Types.NonSerializableSquare' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required." }; + } + + [Theory] + [MemberData(nameof(GetSchemaTypeName_MemberData))] + public void GetSchemaTypeName(string testname, Type t, XmlQualifiedName qname, Type expectedExceptionType = null, string msg = null) + { + _output.WriteLine($"=============== {testname} ==============="); + XsdDataContractExporter exporter = new XsdDataContractExporter(); + + if (expectedExceptionType == null) + { + XmlQualifiedName schemaTypeName = exporter.GetSchemaTypeName(t); + Assert.Equal(qname, schemaTypeName); + } + else + { + var ex = Assert.Throws(expectedExceptionType, () => exporter.GetSchemaTypeName(t)); + if (!string.IsNullOrEmpty(msg)) + Assert.Equal(msg, ex.Message); + } + } + public static IEnumerable GetSchemaTypeName_MemberData() + { + // GetSchemaTypeName(Type) + yield return new object[] { "GSTN_Point", typeof(Types.Point), new XmlQualifiedName("Point", "http://basic") }; + yield return new object[] { "GSTN_null", null, null, typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'type')" }; + yield return new object[] { "GSTN_invalid", typeof(Types.NonSerializableSquare), null, typeof(InvalidDataContractException), @"Type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.Types.NonSerializableSquare' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required." }; + yield return new object[] { "GSTN_Square", typeof(Types.Square), new XmlQualifiedName("Square", "http://shapes") }; + yield return new object[] { "GSTN_ExtSq", typeof(Types.ExtendedSquare), new XmlQualifiedName("ExtendedSquare", "http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Xml.XsdDataContractExporterTests.Types") }; + + // From DataContractTypesTest.cs + yield return new object[] { "DCTT_Addr2", typeof(DataContractTypes.Address2), new XmlQualifiedName("Address", "http://schemas.datacontract.org/2004/07/schemaexport.suites") }; + } + + [Theory] + [MemberData(nameof(GetSchemaType_MemberData))] + public void GetSchemaType(string testname, Type t, XmlSchemaType stName, Type expectedExceptionType = null, string msg = null) + { + _output.WriteLine($"=============== {testname} ==============="); + XsdDataContractExporter exporter = new XsdDataContractExporter(); + + if (expectedExceptionType == null) + { + XmlSchemaType schemaType = exporter.GetSchemaType(t); + Assert.Equal(stName, schemaType); + } + else + { + var ex = Assert.Throws(expectedExceptionType, () => exporter.GetSchemaType(t)); + if (!string.IsNullOrEmpty(msg)) + Assert.Equal(msg, ex.Message); + } + } + public static IEnumerable GetSchemaType_MemberData() + { + // GetSchemaTypeName(Type) + yield return new object[] { "GST_Point", typeof(Types.Point), null }; // Per the docs - "types for which the GetSchemaTypeName method returns a valid name, this method returns null." + yield return new object[] { "GST_null", null, null, typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'type')" }; + yield return new object[] { "GST_invalid", typeof(Types.NonSerializableSquare), null, typeof(InvalidDataContractException), @"Type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.Types.NonSerializableSquare' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required." }; + } + + [Theory] + [MemberData(nameof(GetRootElementName_MemberData))] + public void GetRootElementName(string testname, Type t, XmlQualifiedName rName, Type expectedExceptionType = null, string msg = null) + { + _output.WriteLine($"=============== {testname} ==============="); + XsdDataContractExporter exporter = new XsdDataContractExporter(); + + if (expectedExceptionType == null) + { + XmlQualifiedName rootTypeName = exporter.GetRootElementName(t); + Assert.Equal(rName, rootTypeName); + } + else + { + var ex = Assert.Throws(expectedExceptionType, () => exporter.GetSchemaTypeName(t)); + if (!string.IsNullOrEmpty(msg)) + Assert.Equal(msg, ex.Message); + } + } + public static IEnumerable GetRootElementName_MemberData() + { + // GetSchemaTypeName(Type) + yield return new object[] { "GREN_Point", typeof(Types.Point), new XmlQualifiedName("Point", "http://basic") }; + yield return new object[] { "GREN_null", null, null, typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'type')" }; + yield return new object[] { "GREN_invalid", typeof(Types.NonSerializableSquare), null, typeof(InvalidDataContractException), @"Type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.Types.NonSerializableSquare' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required." }; + yield return new object[] { "GREN_Square", typeof(Types.Square), new XmlQualifiedName("Square", "http://shapes") }; + yield return new object[] { "GREN_ExtSq", typeof(Types.ExtendedSquare), new XmlQualifiedName("ExtendedSquare", "http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Xml.XsdDataContractExporterTests.Types") }; + } + + [Fact] + public void get_Schemas_Bug() + { + // Bug 23200 from who knows which ancient bug database + // I believe the gist of this is that modifying the XmlSchemaSet provided by XsdDataContractExporter.get_Schemas + // can result in that same property throwing an exception? I'm not really sure what this bug is, or if this really + // is a bug. Neither the code in NetFx nor here actually throws an exception without the newly added lines. + XsdDataContractExporter exporter = new XsdDataContractExporter(); + exporter.Export(typeof(Types.Circle)); + XmlSchemaSet schemaSet = exporter.Schemas; // added - exception + foreach (XmlSchema schema in exporter.Schemas.Schemas("http://basic")) // original - Still no exception + exporter.Schemas.Remove(schema); + var ex = Assert.Throws(() => exporter.Schemas); // added + Assert.Equal(@"Type 'http://basic:Point' is not declared.", ex.Message); // added + exporter.Export(typeof(Types.Square)); + ex = Assert.Throws(() => exporter.Schemas); // added + Assert.Equal(@"Type 'http://basic:Point' is not declared.", ex.Message); // added + } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/ExporterTypesTests.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/ExporterTypesTests.cs new file mode 100644 index 00000000000000..211d2013296590 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/ExporterTypesTests.cs @@ -0,0 +1,843 @@ +// 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; +using System.Collections.Generic; +using System.Xml.Serialization; +using Xunit; +using Xunit.Abstractions; + +using SerializableTypes.XsdDataContractExporterTests; + +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +{ + public class ExporterTypesTests + { + private readonly ITestOutputHelper _output; + public ExporterTypesTests(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void TypesTest() + { + var types = new List() + { + typeof(DataContractTypes.Person1), + typeof(DataContractTypes.Person2), + typeof(ExporterTypesTests.Group), + typeof(ExporterTypesTests.NoDataContract), + typeof(ExporterTypesTests.DataContractWithValidMember), + typeof(ExporterTypesTests.DataContractWithValidMember), + typeof(ExporterTypesTests.PersonInfo), + }; + + XsdDataContractExporter exporter = new XsdDataContractExporter(); + ExportOptions options = new ExportOptions(); + options.KnownTypes.Add(typeof(ArrayList)); + options.KnownTypes.Add(typeof(Guid)); + exporter.Options = options; + + exporter.Export(types); + exporter.Export(types); // Run twice, to ensure that types are not re-exported + + string schemas = SchemaUtils.DumpSchema(exporter.Schemas); + _output.WriteLine(schemas); + _output.WriteLine($"----------------- {exporter.Schemas.Count}, {exporter.Schemas.GlobalElements.Count}, {exporter.Schemas.GlobalTypes.Count}"); + + Assert.Equal(5, exporter.Schemas.Count); + Assert.Equal(36, exporter.Schemas.GlobalElements.Count); + Assert.Equal(18, exporter.Schemas.GlobalTypes.Count); + + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + } + + [Theory] + [SkipOnPlatform(TestPlatforms.Browser, "Inconsistent and unpredictable results.")] // TODO - Why does 'TypeWithReadWriteCollectionAndNoCtorOnCollection' only cause an exception sometimes, but not all the time? What's special about wasm here? + [InlineData(typeof(NoDataContractWithoutParameterlessConstructor), typeof(InvalidDataContractException), @"Type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+NoDataContractWithoutParameterlessConstructor' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required.")] + [InlineData(typeof(DataContractWithInvalidMember), typeof(InvalidDataContractException), @"Type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+NoDataContractWithoutParameterlessConstructor' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required.")] + [InlineData(typeof(SerializableWithInvalidMember), typeof(InvalidDataContractException), @"Type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+NoDataContractWithoutParameterlessConstructor' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required.")] + [InlineData(typeof(TypeWithReadWriteCollectionAndNoCtorOnCollection), typeof(InvalidDataContractException), @"System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+CollectionWithoutParameterlessCtor`1[[System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+Person, System.Runtime.Serialization.Xml.Tests, Version=7.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51]] does not have a default constructor.")] + // Yes, the exception type for this next one is different. It was different in NetFx as well. + [InlineData(typeof(ArrayContainer), typeof(InvalidOperationException), @"DataContract for type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+ArrayB' cannot be added to DataContractSet since type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+ArrayA' with the same data contract name 'Array' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Xml.XsdDataContractExporterTests' is already present and the contracts are not equivalent.")] + [InlineData(typeof(KeyValueNameSame), typeof(InvalidDataContractException), @"The collection data contract type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+KeyValueNameSame' specifies the same value 'MyName' for both the KeyName and the ValueName properties. This is not allowed. Consider changing either the KeyName or the ValueName property.")] + [InlineData(typeof(AnyWithRoot), typeof(InvalidDataContractException), @"Type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+AnyWithRoot' cannot specify an XmlRootAttribute attribute because its IsAny setting is 'true'. This type must write all its contents including the root element. Verify that the IXmlSerializable implementation is correct.")] + public void TypesTest_Negative(Type badType, Type exType, string exMsg = null) + { + XsdDataContractExporter exporter = new XsdDataContractExporter(); + var ex = Assert.Throws(exType, () => exporter.Export(badType)); + if (exMsg != null) + Assert.Equal(exMsg, ex.Message); + } + + [Theory] + [InlineData(new Type[] { typeof(AddressA), typeof(AddressB) }, typeof(InvalidOperationException), @"DataContract for type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+AddressB' cannot be added to DataContractSet since type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+AddressA' with the same data contract name 'Address' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Xml.XsdDataContractExporterTests' is already present and the contracts are not equivalent.")] + [InlineData(new Type[] { typeof(AddressA), typeof(AddressC) }, typeof(InvalidOperationException), @"DataContract for type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+AddressC' cannot be added to DataContractSet since type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+AddressA' with the same data contract name 'Address' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Xml.XsdDataContractExporterTests' is already present and the contracts are not equivalent.")] + [InlineData(new Type[] { typeof(OrderA), typeof(OrderB) }, typeof(InvalidOperationException), @"DataContract for type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+OrderB' cannot be added to DataContractSet since type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+OrderA' with the same data contract name 'Order' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Xml.XsdDataContractExporterTests' is already present and the contracts are not equivalent.")] + [InlineData(new Type[] { typeof(ArrayA), typeof(ArrayB) }, typeof(InvalidOperationException), @"DataContract for type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+ArrayB' cannot be added to DataContractSet since type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+ArrayA' with the same data contract name 'Array' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Xml.XsdDataContractExporterTests' is already present and the contracts are not equivalent.")] + [InlineData(new Type[] { typeof(EnumA), typeof(EnumB) }, typeof(InvalidOperationException), @"DataContract for type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+EnumB' cannot be added to DataContractSet since type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+EnumA' with the same data contract name 'Enum' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Xml.XsdDataContractExporterTests' is already present and the contracts are not equivalent.")] + [InlineData(new Type[] { typeof(EnumB), typeof(EnumC) }, typeof(InvalidOperationException), @"DataContract for type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+EnumC' cannot be added to DataContractSet since type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+EnumB' with the same data contract name 'Enum' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Xml.XsdDataContractExporterTests' is already present and the contracts are not equivalent.")] + [InlineData(new Type[] { typeof(EnumContainerA), typeof(EnumContainerB) }, typeof(InvalidOperationException), @"DataContract for type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+EnumContainerB' cannot be added to DataContractSet since type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+EnumContainerA' with the same data contract name 'EnumContainer' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Xml.XsdDataContractExporterTests' is already present and the contracts are not equivalent.")] + [InlineData(new Type[] { typeof(CollectionA), typeof(CollectionB) }, typeof(InvalidOperationException), @"DataContract for type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+CollectionB' cannot be added to DataContractSet since type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+CollectionA' with the same data contract name 'MyCollection' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Xml.XsdDataContractExporterTests' is already present and the contracts are not equivalent.")] + [InlineData(new Type[] { typeof(DictionaryA), typeof(DictionaryB) }, typeof(InvalidOperationException), @"DataContract for type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+DictionaryB' cannot be added to DataContractSet since type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+DictionaryA' with the same data contract name 'MyDictionary' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Xml.XsdDataContractExporterTests' is already present and the contracts are not equivalent.")] + public void TypeArrayTest_Negative(Type[] badTypes, Type exType, string exMsg = null) + { + XsdDataContractExporter exporter = new XsdDataContractExporter(); + var ex = Assert.Throws(exType, () => exporter.Export(badTypes)); + if (exMsg != null) + Assert.Equal(exMsg, ex.Message); + } + + [Fact] + public void ReferenceTypes() // From IsReferenceTypes.cs + { + List types = new List() + { + typeof(ExporterTypesTests.Order_ContainsRef), + typeof(ExporterTypesTests.Customers_ContainsDuplicateRefs), + typeof(ExporterTypesTests.Student_ContainsDuplicateCollectionRefs), + typeof(ExporterTypesTests.CircularLinkedList_ContainsBackpointingRef), + typeof(ExporterTypesTests.RefCircularLinks_ContainsBackpointer), + typeof(ExporterTypesTests.RefCircularNodeA_ContainsRefWithBackpointer), + typeof(ExporterTypesTests.RefNestedNode_ContainsBackpointer), + typeof(ExporterTypesTests.RefSimpleDataContractCycle_ContainsRefWithBackpointer), + typeof(ExporterTypesTests.Fruit), + typeof(ExporterTypesTests.RefApple), + typeof(ExporterTypesTests.EdibleContainer_ContainsPolymorphicRefs), + }; + + XsdDataContractExporter exporter = new XsdDataContractExporter(); + ExportOptions options = new ExportOptions(); + options.KnownTypes.Add(typeof(ArrayList)); + options.KnownTypes.Add(typeof(Guid)); + + exporter.Export(types); + exporter.Export(types); // Run twice, to ensure that types are not re-exported + + string schemas = SchemaUtils.DumpSchema(exporter.Schemas); + _output.WriteLine(schemas); + _output.WriteLine($"----------------- {exporter.Schemas.Count}, {exporter.Schemas.GlobalElements.Count}, {exporter.Schemas.GlobalTypes.Count}"); + + Assert.Equal(3, exporter.Schemas.Count); + Assert.Equal(39, exporter.Schemas.GlobalElements.Count); + Assert.Equal(21, exporter.Schemas.GlobalTypes.Count); + + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + } + + [Theory] + [InlineData(typeof(ExporterTypesTests.Fruit2), typeof(InvalidDataContractException), @"The IsReference setting for type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+Fruit2' is 'False', but the same setting for its parent class 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+RefEdibleItem' is 'True'. Derived types must have the same value for IsReference as the base type. Change the setting on type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+Fruit2' to 'True', or on type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+RefEdibleItem' to 'False', or do not set IsReference explicitly.")] + [InlineData(typeof(ExporterTypesTests.Orange), typeof(InvalidDataContractException), @"The IsReference setting for type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+Orange' is 'False', but the same setting for its parent class 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+Fruit' is 'True'. Derived types must have the same value for IsReference as the base type. Change the setting on type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+Orange' to 'True', or on type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+Fruit' to 'False', or do not set IsReference explicitly.")] + [InlineData(typeof(ExporterTypesTests.RefEnum), typeof(InvalidDataContractException), @"Enum type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+RefEnum' cannot have the IsReference setting of 'True'. Either change the setting to 'False', or remove it completely.")] + public void ReferenceTypes_Negative(Type badRefType, Type exType, string exMsg = null) + { + XsdDataContractExporter exporter = new XsdDataContractExporter(); + var ex = Assert.Throws(exType, () => exporter.Export(badRefType)); + if (exMsg != null) + Assert.Equal(exMsg, ex.Message); + } + +#pragma warning disable CS0169, CS0414 + public class NoDataContract + { + } + + [DataContract] + public class DataContractWithValidMember + { + [DataMember] + NoDataContract member; + } + + [Serializable] + public class SerializableWithValidMember + { + NoDataContract member; + } + + public class NoDataContractWithoutParameterlessConstructor + { + public NoDataContractWithoutParameterlessConstructor(string init) + { + } + } + + [DataContract] + public class DataContractWithInvalidMember + { + [DataMember] + NoDataContractWithoutParameterlessConstructor member; + } + + [Serializable] + public class SerializableWithInvalidMember + { + NoDataContractWithoutParameterlessConstructor member; + } + + [DataContract(Name = "Order")] + public class OrderA + { + [DataMember] + public AddressA address; + } + + [DataContract(Name = "Order")] + public class OrderB + { + [DataMember] + public AddressB address; + } + + [DataContract(Name = "BaseOrder")] + public class BaseOrder + { + } + + [DataContract(Name = "Order")] + public class OrderD : BaseOrder + { + [DataMember] + public AddressA address; + } + + [DataContract(Name = "Address")] + public class AddressA + { + [DataMember] + public string zip; + } + + [DataContract(Name = "Address")] + public class AddressB + { + [DataMember] + public int zip; + } + + [DataContract(Name = "Address")] + public class AddressC + { + [DataMember] + public string street; + [DataMember(IsRequired = true)] + public string zip; + } + + [DataContract] + public class ArrayContainer + { + [DataMember] + public ArrayA a1; + [DataMember] + public ArrayB a2; + } + + [DataContract(Name = "Array")] + public class ArrayA + { + [DataMember] + public ArrayA[] items; + } + + [DataContract(Name = "Array")] + public class ArrayB + { + [DataMember] + public ArrayC[] items; + } + + [DataContract(Name = "Array")] + public class ArrayC + { + [DataMember] + public int items; + } + + [DataContract(Name = "EnumContainer")] + public class EnumContainerA + { + [DataMember] + public EnumA member; + } + + [DataContract(Name = "EnumContainer")] + public class EnumContainerB + { + [DataMember] + public EnumB member; + } + + [DataContract(Name = "Enum")] + public enum EnumA : long + { + } + + [DataContract(Name = "Enum")] + public enum EnumB : long + { + [EnumMember] Min, + [EnumMember] Zero, + [EnumMember] Max, + } + + [DataContract(Name = "Enum")] + public enum EnumC : long + { + [EnumMember] Min, + [EnumMember] Max, + } + + [Serializable] + public class Group + { + public IList People; + } + + [KnownType(typeof(Employee))] + [Serializable] + public class Person + { + string name = "John Smith"; + } + + + [KnownType(typeof(Admin))] + [KnownType(typeof(Architect))] + [KnownType(typeof(Engineer))] + [Serializable] + public class Employee : Person + { + int empId = 42; + } + + [Serializable] + public class Engineer : Employee + { + } + + [Serializable] + [KnownType(typeof(Admin))] + public class Admin : Employee + { + } + + [Serializable] + [KnownType(typeof(Person))] + public class Architect : Employee + { + } + + [CollectionDataContract(Name = "MyCollection", ItemName = "MyItemA")] + public class CollectionA : List + { + } + + [CollectionDataContract(Name = "MyCollection", ItemName = "MyItemB")] + public class CollectionB : List + { + } + + [CollectionDataContract(Name = "MyDictionary", KeyName = "MyKeyA")] + public class DictionaryA : Dictionary + { + } + + [CollectionDataContract(Name = "MyDictionary", KeyName = "MyKeyB")] + public class DictionaryB : Dictionary + { + } + + [CollectionDataContract(KeyName = "MyName", ValueName = "MyName")] + public class KeyValueNameSame : Dictionary + { + } + + [XmlSchemaProvider(null, IsAny = true)] + [XmlRoot(ElementName = "AnyRootElement", IsNullable = false)] + public class AnyWithRoot : XmlSerializableBase + { + } + + public class PersonInfo + { + CollectionWithoutParameterlessCtor localPersons; + ArrayList localPersonArrayList; + public InnerPersonCollection innerPersonInfo = new InnerPersonCollection(); + + public CollectionWithoutParameterlessCtor Persons + { + get + { + localPersons = localPersons ?? new CollectionWithoutParameterlessCtor(5); + return localPersons; + } + } + + public ArrayList PersonArrayList + { + get + { + localPersonArrayList = localPersonArrayList ?? new ArrayList(); + return localPersonArrayList; + } + } + + public PersonInfo() + { + Person p1 = new Person(); + + Person p2 = new Person(); + + Person p3 = new Person(); + + this.Persons.Add(p1); + this.Persons.Add(p2); + + this.PersonArrayList.Add(new Guid()); + this.PersonArrayList.Add("teststring"); + + this.innerPersonInfo.Friends.Add(p3); + + this.innerPersonInfo.PotentialSalaries[0] = 90.0; + this.innerPersonInfo.PotentialSalaries[1] = 100.0; + this.innerPersonInfo.PotentialSalaries[2] = 106.0; + + this.innerPersonInfo.PotentialExpenditures = new double[] { 50.0, 55.0, 69.0 }; + + } + } + + public class InnerPersonCollection + { + private double[] potentialSalaries; + private double[] potentialExpenditures; + CollectionWithoutParameterlessCtor friends; + + public double[] PotentialSalaries + { + get + { + potentialSalaries = potentialSalaries ?? new double[3]; + return potentialSalaries; + } + } + + public double[] PotentialExpenditures + { + get + { + return potentialExpenditures; + } + set + { + potentialExpenditures = value; + } + } + + + public CollectionWithoutParameterlessCtor Friends + { + get + { + friends = friends ?? new CollectionWithoutParameterlessCtor(2); + return friends; + } + } + } + + public class TypeWithReadWriteCollectionAndNoCtorOnCollection + { + private double[] potentialSalaries; + private double[] potentialExpenditures; + CollectionWithoutParameterlessCtor friends; + + public double[] PotentialSalaries + { + get + { + potentialSalaries = potentialSalaries ?? new double[3]; + return potentialSalaries; + } + } + + public double[] PotentialExpenditures + { + get + { + return potentialExpenditures; + } + set + { + potentialExpenditures = value; + } + } + + + public CollectionWithoutParameterlessCtor Friends + { + get + { + friends = friends ?? new CollectionWithoutParameterlessCtor(2); + return friends; + } + set + { + friends = value; + } + } + } + + public class CollectionWithoutParameterlessCtor : ICollection + { + ArrayList list; + + public CollectionWithoutParameterlessCtor(int size) + { + list = new ArrayList(size); + } + + #region ICollection Members + + public void Add(T item) + { + list.Add(item); + } + + public void Clear() + { + list.Clear(); + } + + public bool Contains(T item) + { + return list.Contains(item); + } + + public void CopyTo(T[] array, int arrayIndex) + { + list.CopyTo(array, arrayIndex); + } + + public int Count + { + get { return list.Count; } + } + + public bool IsReadOnly + { + get { return list.IsReadOnly; } + } + + public bool Remove(T item) + { + list.Remove(item); + return true; + } + + #endregion + + #region IEnumerable Members + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < list.Count; i++) + { + yield return (T)list[i]; + } + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return list.GetEnumerator(); + } + + #endregion + } + + #region IsReferenceTypes + [DataContract] + class Order_ContainsRef + { + [DataMember] + public string Id = "29817691"; + [DataMember] + public string Url = "http://www.contoso.com/store/exec/OrderManagement?id=x9876270adh8q"; + [DataMember] + public RefCustomer RefCustomer = RefCustomer.CreateInstance(); + } + + [DataContract(IsReference = true)] + class RefEdibleItem + { + } + + [DataContract(IsReference = false)] + class Fruit2 : RefEdibleItem + { + } + + [DataContract] + class Fruit : RefEdibleItem + { + } + + [DataContract(IsReference = true)] + class RefApple : Fruit + { + } + + [DataContract(IsReference = false)] + class Orange : Fruit + { + } + + [DataContract] + [KnownType(typeof(Fruit))] + [KnownType(typeof(RefApple))] + class EdibleContainer_ContainsPolymorphicRefs + { + [DataMember] + RefEdibleItem w = new Fruit(); + [DataMember] + RefEdibleItem x = new RefApple(); + [DataMember] + Fruit z = new RefApple(); + } + + [DataContract] + class Customers_ContainsDuplicateRefs + { + static RefCustomer customer = RefCustomer.CreateInstance(); + [DataMember] + public RefCustomer RefCustomer1 = customer; + [DataMember] + public RefCustomer RefCustomer2 = customer; + } + + [DataContract] + public class Student_ContainsDuplicateCollectionRefs + { + static RefGrades grades; + + static Student_ContainsDuplicateCollectionRefs() + { + grades = new RefGrades(); + grades.Add("A"); + } + + [DataMember] + RefGrades grades1 = grades; + [DataMember] + RefGrades grades2 = grades; + } + + [CollectionDataContract(IsReference = true)] + public class RefGrades : List + { + } + + [DataContract(IsReference = true)] + class RefCustomer + { + [DataMember] + string Name; + [DataMember] + int ZipCode; + + internal static RefCustomer CreateInstance() + { + RefCustomer x = new RefCustomer(); + x.Name = "Bill Gates"; + x.ZipCode = 98052; + return x; + } + } + + + [DataContract] + public class CircularLinkedList_ContainsBackpointingRef + { + [DataMember] + RefNode start; + + [DataMember] + int numberOfNodes; + + public CircularLinkedList_ContainsBackpointingRef() + { + numberOfNodes = 4; + RefNode currentNode = null, prevNode = null; + start = null; + for (int i = 0; i < numberOfNodes; i++) + { + currentNode = new RefNode(i, "Hello World"); + if (i == 0) + start = currentNode; + if (prevNode != null) + prevNode.Next = currentNode; + prevNode = currentNode; + } + currentNode.Next = start; + } + } + + [DataContract(IsReference = true)] + public class RefNode + { + [DataMember] + public RefNode Next; + + [DataMember] + int id; + + [DataMember] + string name; + + public RefNode(int id, string name) + { + this.id = id; + this.name = name; + } + } + + + [DataContract(IsReference = true)] + public class RefCircularLinks_ContainsBackpointer + { + [DataMember] + RefCircularLinks_ContainsBackpointer link; + + public RefCircularLinks_ContainsBackpointer() + { + link = this; + } + } + + + [DataContract(IsReference = true)] + public class RefCircularNodeA_ContainsRefWithBackpointer + { + [DataMember] + RefCircularNodeB_ContainsRefWithBackpointer linkToB; + + public RefCircularNodeA_ContainsRefWithBackpointer() + { + linkToB = new RefCircularNodeB_ContainsRefWithBackpointer(this); + } + } + + [DataContract(IsReference = true)] + public class RefCircularNodeB_ContainsRefWithBackpointer + { + [DataMember] + RefCircularNodeA_ContainsRefWithBackpointer linkToA; + + public RefCircularNodeB_ContainsRefWithBackpointer(RefCircularNodeA_ContainsRefWithBackpointer nodeA) + { + linkToA = nodeA; + } + } + + + [DataContract(IsReference = true)] + public class RefNestedNode_ContainsBackpointer + { + [DataMember] + RefNestedNode_ContainsBackpointer node; + + [DataMember] + int level; + + public RefNestedNode_ContainsBackpointer(int level) + : this(level, null) + { + } + + public RefNestedNode_ContainsBackpointer(int level, RefNestedNode_ContainsBackpointer rootNode) + { + if (level > 0) + this.node = new RefNestedNode_ContainsBackpointer(level - 1, (rootNode == null ? this : rootNode)); + else + this.node = rootNode; + this.level = level; + } + } + + + [DataContract(IsReference = true)] + public class RefSimpleDataContractCycle_ContainsRefWithBackpointer + { + [DataMember] + object emptyFirstMember = new object(); + [DataMember] + public RefSimpleDataContractCycleNextLink next; + + public static RefSimpleDataContractCycle_ContainsRefWithBackpointer CreateInstance() + { + RefSimpleDataContractCycle_ContainsRefWithBackpointer simpleCycle = new RefSimpleDataContractCycle_ContainsRefWithBackpointer(); + RefSimpleDataContractCycleNextLink childLink = new RefSimpleDataContractCycleNextLink(); + simpleCycle.next = childLink; + childLink.backLink = simpleCycle; + return simpleCycle; + } + } + + + [DataContract] + public class RefSimpleDataContractCycleNextLink + { + [DataMember] + public RefSimpleDataContractCycle_ContainsRefWithBackpointer backLink; + } + + [DataContract(IsReference = true)] + enum RefEnum + { + + } + #endregion + +#pragma warning restore CS0169, CS0414 + } +} diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SchemaUtils.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SchemaUtils.cs new file mode 100644 index 00000000000000..9379df362e5933 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SchemaUtils.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using System.Text; +using System.Xml; +using System.Xml.Schema; +using Xunit; + +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +{ + internal class SchemaUtils + { + internal static string SerializationNamespace = "http://schemas.microsoft.com/2003/10/Serialization/"; + static XmlWriterSettings writerSettings = new XmlWriterSettings() { Indent = true }; + + public static string OrderedContains(string expected, ref string actual) + { + Assert.Contains(expected, actual); + actual = actual.Substring(actual.IndexOf(expected)); + return actual; + } + + public static string DumpSchema(XmlSchemaSet schemas) + { + StringBuilder sb = new StringBuilder(); + StringWriter sw = new StringWriter(sb); + foreach (XmlSchema schema in schemas.Schemas()) + { + if (schema.TargetNamespace != SerializationNamespace) + { + schema.Write(sw); + } + sw.WriteLine(); + } + sw.Flush(); + return sb.ToString(); + } + + internal static XmlSchema GetSchema(XmlSchemaSet schemaSet, string targetNs) + { + XmlSchema schema = null; + foreach (XmlSchema ctSchema in schemaSet.Schemas()) + { + if (ctSchema.TargetNamespace == targetNs) + { + schema = ctSchema; + break; + } + } + return schema; + } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ArrayTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ArrayTypes.cs new file mode 100644 index 00000000000000..3ae741883b3969 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ArrayTypes.cs @@ -0,0 +1,102 @@ +using System; +using System.Runtime.Serialization; +using System.Collections.Generic; + +#if UseSeparateAssemblyNamespace +using Address = SerializableTypes.XsdDataContractExporterTests.Address; +using Employee = SerializableTypes.XsdDataContractExporterTests.Employee; + +namespace SerializableTypes.XsdDataContractExporterTests.ArrayTypes +#else +using Address = System.Runtime.Serialization.Xml.XsdDataContractExporterTests.Address; +using Employee = System.Runtime.Serialization.Xml.XsdDataContractExporterTests.Employee; + +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ArrayTypes +#endif +{ + [Serializable] + public class Company + { + string name; + string[] products; + Address address; + [OptionalField] + Employee[] employees; + } + + // IsRequired default different for [Serializable] and [DataContract] + [DataContract(Name="Company")] + public class Company2 + { + [DataMember(IsRequired=true)] + public string name; + + [DataMember(Name="products", IsRequired=true)] + public string[] Products; + + [DataMember(IsRequired = true)] + Address address; + + [DataMember] + Employee[] employees; + + public Company2() + { + } + } + + [DataContract(Namespace="http://schemas.datacontract.org/2004/07/SerializableTypes.XsdDataContractExporterTests")] + public class Employee + { + [DataMember(IsRequired=true)] + Company company; + } + + [DataContract] + public class JaggedArrays + { + [DataMember] + Company[] companyArray_1rank; + + [DataMember] + Company[][] companyArray_2rank; + + [DataMember] + Company[][][] companyArray_3rank; + + [DataMember] + object[] objectArray_1rank; + + [DataMember] + object[][] objectArray_2rank; + + [DataMember] + ManagerEmployeeList peerList; + + [DataMember] + DateTimeOffset[] dateTimeOffsetArray_1rank; + + [DataMember] + DateTimeOffset[][] dateTimeOffsetArray_2rank; + } + + [DataContract] + public class SystemArray + { + + [DataMember] + public Array[] arrayArray; + + [DataMember] + public Array array; + + } + + [CollectionDataContract] + public class ManagerEmployeeList : List> + { + } +} + + + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ConfigTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ConfigTypes.cs new file mode 100644 index 00000000000000..93f3e69494afcc --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ConfigTypes.cs @@ -0,0 +1,52 @@ +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +#endif +{ + using System; + + [Serializable] + public class ConfigBase1 + { + string foo = "bar"; + } + + [Serializable] + public class ConfigDerived1 : ConfigBase1 + { + } + + [Serializable] + public class ConfigBase2 + { + string foo = "bar"; + } + + [Serializable] + public class ConfigDerived2 : ConfigBase2 + { + } + + [Serializable] + public class ConfigBase3 + { + string foo = "bar"; + } + + [Serializable] + public class ConfigDerived3 : ConfigBase3 + { + } + + [Serializable] + public class ConfigBase4 + { + string foo = "bar"; + } + + [Serializable] + public class ConfigDerived4 : ConfigBase4 + { + } +} diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ConflictingNameTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ConflictingNameTypes.cs new file mode 100644 index 00000000000000..b44de0e7f640ea --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ConflictingNameTypes.cs @@ -0,0 +1,72 @@ +using System; +using System.Runtime.Serialization; + +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +#endif +{ + public class ConflictingNameTypes + { + [DataContract] + public class ConflictBase + { + [DataMember(IsRequired=true)] + int a; + } + + [DataContract] + public class ConflictDerived1 : ConflictBase + { + [DataMember(IsRequired = true)] + int a; + } + + [DataContract] + public class ConflictDerived2 : ConflictBase + { + [DataMember(IsRequired = true)] + string[] a; + } + + [DataContract] + public class ConflictDerived11 : ConflictDerived1 + { + [DataMember(IsRequired = true)] + int a; + } + + [DataContract] + public class ConflictDerived12 : ConflictDerived1 + { + [DataMember(IsRequired = true)] + string a; + } + + [DataContract] + public class NoConflictBase + { + [DataMember(IsRequired = true)] + int a; + } + + [DataContract] + public class NoConflictDerived1 : NoConflictBase + { + [DataMember(IsRequired = true)] + int a; + } + + [DataContract(Namespace="http://www.tempuri.org/")] + public class NoConflictDerived2 : NoConflictBase + { + [DataMember(IsRequired = true)] + string a; + } + + } +} + + + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/DataContractSurrogate.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/DataContractSurrogate.cs new file mode 100644 index 00000000000000..c7570de114bd68 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/DataContractSurrogate.cs @@ -0,0 +1,98 @@ +using System; +using System.Runtime.Serialization; +using System.Xml; +using System.Xml.Serialization; +using System.Xml.Schema; + +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +#endif +{ + public class DataContractSurrogate + { + [DataContract] + public class CircleContainer + { + [DataMember] + Circle circle; + [DataMember] + public Circle[] Circles{ get { return null;} set {}} + } + + [Serializable] + public class Circle + { + public int Radius; + } + + [Serializable] + public class Square + { + public int Side; + } + + public class Node + { + Node next; + } + + [Serializable] + public class SerializableNode + { + SerializableNode next; + } + + + [XmlRoot("XmlSerializerPersonElement")] + public class XmlSerializerPerson + { + public XmlSerializerPerson(){} + [XmlAttribute] + public string Name; + [XmlAttribute] + public int Age; + } + + [XmlSchemaProvider("StaticGetSchema")] + public class XmlSerializerAdapter : IXmlSerializable + { + public XmlSchema GetSchema() + { + throw new NotImplementedException(); + } + + public void ReadXml(XmlReader reader) + { + throw new NotImplementedException(); + } + + public void WriteXml(XmlWriter writer) + { + throw new NotImplementedException(); + } + + static XmlQualifiedName StaticGetSchema(XmlSchemaSet schemaSet) + { + XmlReflectionImporter importer = new XmlReflectionImporter(); + XmlTypeMapping xmlTypeMapping = importer.ImportTypeMapping(typeof(T)); + XmlSchemas schemas = new XmlSchemas(); + XmlSchemaExporter exporter = new XmlSchemaExporter(schemas); + exporter.ExportTypeMapping(xmlTypeMapping); + schemas.Compile(new ValidationEventHandler (ValidationCallbackWithErrorCode), true); + for (int i = 0; i < schemas.Count; i++) + { + XmlSchema schema = schemas[i]; + schemaSet.Add(schema); + } + return new XmlQualifiedName(xmlTypeMapping.TypeName, xmlTypeMapping.Namespace); + } + + private static void ValidationCallbackWithErrorCode (object sender, ValidationEventArgs args) { + Console.WriteLine("Schema warning: " + args.Message); + } + } + } +} + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/DataContractTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/DataContractTypes.cs new file mode 100644 index 00000000000000..37c256f96a34d9 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/DataContractTypes.cs @@ -0,0 +1,607 @@ +using System; +using System.Runtime.Serialization; +using System.Collections.Generic; + +[assembly:ContractNamespace("http://special1.tempuri.org", ClrNamespace= "SerializableTypes.XsdDataContractExporterTests.More")] + +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests.More +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests.More +#endif +{ + [DataContract] + public class Foo + { + [DataMember] + int id; + } + + [KnownType(typeof(GenericBasePOCO>))] + [KnownType(typeof(GenericBasePOCO))] + [KnownType(typeof(SimpleBaseContainerPOCO))] + public class GenericContainerPOCO + { + public GenericBasePOCO2 GenericData; + public object TestGenericBasePOCO; + public GenericContainerPOCO() + { + } + } + + + public class GenericBasePOCO where T : new() + { + public object genericData = new T(); + } + + + public class GenericBasePOCO2 + where T : new() + where K : new() + { + public T genericData1 = new T(); + + public K genericData2 = new K(); + } + + [KnownType(typeof(SimpleBaseDerivedPOCO2))] + public class SimpleBaseContainerPOCO + { + public SimpleBasePOCO Base1; + + public List Base2; + + public SimpleBaseContainerPOCO() + { + } + } + + [KnownType(typeof(SimpleBaseDerivedPOCO))] + public class SimpleBasePOCO + { + public string BaseData = String.Empty; + } + + + public class SimpleBaseDerivedPOCO : SimpleBasePOCO + { + public string DerivedData = String.Empty; + } + + public class SimpleBaseDerivedPOCO2 : SimpleBasePOCO + { + public string DerivedData = String.Empty; + } + + [DataContract] + [KnownType(typeof(GenericBaseDC>))] + [KnownType(typeof(SimpleBaseContainerDC))] + public class GenericContainerDC + { + [DataMember] + public GenericBaseDC2 GenericData; + public GenericContainerDC() + { + } + } + + + [DataContract] + public class GenericBaseDC where T : new() + { + [DataMember] + public object genericData = new T(); + } + + + [DataContract] + public class GenericBaseDC2 + where T : new() + where K : new() + { + [DataMember] + public T genericData1 = new T(); + + [DataMember] + public K genericData2 = new K(); + } + + [DataContract] + [KnownType(typeof(SimpleBaseDerivedDC2))] + public class SimpleBaseContainerDC + { + [DataMember] + public SimpleBaseDC Base1; + + [DataMember] + public List Base2; + + public SimpleBaseContainerDC() + { } + } + + [DataContract] + [KnownType(typeof(SimpleBaseDerivedDC))] + public class SimpleBaseDC + { + [DataMember] + public string BaseData = String.Empty; + } + + + [DataContract] + public class SimpleBaseDerivedDC : SimpleBaseDC + { + [DataMember] + public string DerivedData = String.Empty; + } + + [DataContract] + public class SimpleBaseDerivedDC2 : SimpleBaseDC + { + [DataMember] + public string DerivedData = String.Empty; + } +} + +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +#endif +{ + public class DataContractTypes + { + [DataContract] + public class Person1 + { + [DataMember] + public string name; + + [DataMember] + public int age; + + [DataMember] + float salary; + + Person1() + { + } + + public Person1(string init) + { + name = "John Anderson"; + age = 25; + salary = 100000; + } + } + + // Unknown data does not affect the data contract of the class + [DataContract(Name="DataContractTypes.Person1")] + public class Person2 : IExtensibleDataObject + { + public string firstName; + public string lastName; + + [DataMember(Name="name")] + internal string Name + { + get { return firstName + " " + lastName; } + private set + { + int splitIndex = value.IndexOf(' '); + firstName = value.Substring(0, splitIndex); + lastName = value.Substring(splitIndex+1); + } + } + + protected int personAge; + + [DataMember(Name="age")] + public virtual int Age + { + get { return personAge; } + set { personAge = value; } + } + + [DataMember] + internal float salary; + + protected Person2() + { + } + + public Person2(string init) + { + Name = "John Anderson"; + Age = 25; + salary = 100000; + } + + ExtensionDataObject extensionData; + public ExtensionDataObject ExtensionData + { + get { return extensionData; } + set { extensionData = value; } + } + } + + [DataContract(Name="PersonContract")] + internal class Person3 + { + [DataMember(Name="Name")] + public string name; + + [DataMember(Name="Nickname")] + public string name2; + + [DataMember(Name="Age")] + public int age; + + [DataMember(Name="Salary")] + float salary; + + [DataMember] + Address address; + + [DataMember] + public Address Address + { + get { return address; } + set { address = value; } + } + + public Person3() + { + name = "John Anderson"; + name2 = "Johny"; + age = 25; + salary = 100000; + Address = new Address(null); + } + } + + [DataContract(Name="Person")] + public struct Person4 + { + [DataMember] + public string name; + + [DataMember] + public int age; + + [DataMember] + float salary; + + [DataMember] + Address address; + + [DataMember] + DateTimeOffset hireDate; + + public Person4(StreamingContext context) + { + name = "John Anderson"; + age = 25; + salary = 100000; + address = new Address(null); + hireDate = new DateTimeOffset(new DateTime(1995, 01, 17, 5, 30, 0), new TimeSpan(1,15,120)); + } + } + + [DataContract(Name="Address")] + public class Address + { + [DataMember(IsRequired=true)] + public string street; + + string city; + + [DataMember(Name = "city", IsRequired = true)] + public string City + { + get { return city; } + set { city = value; } + } + + [DataMember(IsRequired=true)] + string state; + + int zip; + + [DataMember(Name = "zip", IsRequired = true)] + int Zip + { + get { return zip; } + set {zip = value; } + } + + public Address() + { + } + + public Address(string init) + { + street = "One FooBar Avenue"; + City = "BazTown"; + state = "WA"; + Zip = 66066; + } + } + + [DataContract(Name="Address", Namespace="http://schemas.datacontract.org/2004/07/schemaexport.suites")] + public struct Address2 + { + [DataMember] + public string street; + + [DataMember] + string city; + + string state; + + [DataMember(Name="state")] + public string State + { + get { return state; } + set { state = value; } + } + + [DataMember] + int zip; + + public Address2(string init) + { + street = "One FooBar Avenue"; + city = "BazTown"; + state = "WA"; + zip = 66066; + } + } + + [DataContract(Namespace="http://invalid.org?query")] + public class Child : Person2 + { + [DataMember(Name="age")] + public override int Age + { + get { return personAge; } + set + { + if (personAge > 18) + throw new Exception("Children must be aged 18 or younger"); + personAge = value; + } + } + + [DataMember] + Person2 mother; + [DataMember] + Person2 father; + + Child(StreamingContext context) + { + } + + public Child(string init) : base(init) + { + personAge = 13; + mother = new Person2(null); + father = null; + } + } + + [DataContract(Name="DerivedAddress")] + public class DerivedAddress : Address + { + [DataMember] + string email; + + [DataMember] + string phone; + + public DerivedAddress() : base(null) + { + email = "neo@zion.net"; + phone = "222-111-2222"; + } + } + + [DataContract(Name="Node")] + class Node + { + [DataMember] + Node nextNode; + + [DataMember] + Node previousNode; + + Node() + { + } + } + + [DataContract(Name="Node")] + class Node2 + { + [DataMember] + Node2 nextNode; + + [DataMember] + Node3 previousNode; + + Node2() + { + } + } + + [DataContract(Name="Node")] + class Node3 + { + [DataMember] + Node3 nextNode; + + [DataMember] + Node4 previousNode; + + Node3() + { + } + } + + [DataContract(Name="Node")] + class Node4 + { + [DataMember] + Node3 nextNode; + + [DataMember] + Node2 previousNode; + + Node4() + { + } + } + + [DataContract] + class A + { + [DataMember] + B member; + + A() + { + } + } + + [DataContract] + class B + { + [DataMember] + A member; + + B() + { + } + } + + [DataContract] + public class ClassWithInterfaceMember + { + [DataMember] + ISampleInterface interfaceMember; + } + + interface ISampleInterface + { + void InterfaceMethod(); + } + + [DataContract(Name="DerivedAddress2")] + public class DerivedAddress2 : DerivedAddress + { + [DataMember] + byte[] extraData; + + public DerivedAddress2() : base() + { + } + } + + [DataContract] + public class Foo + { + [DataMember(IsRequired=false)] + int i=1; + [DataMember(IsRequired=true)] + int j=3; + [DataMember(Order=3)] + string a = "a"; + [DataMember(Order=4, IsRequired=false)] + public int z = 32; + + } + + [DataContract] + public class Bar + { + [DataMember] + int i=1; + [DataMember(Order=4, IsRequired=true)] + int j=3; + [DataMember(Order=5)] + string a = "a"; + [DataMember(Order=8)] + public int z = 32; + + } + + [DataContract] + [Serializable] + public class MixedDCSerializable + { + int serializableInt = 0; + [DataMember] + int dataContractInt = 0; + } + + [Serializable] + public class SerializableOnly : MixedDCSerializable + { + string serializableString; + [NonSerialized] + int nonSerializedInt; + } + + [DataContract] + [Serializable] + public class DerivedMixedDCSerializable : SerializableOnly + { + public float serializableFloat = 0.0F; + [DataMember] + public float dataContractFloat = 0.0F; + } + + [Serializable] + public class AllPrimitives + { + public object objectMember; + public char charMember; + public bool boolMember; + public byte unsignedByteMember; + //[CLSCompliant(false)] + public sbyte byteMember; + public short shortMember; + //[CLSCompliant(false)] + public ushort unsignedShortMember; + public int intMember; + //[CLSCompliant(false)] + public uint unsignedIntMember; + public long longMember; + //[CLSCompliant(false)] + public ulong unsignedLongMember; + public float floatMember; + public double doubleMember; + public decimal decimalMember; + public DateTime dateTimeMember; + public string stringMember; + byte[] byteArrayMember; + public Guid guidMember; + public TimeSpan timeSpanMember; + public Uri uri; + } + + [DataContract] + public class AllSpecialTypes + { + [DataMember] System.Enum enumMember; + [DataMember] System.ValueType valueTypeMember; + [DataMember] System.Array arrayMember; + } + + } +} + + + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/Enums.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/Enums.cs new file mode 100644 index 00000000000000..4d22f65ba83250 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/Enums.cs @@ -0,0 +1,138 @@ +using System; +using System.Runtime.Serialization; + +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +#endif +{ + public class Enums + { + public enum Mode + { + NotSpecified, + Single, + Multiple, + } + + [DataContract(Name="DifferentMode")] + public enum Mode2 + { + [EnumMember(Value="None")] + NotSpecified = -1, + Single, + [EnumMember] + Multiple, + } + + public enum Color : byte + { + Red, + Green, + Blue, + Reserved, + } + + //[CLSCompliant(false)] + public enum ULongRange : ulong + { + Min = UInt64.MinValue, + Small = 1, + Medium = 10, + Large = 100, + Max = UInt64.MaxValue, + } + + [Flags] + public enum FlagsEnum + { + Foo = 1, + Bar = 2, + Baz = 4, + Bazooka = 8 + } + + [Flags] + [DataContract(Namespace="http://special2.tempuri.org")] + public enum LongRange : long + { + [EnumMember] + Min = Int64.MinValue, + [EnumMember(Value="Small")] + Value1 = 1L, + Value10 = 10L, + [EnumMember(Value="Medium")] + Value100 = 100L, + [EnumMember(Value="Large")] + Value1000 = 1000L, + [EnumMember] + Max = Int64.MaxValue, + } + + [DataContract] + public class EnumContainer + { + [DataMember] + Mode mode; + + [DataMember] + Mode2 mode2; + + [DataMember] + Color color; + + [DataMember] + ULongRange enumValue; + + [DataMember] + FlagsEnum flagsEnum; + + [DataMember] + LongRange longFlagsEnum; + + public EnumContainer() + { + } + + [DataContract] + public class NestedEnumContainer + { + [DataMember] + Mode mode; + + NestedEnumContainer() + { + } + } + + internal enum NestedSimpleEnum + { + Negative = -1, + NoComment = 0, + Affirmative = 1, + } + } + } + + public enum Mode + { + NotSpecified, + Single, + Multiple, + } + + [DataContract(Name="Mode")] + public enum DifferentMode + { + [EnumMember(Value="NotSpecified")] + Member1, + [EnumMember] + Single=1, + [EnumMember(Value="Multiple")] + Mult, + } + +} + + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/GenericTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/GenericTypes.cs new file mode 100644 index 00000000000000..01b0e7f76394d4 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/GenericTypes.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using System.Security; + +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +#endif +{ + [Serializable] + internal class DateTimeOffsetGeneric + { + T single; + T[] array; + List list; + Dictionary dictionary; + } + + [Serializable] + internal class Foo where Q : new() + { + Q single; + Q[] array; + List list; + Dictionary dictionary; + } + + [DataContract(Name = "PairOf{0}and{1}")] + internal struct Pair + { + [DataMember] + T1 t1; + [DataMember] + T2 t2; + } + + [KnownType(typeof(Pair>, Foo>))] + [Serializable] + public class Bar + { + Foo fooBar; + XsdType xsdFloat; + XsdType xsdDecimal; + WrapperISerializable wrappedInt; + WrapperISerializable wrappedIntArray; + DateTimeOffsetGeneric genericDateTimeOffset; + DateTimeOffsetGeneric genericDateTimeOffsetArray; + } + + [Serializable] + internal class WrapperISerializable : ISerializable + { + [SecurityCritical] + public void GetObjectData(SerializationInfo info, StreamingContext context) { } + } + + [XmlSchemaProvider("StaticGetSchema")] + public class XsdType : IXmlSerializable + { + static XmlSchemaType StaticGetSchema(XmlSchemaSet schemas) + { + const string ns = "http://GenericXmlSerializableNs"; + XmlSchema schema = new XmlSchema(); + schema.TargetNamespace = ns; + schema.Namespaces.Add("tns", ns); + schemas.Add(schema); + + XmlSchemaComplexType schemaType = new XmlSchemaComplexType(); + schemaType.Name = typeof(T).Name + "Wrapper"; + XmlSchemaSequence sequence = new XmlSchemaSequence(); + schemaType.Particle = sequence; + XmlSchemaElement element = new XmlSchemaElement(); + element.Name = "MyElement"; + if (typeof(T) == typeof(decimal)) + element.SchemaTypeName = new XmlQualifiedName("decimal", XmlSchema.Namespace); + else if (typeof(T) == typeof(float)) + element.SchemaTypeName = new XmlQualifiedName("float", XmlSchema.Namespace); + else + element.SchemaTypeName = new XmlQualifiedName("anyType", XmlSchema.Namespace); + sequence.Items.Add(element); + schema.Items.Add(schemaType); + schemas.Add(schema); + return schemaType; + } + + public XmlSchema GetSchema() { throw new NotImplementedException(); } + public void ReadXml(XmlReader reader) { throw new NotImplementedException(); } + public void WriteXml(XmlWriter writer) { throw new NotImplementedException(); } + } + +} + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ISerializableTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ISerializableTypes.cs new file mode 100644 index 00000000000000..e4e667cfb19547 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ISerializableTypes.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections; +using System.Runtime.Serialization; +using System.Security; + +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +#endif +{ + [Serializable] + public class BaseISerializable : ISerializable + { + protected BaseISerializable() + { + } + + protected BaseISerializable(SerializationInfo info, StreamingContext context) + { + } + + public string street; + int zip; + [NonSerialized] + float privateData; + + [SecurityCritical] + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + } + } + + [Serializable] + public class DerivedISerializable : BaseISerializable + { + DerivedISerializable(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + } + + [Serializable] + public class MyUri : Uri + { + protected MyUri(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } + + [Serializable] + public class MyDerivedUri : MyUri + { + protected MyDerivedUri(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } + + [Serializable] + public struct StructISerializable : ISerializable + { + public StructISerializable(SerializationInfo serInfo, StreamingContext context) + { + } + + [SecurityCritical] + public void GetObjectData(SerializationInfo serInfo, StreamingContext context) + { + } + + } + + [DataContract] + public class UseISerializable + { + [DataMember] + Hashtable hashtable; + + [DataMember] + InvalidOperationException exception; + +#if !HideTypesWithoutSerializableAttribute + [DataMember] + System.Reflection.Assembly assembly; + + [DataMember] + System.IO.DirectoryInfo directoryInfo; +#endif + [DataMember] + StructISerializable structISerMember; + + [DataMember] + BaseISerializable classISerMember; + } + + [Serializable] + public class ISerializableDerivingDC : DataContractTypes.Address, ISerializable + { + public ISerializableDerivingDC(SerializationInfo serInfo, StreamingContext context) + { + } + + [SecurityCritical] + void ISerializable.GetObjectData(SerializationInfo serInfo, StreamingContext context) + { + } + } + +} + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/IXmlSerializableTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/IXmlSerializableTypes.cs new file mode 100644 index 00000000000000..e42d9fb96407f5 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/IXmlSerializableTypes.cs @@ -0,0 +1,399 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.Data.SqlTypes; +using System.Runtime.Serialization; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; + +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +#endif +{ + public class XmlSerializableBase : IXmlSerializable + { + public XmlSchema GetSchema() + { + return null; + } + + public void ReadXml(XmlReader reader) + { + throw new NotImplementedException(); + } + + public void WriteXml(XmlWriter writer) + { + throw new NotImplementedException(); + } + + public static XmlSchema GetSchema(string ns, XmlSchemaSet schemas) + { + if (ns == null) + { + ns = String.Empty; + } + + ICollection currentSchemas = schemas.Schemas(); + foreach (XmlSchema schema in currentSchemas) + { + if ((schema.TargetNamespace == null && ns.Length == 0) || ns.Equals(schema.TargetNamespace)) + return schema; + } + if (ns.Length > 0) + { + XmlSchema newSchema = new XmlSchema(); + newSchema.TargetNamespace = ns; + newSchema.Namespaces.Add("tns", ns); + schemas.Add(newSchema); + return newSchema; + } + return null; + } + } + + [XmlSchemaProvider("StaticGetSchema")] + [XmlRoot(ElementName ="ComplexTypeElement", Namespace ="http://ElementNs/", IsNullable = false)] + public class ComplexType : XmlSerializableBase + { + static XmlSchemaType StaticGetSchema(XmlSchemaSet schemas) + { + XmlSchema schema = GetSchema("http://TypeNs/", schemas); + XmlSchemaComplexType schemaType = new XmlSchemaComplexType(); + schemaType.Name = "MyComplexType"; + XmlSchemaSequence sequence = new XmlSchemaSequence(); + schemaType.Particle = sequence; + XmlSchemaElement element = new XmlSchemaElement(); + element.Name = "MyElement"; + element.SchemaTypeName = new XmlQualifiedName("int", XmlSchema.Namespace); + sequence.Items.Add(element); + schema.Items.Add(schemaType); + schemas.Add(schema); + return schemaType; + } + } + + [XmlSchemaProvider("StaticGetSchema")] + public class SimpleType : XmlSerializableBase + { + static XmlQualifiedName StaticGetSchema(XmlSchemaSet schemas) + { + XmlSchema schema = GetSchema("http://TypeNs/", schemas); + XmlSchemaSimpleType schemaType = new XmlSchemaSimpleType(); + schemaType.Name = "MySimpleType"; + XmlSchemaSimpleTypeRestriction content = new XmlSchemaSimpleTypeRestriction(); + schemaType.Content = content; + content.BaseType = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.Boolean); + schema.Items.Add(schemaType); + schemas.Add(schema); + return new XmlQualifiedName(schemaType.Name, schema.TargetNamespace); + } + } + + [XmlSchemaProvider("StaticGetSchema")] + [XmlRoot(ElementName ="IntElement", Namespace ="http://ElementNs/", IsNullable = false)] + public struct XsdInt : IXmlSerializable + { + static XmlQualifiedName StaticGetSchema(XmlSchemaSet schemas) + { + XmlSchemaType schemaType = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.Int); + return schemaType.QualifiedName; + } + + public XmlSchema GetSchema() + { + throw new NotImplementedException(); + } + + public void ReadXml(XmlReader reader) + { + throw new NotImplementedException(); + } + + public void WriteXml(XmlWriter writer) + { + throw new NotImplementedException(); + } + } + + [XmlSchemaProvider("StaticGetSchema")] + [XmlRoot(ElementName ="StringElement", Namespace ="http://ElementNs/", IsNullable = true)] + public class XsdString : IXmlSerializable + { + static XmlQualifiedName StaticGetSchema(XmlSchemaSet schemas) + { + XmlSchemaType schemaType = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.String); + return schemaType.QualifiedName; + } + + public XmlSchema GetSchema() + { + throw new NotImplementedException(); + } + + public void ReadXml(XmlReader reader) + { + throw new NotImplementedException(); + } + + public void WriteXml(XmlWriter writer) + { + throw new NotImplementedException(); + } + } + + [XmlSchemaProvider("StaticGetSchema")] + [XmlRoot(ElementName="ComplexStructElement", Namespace="http://ElementNs/", IsNullable = false)] + public struct ComplexStruct : IXmlSerializable + { + static XmlSchemaType StaticGetSchema(XmlSchemaSet schemas) + { + XmlSchema schema = XmlSerializableBase.GetSchema("http://TypeNs/", schemas); + XmlSchemaComplexType schemaType = new XmlSchemaComplexType(); + schemaType.Name = "MyComplexStruct"; + XmlSchemaSequence sequence = new XmlSchemaSequence(); + schemaType.Particle = sequence; + XmlSchemaElement element = new XmlSchemaElement(); + element.Name = "MyElement"; + element.SchemaTypeName = new XmlQualifiedName("int", XmlSchema.Namespace); + sequence.Items.Add(element); + schema.Items.Add(schemaType); + schemas.Add(schema); + return schemaType; + } + + public XmlSchema GetSchema() + { + throw new NotImplementedException(); + } + + public void ReadXml(XmlReader reader) + { + throw new NotImplementedException(); + } + + public void WriteXml(XmlWriter writer) + { + throw new NotImplementedException(); + } + } + + [XmlSchemaProvider("StaticGetSchema")] + [XmlRoot(ElementName ="AnonElement", Namespace ="http://ElementNs/", IsNullable = true)] + public class AnonymousType : XmlSerializableBase + { + static XmlSchemaType StaticGetSchema(XmlSchemaSet schemas) + { + XmlSchemaComplexType schemaType = new XmlSchemaComplexType(); + XmlSchemaSequence sequence = new XmlSchemaSequence(); + schemaType.Particle = sequence; + XmlSchemaElement element = new XmlSchemaElement(); + element.Name = "MyElement"; + element.SchemaTypeName = new XmlQualifiedName("int", XmlSchema.Namespace); + sequence.Items.Add(element); + return schemaType; + } + } + + public class NoSchema : XmlSerializableBase + { + } + + [XmlRoot] + public class EmptyXmlRoot : XmlSerializableBase + { + } + + [XmlRoot(IsNullable=true)] + public class NullableOnlyXmlRoot : XmlSerializableBase + { + } + + [XmlRoot(ElementName=null)] + public class NullElementXmlRoot : XmlSerializableBase + { + } + + [XmlRoot(ElementName="")] + public class EmptyElementXmlRoot : XmlSerializableBase + { + } + + [XmlSchemaProvider(null, IsAny=true)] + public class AnyBasic : XmlSerializableBase + { + } + + [XmlSchemaProvider("StaticGetSchema", IsAny = true)] + public class AnyWithSchemaTypeMethod : XmlSerializableBase + { + static XmlSchemaType StaticGetSchema(XmlSchemaSet schemas) + { + return null; + } + } + + [XmlSchemaProvider("StaticGetSchema", IsAny = true)] + public class AnyWithQnameMethod : XmlSerializableBase + { + static XmlQualifiedName StaticGetSchema(XmlSchemaSet schemas) + { + return null; + } + } + + [XmlSchemaProvider("StaticGetSchema")] + public class AnyImplicitWithSchemaTypeMethod : XmlSerializableBase + { + static XmlSchemaType StaticGetSchema(XmlSchemaSet schemas) + { + return null; + } + } + + [XmlSchemaProvider("StaticGetSchema")] + public class AnyImplicitWithQnameMethod : XmlSerializableBase + { + static XmlQualifiedName StaticGetSchema(XmlSchemaSet schemas) + { + return null; + } + } + + public class NoSchemaProviderWithSchema : IXmlSerializable + { + public XmlSchema GetSchema() + { + XmlSchema schema = new XmlSchema(); + schema.Id = this.GetType().Name; + XmlSchemaElement element = new XmlSchemaElement(); + element.Name = "userElement"; + element.SchemaTypeName = new XmlQualifiedName("int", XmlSchema.Namespace); + schema.Items.Add(element); + return schema; + } + + public void ReadXml(XmlReader reader) + { + throw new NotImplementedException(); + } + + public void WriteXml(XmlWriter writer) + { + throw new NotImplementedException(); + } + } + + [XmlSchemaProvider("GetTypedDataSetSchema")] + [XmlRoot("TypedDataSet", Namespace = "http://datasetns/")] + public class TypedDataSet : DataSet + { + public static System.Xml.Schema.XmlSchemaComplexType GetTypedDataSetSchema(System.Xml.Schema.XmlSchemaSet xs) + { + TypedDataSet ds = new TypedDataSet(); + System.Xml.Schema.XmlSchemaComplexType type = new System.Xml.Schema.XmlSchemaComplexType(); + System.Xml.Schema.XmlSchemaSequence sequence = new System.Xml.Schema.XmlSchemaSequence(); + xs.Add(ds.GetSchemaSerializable()); + if (PublishLegacyWSDL()) + { + System.Xml.Schema.XmlSchemaAny any = new System.Xml.Schema.XmlSchemaAny(); + any.Namespace = ds.Namespace; + sequence.Items.Add(any); + } + else + { + System.Xml.Schema.XmlSchemaAny any1 = new System.Xml.Schema.XmlSchemaAny(); + any1.Namespace = "http://www.w3.org/2001/XMLSchema"; + any1.MinOccurs = new System.Decimal(0); + any1.ProcessContents = System.Xml.Schema.XmlSchemaContentProcessing.Lax; + sequence.Items.Add(any1); + System.Xml.Schema.XmlSchemaAny any2 = new System.Xml.Schema.XmlSchemaAny(); + any2.Namespace = "urn:schemas-microsoft-com:xml-diffgram-v1"; + any2.MinOccurs = new System.Decimal(0); + any2.ProcessContents = System.Xml.Schema.XmlSchemaContentProcessing.Lax; + sequence.Items.Add(any2); + sequence.MaxOccurs = System.Decimal.MaxValue; + System.Xml.Schema.XmlSchemaAttribute attribute = new System.Xml.Schema.XmlSchemaAttribute(); + attribute.Name = "namespace"; + attribute.FixedValue = ds.Namespace; + type.Attributes.Add(attribute); + } + type.Particle = sequence; + return type; + } + protected override System.Xml.Schema.XmlSchema GetSchemaSerializable() + { + System.IO.MemoryStream stream = new System.IO.MemoryStream(); + this.WriteXmlSchema(new System.Xml.XmlTextWriter(stream, null)); + stream.Position = 0; + return System.Xml.Schema.XmlSchema.Read(new System.Xml.XmlTextReader(stream), null); + } + protected static bool PublishLegacyWSDL() + { + //System.Collections.Specialized.NameValueCollection settings = ((System.Collections.Specialized.NameValueCollection)(System.Configuration.ConfigurationManager.GetSection("system.data.dataset"))); + //if ((settings != null)) + //{ + // string[] values = settings.GetValues("WSDL_VERSION"); + // if ((values != null)) + // { + // System.Single version = System.Single.Parse(((string)(values[0])), ((System.IFormatProvider)(null))); + // return(version < 2); + // } + //} + return true; + } + } + + [Serializable] + public class IXmlSerializablesContainer + { + ComplexType complexType; + ComplexStruct complexStruct; + SimpleType simpleType; + AnonymousType anonymousType; + NoSchema noSchema; + XsdInt xsdInt; + XsdString xsdString; + DataSet dataSet; + TypedDataSet typedDataSet; + AnyBasic anyBasic; + AnyBasic[] anyArray; + AnyWithSchemaTypeMethod anyWithSchemaType; + AnyWithQnameMethod anyWithQname; + AnyImplicitWithSchemaTypeMethod anyImplicitWithSchemaType; + AnyImplicitWithQnameMethod anyImplicitWithQname; + NoSchemaProviderWithSchema noSchemaProviderWithSchema; + XmlElement xmlElement; + XmlElement[] xmlElementArray; + XmlNode[] xmlNodes; + XmlNode[][] xmlNodesArray; + Dictionary xmlElementDictionary; + } + + [Serializable] + public class SqlTypeContainer + { + // The following were disabled in NetFx test... but should work now. + // SqlBinary, SqlChars, SqlInt32, SqlString, SqlDateTime, SqlGuid + + public SqlBinary sqlBinary = new SqlBinary(new byte[]{4,2}); + public SqlByte sqlByte = new SqlByte(4); + public SqlBytes sqlBytes = new SqlBytes(new byte[]{4,2}); + public SqlChars sqlChars = new SqlChars(new char[]{'4', '2'}); + public SqlDecimal sqlDecimal = new SqlDecimal(4.2); + public SqlDouble sqlDouble = new SqlDouble(4.2); + public SqlInt16 sqlInt16 = new SqlInt16(42); + public SqlInt32 sqlInt32 = new SqlInt32(42); + public SqlInt64 sqlInt64 = new SqlInt64(42L); + public SqlMoney sqlMoney = new SqlMoney(42); + public SqlSingle sqlSingle = new SqlSingle(4.2); + public SqlString sqlString = new SqlString("MySqlString"); + public SqlDateTime sqlDateTime = new SqlDateTime(); + public SqlGuid sqlGuid = new SqlGuid(); + } +} + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/LegacyTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/LegacyTypes.cs new file mode 100644 index 00000000000000..332cb876f66393 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/LegacyTypes.cs @@ -0,0 +1,42 @@ +using System; +using System.Xml; +using System.Collections; +using System.Collections.Generic; +using System.Security; +using System.Runtime.Serialization; + +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +#endif +{ + [Serializable] + [SecuritySafeCritical] +#if UseSeparateAssemblyNamespace + public unsafe class LegacyTypes +#else + public class LegacyTypes +#endif + { + Hashtable h; + List lInt; + public IList list; + public IList stringList; + public ICollection collection; + public ICollection stringCollection; + public IEnumerable enumerable; + public IEnumerable stringEnumerable; + public IDictionary dictionary; + public IDictionary dictionaryOfStringToInt; + Dictionary dExVer; +#if !HideTypesWithoutSerializableAttribute + float* f; +#endif + IntPtr iPtr; + DBNull dbNull; + } + +} + + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/NullableTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/NullableTypes.cs new file mode 100644 index 00000000000000..109ab772167b20 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/NullableTypes.cs @@ -0,0 +1,66 @@ +using System; +using System.Runtime.Serialization; +using System.Security; + +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +#endif +{ + [DataContract] + public struct Point + { + + Nullable x; + Nullable y; + + [DataMember] + public Nullable X { get { return x; } set { x = value; } } + [DataMember] + public Nullable Y { get { return y; } set { y = value; } } + + } + + [DataContract] + public struct Rectangle + { + [DataMember] + public Nullable TopLeft; + [DataMember] + public Point? BottomRight; + } + + [Serializable] + [KnownType(typeof(Point))] + public struct Polygon : ISerializable + { + Nullable[] points; + + [SecurityCritical] + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + } + } + + [Serializable] + [KnownType(typeof(Polygon))] + [KnownType(typeof(Nullable))] + [KnownType(typeof(Container))] + public class Container + { + Polygon polygon; + Rectangle? excessivelyNullableRectangle; //not excessively anymore + Nullable[] nullableInts; + Nullable[][] nullableLongss; + } + + [DataContract] + public struct NullableDateTimeOffset + { + Nullable nullableDTO; + + [DataMember] + public Nullable NullableDTO { get { return nullableDTO; } set { nullableDTO = value; } } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/PartialTrust.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/PartialTrust.cs new file mode 100644 index 00000000000000..596176da620084 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/PartialTrust.cs @@ -0,0 +1,110 @@ +using System; +using System.Runtime.Serialization; +using System.Security; +using System.Security.Permissions; +using System.Xml; +using System.Xml.Serialization; +using System.Xml.Schema; + +[assembly:AllowPartiallyTrustedCallers] +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +#endif +{ + public class PartialTrust + { + [Serializable] + //[SerializationPermissionNotRequired] + public class SafePoint + { + int x = 42, y = 43; + } + + [Serializable] + //[SerializationPermissionNotRequired] + public class SafePoint3D : SafePoint + { + int z = 44; + DateTimeOffset dateCreated = new DateTimeOffset(new DateTime(1997, 03, 11, 07, 15, 30), new TimeSpan(1,2,60)); + } + + [Serializable] + //[SerializationPermissionNotRequired] + public class SafeCube + { + SafePoint3D topLeftBehind = new SafePoint3D(); + SafePoint3D bottomRightFront = new SafePoint3D(); + } + + [Serializable] + public class UnsafePoint + { + int x = 42, y = 43; + } + + [Serializable] + //[SerializationPermissionNotRequired] + public class UnsafePoint3D : UnsafePoint + { + int z = 44; + } + + [Serializable] + //[SerializationPermissionNotRequired] + public class UnsafeCube + { + UnsafePoint3D topLeftBehind = new UnsafePoint3D(); + UnsafePoint3D bottomRightFront = new UnsafePoint3D(); + } + + //[SerializationPermissionNotRequired] + public class AttributeOnlyIXmlSerializable : IXmlSerializable + { + public AttributeOnlyIXmlSerializable() + { + // This was not commented in NetFx. It clutters output though and seems unneccesary for our needs. + //Console.WriteLine("Default Ctor"); + } + + public AttributeOnlyIXmlSerializable(string init) + { + + } + + public XmlSchema GetSchema() + { + return null; + } + + public void ReadXml(XmlReader reader) + { + Console.WriteLine(reader.NodeType + " " + reader.Name); + Console.WriteLine("Value1 = " + reader.GetAttribute("myAttribute1")); + Console.WriteLine("Value2 = " + reader.GetAttribute("myAttribute2")); + } + + public void WriteXml(XmlWriter writer) + { + writer.WriteAttributeString("myAttribute1", "", "myAttribute1Value"); + writer.WriteAttributeString("myAttribute2", "", "myAttribute2Value"); + } + } + + public class UnsafeAttributeOnlyIXmlSerializable : AttributeOnlyIXmlSerializable + { + public UnsafeAttributeOnlyIXmlSerializable() + { + //may be called to invoke GetSchema() method + } + + public UnsafeAttributeOnlyIXmlSerializable(string init) + { + + } + + } + } +} + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/SerializableTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/SerializableTypes.cs new file mode 100644 index 00000000000000..87fc463c59c8ca --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/SerializableTypes.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections; +using System.Runtime.Serialization; + +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +#endif +{ + [Serializable] + public class Address + { + public string street; + string city; + string state; + int zip; + + [NonSerialized] + float privateData; + + public string Apartment + { + get { return null; } + set { } + } + + public Address() + { + } + } + + [Serializable] + public struct Address2 + { + public string street; + string city; + string state; + int zip; + + [NonSerialized] + float privateData; + + } + + [Serializable] + public class Employee //: DataContractTypes.Person2 + { + ArrayTypes.Company company; + + Employee(StreamingContext context) + { + } + } + + [Serializable] + [KnownType(typeof(ArrayList))] + [KnownType(typeof(int))] + [KnownType(typeof(DateTime))] + [KnownType(typeof(Employee))] + [KnownType(typeof(ObjectContainer))] + public class ObjectContainer + { + object obj; + } + +} diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/SerializationTypes.csproj b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/SerializationTypes.csproj new file mode 100644 index 00000000000000..440b641b2ac934 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/SerializationTypes.csproj @@ -0,0 +1,21 @@ + + + + netstandard2.1 + true + UseSeparateAssemblyNamespace;HideTypesWithoutSerializableAttribute + $(NoWarn);169;414 + + + + + all + + + + + + + + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SurrogateTests.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SurrogateTests.cs new file mode 100644 index 00000000000000..7a2c32aa527d96 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SurrogateTests.cs @@ -0,0 +1,525 @@ +// 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.Collections.ObjectModel; +using System.Reflection; +using System.Runtime.Serialization; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using Xunit; +using Xunit.Abstractions; + + +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +{ + public class SurrogateTests + { + private readonly ITestOutputHelper _output; + public SurrogateTests(ITestOutputHelper output) + { + _output = output; + } + + [Theory] + [MemberData(nameof(SurrogateProvider_MemberData))] + public void SurrogateProvider(Type type, ISerializationSurrogateProvider surrogate, Action schemaCheck = null) + { + ExportOptions options = new ExportOptions() { DataContractSurrogate = surrogate }; + XsdDataContractExporter exporter = new XsdDataContractExporter() { Options = options }; + + exporter.Export(type); + string schema = SchemaUtils.DumpSchema(exporter.Schemas); + _output.WriteLine(schema); + + if (schemaCheck != null) + schemaCheck(schema, exporter.Schemas); + } + public static IEnumerable SurrogateProvider_MemberData() + { + yield return new object[] { typeof(SurrogateTests.CircleContainer), new NodeToSerializableNode(new CircleToSquare(new XmlSerializerToXmlFormatter(null))), (string s, XmlSchemaSet ss) => { + SchemaUtils.OrderedContains(@"", ref s); + + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"Property", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"Field", ref s); + SchemaUtils.OrderedContains(@"", ref s); + + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"0", ref s); + SchemaUtils.OrderedContains(@"7", ref s); + SchemaUtils.OrderedContains(@"0", ref s); + SchemaUtils.OrderedContains(@"0", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"Field", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + } }; + yield return new object[] { typeof(SurrogateTests.Node), new NodeToSerializableNode(new CircleToSquare(new XmlSerializerToXmlFormatter(null))), (string s, XmlSchemaSet ss) => { + SchemaUtils.OrderedContains(@"", ref s); + + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"0", ref s); + SchemaUtils.OrderedContains(@"7", ref s); + SchemaUtils.OrderedContains(@"0", ref s); + SchemaUtils.OrderedContains(@"0", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"Field", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + } }; + yield return new object[] { typeof(SurrogateTests.XmlSerializerPerson), new NodeToSerializableNode(new CircleToSquare(new XmlSerializerToXmlFormatter(null))), (string s, XmlSchemaSet ss) => { + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"XmlSerializable", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + } }; + yield return new object[] { typeof(SurrogateTests.ValidSurrogateTest), new PersonSurrogate(), (string s, XmlSchemaSet ss) => { + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + } }; + yield return new object[] { typeof(SurrogateTests.ValidSurrogateTestDC), new PersonSurrogate(), (string s, XmlSchemaSet ss) => { + SchemaUtils.OrderedContains(@"", ref s); + + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + } }; + } + + [Theory] + [MemberData(nameof(SurrogateProvider_Negative_MemberData))] + public void SurrogateProvider_Negative(Type badType, ISerializationSurrogateProvider surrogate, Type exceptionType, string exMsg = null) + { + XsdDataContractExporter exporter = new XsdDataContractExporter(); + exporter.Options = new ExportOptions(); + exporter.Options.DataContractSurrogate = surrogate; + + var ex = Assert.Throws(exceptionType, () => exporter.Export(badType)); + if (exMsg != null) + Assert.Equal(exMsg, ex.Message); + } + public static IEnumerable SurrogateProvider_Negative_MemberData() + { + yield return new object[] { typeof(SurrogateTests.InvalidSurrogateTest), new CollectionASurrogate(), typeof(InvalidDataContractException) }; + yield return new object[] { typeof(SurrogateTests.InvalidSurrogateTestDC), new CollectionASurrogate(), typeof(InvalidDataContractException) }; + } + + #region SurrogateProviders + public class CircleToSquare : ISerializationSurrogateProvider2 + { + ISerializationSurrogateProvider2? _nextSurrogate; + public CircleToSquare(ISerializationSurrogateProvider2? nextSurrogate) + { + this._nextSurrogate = nextSurrogate; + } + + public Type GetSurrogateType(Type type) + { + if (type == typeof(SurrogateTests.Circle)) + return typeof(SurrogateTests.Square); + return (_nextSurrogate != null) ? _nextSurrogate.GetSurrogateType(type) : type; + } + + public object GetCustomDataToExport(Type clrType, Type dcType) + { + if (clrType == typeof(SurrogateTests.Circle) && dcType == typeof(SurrogateTests.Square)) + return clrType.Assembly.GetName().Version; + return (_nextSurrogate != null) ? _nextSurrogate.GetCustomDataToExport(clrType, dcType) : null; + } + + public object GetCustomDataToExport(MemberInfo memberInfo, Type dcType) => memberInfo.MemberType.ToString(); + public void GetKnownCustomDataTypes(Collection knownTypes) { } + public object GetObjectToSerialize(object obj, Type memberType) => throw new NotImplementedException(); + public object GetDeserializedObject(object obj, Type memberType) => throw new NotImplementedException(); + public Type GetReferencedTypeOnImport(string name, string ns, object customData) => null; + } + + public class NodeToSerializableNode : ISerializationSurrogateProvider2 + { + ISerializationSurrogateProvider2? _nextSurrogate; + public NodeToSerializableNode(ISerializationSurrogateProvider2? nextSurrogate) + { + this._nextSurrogate = nextSurrogate; + } + + public Type GetSurrogateType(Type type) + { + if (type == typeof(SurrogateTests.Node)) + return typeof(SurrogateTests.SerializableNode); + return (_nextSurrogate != null) ? _nextSurrogate.GetSurrogateType(type) : type; + } + + public object GetCustomDataToExport(Type clrType, Type dcType) + { + if (clrType == typeof(SurrogateTests.Node) && dcType == typeof(SurrogateTests.SerializableNode)) + return clrType.Assembly.GetName().Version; + return (_nextSurrogate != null) ? _nextSurrogate.GetCustomDataToExport(clrType, dcType) : null; + } + + public object GetCustomDataToExport(MemberInfo memberInfo, Type dcType) => memberInfo.MemberType.ToString(); + public void GetKnownCustomDataTypes(Collection knownTypes) => knownTypes.Add(typeof(Version)); + public object GetObjectToSerialize(object obj, Type memberType) => throw new NotImplementedException(); + public object GetDeserializedObject(object obj, Type memberType) => throw new NotImplementedException(); + public Type GetReferencedTypeOnImport(string name, string ns, object customData) => null; + } + + + public class XmlSerializerToXmlFormatter : ISerializationSurrogateProvider2 + { + ISerializationSurrogateProvider2? _nextSurrogate; + public XmlSerializerToXmlFormatter(ISerializationSurrogateProvider2? nextSurrogate) + { + this._nextSurrogate = nextSurrogate; + } + + public Type GetSurrogateType(Type type) + { + if (type == typeof(SurrogateTests.XmlSerializerPerson)) + return typeof(SurrogateTests.XmlSerializerAdapter); + return (_nextSurrogate != null) ? _nextSurrogate.GetSurrogateType(type) : type; + } + + public object GetCustomDataToExport(Type clrType, Type dcType) + { + if (clrType == typeof(SurrogateTests.XmlSerializerPerson) && dcType == typeof(SurrogateTests.XmlSerializerAdapter)) + return "XmlSerializable"; + return (_nextSurrogate != null) ? _nextSurrogate.GetCustomDataToExport(clrType, dcType) : null; + } + + public object GetCustomDataToExport(MemberInfo memberInfo, Type dcType) => memberInfo.MemberType.ToString(); + public void GetKnownCustomDataTypes(Collection knownTypes) { } + public Type GetReferencedTypeOnImport(string name, string ns, object customData) => null; + public object GetObjectToSerialize(object obj, Type memberType) => throw new NotImplementedException(); + public object GetDeserializedObject(object obj, Type memberType) => throw new NotImplementedException(); + } + + class PersonSurrogate : ISerializationSurrogateProvider2 + { + public Type GetSurrogateType(Type type) + { + if (typeof(SurrogateTests.NonSerializablePerson).IsAssignableFrom(type)) + { + return typeof(SurrogateTests.Person); + } + + if (typeof(SurrogateTests.NonSerializablePersonDC).IsAssignableFrom(type)) + { + return typeof(SurrogateTests.PersonDC); + } + return type; + } + + public object GetObjectToSerialize(object obj, Type targetType) + { + SurrogateTests.NonSerializablePerson nonSerializablePerson = obj as SurrogateTests.NonSerializablePerson; + if (nonSerializablePerson != null) + { + return new Person(); + } + SurrogateTests.NonSerializablePersonDC nonSerializablePersonDC = obj as SurrogateTests.NonSerializablePersonDC; + if (nonSerializablePersonDC != null) + { + return new SurrogateTests.PersonDC(); + } + return obj; + } + + public object GetDeserializedObject(object obj, Type targetType) + { + SurrogateTests.Person ps = obj as SurrogateTests.Person; + if (ps != null) + { + return new SurrogateTests.NonSerializablePerson("John Smith"); + } + SurrogateTests.PersonDC psDC = obj as SurrogateTests.PersonDC; + if (psDC != null) + { + return new SurrogateTests.NonSerializablePersonDC("John Smith"); + } + return obj; + } + + public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData) + { + if (typeNamespace.Equals("http://schemas.datacontract.org/2004/07/Suites.SchemaExport")) + { + if (typeName.Equals("DataContractSurrogateTest.Person")) + { + return typeof(SurrogateTests.NonSerializablePerson); + } + if (typeName.Equals("DataContractSurrogateTest.PersonDC")) + { + return typeof(SurrogateTests.NonSerializablePersonDC); + } + } + return null; + } + + public object GetCustomDataToExport(Type clrType, Type dataContractType) => null; + public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType) => null; + public void GetKnownCustomDataTypes(Collection customDataTypes) { } + } + + //This is the surrogate that substitutes CollectionWithoutParameterlessCtor for CollectionA. + class CollectionASurrogate : ISerializationSurrogateProvider2 + { + public Type GetSurrogateType(Type type) + { + if (typeof(ExporterTypesTests.CollectionA).IsAssignableFrom(type)) + { + return typeof(ExporterTypesTests.CollectionWithoutParameterlessCtor); + } + return type; + } + + public object GetObjectToSerialize(object obj, Type targetType) + { + ExporterTypesTests.CollectionA collectionA = obj as ExporterTypesTests.CollectionA; + if (collectionA != null) + { + ExporterTypesTests.CollectionWithoutParameterlessCtor validCollection = new ExporterTypesTests.CollectionWithoutParameterlessCtor(1); + validCollection.Add(1); + return validCollection; + } + return obj; + } + + public object GetDeserializedObject(object obj, Type targetType) + { + ExporterTypesTests.CollectionWithoutParameterlessCtor validCollection = obj as ExporterTypesTests.CollectionWithoutParameterlessCtor; + if (validCollection != null) + { + return new ExporterTypesTests.CollectionA(); + } + return obj; + } + + public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData) + { + if (typeNamespace.Equals("http://schemas.datacontract.org/2004/07/Suites.SchemaExport")) + { + if (typeName.Equals("ExporterTypesTests.CollectionWithoutParameterlessCtor`1")) + { + return typeof(ExporterTypesTests.CollectionA); + } + } + return null; + } + + public object GetCustomDataToExport(Type clrType, Type dataContractType) => null; + public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType) => null; + public void GetKnownCustomDataTypes(Collection customDataTypes) { } + } + #endregion + + #region Surrogate Test Types +#pragma warning disable CS0169, CS0414 + public class ValidSurrogateTest + { + ExporterTypesTests.CollectionWithoutParameterlessCtor friends; + + public ExporterTypesTests.CollectionWithoutParameterlessCtor Friends + { + get + { + friends = friends ?? new ExporterTypesTests.CollectionWithoutParameterlessCtor(2); + return friends; + } + } + } + + public class InvalidSurrogateTest + { + ExporterTypesTests.CollectionA localList = new ExporterTypesTests.CollectionA(); + + public ExporterTypesTests.CollectionA Surrogated + { + get + { + return localList; + } + } + } + + [DataContract] + public class ValidSurrogateTestDC + { + ExporterTypesTests.CollectionWithoutParameterlessCtor friends; + + [DataMember] + public ExporterTypesTests.CollectionWithoutParameterlessCtor Friends + { + get + { + friends = friends ?? new ExporterTypesTests.CollectionWithoutParameterlessCtor(2); + return friends; + } + } + } + + [DataContract] + public class InvalidSurrogateTestDC + { + ExporterTypesTests.CollectionA localList = new ExporterTypesTests.CollectionA(); + + [DataMember] + public ExporterTypesTests.CollectionA Surrogated + { + get + { + return localList; + } + } + } + + [DataContract] + public class CircleContainer + { + [DataMember] + Circle circle; + [DataMember] + public Circle[] Circles { get { return null; } set { } } + } + + [Serializable] + public class Circle + { + public int Radius; + } + + [Serializable] + public class Square + { + public int Side; + } + + public class Node + { + Node next; + } + + [Serializable] + public class SerializableNode + { + SerializableNode next; + } + + [XmlRoot("XmlSerializerPersonElement")] + public class XmlSerializerPerson + { + public XmlSerializerPerson() { } + [XmlAttribute] + public string Name; + [XmlAttribute] + public int Age; + } + + [XmlSchemaProvider("StaticGetSchema")] + public class XmlSerializerAdapter : IXmlSerializable + { + public XmlSchema GetSchema() + { + throw new NotImplementedException(); + } + + public void ReadXml(XmlReader reader) + { + throw new NotImplementedException(); + } + + public void WriteXml(XmlWriter writer) + { + throw new NotImplementedException(); + } + + static XmlQualifiedName StaticGetSchema(XmlSchemaSet schemaSet) + { + XmlReflectionImporter importer = new XmlReflectionImporter(); + XmlTypeMapping xmlTypeMapping = importer.ImportTypeMapping(typeof(T)); + XmlSchemas schemas = new XmlSchemas(); + XmlSchemaExporter exporter = new XmlSchemaExporter(schemas); + exporter.ExportTypeMapping(xmlTypeMapping); + schemas.Compile(new ValidationEventHandler(ValidationCallbackWithErrorCode), true); + for (int i = 0; i < schemas.Count; i++) + { + XmlSchema schema = schemas[i]; + schemaSet.Add(schema); + } + return new XmlQualifiedName(xmlTypeMapping.TypeName, xmlTypeMapping.Namespace); + } + + private static void ValidationCallbackWithErrorCode(object sender, ValidationEventArgs args) + { + Console.WriteLine("Schema warning: " + args.Message); + } + } + + public class Person + { + public string name = "John Smith"; + } + + public class NonSerializablePerson + { + public string name; + + public NonSerializablePerson(string name) + { + this.name = name; + } + } + + [DataContract] + public class PersonDC + { + [DataMember] + public string name = "John Smith"; + } + + public class NonSerializablePersonDC + { + public string name; + + public NonSerializablePersonDC(string name) + { + this.name = name; + } + } +#pragma warning restore CS0169, CS0414 + #endregion + } +} diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/Types.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/Types.cs new file mode 100644 index 00000000000000..f50cdb081d5249 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/Types.cs @@ -0,0 +1,138 @@ +// 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; +using System.Collections.Generic; + +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests.Types +{ + [DataContract(Namespace = "http://basic")] + public class Point + { + [DataMember] + public int X = 42; + [DataMember] + public int Y = 43; + } + + [DataContract(Namespace = "http://shapes")] + public class Circle + { + [DataMember] + public Point Center = new Point(); + [DataMember] + public int Radius = 5; + } + + [DataContract(Namespace = "http://shapes")] + public class Square + { + [DataMember] + public Point BottomLeft = new Point(); + [DataMember] + public int Side = 5; + } + + public class NonSerializableSquare + { + public int Length = 5; + + public NonSerializableSquare(int length) + { + Length = length; + } + } + + public struct NonAttributedPersonStruct + { + public string firstName; + public string lastName; + } + + public class NonAttributedPersonClass + { + public string firstName = "John"; + public string lastName = "Smith"; + + internal NonAttributedPersonClass() + { + } + } + + public class ExtendedSquare : Square + { + public string lineColor = "black"; + } + + public class RecursiveCollection1 : IEnumerable + { + public void Add(RecursiveCollection1 item) + { + } + + public IEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + } + + public class RecursiveCollection2 : IEnumerable> + { + public void Add(RecursiveCollection1 item) + { + } + + public IEnumerator> GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + } + + public class Box + { + } + + public class RecursiveCollection3 : IEnumerable>> + { + public void Add(RecursiveCollection1 item) + { + } + + public IEnumerator>> GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + } + + public class RecursiveCollection4 : IEnumerable> + { + public void Add(RecursiveCollection1 item) + { + } + + public IEnumerator> GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.csproj b/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.csproj index 243d4766ec96ad..cc75876fe686c0 100644 --- a/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.csproj +++ b/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.csproj @@ -2,12 +2,14 @@ $(NetCoreAppCurrent) + + - - - + + + diff --git a/src/libraries/oob.proj b/src/libraries/oob.proj index 6dbbd63794b270..29594462f92b0d 100644 --- a/src/libraries/oob.proj +++ b/src/libraries/oob.proj @@ -44,7 +44,7 @@ - + diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 0b1ca8411cc6da..17148cf01526c4 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -530,6 +530,7 @@ + From 0c74b9a060ff342749787c36bfa4243c55091831 Mon Sep 17 00:00:00 2001 From: !cake Date: Sat, 13 Aug 2022 08:09:01 +0100 Subject: [PATCH 53/56] Fix `DataContractJsonSerializer`'s handling of -0.0 (#69020) * Add serialization test for -0.0 * Fix `DataContractJsonSerializer`'s handling of -0.0 Fixes #69019. --- .../src/System/Xml/XmlConverter.cs | 4 ++-- .../tests/DataContractJsonSerializer.cs | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlConverter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlConverter.cs index 0a856e04e7f1cd..833ed67f9b82d2 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlConverter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlConverter.cs @@ -615,7 +615,7 @@ private static bool TryParseSingle(byte[] chars, int offset, int count, out floa if (count == 10) return false; if (negative) - result = -value; + result = -(float)value; else result = value; return true; @@ -667,7 +667,7 @@ private static bool TryParseDouble(byte[] chars, int offset, int count, out doub if (count == 10) return false; if (negative) - result = -value; + result = -(double)value; else result = value; return true; diff --git a/src/libraries/System.Runtime.Serialization.Json/tests/DataContractJsonSerializer.cs b/src/libraries/System.Runtime.Serialization.Json/tests/DataContractJsonSerializer.cs index 018565ccacf3bd..404f6053af9042 100644 --- a/src/libraries/System.Runtime.Serialization.Json/tests/DataContractJsonSerializer.cs +++ b/src/libraries/System.Runtime.Serialization.Json/tests/DataContractJsonSerializer.cs @@ -140,7 +140,9 @@ public static void DCJS_DecimalAsRoot() public static void DCJS_DoubleAsRoot() { Assert.StrictEqual(-1.2, SerializeAndDeserialize(-1.2, "-1.2")); - Assert.StrictEqual(0, SerializeAndDeserialize(0, "0")); + Assert.StrictEqual(0.0, SerializeAndDeserialize(0.0, "0")); + Assert.StrictEqual(0.0, SerializeAndDeserialize(-0.0, "-0")); + Assert.Equal("-0", SerializeAndDeserialize(-0.0, "-0").ToString()); Assert.StrictEqual(2.3, SerializeAndDeserialize(2.3, "2.3")); Assert.StrictEqual(double.MinValue, SerializeAndDeserialize(double.MinValue, "-1.7976931348623157E+308")); Assert.StrictEqual(double.MaxValue, SerializeAndDeserialize(double.MaxValue, "1.7976931348623157E+308")); @@ -150,7 +152,9 @@ public static void DCJS_DoubleAsRoot() public static void DCJS_FloatAsRoot() { Assert.StrictEqual((float)-1.2, SerializeAndDeserialize((float)-1.2, "-1.2")); - Assert.StrictEqual((float)0, SerializeAndDeserialize((float)0, "0")); + Assert.StrictEqual((float)0.0, SerializeAndDeserialize((float)0.0, "0")); + Assert.StrictEqual((float)0.0, SerializeAndDeserialize((float)-0.0, "-0")); + Assert.Equal("-0", SerializeAndDeserialize((float)-0.0, "-0").ToString()); Assert.StrictEqual((float)2.3, SerializeAndDeserialize((float)2.3, "2.3")); } From d3a803c872626309dd916e224c9c66d420d8039b Mon Sep 17 00:00:00 2001 From: Steve Molloy Date: Sat, 13 Aug 2022 00:17:00 -0700 Subject: [PATCH 54/56] 66163 fatal error xml serializer immutable array (#73729) * Fiddle the RefEmit IL for value-type arrays like ImmutableArray. Add tests for expected and non-fatal read-only failures. * Extend fixup to cover other 'Immutable' collection types. * Skip ROC and Immutable tests in pregenerated test suite. Those types aren't in the pregen dll. --- .../System/Xml/Serialization/CodeGenerator.cs | 5 + .../System/Xml/Serialization/SourceInfo.cs | 2 +- .../XmlSerializationReaderILGen.cs | 10 +- .../tests/XmlSerializer/XmlSerializerTests.cs | 151 ++++++++++++++++++ 4 files changed, 164 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs index 493a5ca37e1136..20e9d52fa5867d 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs @@ -303,6 +303,11 @@ internal void EndFor() CodeGenerator.InstanceBindingFlags, Type.EmptyTypes )!; + // ICollection is not a value type, and ICollection::get_Count is a virtual method. So Call() here + // will do a 'callvirt'. If we are working with a value type, box it before calling. + Debug.Assert(ICollection_get_Count.IsVirtual && !ICollection_get_Count.DeclaringType!.IsValueType); + if (varType.IsValueType) + Box(varType); Call(ICollection_get_Count); } Blt(forState.BeginLabel); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/SourceInfo.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/SourceInfo.cs index 971b9184216f15..3a544fb9dcd954 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/SourceInfo.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/SourceInfo.cs @@ -103,7 +103,7 @@ private void InternalLoad(Type? elementType, bool asAddress = false) } else { - ILG.Load(varA); + ILG.LoadAddress(varA); ILG.Load(varIA); MethodInfo get_Item = varType.GetMethod( "get_Item", diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReaderILGen.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReaderILGen.cs index 7b39193b4a72d2..f7d8514e55a2c6 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReaderILGen.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReaderILGen.cs @@ -2176,7 +2176,7 @@ private void WriteMemberBegin(Member[] members) } else { - if (member.IsList && !member.Mapping.ReadOnly && member.Mapping.TypeDesc.IsNullable) + if (member.IsList && !member.Mapping.ReadOnly) //&& member.Mapping.TypeDesc.IsNullable) // nullable or not, we are likely to assign null in the next step if we don't do this initialization. So just do this. { // we need to new the Collections and ArrayLists ILGenLoad(member.Source, typeof(object)); @@ -2881,7 +2881,8 @@ private void WriteArray(string source, string? arrayName, ArrayMapping arrayMapp )!; ilg.Ldarg(0); ilg.Call(XmlSerializationReader_ReadNull); - ilg.IfNot(); + ilg.IfNot(); // if (!ReadNull()) { // EnterScope + ilg.EnterScope(); MemberMapping memberMapping = new MemberMapping(); memberMapping.Elements = arrayMapping.Elements; @@ -2976,11 +2977,14 @@ private void WriteArray(string source, string? arrayName, ArrayMapping arrayMapp if (isNullable) { - ilg.Else(); + ilg.ExitScope(); // if(!ReadNull()) { ExitScope + ilg.Else(); // } else { EnterScope + ilg.EnterScope(); member.IsNullable = true; WriteMemberBegin(members); WriteMemberEnd(members); } + ilg.ExitScope(); // if(!ReadNull())/else ExitScope ilg.EndIf(); } diff --git a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs index d1a0be195a0221..45d22fefab4389 100644 --- a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs +++ b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs @@ -3,7 +3,10 @@ using SerializationTypes; using System; +using System.Collections; using System.Collections.Generic; +using System.Collections.Immutable; +using System.Collections.ObjectModel; using System.Globalization; using System.IO; using System.Linq; @@ -194,6 +197,103 @@ public static void Xml_ListRoot() Assert.Equal((string)x[1], (string)y[1]); } +// ROC and Immutable types are not types from 'SerializableAssembly.dll', so they were not included in the +// pregenerated serializers for the sgen tests. We could wrap them in a type that does exist there... +// but I think the RO/Immutable story is wonky enough and RefEmit vs Reflection is near enough on the +// horizon that it's not worth the trouble. +#if !XMLSERIALIZERGENERATORTESTS + [Fact] + public static void Xml_ReadOnlyCollection() + { + ReadOnlyCollection roc = new ReadOnlyCollection(new string[] { "one", "two" }); + +#if ReflectionOnly + // Expect exception when _using_ the serializer + var serializer = new XmlSerializer(typeof(ReadOnlyCollection)); + var ex = Assert.Throws(() => Serialize(roc, null, () => serializer)); + Assert.Equal("There was an error generating the XML document.", ex.Message); + Assert.NotNull(ex.InnerException); + Assert.IsType(ex.InnerException); + Assert.StartsWith("To be XML serializable, types which inherit from ICollection must have an implementation of Add(System.String) at all levels of their inheritance hierarchy.", ex.InnerException.Message); +#else + // Expect exception when _creating_ the serializer + var ex = Assert.Throws(() => new XmlSerializer(typeof(ReadOnlyCollection))); + Assert.StartsWith("To be XML serializable, types which inherit from ICollection must have an implementation of Add(System.String) at all levels of their inheritance hierarchy.", ex.Message); +#endif + } + + [Theory] + [MemberData(nameof(Xml_ImmutableCollections_MemberData))] + public static void Xml_ImmutableCollections(Type type, object collection, Type createException, Type addException, string expectedXml, string exMsg = null) + { + XmlSerializer serializer; + + // Some collections implement the required enumerator/Add combo (ImmutableList, ImmutableArray) and some don't (ImmutableStack, + // ImmutableQueue). If they do not, they will throw upon serializer construction in RefEmit mode. They should throw when + // first using the serializer in Reflection mode. +#if ReflectionOnly + serializer = new XmlSerializer(type); + if (createException != null) + { + var ex = Assert.Throws(createException, () => Serialize(collection, expectedXml, () => serializer)); + if (exMsg != null) + Assert.Contains(exMsg, $"{ex.Message} : {ex.InnerException?.Message}"); + return; + } +#else + if (createException != null) + { + var ex = Assert.Throws(createException, () => serializer = new XmlSerializer(type)); + if (exMsg != null) + Assert.Contains(exMsg, $"{ex.Message} : {ex.InnerException?.Message}"); + return; + } + serializer = new XmlSerializer(type); +#endif + + // If they do meet the signature requirement, they may succeed or fail depending on whether their Add/Indexer explicitly throw + // or not. (ImmutableArray throws. ImmutableList does not - it returns a new copy instead... which gets ignored and is thus + // essentially a silent failure.) Serializing out to a string first should work though. + string serializedValue = Serialize(collection, expectedXml, () => serializer); + + if (addException != null) + { + var ex = Assert.Throws(addException, () => Deserialize(serializer, serializedValue)); + if (exMsg != null) + Assert.Contains(exMsg, $"{ex.Message} : {ex.InnerException?.Message}"); + return; + } + + // In this case, we can execute everything without exception. But since our calls to '.Add()' do nothing, we end up + // with an empty collection + var rttCollection = Deserialize(serializer, serializedValue); + Assert.NotNull(rttCollection); + Assert.Empty((IEnumerable)rttCollection); + } + public static IEnumerable Xml_ImmutableCollections_MemberData() + { + string arrayOfInt = "42"; + string arrayOfAny = ""; + +#if ReflectionOnly + yield return new object[] { typeof(ImmutableArray), ImmutableArray.Create(42), null, typeof(InvalidOperationException), arrayOfInt, "Specified method is not supported." }; + yield return new object[] { typeof(ImmutableArray), ImmutableArray.Create(new object()), null, typeof(InvalidOperationException), arrayOfAny, "Specified method is not supported." }; + yield return new object[] { typeof(ImmutableList), ImmutableList.Create(42), null, typeof(InvalidOperationException), arrayOfInt, "Specified method is not supported." }; + yield return new object[] { typeof(ImmutableStack), ImmutableStack.Create(42), typeof(InvalidOperationException), null, arrayOfInt, "To be XML serializable, types which inherit from IEnumerable must have an implementation of Add" }; + yield return new object[] { typeof(ImmutableQueue), ImmutableQueue.Create(42), typeof(InvalidOperationException), null, arrayOfInt, "To be XML serializable, types which inherit from IEnumerable must have an implementation of Add" }; + yield return new object[] { typeof(ImmutableDictionary), new Dictionary() { { "one", 1 } }.ToImmutableDictionary(), typeof(InvalidOperationException), null, null, "is not supported because it implements IDictionary." }; +#else + yield return new object[] { typeof(ImmutableArray), ImmutableArray.Create(42), null, typeof(InvalidOperationException), arrayOfInt, "Parameterless constructor is required for collections and enumerators." }; + yield return new object[] { typeof(ImmutableArray), ImmutableArray.Create(new object()), null, typeof(InvalidOperationException), arrayOfAny, "Parameterless constructor is required for collections and enumerators." }; + yield return new object[] { typeof(ImmutableList), ImmutableList.Create(42), null, null, arrayOfInt }; + yield return new object[] { typeof(ImmutableStack), ImmutableStack.Create(42), typeof(InvalidOperationException), null, arrayOfInt, "To be XML serializable, types which inherit from IEnumerable must have an implementation of Add" }; + yield return new object[] { typeof(ImmutableQueue), ImmutableQueue.Create(42), typeof(InvalidOperationException), null, arrayOfInt, "To be XML serializable, types which inherit from IEnumerable must have an implementation of Add" }; + // IDictionary types are denied right from the start with a NotSupportedExcpetion + yield return new object[] { typeof(ImmutableDictionary), new Dictionary() { { "one", 1 } }.ToImmutableDictionary(), typeof(NotSupportedException), null, null, "is not supported because it implements IDictionary." }; +#endif + } +#endif // !XMLSERIALIZERGENERATORTESTS + [Fact] public static void Xml_EnumAsRoot() { @@ -2204,4 +2304,55 @@ private static T SerializeAndDeserializeWithWrapper(T value, XmlSerializer se Assert.True(e is ExceptionType, $"Assert.True failed for {typeof(T)}. Expected: {typeof(ExceptionType)}; Actual: {e.GetType()}"); } } + + private static string Serialize(T value, string baseline, Func serializerFactory = null, + bool skipStringCompare = false, XmlSerializerNamespaces xns = null) + { + XmlSerializer serializer = (serializerFactory != null) ? serializerFactory() : new XmlSerializer(typeof(T)); + + using (MemoryStream ms = new MemoryStream()) + { + if (xns == null) + { + serializer.Serialize(ms, value); + } + else + { + serializer.Serialize(ms, value, xns); + } + + ms.Position = 0; + + string actualOutput = new StreamReader(ms).ReadToEnd(); + + if (!skipStringCompare) + { + Utils.CompareResult result = Utils.Compare(baseline, actualOutput); + Assert.True(result.Equal, string.Format("{1}{0}Test failed for input: {2}{0}Expected: {3}{0}Actual: {4}", + Environment.NewLine, result.ErrorMessage, value, baseline, actualOutput)); + } + + return actualOutput; + } + } + + private static object? Deserialize(XmlSerializer serializer, string xmlInput) + { + using (Stream stream = StringToStream(xmlInput)) + { + return serializer.Deserialize(stream); + } + } + + private static Stream StringToStream(string input) + { + MemoryStream ms = new MemoryStream(); + StreamWriter sw = new StreamWriter(ms); + + sw.Write(input); + sw.Flush(); + ms.Position = 0; + + return ms; + } } From ad17b93b0992d3db4d4eb01cf8704ed134466fbb Mon Sep 17 00:00:00 2001 From: Steve Molloy Date: Sat, 13 Aug 2022 00:24:59 -0700 Subject: [PATCH 55/56] Implement IXmlTextWriterInitializer on the async writer wrapper. (#73730) * Implement IXmlTextWriterInitializer on the async writer wrapper. * Don't use generics per PR feedback. * Use cast in SetOutput instead of typed field. --- .../src/System/Xml/XmlDictionaryAsyncCheckWriter.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryAsyncCheckWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryAsyncCheckWriter.cs index cbb815091a66a8..ae19fc44bc2070 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryAsyncCheckWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryAsyncCheckWriter.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.CompilerServices; @@ -11,13 +12,14 @@ namespace System.Xml { - internal sealed class XmlDictionaryAsyncCheckWriter : XmlDictionaryWriter + internal sealed class XmlDictionaryAsyncCheckWriter : XmlDictionaryWriter, IXmlTextWriterInitializer { private readonly XmlDictionaryWriter _coreWriter; private Task? _lastTask; public XmlDictionaryAsyncCheckWriter(XmlDictionaryWriter writer) { + Debug.Assert(writer is IXmlTextWriterInitializer); _coreWriter = writer; } @@ -694,5 +696,14 @@ protected override void Dispose(bool disposing) CheckAsync(); CoreWriter.Dispose(); } + + public void SetOutput(Stream stream, Encoding encoding, bool ownsStream) + { + if (CoreWriter is IXmlTextWriterInitializer initializer) + { + CheckAsync(); + initializer.SetOutput(stream, encoding, ownsStream); + } + } } } From 625fb02cb44590bb2d01beda8ab3ca36116f2950 Mon Sep 17 00:00:00 2001 From: Steve Molloy Date: Sat, 13 Aug 2022 00:25:35 -0700 Subject: [PATCH 56/56] Bring back readability lost in .Net 6, and stop emitting BOM. (#73731) * Bring back readability lost in .Net 6, and stop emitting BOM. * Adjust test for wasm. --- .../System/Xml/Serialization/XmlSerializer.cs | 6 ++-- .../tests/XmlSerializer/XmlSerializerTests.cs | 29 +++++++++++++++++-- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs index 6331b414e36c3f..912d73d79d2a0a 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs @@ -140,6 +140,8 @@ private static bool ReflectionMethodEnabled private static readonly TempAssemblyCache s_cache = new TempAssemblyCache(); private static volatile XmlSerializerNamespaces? s_defaultNamespaces; + private static readonly XmlWriterSettings s_writerSettings = new XmlWriterSettings() { Encoding = new UTF8Encoding(false), Indent = true }; + private static XmlSerializerNamespaces DefaultNamespaces { get @@ -323,7 +325,7 @@ public void Serialize(TextWriter textWriter, object? o) [RequiresUnreferencedCode(TrimSerializationWarning)] public void Serialize(TextWriter textWriter, object? o, XmlSerializerNamespaces? namespaces) { - XmlWriter xmlWriter = XmlWriter.Create(textWriter); + XmlWriter xmlWriter = XmlWriter.Create(textWriter, s_writerSettings); Serialize(xmlWriter, o, namespaces); } @@ -336,7 +338,7 @@ public void Serialize(Stream stream, object? o) [RequiresUnreferencedCode(TrimSerializationWarning)] public void Serialize(Stream stream, object? o, XmlSerializerNamespaces? namespaces) { - XmlWriter xmlWriter = XmlWriter.Create(stream); + XmlWriter xmlWriter = XmlWriter.Create(stream, s_writerSettings); Serialize(xmlWriter, o, namespaces); } diff --git a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs index 45d22fefab4389..014ff5e1a4aea2 100644 --- a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs +++ b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs @@ -815,7 +815,7 @@ public static void Xml_TypeWithTimeSpanProperty() { var obj = new TypeWithTimeSpanProperty { TimeSpanProperty = TimeSpan.FromMilliseconds(1) }; var deserializedObj = SerializeAndDeserialize(obj, -@" +@" PT0.001S "); @@ -1055,7 +1055,7 @@ public static void Xml_SimpleType() { var obj = new SimpleType { P1 = "foo", P2 = 1 }; var deserializedObj = SerializeAndDeserialize(obj, -@" +@" foo 1 @@ -1065,6 +1065,31 @@ public static void Xml_SimpleType() Assert.StrictEqual(obj.P2, deserializedObj.P2); } + [Fact] + public static void Xml_SerializedFormat() + { + var obj = new SimpleType { P1 = "foo", P2 = 1 }; + XmlSerializer serializer = new XmlSerializer(typeof(SimpleType)); + using (MemoryStream ms = new MemoryStream()) + { + serializer.Serialize(ms, obj); + + // No BOM? + byte[] expectedBytes = new byte[] { (byte)'<', (byte)'?', (byte)'x', (byte)'m', (byte)'l' }; + byte[] firstBytes = new byte[5]; + ms.Position = 0; + ms.Read(firstBytes, 0, firstBytes.Length); + Assert.Equal(expectedBytes, firstBytes); + + // Human readable? + ms.Position = 0; + string nl = Environment.NewLine; + string actualFormatting = new StreamReader(ms).ReadToEnd(); + string expectedFormatting = $"{nl}{nl} foo{nl} 1{ nl}"; + Assert.Equal(expectedFormatting, actualFormatting); + } + } + [Fact] public static void Xml_BaseClassAndDerivedClass2WithSameProperty() {