diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 7acdf9970510fc..44d6d3c30833e5 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -8,79 +8,83 @@ https://github.com/dotnet/msquic 98129287d56a5e0348c291ce4260e630b4aa510d - + https://github.com/dotnet/emsdk - 5cac15def164fc5ab593f8a0d3159dc51acd3cdd + 9838ec0843442f761488cfec9cf34612c9f675e6 - + https://github.com/dotnet/arcade - 7324320f814152b72295946847ca72413507705a + dd9dbfedbdb31401bb16bba8366f31bbd382549b - + https://github.com/dotnet/arcade - 7324320f814152b72295946847ca72413507705a + dd9dbfedbdb31401bb16bba8366f31bbd382549b - + https://github.com/dotnet/arcade - 7324320f814152b72295946847ca72413507705a + dd9dbfedbdb31401bb16bba8366f31bbd382549b - + https://github.com/dotnet/arcade - 7324320f814152b72295946847ca72413507705a + dd9dbfedbdb31401bb16bba8366f31bbd382549b - + https://github.com/dotnet/arcade - 7324320f814152b72295946847ca72413507705a + dd9dbfedbdb31401bb16bba8366f31bbd382549b - + https://github.com/dotnet/arcade - 7324320f814152b72295946847ca72413507705a + dd9dbfedbdb31401bb16bba8366f31bbd382549b - + https://github.com/dotnet/arcade - 7324320f814152b72295946847ca72413507705a + dd9dbfedbdb31401bb16bba8366f31bbd382549b - + https://github.com/dotnet/arcade - 7324320f814152b72295946847ca72413507705a + dd9dbfedbdb31401bb16bba8366f31bbd382549b - + https://github.com/dotnet/arcade - 7324320f814152b72295946847ca72413507705a + dd9dbfedbdb31401bb16bba8366f31bbd382549b - + https://github.com/dotnet/arcade - 7324320f814152b72295946847ca72413507705a + dd9dbfedbdb31401bb16bba8366f31bbd382549b - + https://github.com/dotnet/arcade - 7324320f814152b72295946847ca72413507705a + dd9dbfedbdb31401bb16bba8366f31bbd382549b - + https://github.com/dotnet/arcade - 7324320f814152b72295946847ca72413507705a + dd9dbfedbdb31401bb16bba8366f31bbd382549b - + https://github.com/dotnet/arcade - 7324320f814152b72295946847ca72413507705a + dd9dbfedbdb31401bb16bba8366f31bbd382549b - + https://github.com/dotnet/arcade - 7324320f814152b72295946847ca72413507705a + dd9dbfedbdb31401bb16bba8366f31bbd382549b - + https://github.com/dotnet/arcade - 7324320f814152b72295946847ca72413507705a + dd9dbfedbdb31401bb16bba8366f31bbd382549b - + https://github.com/dotnet/arcade - 7324320f814152b72295946847ca72413507705a + dd9dbfedbdb31401bb16bba8366f31bbd382549b - + https://github.com/dotnet/arcade - 7324320f814152b72295946847ca72413507705a + dd9dbfedbdb31401bb16bba8366f31bbd382549b + + + https://github.com/dotnet/arcade + dd9dbfedbdb31401bb16bba8366f31bbd382549b https://github.com/microsoft/vstest @@ -202,9 +206,9 @@ https://github.com/dotnet/xharness e9669dc84ecd668d3bbb748758103e23b394ffef - + https://github.com/dotnet/arcade - 7324320f814152b72295946847ca72413507705a + dd9dbfedbdb31401bb16bba8366f31bbd382549b https://dev.azure.com/dnceng/internal/_git/dotnet-optimization diff --git a/eng/Versions.props b/eng/Versions.props index 157120cdfad981..55b34e750e955b 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -53,20 +53,21 @@ 1.0.0-rc.2.21419.17 - 6.0.0-beta.21460.7 - 6.0.0-beta.21460.7 - 6.0.0-beta.21460.7 - 6.0.0-beta.21460.7 - 6.0.0-beta.21460.7 - 6.0.0-beta.21460.7 - 2.5.1-beta.21460.7 - 6.0.0-beta.21460.7 - 6.0.0-beta.21460.7 - 6.0.0-beta.21460.7 - 6.0.0-beta.21460.7 - 6.0.0-beta.21460.7 - 6.0.0-beta.21460.7 - 6.0.0-beta.21460.7 + 6.0.0-beta.21467.3 + 6.0.0-beta.21467.3 + 6.0.0-beta.21467.3 + 6.0.0-beta.21467.3 + 6.0.0-beta.21467.3 + 6.0.0-beta.21467.3 + 2.5.1-beta.21467.3 + 6.0.0-beta.21467.3 + 6.0.0-beta.21467.3 + 6.0.0-beta.21467.3 + 6.0.0-beta.21467.3 + 6.0.0-beta.21467.3 + 6.0.0-beta.21467.3 + 6.0.0-beta.21467.3 + 6.0.0-beta.21467.3 6.0.0-preview.1.102 @@ -166,7 +167,7 @@ 2.14.3 6.0.100-rc.2.21463.12 - 5.0.0-preview-20201009.2 + 6.0.0-preview-20210916.1 6.0.100-1.21459.1 $(MicrosoftNETILLinkTasksVersion) @@ -184,7 +185,7 @@ 11.1.0-alpha.1.21416.1 11.1.0-alpha.1.21416.1 - 6.0.0-rtm.21464.2 + 6.0.0-rc.2.21464.1 $(MicrosoftNETWorkloadEmscriptenManifest60100Version) 1.1.87-gba258badda diff --git a/global.json b/global.json index 0cfd182128d924..79641b503fbb19 100644 --- a/global.json +++ b/global.json @@ -12,10 +12,10 @@ "python3": "3.7.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21460.7", - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21460.7", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21460.7", - "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21460.7", + "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21467.3", + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21467.3", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21467.3", + "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21467.3", "Microsoft.Build.NoTargets": "3.1.0", "Microsoft.Build.Traversal": "3.0.23", "Microsoft.NET.Sdk.IL": "6.0.0-rc.1.21415.6" diff --git a/src/coreclr/debug/daccess/daccess.cpp b/src/coreclr/debug/daccess/daccess.cpp index 61f0d827e60f19..dee4fe132fa74c 100644 --- a/src/coreclr/debug/daccess/daccess.cpp +++ b/src/coreclr/debug/daccess/daccess.cpp @@ -6077,13 +6077,20 @@ ClrDataAccess::GetMethodVarInfo(MethodDesc* methodDesc, SUPPORTS_DAC; COUNT_T countNativeVarInfo; NewHolder nativeVars(NULL); - - NativeCodeVersion requestedNativeCodeVersion = ExecutionManager::GetNativeCodeVersion(address); - if (requestedNativeCodeVersion.IsNull() || requestedNativeCodeVersion.GetNativeCode() == NULL) + TADDR nativeCodeStartAddr; + if (address != NULL) { - return E_INVALIDARG; + NativeCodeVersion requestedNativeCodeVersion = ExecutionManager::GetNativeCodeVersion(address); + if (requestedNativeCodeVersion.IsNull() || requestedNativeCodeVersion.GetNativeCode() == NULL) + { + return E_INVALIDARG; + } + nativeCodeStartAddr = PCODEToPINSTR(requestedNativeCodeVersion.GetNativeCode()); + } + else + { + nativeCodeStartAddr = PCODEToPINSTR(methodDesc->GetNativeCode()); } - TADDR nativeCodeStartAddr = PCODEToPINSTR(requestedNativeCodeVersion.GetNativeCode()); DebugInfoRequest request; request.InitFromStartingAddr(methodDesc, nativeCodeStartAddr); @@ -6128,13 +6135,20 @@ ClrDataAccess::GetMethodNativeMap(MethodDesc* methodDesc, // Use the DebugInfoStore to get IL->Native maps. // It doesn't matter whether we're jitted, ngenned etc. - - NativeCodeVersion requestedNativeCodeVersion = ExecutionManager::GetNativeCodeVersion(address); - if (requestedNativeCodeVersion.IsNull() || requestedNativeCodeVersion.GetNativeCode() == NULL) + TADDR nativeCodeStartAddr; + if (address != NULL) { - return E_INVALIDARG; + NativeCodeVersion requestedNativeCodeVersion = ExecutionManager::GetNativeCodeVersion(address); + if (requestedNativeCodeVersion.IsNull() || requestedNativeCodeVersion.GetNativeCode() == NULL) + { + return E_INVALIDARG; + } + nativeCodeStartAddr = PCODEToPINSTR(requestedNativeCodeVersion.GetNativeCode()); + } + else + { + nativeCodeStartAddr = PCODEToPINSTR(methodDesc->GetNativeCode()); } - TADDR nativeCodeStartAddr = PCODEToPINSTR(requestedNativeCodeVersion.GetNativeCode()); DebugInfoRequest request; request.InitFromStartingAddr(methodDesc, nativeCodeStartAddr); diff --git a/src/installer/pkg/sfx/installers/dotnet-host.proj b/src/installer/pkg/sfx/installers/dotnet-host.proj index 953b0f4f8522f9..aba056f06d17cb 100644 --- a/src/installer/pkg/sfx/installers/dotnet-host.proj +++ b/src/installer/pkg/sfx/installers/dotnet-host.proj @@ -15,7 +15,7 @@ true sharedhost false - osx_scripts/host + osx_scripts/host The .NET Shared Host. $(MSBuildThisFileDirectory)rpm_scripts/host $(RpmScriptsDirectory)/after_install.sh @@ -78,4 +78,36 @@ + + + <_MacOSIntermediatesPath>$(IntermediateOutputPath)macos/ + + $(_MacOSIntermediatesPath)scripts + + + <_MacOSScript Include="$(MacOSScriptsTemplateDirectory)/*" Destination="$(MacOSScriptsDirectory)/%(FileName)%(Extension)"/> + + + + + <_UnameMachineRegex>$(InstallerTargetArchitecture) + <_UnameMachineRegex Condition="'$(InstallerTargetArchitecture)' == 'arm64'">arm64|aarch64 + <_UnameMachineRegex Condition="'$(InstallerTargetArchitecture)' == 'x64'">amd64|x86_64 + <_MacOSScriptsTemplateProperties>InstallerTargetArchitecture=$(InstallerTargetArchitecture);UnameMachineRegex=$(_UnameMachineRegex) + + + + + + + + + + diff --git a/src/installer/pkg/sfx/installers/dotnet-hostfxr.proj b/src/installer/pkg/sfx/installers/dotnet-hostfxr.proj index 2c5fa5d8188867..b9eb792a38fade 100644 --- a/src/installer/pkg/sfx/installers/dotnet-hostfxr.proj +++ b/src/installer/pkg/sfx/installers/dotnet-hostfxr.proj @@ -14,7 +14,6 @@ HostFxrSrc true hostfxr - osx_scripts/hostfxr The .NET HostFxr diff --git a/src/installer/pkg/sfx/installers/host.wxs b/src/installer/pkg/sfx/installers/host.wxs index 773246beb70f1a..91ca74c69914f7 100644 --- a/src/installer/pkg/sfx/installers/host.wxs +++ b/src/installer/pkg/sfx/installers/host.wxs @@ -5,7 +5,7 @@ - + - + + + + - + + + + + + + + NOT NON_NATIVE_ARCHITECTURE + + + + + - + @@ -47,6 +62,16 @@ + + + + - - + + + + + NON_NATIVE_ARCHITECTURE + + + \ No newline at end of file diff --git a/src/installer/pkg/sfx/installers/osx_scripts/host/postinstall b/src/installer/pkg/sfx/installers/osx_scripts/host/postinstall index 531cb93fd382fc..eaee94e2798738 100755 --- a/src/installer/pkg/sfx/installers/osx_scripts/host/postinstall +++ b/src/installer/pkg/sfx/installers/osx_scripts/host/postinstall @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # # Licensed to the .NET Foundation under one or more agreements. # The .NET Foundation licenses this file to you under the MIT license. @@ -10,9 +10,21 @@ INSTALL_DESTINATION=$2 # A temporary fix for the permissions issue(s) chmod 755 $INSTALL_DESTINATION/dotnet -# Add the installation directory to the system-wide paths -# But first create the directory if it doesn't exist -mkdir -p /etc/paths.d -echo $INSTALL_DESTINATION | tee /etc/paths.d/dotnet +if [ -e /etc/dotnet/install_location ]; then + # clear out any entries for this architecture if they exist + sed -i old '/^${InstallerTargetArchitecture}=/d' /etc/dotnet/install_location +else + mkdir -p /etc/dotnet +fi + +echo ${InstallerTargetArchitecture}=$INSTALL_DESTINATION | tee -a /etc/dotnet/install_location + +# if we're running on the native architecture +if [[ "$(uname -m)" =~ "${UnameMachineRegex}" ]]; then + # Add the installation directory to the system-wide paths + # But first create the directory if it doesn't exist + mkdir -p /etc/paths.d + echo $INSTALL_DESTINATION | tee /etc/paths.d/dotnet +fi exit 0 diff --git a/src/installer/pkg/sfx/installers/osx_scripts/hostfxr/postinstall b/src/installer/pkg/sfx/installers/osx_scripts/hostfxr/postinstall deleted file mode 100755 index 2d0ceb82786936..00000000000000 --- a/src/installer/pkg/sfx/installers/osx_scripts/hostfxr/postinstall +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -# -# Licensed to the .NET Foundation under one or more agreements. -# The .NET Foundation licenses this file to you under the MIT license. -# - -PACKAGE=$1 -INSTALL_DESTINATION=$2 - -# A temporary fix for the permissions issue(s) -chmod 755 $INSTALL_DESTINATION/dotnet - -# Add the installation directory to the system-wide paths -echo $INSTALL_DESTINATION | tee /etc/paths.d/dotnet - -exit 0 diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs index b448bc79081e8d..93d7590059b310 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs @@ -438,11 +438,21 @@ public IReadOnlyList GetLogClasses(IEnumerable diagnostics = await RunGenerator(@" + using Microsoft.Extensions.Logging; + + namespace MyLibrary; + + internal partial class Logger + { + [LoggerMessage(EventId = 1, Level = LogLevel.Information, Message = ""Hello {Name}!"")] + public static partial void Greeting(ILogger logger, string name); + } + "); + + Assert.Empty(diagnostics); + } + [Theory] [InlineData("false")] [InlineData("true")] diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/openssl.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native/openssl.c index 44fa7705c26ac9..c18eb0ffb7c6bb 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/openssl.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/openssl.c @@ -564,7 +564,7 @@ BIO* CryptoNative_GetX509NameInfo(X509* x509, int32_t nameType, int32_t forIssue if (answer) { BIO* b = BIO_new(BIO_s_mem()); - ASN1_STRING_print_ex(b, answer, 0); + ASN1_STRING_print_ex(b, answer, ASN1_STRFLGS_UTF8_CONVERT); return b; } } @@ -646,7 +646,7 @@ BIO* CryptoNative_GetX509NameInfo(X509* x509, int32_t nameType, int32_t forIssue if (str) { BIO* b = BIO_new(BIO_s_mem()); - ASN1_STRING_print_ex(b, str, 0); + ASN1_STRING_print_ex(b, str, ASN1_STRFLGS_UTF8_CONVERT); sk_GENERAL_NAME_free(altNames); return b; } diff --git a/src/libraries/System.Private.Uri/src/Resources/Strings.resx b/src/libraries/System.Private.Uri/src/Resources/Strings.resx index 92a87cd44ccaa1..9975314e3d97c3 100644 --- a/src/libraries/System.Private.Uri/src/Resources/Strings.resx +++ b/src/libraries/System.Private.Uri/src/Resources/Strings.resx @@ -198,4 +198,7 @@ UriParser's base InitializeAndValidate may only be called once on a single Uri instance and only from an override of InitializeAndValidate. + + GetComponents() may not be used for Path/Query on a Uri instance created with UriCreationOptions.DangerousDisablePathAndQueryCanonicalization. + \ No newline at end of file diff --git a/src/libraries/System.Private.Uri/src/System.Private.Uri.csproj b/src/libraries/System.Private.Uri/src/System.Private.Uri.csproj index 4b63c5b3cb3061..ce5a97d87051bf 100644 --- a/src/libraries/System.Private.Uri/src/System.Private.Uri.csproj +++ b/src/libraries/System.Private.Uri/src/System.Private.Uri.csproj @@ -24,6 +24,7 @@ + diff --git a/src/libraries/System.Private.Uri/src/System/Uri.cs b/src/libraries/System.Private.Uri/src/System/Uri.cs index ad6efdba8540d1..7d1b4d72201a38 100644 --- a/src/libraries/System.Private.Uri/src/System/Uri.cs +++ b/src/libraries/System.Private.Uri/src/System/Uri.cs @@ -121,6 +121,11 @@ internal enum Flags : ulong IriCanonical = 0x78000000000, UnixPath = 0x100000000000, + /// + /// Disables any validation/normalization past the authority. Fragments will always be empty. GetComponents will throw for Path/Query. + /// + DisablePathAndQueryCanonicalization = 0x200000000000, + /// /// Used to ensure that InitializeAndValidate is only called once per Uri instance and only from an override of InitializeAndValidate /// @@ -267,6 +272,8 @@ internal static bool IriParsingStatic(UriParser? syntax) return syntax is null || syntax.InFact(UriSyntaxFlags.AllowIriParsing); } + internal bool DisablePathAndQueryCanonicalization => (_flags & Flags.DisablePathAndQueryCanonicalization) != 0; + internal bool UserDrivenParsing { get @@ -410,6 +417,20 @@ public Uri(string uriString, UriKind uriKind) DebugSetLeftCtor(); } + /// + /// Initializes a new instance of the class with the specified URI and additional . + /// + /// A string that identifies the resource to be represented by the instance. + /// Options that control how the is created and behaves. + public Uri(string uriString, in UriCreationOptions creationOptions) + { + if (uriString is null) + throw new ArgumentNullException(nameof(uriString)); + + CreateThis(uriString, false, UriKind.Absolute, in creationOptions); + DebugSetLeftCtor(); + } + // // Uri(Uri, string) // @@ -1639,6 +1660,9 @@ public override bool Equals([NotNullWhen(true)] object? comparand) // canonicalize the comparand, making comparison possible if (obj is null) { + if (DisablePathAndQueryCanonicalization) + return false; + if (!(comparand is string s)) return false; @@ -1649,6 +1673,9 @@ public override bool Equals([NotNullWhen(true)] object? comparand) return false; } + if (DisablePathAndQueryCanonicalization != obj.DisablePathAndQueryCanonicalization) + return false; + if (ReferenceEquals(OriginalString, obj.OriginalString)) { return true; @@ -2553,7 +2580,7 @@ private unsafe void GetHostViaCustomSyntax() // internal string GetParts(UriComponents uriParts, UriFormat formatAs) { - return GetComponents(uriParts, formatAs); + return InternalGetComponents(uriParts, formatAs); } private string GetEscapedParts(UriComponents uriParts) @@ -3158,9 +3185,6 @@ private unsafe void ParseRemaining() idx = _info.Offset.Path; origIdx = _info.Offset.Path; - //Some uris do not have a query - // When '?' is passed as delimiter, then it's special case - // so both '?' and '#' will work as delimiters if (buildIriStringFromPath) { DebugAssertInCtor(); @@ -3180,6 +3204,45 @@ private unsafe void ParseRemaining() _info.Offset.Path = (ushort)_string.Length; idx = _info.Offset.Path; + } + + // If the user explicitly disabled canonicalization, only figure out the offsets + if (DisablePathAndQueryCanonicalization) + { + if (buildIriStringFromPath) + { + DebugAssertInCtor(); + _string += _originalUnicodeString.Substring(origIdx); + } + + string str = _string; + + if (IsImplicitFile || (syntaxFlags & UriSyntaxFlags.MayHaveQuery) == 0) + { + idx = str.Length; + } + else + { + idx = str.IndexOf('?'); + if (idx == -1) + { + idx = str.Length; + } + } + + _info.Offset.Query = (ushort)idx; + _info.Offset.Fragment = (ushort)str.Length; // There is no fragment in UseRawTarget mode + _info.Offset.End = (ushort)str.Length; + + goto Done; + } + + //Some uris do not have a query + // When '?' is passed as delimiter, then it's special case + // so both '?' and '#' will work as delimiters + if (buildIriStringFromPath) + { + DebugAssertInCtor(); int offset = origIdx; if (IsImplicitFile || ((syntaxFlags & (UriSyntaxFlags.MayHaveQuery | UriSyntaxFlags.MayHaveFragment)) == 0)) diff --git a/src/libraries/System.Private.Uri/src/System/UriCreationOptions.cs b/src/libraries/System.Private.Uri/src/System/UriCreationOptions.cs new file mode 100644 index 00000000000000..2d0ee84d08b218 --- /dev/null +++ b/src/libraries/System.Private.Uri/src/System/UriCreationOptions.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. + +namespace System +{ + /// + /// Options that control how a is created and behaves. + /// + public struct UriCreationOptions + { + private bool _disablePathAndQueryCanonicalization; + + /// + /// Disables validation and normalization of the Path and Query. + /// No transformations of the URI past the Authority will take place. + /// instances created with this option do not support s. + /// may not be used for or . + /// Be aware that disabling canonicalization also means that reserved characters will not be escaped, + /// which may corrupt the HTTP request and makes the application subject to request smuggling. + /// Only set this option if you have ensured that the URI string is already sanitized. + /// + public bool DangerousDisablePathAndQueryCanonicalization + { + readonly get => _disablePathAndQueryCanonicalization; + set => _disablePathAndQueryCanonicalization = value; + } + } +} diff --git a/src/libraries/System.Private.Uri/src/System/UriExt.cs b/src/libraries/System.Private.Uri/src/System/UriExt.cs index a644dfa566699a..070e11a7f987f3 100644 --- a/src/libraries/System.Private.Uri/src/System/UriExt.cs +++ b/src/libraries/System.Private.Uri/src/System/UriExt.cs @@ -13,7 +13,7 @@ public partial class Uri // // All public ctors go through here // - private void CreateThis(string? uri, bool dontEscape, UriKind uriKind) + private void CreateThis(string? uri, bool dontEscape, UriKind uriKind, in UriCreationOptions creationOptions = default) { DebugAssertInCtor(); @@ -31,6 +31,9 @@ private void CreateThis(string? uri, bool dontEscape, UriKind uriKind) if (dontEscape) _flags |= Flags.UserEscaped; + if (creationOptions.DangerousDisablePathAndQueryCanonicalization) + _flags |= Flags.DisablePathAndQueryCanonicalization; + ParsingError err = ParseScheme(_string, ref _flags, ref _syntax!); InitializeUri(err, uriKind, out UriFormatException? e); @@ -259,6 +262,26 @@ public static bool TryCreate([NotNullWhen(true)] string? uriString, UriKind uriK return e is null && result != null; } + /// + /// Creates a new using the specified instance and . + /// + /// The string representation of the . + /// Options that control how the is created and behaves. + /// The constructed . + /// if the was successfully created; otherwise, . + public static bool TryCreate([NotNullWhen(true)] string? uriString, in UriCreationOptions creationOptions, [NotNullWhen(true)] out Uri? result) + { + if (uriString is null) + { + result = null; + return false; + } + UriFormatException? e = null; + result = CreateHelper(uriString, false, UriKind.Absolute, ref e, in creationOptions); + result?.DebugSetLeftCtor(); + return e is null && result != null; + } + public static bool TryCreate(Uri? baseUri, string? relativeUri, [NotNullWhen(true)] out Uri? result) { if (TryCreate(relativeUri, UriKind.RelativeOrAbsolute, out Uri? relativeLink)) @@ -309,6 +332,16 @@ public static bool TryCreate(Uri? baseUri, Uri? relativeUri, [NotNullWhen(true)] } public string GetComponents(UriComponents components, UriFormat format) + { + if (DisablePathAndQueryCanonicalization && (components & (UriComponents.Path | UriComponents.Query)) != 0) + { + throw new InvalidOperationException(SR.net_uri_GetComponentsCalledWhenCanonicalizationDisabled); + } + + return InternalGetComponents(components, format); + } + + private string InternalGetComponents(UriComponents components, UriFormat format) { if (((components & UriComponents.SerializationInfoString) != 0) && components != UriComponents.SerializationInfoString) throw new ArgumentOutOfRangeException(nameof(components), components, SR.net_uri_NotJustSerialization); @@ -590,7 +623,7 @@ private Uri(Flags flags, UriParser? uriParser, string uri) // // a Uri.TryCreate() method goes through here. // - internal static Uri? CreateHelper(string uriString, bool dontEscape, UriKind uriKind, ref UriFormatException? e) + internal static Uri? CreateHelper(string uriString, bool dontEscape, UriKind uriKind, ref UriFormatException? e, in UriCreationOptions creationOptions = default) { // if (!Enum.IsDefined(typeof(UriKind), uriKind)) -- We currently believe that Enum.IsDefined() is too slow // to be used here. @@ -606,6 +639,9 @@ private Uri(Flags flags, UriParser? uriParser, string uri) if (dontEscape) flags |= Flags.UserEscaped; + if (creationOptions.DangerousDisablePathAndQueryCanonicalization) + flags |= Flags.DisablePathAndQueryCanonicalization; + // We won't use User factory for these errors if (err != ParsingError.None) { diff --git a/src/libraries/System.Private.Uri/src/System/UriScheme.cs b/src/libraries/System.Private.Uri/src/System/UriScheme.cs index d9d96ab59ea3cb..7c39a6c44a336e 100644 --- a/src/libraries/System.Private.Uri/src/System/UriScheme.cs +++ b/src/libraries/System.Private.Uri/src/System/UriScheme.cs @@ -146,6 +146,9 @@ protected virtual string GetComponents(Uri uri, UriComponents components, UriFor if (!uri.IsAbsoluteUri) throw new InvalidOperationException(SR.net_uri_NotAbsolute); + if (uri.DisablePathAndQueryCanonicalization && (components & (UriComponents.Path | UriComponents.Query)) != 0) + throw new InvalidOperationException(SR.net_uri_GetComponentsCalledWhenCanonicalizationDisabled); + return uri.GetComponentsHelper(components, format); } diff --git a/src/libraries/System.Private.Uri/tests/FunctionalTests/System.Private.Uri.Functional.Tests.csproj b/src/libraries/System.Private.Uri/tests/FunctionalTests/System.Private.Uri.Functional.Tests.csproj index 8f07bb90af1ffe..21bb5200471aa1 100644 --- a/src/libraries/System.Private.Uri/tests/FunctionalTests/System.Private.Uri.Functional.Tests.csproj +++ b/src/libraries/System.Private.Uri/tests/FunctionalTests/System.Private.Uri.Functional.Tests.csproj @@ -17,6 +17,7 @@ + diff --git a/src/libraries/System.Private.Uri/tests/FunctionalTests/UriCreationOptionsTest.cs b/src/libraries/System.Private.Uri/tests/FunctionalTests/UriCreationOptionsTest.cs new file mode 100644 index 00000000000000..3f7438738f0bf9 --- /dev/null +++ b/src/libraries/System.Private.Uri/tests/FunctionalTests/UriCreationOptionsTest.cs @@ -0,0 +1,294 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace System.PrivateUri.Tests +{ + public class UriCreationOptionsTest + { + [Fact] + public void UriCreationOptions_HasReasonableDefaults() + { + UriCreationOptions options = default; + + Assert.False(options.DangerousDisablePathAndQueryCanonicalization); + } + + [Fact] + public void UriCreationOptions_StoresCorrectValues() + { + var options = new UriCreationOptions { DangerousDisablePathAndQueryCanonicalization = true }; + Assert.True(options.DangerousDisablePathAndQueryCanonicalization); + + options = new UriCreationOptions { DangerousDisablePathAndQueryCanonicalization = false }; + Assert.False(options.DangerousDisablePathAndQueryCanonicalization); + } + + public static IEnumerable DisableCanonicalization_TestData() + { + var schemes = new string[] { "http", "hTTp", " http", "https" }; + var hosts = new string[] { "foo", "f\u00F6\u00F6.com" }; + var ports = new string[] { ":80", ":443", ":0123", ":", "" }; + + var pathAndQueries = new string[] + { + "", + " ", + "a b", + "a%20b", + "?a b", + "?a%20b", + "foo/./", + "foo/../", + "//\\//", + "%41", + "A?%41=%42", + "?%41=%42", + "? ", + }; + + var fragments = new string[] { "", "#", "#/foo ? %20%41/..//\\a" }; + var unicodeInPathModes = new int[] { 0, 1, 2, 3 }; + var pathDelimiters = new string[] { "", "/" }; + + // Get various combinations of paths with unicode characters and delimiters + string[] rawTargets = pathAndQueries + .SelectMany(pq => fragments.Select(fragment => pq + fragment)) + .SelectMany(pqf => unicodeInPathModes.Select(unicodeMode => unicodeMode switch + { + 0 => pqf, + 1 => "\u00F6" + pqf, + 2 => pqf + "\u00F6", + _ => pqf.Insert(pqf.Length / 2, "\u00F6") + })) + .ToHashSet() + .SelectMany(pqf => pathDelimiters.Select(delimiter => delimiter + pqf)) + .Where(target => target.StartsWith('/') || target.StartsWith('?')) // Can't see where the authority ends and the path starts otherwise + .ToArray(); + + foreach (string scheme in schemes) + { + foreach (string host in hosts) + { + foreach (string port in ports) + { + foreach (string rawTarget in rawTargets) + { + string uriString = $"{scheme}://{host}{port}{rawTarget}"; + + int expectedPort = port.Length > 1 ? int.Parse(port.AsSpan(1)) : new Uri($"{scheme}://foo").Port; + + string expectedQuery = rawTarget.Contains('?') ? rawTarget.Substring(rawTarget.IndexOf('?')) : ""; + + string expectedPath = rawTarget.Substring(0, rawTarget.Length - expectedQuery.Length); + + yield return new object[] { uriString, host, expectedPort, expectedPath, expectedQuery }; + } + } + } + } + } + + [Theory] + [MemberData(nameof(DisableCanonicalization_TestData))] + public void DisableCanonicalization_IsRespected(string uriString, string expectedHost, int expectedPort, string expectedPath, string expectedQuery) + { + var options = new UriCreationOptions { DangerousDisablePathAndQueryCanonicalization = true }; + + var uri = new Uri(uriString, options); + DoAsserts(uri); + + Assert.True(Uri.TryCreate(uriString, options, out uri)); + DoAsserts(uri); + + void DoAsserts(Uri uri) + { + Assert.Equal(new Uri($"http://{expectedHost}").Host, uri.Host); + Assert.Equal(new Uri($"http://{expectedHost}").IdnHost, uri.IdnHost); + + Assert.Equal(expectedPort, uri.Port); + + Assert.Same(uri.AbsolutePath, uri.AbsolutePath); + Assert.Equal(expectedPath, uri.AbsolutePath); + + Assert.Same(uri.Query, uri.Query); + Assert.Equal(expectedQuery, uri.Query); + + string expectedPathAndQuery = expectedPath + expectedQuery; + Assert.Same(uri.PathAndQuery, uri.PathAndQuery); + Assert.Equal(expectedPathAndQuery, uri.PathAndQuery); + + Assert.Same(uri.Fragment, uri.Fragment); + Assert.Empty(uri.Fragment); // Fragment is always empty if DisableCanonicalization is set + } + } + + [Fact] + public void DisableCanonicalization_OnlyEqualToUrisWithMatchingFlag() + { + const string AbsoluteUri = "http://host"; + const string Path = "/foo"; + + var absolute = new Uri(AbsoluteUri + Path, new UriCreationOptions { DangerousDisablePathAndQueryCanonicalization = false }); + var absoluteRaw = new Uri(AbsoluteUri + Path, new UriCreationOptions { DangerousDisablePathAndQueryCanonicalization = true }); + NotEqual(absolute, absoluteRaw); + Equal(absolute, absolute); + Equal(absoluteRaw, absoluteRaw); + + var absoluteRawCopy = new Uri(AbsoluteUri + Path, new UriCreationOptions { DangerousDisablePathAndQueryCanonicalization = true }); + Equal(absoluteRaw, absoluteRawCopy); + + var absoluteRawDifferentPath = new Uri(AbsoluteUri + "/bar", new UriCreationOptions { DangerousDisablePathAndQueryCanonicalization = true }); + NotEqual(absoluteRaw, absoluteRawDifferentPath); + + var absoluteRawSameAuthority = new Uri(AbsoluteUri + ":80" + Path, new UriCreationOptions { DangerousDisablePathAndQueryCanonicalization = true }); + Equal(absoluteRaw, absoluteRawSameAuthority); + + static void Equal(Uri left, Uri right) + { + Assert.True(left.Equals(right)); + Assert.True(right.Equals(left)); + Assert.Equal(left.GetHashCode(), right.GetHashCode()); + } + + static void NotEqual(Uri left, Uri right) + { + Assert.False(left.Equals(right)); + Assert.False(right.Equals(left)); + } + } + + private const string FilePathRawData = "//\\A%41 %20\u00F6/.././%5C%2F#%42?%43#%44"; + + public static IEnumerable ImplicitFilePaths_TestData() + { + yield return Entry("C:/"); + yield return Entry("C|/"); + + yield return Entry(@"//foo"); + yield return Entry(@"\/foo"); + yield return Entry(@"/\foo"); + yield return Entry(@"\\foo"); + + if (!PlatformDetection.IsWindows) + { + yield return Entry("/foo"); + } + + static object[] Entry(string filePath) => new object[] { $"{filePath}/{FilePathRawData}" }; + } + + [Theory] + [MemberData(nameof(ImplicitFilePaths_TestData))] + public void DisableCanonicalization_WorksWithFileUris(string implicitFilePath) + { + var options = new UriCreationOptions { DangerousDisablePathAndQueryCanonicalization = true }; + + var uri = new Uri(implicitFilePath, options); + DoAsserts(uri); + + Assert.True(Uri.TryCreate(implicitFilePath, options, out uri)); + DoAsserts(uri); + + static void DoAsserts(Uri uri) + { + Assert.True(uri.IsAbsoluteUri); + Assert.True(uri.IsFile); + Assert.Contains(FilePathRawData, uri.AbsolutePath); + Assert.Contains(FilePathRawData, uri.AbsoluteUri); + Assert.Contains(FilePathRawData, uri.ToString()); + } + } + + [Theory] + [InlineData("http")] + [InlineData("https")] + [InlineData("ftp")] + [InlineData("file")] + [InlineData("custom-unknown")] + [InlineData("custom-registered")] + public void DisableCanonicalization_WorksWithDifferentSchemes(string scheme) + { + if (scheme == "custom-registered") + { + scheme += "DisableCanonicalization"; + UriParser.Register(new HttpStyleUriParser(), scheme, defaultPort: Random.Shared.Next(-1, 65536)); + } + + string uriString = $"{scheme}://host/p%41th?a=%42#fragm%45nt"; + var options = new UriCreationOptions { DangerousDisablePathAndQueryCanonicalization = true }; + + var referenceUri = new Uri(uriString); + + var uri = new Uri(uriString, options); + DoAsserts(uri); + + Assert.True(Uri.TryCreate(uriString, options, out uri)); + DoAsserts(uri); + + void DoAsserts(Uri uri) + { + Assert.Same(referenceUri.Scheme, uri.Scheme); + Assert.Equal(referenceUri.Host, uri.Host); + Assert.Equal(referenceUri.IdnHost, uri.IdnHost); + Assert.Equal(referenceUri.Authority, uri.Authority); + Assert.Equal(referenceUri.Port, uri.Port); + Assert.Equal(referenceUri.IsDefaultPort, uri.IsDefaultPort); + + string referencePath = "/pAth"; + string referenceQuery = "?a=B"; + string path = "/p%41th"; + string query = "?a=%42#fragm%45nt"; + + if (scheme == "ftp") // No query + { + referencePath += referenceQuery.Replace("?", "%3F"); + path += query; + + referenceQuery = string.Empty; + query = string.Empty; + } + + Assert.Equal(referencePath, referenceUri.AbsolutePath); + Assert.Equal(path, uri.AbsolutePath); + + Assert.Equal(referenceQuery, referenceUri.Query); + Assert.Equal(query, uri.Query); + + Assert.Equal(referencePath + referenceQuery, referenceUri.PathAndQuery); + Assert.Equal(path + query, uri.PathAndQuery); + + Assert.Equal("#fragmEnt", referenceUri.Fragment); + Assert.Empty(uri.Fragment); + + _ = referenceUri.GetComponents(UriComponents.AbsoluteUri, UriFormat.UriEscaped); + Assert.Throws(() => uri.GetComponents(UriComponents.Path, UriFormat.UriEscaped)); + } + } + + [Theory] + [InlineData(UriFormat.UriEscaped)] + [InlineData(UriFormat.Unescaped)] + [InlineData(UriFormat.SafeUnescaped)] + public void DisableCanonicalization_GetComponentsThrowsForPathAndQuery(UriFormat format) + { + var uri = new Uri("http://host/foo?bar=abc#fragment", new UriCreationOptions { DangerousDisablePathAndQueryCanonicalization = true }); + + Assert.Equal("http", uri.GetComponents(UriComponents.Scheme, format)); + Assert.Equal("host", uri.GetComponents(UriComponents.Host, format)); + Assert.Equal("80", uri.GetComponents(UriComponents.StrongPort, format)); + Assert.Empty(uri.GetComponents(UriComponents.Fragment, format)); + + Assert.Throws(() => uri.GetComponents(UriComponents.Path, format)); + Assert.Throws(() => uri.GetComponents(UriComponents.Query, format)); + Assert.Throws(() => uri.GetComponents(UriComponents.PathAndQuery, format)); + Assert.Throws(() => uri.GetComponents(UriComponents.AbsoluteUri, format)); + } + + + private sealed class CustomUriParser : UriParser { } + } +} diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index b617e4dd85c84b..caebddce443148 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -7716,6 +7716,7 @@ protected Uri(System.Runtime.Serialization.SerializationInfo serializationInfo, public Uri(string uriString) { } [System.ObsoleteAttribute("This constructor has been deprecated; the dontEscape parameter is always false. Use Uri(string) instead.")] public Uri(string uriString, bool dontEscape) { } + public Uri(string uriString, in System.UriCreationOptions creationOptions) { } public Uri(string uriString, System.UriKind uriKind) { } public Uri(System.Uri baseUri, string? relativeUri) { } [System.ObsoleteAttribute("This constructor has been deprecated; the dontEscape parameter is always false. Use Uri(Uri, string) instead.")] @@ -7785,6 +7786,7 @@ protected void GetObjectData(System.Runtime.Serialization.SerializationInfo seri protected virtual void Parse() { } void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) { } public override string ToString() { throw null; } + public static bool TryCreate([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? uriString, in System.UriCreationOptions creationOptions, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Uri? result) { throw null; } public static bool TryCreate([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? uriString, System.UriKind uriKind, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Uri? result) { throw null; } public static bool TryCreate(System.Uri? baseUri, string? relativeUri, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Uri? result) { throw null; } public static bool TryCreate(System.Uri? baseUri, System.Uri? relativeUri, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Uri? result) { throw null; } @@ -7821,6 +7823,10 @@ public UriBuilder(System.Uri uri) { } public override int GetHashCode() { throw null; } public override string ToString() { throw null; } } + public partial struct UriCreationOptions + { + public bool DangerousDisablePathAndQueryCanonicalization { readonly get { throw null; } set { } } + } [System.FlagsAttribute] public enum UriComponents { diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/PropsTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/PropsTests.cs index 4a78718b91be33..a2759f5126dd46 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/PropsTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/PropsTests.cs @@ -389,6 +389,20 @@ public static void TestGetNameInfo() } } + [Theory] + [InlineData(true)] + [InlineData(false)] + public static void GetNameInfo_HandlesUtf8Encoding(bool issuer) + { + using (X509Certificate2 c = new X509Certificate2(TestData.CertificateWithUtf8)) + { + // Russian word for "potato" in Cyrillic, kartoshka. + string expected = "\u043A\u0430\u0440\u0442\u043E\u0448\u043A\u0430"; + string cn = c.GetNameInfo(X509NameType.SimpleName, issuer); + Assert.Equal(expected, cn); + } + } + [Fact] public static void ComplexGetNameInfo_SimpleName_Cert() { diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestData.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestData.cs index a738e45375ecab..df98b93190cf3c 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestData.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestData.cs @@ -2463,6 +2463,33 @@ internal struct ECDsaCngKeyValues "028bedeefab9a2be80466fe278fdc50db1b9530e1796b23271b4df2cddd9" + "4769c8a21a8f66c6d4bc181713").HexToByteArray(); + internal static byte[] CertificateWithUtf8 = ( + "30820315308201fda003020102020900e48b784d93645921300d06092a864886" + + "f70d01010b0500301b3119301706035504030c10d0bad0b0d180d182d0bed188" + + "d0bad0b0301e170d3231303931343230353635365a170d333230383237323035" + + "3635365a301b3119301706035504030c10d0bad0b0d180d182d0bed188d0bad0" + + "b030820122300d06092a864886f70d01010105000382010f003082010a028201" + + "0100c394711b3839a612aaadafde855ccc3479bc1dbe253a29e508cc46464efb" + + "ab09b16b85fb67e4be0ab8b09fa6ff73cb3ec5d8f8b7d2869a79c414d025a43e" + + "4c158c9711638dbfb070ef92b3633037d5e633a870ed024ab9017c97e26f02de" + + "2273dd92c0837a95ff12229981cd661b140afd841671d3397fd44aea05878dd0" + + "9362bd2de8da17225dd3caf0181aadf2c9e13faefbbce3ce80ef355dcc15c1b9" + + "c6e86bb1cddf32b3113e7a72ad3799cc67f64ea34c0518c22727972802084801" + + "e6565b2323e87fd20019a9db151e87b2b1db004583e15dd64c5393439a0ba5ed" + + "3b9fd262c8d8bf0263dcba092ca5591d4d34f990daa9ee41811cec6229f2121b" + + "582f0203010001a35c305a301b0603551d11041430128210786e2d2d38306161" + + "326162746c6f3363301d0603551d250416301406082b0601050507030106082b" + + "06010505070302300f0603551d130101ff040530030101ff300b0603551d0f04" + + "040302028c300d06092a864886f70d01010b050003820101001c0512d3d99267" + + "4ca3a1fdf942052064ffbda824aaeff07d9869eefa86e592102afca0dba4e267" + + "e54753552fc23da97b18f739ea437794aac5bb2ae7413ae54ce921b4b96fe420" + + "f3fd86f922adcab202589823c4095fc6e7904e61be1716faec7e09ce2d1bf431" + + "fa9fc2d7284689d2c940f7d027e16b9c3305f9cd6d5dc6bfee9089d85d342f5b" + + "d07920c51899288674a1732708a7e3a39fb11c152417d50a3bb9b09975852baa" + + "39767c289baea9330902ea38388932964c410cd22049415712223fb5710a21ef" + + "153ac22391a1603547ffb3f9b328c59e59b5d64568b9c451df233fe9a581116f" + + "05be8c67b92bc7df9984f30535ad53817cb4abcd77cb521856").HexToByteArray(); + internal static byte[] ConcatenatedPemFile = ByteUtils.AsciiBytes( @"-----BEGIN CERTIFICATE----- MIIFcDCCBFigAwIBAgIQB6krbZc11OZ5l2/FnU3CpTANBgkqhkiG9w0BAQsFADBG diff --git a/src/libraries/System.Text.Json/Common/JsonSourceGenerationOptionsAttribute.cs b/src/libraries/System.Text.Json/Common/JsonSourceGenerationOptionsAttribute.cs index 84ffc3d5414b34..45f11a41063ccb 100644 --- a/src/libraries/System.Text.Json/Common/JsonSourceGenerationOptionsAttribute.cs +++ b/src/libraries/System.Text.Json/Common/JsonSourceGenerationOptionsAttribute.cs @@ -30,11 +30,6 @@ sealed class JsonSourceGenerationOptionsAttribute : JsonAttribute /// public bool IgnoreReadOnlyProperties { get; set; } - /// - /// Specifies whether to ignore custom converters provided at run time. - /// - public bool IgnoreRuntimeCustomConverters { get; set; } - /// /// Specifies whether to include fields for serialization and deserialization. /// diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs index 131e08d0d0c2bf..217aed6f271c40 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs @@ -23,13 +23,18 @@ private sealed partial class Emitter private const string CtorParamInitMethodNameSuffix = "CtorParamInit"; private const string DefaultOptionsStaticVarName = "s_defaultOptions"; private const string DefaultContextBackingStaticVarName = "s_defaultContext"; + private const string ElementInfoPropName = "ElementInfo"; internal const string GetConverterFromFactoryMethodName = "GetConverterFromFactory"; + private const string InfoVarName = "info"; private const string JsonSerializerContextName = "JsonSerializerContext"; internal const string JsonContextVarName = "jsonContext"; + private const string KeyInfoPropName = "KeyInfo"; + private const string NumberHandlingPropName = "NumberHandling"; + private const string ObjectCreatorPropName = "ObjectCreator"; private const string OptionsInstanceVariableName = "Options"; private const string PropInitMethodNameSuffix = "PropInit"; private const string RuntimeCustomConverterFetchingMethodName = "GetRuntimeProvidedCustomConverter"; - private const string SerializeMethodNameSuffix = "Serialize"; + private const string SerializeHandlerPropName = "SerializeHandler"; private const string ValueVarName = "value"; private const string WriterVarName = "writer"; @@ -55,6 +60,7 @@ private sealed partial class Emitter private const string Utf8JsonWriterTypeRef = "global::System.Text.Json.Utf8JsonWriter"; private const string JsonConverterTypeRef = "global::System.Text.Json.Serialization.JsonConverter"; private const string JsonConverterFactoryTypeRef = "global::System.Text.Json.Serialization.JsonConverterFactory"; + private const string JsonCollectionInfoValuesTypeRef = "global::System.Text.Json.Serialization.Metadata.JsonCollectionInfoValues"; private const string JsonIgnoreConditionTypeRef = "global::System.Text.Json.Serialization.JsonIgnoreCondition"; private const string JsonNumberHandlingTypeRef = "global::System.Text.Json.Serialization.JsonNumberHandling"; private const string JsonSerializerContextTypeRef = "global::System.Text.Json.Serialization.JsonSerializerContext"; @@ -62,6 +68,7 @@ private sealed partial class Emitter private const string JsonObjectInfoValuesTypeRef = "global::System.Text.Json.Serialization.Metadata.JsonObjectInfoValues"; private const string JsonParameterInfoValuesTypeRef = "global::System.Text.Json.Serialization.Metadata.JsonParameterInfoValues"; private const string JsonPropertyInfoTypeRef = "global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo"; + private const string JsonPropertyInfoValuesTypeRef = "global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues"; private const string JsonTypeInfoTypeRef = "global::System.Text.Json.Serialization.Metadata.JsonTypeInfo"; private const string NotSupportedExceptionTypeRef = "global::System.NotSupportedException"; @@ -416,37 +423,37 @@ private string GenerateForCollection(TypeGenerationSpec typeGenerationSpec) string numberHandlingArg = $"{GetNumberHandlingAsStr(typeGenerationSpec.NumberHandling)}"; - string serializeFuncNamedArg; + string serializeHandlerValue; - string? serializeFuncSource; + string? serializeHandlerSource; if (!typeGenerationSpec.GenerateSerializationLogic) { - serializeFuncSource = null; - serializeFuncNamedArg = "serializeFunc: null"; + serializeHandlerSource = null; + serializeHandlerValue = "null"; } else { - serializeFuncSource = typeGenerationSpec.ClassType == ClassType.Enumerable + serializeHandlerSource = typeGenerationSpec.ClassType == ClassType.Enumerable ? GenerateFastPathFuncForEnumerable(typeGenerationSpec) : GenerateFastPathFuncForDictionary(typeGenerationSpec); - serializeFuncNamedArg = $"serializeFunc: {typeGenerationSpec.FastPathSerializeMethodName}"; + serializeHandlerValue = $"{typeGenerationSpec.TypeInfoPropertyName}{SerializeHandlerPropName}"; } CollectionType collectionType = typeGenerationSpec.CollectionType; string typeRef = typeGenerationSpec.TypeRef; - string createObjectFuncArg; + string objectCreatorValue; if (typeGenerationSpec.RuntimeTypeRef != null) { - createObjectFuncArg = $"createObjectFunc: () => new {typeGenerationSpec.RuntimeTypeRef}()"; + objectCreatorValue = $"() => new {typeGenerationSpec.RuntimeTypeRef}()"; } else { - createObjectFuncArg = typeGenerationSpec.ConstructionStrategy == ObjectConstructionStrategy.ParameterlessConstructor - ? $"createObjectFunc: () => new {typeRef}()" - : "createObjectFunc: null"; + objectCreatorValue = typeGenerationSpec.ConstructionStrategy == ObjectConstructionStrategy.ParameterlessConstructor + ? $"() => new {typeRef}()" + : "null"; } string collectionInfoCreationPrefix = collectionType switch @@ -455,15 +462,15 @@ private string GenerateForCollection(TypeGenerationSpec typeGenerationSpec) CollectionType.ICollectionOfT => $"{JsonMetadataServicesTypeRef}.CreateICollectionInfo<", CollectionType.StackOfT => $"{JsonMetadataServicesTypeRef}.CreateStackInfo<", CollectionType.QueueOfT => $"{JsonMetadataServicesTypeRef}.CreateQueueInfo<", - CollectionType.Stack => $"{JsonMetadataServicesTypeRef}.CreateStackOrQueueInfo<", - CollectionType.Queue => $"{JsonMetadataServicesTypeRef}.CreateStackOrQueueInfo<", + CollectionType.Stack => $"{JsonMetadataServicesTypeRef}.CreateStackInfo<", + CollectionType.Queue => $"{JsonMetadataServicesTypeRef}.CreateQueueInfo<", CollectionType.IEnumerableOfT => $"{JsonMetadataServicesTypeRef}.CreateIEnumerableInfo<", CollectionType.IDictionaryOfTKeyTValue => $"{JsonMetadataServicesTypeRef}.CreateIDictionaryInfo<", _ => $"{JsonMetadataServicesTypeRef}.Create{collectionType}Info<" }; - string dictInfoCreationPrefix = $"{collectionInfoCreationPrefix}{typeRef}, {keyTypeCompilableName!}, {valueTypeCompilableName}>({OptionsInstanceVariableName}, {createObjectFuncArg}, {keyTypeMetadataPropertyName!}, {valueTypeMetadataPropertyName}, {numberHandlingArg}, {serializeFuncNamedArg}"; - string enumerableInfoCreationPrefix = $"{collectionInfoCreationPrefix}{typeRef}, {valueTypeCompilableName}>({OptionsInstanceVariableName}, {createObjectFuncArg}, {valueTypeMetadataPropertyName}, {numberHandlingArg}, {serializeFuncNamedArg}"; + string dictInfoCreationPrefix = $"{collectionInfoCreationPrefix}{typeRef}, {keyTypeCompilableName!}, {valueTypeCompilableName}>({OptionsInstanceVariableName}, {InfoVarName}"; + string enumerableInfoCreationPrefix = $"{collectionInfoCreationPrefix}{typeRef}, {valueTypeCompilableName}>({OptionsInstanceVariableName}, {InfoVarName}"; string immutableCollectionCreationSuffix = $"createRangeFunc: {typeGenerationSpec.ImmutableCollectionBuilderName}"; string collectionTypeInfoValue; @@ -471,23 +478,23 @@ private string GenerateForCollection(TypeGenerationSpec typeGenerationSpec) switch (collectionType) { case CollectionType.Array: - collectionTypeInfoValue = $"{collectionInfoCreationPrefix}{valueTypeCompilableName}>({OptionsInstanceVariableName}, {valueTypeMetadataPropertyName}, {numberHandlingArg}, {serializeFuncNamedArg})"; + collectionTypeInfoValue = $"{collectionInfoCreationPrefix}{valueTypeCompilableName}>({OptionsInstanceVariableName}, {InfoVarName})"; break; case CollectionType.IEnumerable: case CollectionType.IList: - collectionTypeInfoValue = $"{collectionInfoCreationPrefix}{typeRef}>({OptionsInstanceVariableName}, {createObjectFuncArg}, {valueTypeMetadataPropertyName}, {numberHandlingArg}, {serializeFuncNamedArg})"; + collectionTypeInfoValue = $"{collectionInfoCreationPrefix}{typeRef}>({OptionsInstanceVariableName}, {InfoVarName})"; break; case CollectionType.Stack: case CollectionType.Queue: string addMethod = collectionType == CollectionType.Stack ? "Push" : "Enqueue"; string addFuncNamedArg = $"addFunc: (collection, {ValueVarName}) => collection.{addMethod}({ValueVarName})"; - collectionTypeInfoValue = $"{collectionInfoCreationPrefix}{typeRef}>({OptionsInstanceVariableName}, {createObjectFuncArg}, {valueTypeMetadataPropertyName}, {numberHandlingArg}, {serializeFuncNamedArg}, {addFuncNamedArg})"; + collectionTypeInfoValue = $"{collectionInfoCreationPrefix}{typeRef}>({OptionsInstanceVariableName}, {InfoVarName}, {addFuncNamedArg})"; break; case CollectionType.ImmutableEnumerable: collectionTypeInfoValue = $"{enumerableInfoCreationPrefix}, {immutableCollectionCreationSuffix})"; break; case CollectionType.IDictionary: - collectionTypeInfoValue = $"{collectionInfoCreationPrefix}{typeRef}>({OptionsInstanceVariableName}, {createObjectFuncArg}, {keyTypeMetadataPropertyName!}, {valueTypeMetadataPropertyName}, {numberHandlingArg}, {serializeFuncNamedArg})"; + collectionTypeInfoValue = $"{collectionInfoCreationPrefix}{typeRef}>({OptionsInstanceVariableName}, {InfoVarName})"; break; case CollectionType.Dictionary: case CollectionType.IDictionaryOfTKeyTValue: @@ -502,9 +509,19 @@ private string GenerateForCollection(TypeGenerationSpec typeGenerationSpec) break; } - string metadataInitSource = @$"_{typeGenerationSpec.TypeInfoPropertyName} = {collectionTypeInfoValue};"; + string metadataInitSource = @$"{JsonCollectionInfoValuesTypeRef}<{typeRef}> {InfoVarName} = new {JsonCollectionInfoValuesTypeRef}<{typeRef}>() + {{ + {ObjectCreatorPropName} = {objectCreatorValue}, + {KeyInfoPropName} = {keyTypeMetadataPropertyName!}, + {ElementInfoPropName} = {valueTypeMetadataPropertyName}, + {NumberHandlingPropName} = {numberHandlingArg}, + {SerializeHandlerPropName} = {serializeHandlerValue} + }}; + + _{typeGenerationSpec.TypeInfoPropertyName} = {collectionTypeInfoValue}; +"; - return GenerateForType(typeGenerationSpec, metadataInitSource, serializeFuncSource); + return GenerateForType(typeGenerationSpec, metadataInitSource, serializeHandlerSource); } private string GenerateFastPathFuncForEnumerable(TypeGenerationSpec typeGenerationSpec) @@ -555,7 +572,7 @@ private string GenerateFastPathFuncForEnumerable(TypeGenerationSpec typeGenerati {WriterVarName}.WriteEndArray();"; return GenerateFastPathFuncForType( - typeGenerationSpec.FastPathSerializeMethodName, + $"{typeGenerationSpec.TypeInfoPropertyName}{SerializeHandlerPropName}", typeGenerationSpec.TypeRef, serializationLogic, typeGenerationSpec.CanBeNull); @@ -599,7 +616,7 @@ private string GenerateFastPathFuncForDictionary(TypeGenerationSpec typeGenerati {WriterVarName}.WriteEndObject();"; return GenerateFastPathFuncForType( - typeGenerationSpec.FastPathSerializeMethodName, + $"{typeGenerationSpec.TypeInfoPropertyName}{SerializeHandlerPropName}", typeGenerationSpec.TypeRef, serializationLogic, typeGenerationSpec.CanBeNull); @@ -641,23 +658,23 @@ private string GenerateForObject(TypeGenerationSpec typeMetadata) if (typeMetadata.GenerateSerializationLogic) { serializeFuncSource = GenerateFastPathFuncForObject(typeMetadata); - serializeMethodName = $"{typeFriendlyName}{SerializeMethodNameSuffix}"; + serializeMethodName = $"{typeFriendlyName}{SerializeHandlerPropName}"; } const string ObjectInfoVarName = "objectInfo"; string genericArg = typeMetadata.TypeRef; string objectInfoInitSource = $@"{JsonObjectInfoValuesTypeRef}<{genericArg}> {ObjectInfoVarName} = new {JsonObjectInfoValuesTypeRef}<{genericArg}>() - {{ - ObjectCreator = {creatorInvocation}, - ObjectWithParameterizedConstructorCreator = {parameterizedCreatorInvocation}, - PropertyMetadataInitializer = {propInitMethodName}, - ConstructorParameterMetadataInitializer = {ctorParamMetadataInitMethodName}, - NumberHandling = {GetNumberHandlingAsStr(typeMetadata.NumberHandling)}, - SerializeHandler = {serializeMethodName} - }}; + {{ + {ObjectCreatorPropName} = {creatorInvocation}, + ObjectWithParameterizedConstructorCreator = {parameterizedCreatorInvocation}, + PropertyMetadataInitializer = {propInitMethodName}, + ConstructorParameterMetadataInitializer = {ctorParamMetadataInitMethodName}, + {NumberHandlingPropName} = {GetNumberHandlingAsStr(typeMetadata.NumberHandling)}, + {SerializeHandlerPropName} = {serializeMethodName} + }}; - _{typeFriendlyName} = {JsonMetadataServicesTypeRef}.CreateObjectInfo<{typeMetadata.TypeRef}>({OptionsInstanceVariableName}, {ObjectInfoVarName});"; + _{typeFriendlyName} = {JsonMetadataServicesTypeRef}.CreateObjectInfo<{typeMetadata.TypeRef}>({OptionsInstanceVariableName}, {ObjectInfoVarName});"; string additionalSource = @$"{propMetadataInitFuncSource}{serializeFuncSource}{ctorParamMetadataInitFuncSource}"; @@ -705,63 +722,66 @@ private string GeneratePropMetadataInitFunc(TypeGenerationSpec typeGenerationSpe ? "null" : $"{JsonContextVarName}.{memberTypeMetadata.TypeInfoPropertyName}"; - string typeTypeInfoNamedArg = $"propertyTypeInfo: {memberTypeFriendlyName}"; - - string jsonPropertyNameNamedArg = memberMetadata.JsonPropertyName != null - ? @$"jsonPropertyName: ""{memberMetadata.JsonPropertyName}""" - : "jsonPropertyName: null"; + string jsonPropertyNameValue = memberMetadata.JsonPropertyName != null + ? @$"""{memberMetadata.JsonPropertyName}""" + : "null"; - string getterNamedArg = memberMetadata switch + string getterValue = memberMetadata switch { - { DefaultIgnoreCondition: JsonIgnoreCondition.Always } => "getter: null", - { CanUseGetter: true } => $"getter: static (obj) => (({declaringTypeCompilableName})obj).{clrPropertyName}", + { DefaultIgnoreCondition: JsonIgnoreCondition.Always } => "null", + { CanUseGetter: true } => $"static (obj) => (({declaringTypeCompilableName})obj).{clrPropertyName}", { CanUseGetter: false, HasJsonInclude: true } - => @$"getter: static (obj) => throw new {InvalidOperationExceptionTypeRef}(""{string.Format(ExceptionMessages.InaccessibleJsonIncludePropertiesNotSupported, typeGenerationSpec.Type.Name, memberMetadata.ClrName)}"")", - _ => "getter: null" + => @$"static (obj) => throw new {InvalidOperationExceptionTypeRef}(""{string.Format(ExceptionMessages.InaccessibleJsonIncludePropertiesNotSupported, typeGenerationSpec.Type.Name, memberMetadata.ClrName)}"")", + _ => "null" }; - string setterNamedArg = memberMetadata switch + string setterValue = memberMetadata switch { - { DefaultIgnoreCondition: JsonIgnoreCondition.Always } => "setter: null", + { DefaultIgnoreCondition: JsonIgnoreCondition.Always } => "null", { CanUseSetter: true, IsInitOnlySetter: true } - => @$"setter: static (obj, value) => throw new {InvalidOperationExceptionTypeRef}(""{ExceptionMessages.InitOnlyPropertyDeserializationNotSupported}"")", + => @$"static (obj, value) => throw new {InvalidOperationExceptionTypeRef}(""{ExceptionMessages.InitOnlyPropertyDeserializationNotSupported}"")", { CanUseSetter: true } when typeGenerationSpec.IsValueType - => $@"setter: static (obj, value) => {UnsafeTypeRef}.Unbox<{declaringTypeCompilableName}>(obj).{clrPropertyName} = value!", + => $@"static (obj, value) => {UnsafeTypeRef}.Unbox<{declaringTypeCompilableName}>(obj).{clrPropertyName} = value!", { CanUseSetter: true } - => @$"setter: static (obj, value) => (({declaringTypeCompilableName})obj).{clrPropertyName} = value!", + => @$"static (obj, value) => (({declaringTypeCompilableName})obj).{clrPropertyName} = value!", { CanUseSetter: false, HasJsonInclude: true } - => @$"setter: static (obj, value) => throw new {InvalidOperationExceptionTypeRef}(""{string.Format(ExceptionMessages.InaccessibleJsonIncludePropertiesNotSupported, typeGenerationSpec.Type.Name, memberMetadata.ClrName)}"")", - _ => "setter: null", + => @$"static (obj, value) => throw new {InvalidOperationExceptionTypeRef}(""{string.Format(ExceptionMessages.InaccessibleJsonIncludePropertiesNotSupported, typeGenerationSpec.Type.Name, memberMetadata.ClrName)}"")", + _ => "null", }; JsonIgnoreCondition? ignoreCondition = memberMetadata.DefaultIgnoreCondition; string ignoreConditionNamedArg = ignoreCondition.HasValue - ? $"ignoreCondition: {JsonIgnoreConditionTypeRef}.{ignoreCondition.Value}" - : "ignoreCondition: null"; + ? $"{JsonIgnoreConditionTypeRef}.{ignoreCondition.Value}" + : "null"; - string converterNamedArg = memberMetadata.ConverterInstantiationLogic == null - ? "converter: null" - : $"converter: {memberMetadata.ConverterInstantiationLogic}"; + string converterValue = memberMetadata.ConverterInstantiationLogic == null + ? "null" + : $"{memberMetadata.ConverterInstantiationLogic}"; string memberTypeCompilableName = memberTypeMetadata.TypeRef; + string infoVarName = $"{InfoVarName}{i}"; + sb.Append($@" - {PropVarName}[{i}] = {JsonMetadataServicesTypeRef}.CreatePropertyInfo<{memberTypeCompilableName}>( - options, - isProperty: {ToCSharpKeyword(memberMetadata.IsProperty)}, - isPublic: {ToCSharpKeyword(memberMetadata.IsPublic)}, - isVirtual: {ToCSharpKeyword(memberMetadata.IsVirtual)}, - declaringType: typeof({memberMetadata.DeclaringTypeRef}), - {typeTypeInfoNamedArg}, - {converterNamedArg}, - {getterNamedArg}, - {setterNamedArg}, - {ignoreConditionNamedArg}, - hasJsonInclude: {ToCSharpKeyword(memberMetadata.HasJsonInclude)}, - isExtensionData: {ToCSharpKeyword(memberMetadata.IsExtensionData)}, - numberHandling: {GetNumberHandlingAsStr(memberMetadata.NumberHandling)}, - propertyName: ""{clrPropertyName}"", - {jsonPropertyNameNamedArg}); + {JsonPropertyInfoValuesTypeRef}<{memberTypeCompilableName}> {infoVarName} = new {JsonPropertyInfoValuesTypeRef}<{memberTypeCompilableName}>() + {{ + IsProperty = {ToCSharpKeyword(memberMetadata.IsProperty)}, + IsPublic = {ToCSharpKeyword(memberMetadata.IsPublic)}, + IsVirtual = {ToCSharpKeyword(memberMetadata.IsVirtual)}, + DeclaringType = typeof({memberMetadata.DeclaringTypeRef}), + PropertyTypeInfo = {memberTypeFriendlyName}, + Converter = {converterValue}, + Getter = {getterValue}, + Setter = {setterValue}, + IgnoreCondition = {ignoreConditionNamedArg}, + HasJsonInclude = {ToCSharpKeyword(memberMetadata.HasJsonInclude)}, + IsExtensionData = {ToCSharpKeyword(memberMetadata.IsExtensionData)}, + NumberHandling = {GetNumberHandlingAsStr(memberMetadata.NumberHandling)}, + PropertyName = ""{clrPropertyName}"", + JsonPropertyName = {jsonPropertyNameValue} + }}; + + {PropVarName}[{i}] = {JsonMetadataServicesTypeRef}.CreatePropertyInfo<{memberTypeCompilableName}>(options, {infoVarName}); "); } @@ -775,7 +795,6 @@ private string GeneratePropMetadataInitFunc(TypeGenerationSpec typeGenerationSpe private string GenerateCtorParamMetadataInitFunc(TypeGenerationSpec typeGenerationSpec) { const string parametersVarName = "parameters"; - const string infoVarName = "info"; ParameterGenerationSpec[] parameters = typeGenerationSpec.CtorParamGenSpecArray; int paramCount = parameters.Length; @@ -794,7 +813,7 @@ private string GenerateCtorParamMetadataInitFunc(TypeGenerationSpec typeGenerati ParameterInfo reflectionInfo = parameters[i].ParameterInfo; sb.Append(@$" - {infoVarName} = new() + {InfoVarName} = new() {{ Name = ""{reflectionInfo.Name!}"", ParameterType = typeof({reflectionInfo.ParameterType.GetCompilableName()}), @@ -802,7 +821,7 @@ private string GenerateCtorParamMetadataInitFunc(TypeGenerationSpec typeGenerati HasDefaultValue = {ToCSharpKeyword(reflectionInfo.HasDefaultValue)}, DefaultValue = {GetParamDefaultValueAsString(reflectionInfo.DefaultValue)} }}; - {parametersVarName}[{i}] = {infoVarName}; + {parametersVarName}[{i}] = {InfoVarName}; "); } @@ -817,7 +836,7 @@ private string GenerateFastPathFuncForObject(TypeGenerationSpec typeGenSpec) { JsonSourceGenerationOptionsAttribute options = _currentContext.GenerationOptions; string typeRef = typeGenSpec.TypeRef; - string serializeMethodName = $"{typeGenSpec.TypeInfoPropertyName}{SerializeMethodNameSuffix}"; + string serializeMethodName = $"{typeGenSpec.TypeInfoPropertyName}{SerializeHandlerPropName}"; if (!typeGenSpec.TryFilterSerializableProps( options, @@ -1049,7 +1068,7 @@ private string GetSerializeLogicForNonPrimitiveType(string typeInfoPropertyName, if (serializationLogicGenerated) { - return $"{typeInfoPropertyName}{SerializeMethodNameSuffix}({WriterVarName}, {valueToWrite});"; + return $"{typeInfoPropertyName}{SerializeHandlerPropName}({WriterVarName}, {valueToWrite});"; } return $"{JsonSerializerTypeRef}.Serialize({WriterVarName}, {valueToWrite}, {typeInfoRef});"; @@ -1099,7 +1118,7 @@ private string GenerateForType(TypeGenerationSpec typeMetadata, string metadataI {{ if (_{typeFriendlyName} == null) {{ - {WrapWithCheckForCustomConverterIfRequired(metadataInitSource, typeCompilableName, typeFriendlyName, GetNumberHandlingAsStr(typeMetadata.NumberHandling))} + {WrapWithCheckForCustomConverter(metadataInitSource, typeCompilableName, typeFriendlyName, GetNumberHandlingAsStr(typeMetadata.NumberHandling))} }} return _{typeFriendlyName}; @@ -1107,23 +1126,16 @@ private string GenerateForType(TypeGenerationSpec typeMetadata, string metadataI }}{additionalSource}"; } - private string WrapWithCheckForCustomConverterIfRequired(string source, string typeCompilableName, string typeFriendlyName, string numberHandlingNamedArg) - { - if (_currentContext.GenerationOptions.IgnoreRuntimeCustomConverters) - { - return source; - } - - return @$"{JsonConverterTypeRef}? customConverter; - if ({OptionsInstanceVariableName}.Converters.Count > 0 && (customConverter = {RuntimeCustomConverterFetchingMethodName}(typeof({typeCompilableName}))) != null) - {{ - _{typeFriendlyName} = {JsonMetadataServicesTypeRef}.{GetCreateValueInfoMethodRef(typeCompilableName)}({OptionsInstanceVariableName}, customConverter); - }} - else - {{ - {IndentSource(source, numIndentations: 1)} - }}"; - } + private string WrapWithCheckForCustomConverter(string source, string typeCompilableName, string typeFriendlyName, string numberHandlingNamedArg) + => @$"{JsonConverterTypeRef}? customConverter; + if ({OptionsInstanceVariableName}.Converters.Count > 0 && (customConverter = {RuntimeCustomConverterFetchingMethodName}(typeof({typeCompilableName}))) != null) + {{ + _{typeFriendlyName} = {JsonMetadataServicesTypeRef}.{GetCreateValueInfoMethodRef(typeCompilableName)}({OptionsInstanceVariableName}, customConverter); + }} + else + {{ + {IndentSource(source, numIndentations: 1)} + }}"; private string GetRootJsonContextImplementation( bool generateGetConverterMethodForTypes, @@ -1139,11 +1151,13 @@ private string GetRootJsonContextImplementation( private static {contextTypeRef}? {DefaultContextBackingStaticVarName}; public static {contextTypeRef} Default => {DefaultContextBackingStaticVarName} ??= new {contextTypeRef}(new {JsonSerializerOptionsTypeRef}({DefaultOptionsStaticVarName})); -public {contextTypeName}() : base(null, {DefaultOptionsStaticVarName}) +protected override {JsonSerializerOptionsTypeRef}? GeneratedSerializerOptions {{ get; }} = {DefaultOptionsStaticVarName}; + +public {contextTypeName}() : base(null) {{ }} -public {contextTypeName}({JsonSerializerOptionsTypeRef} options) : base(options, {DefaultOptionsStaticVarName}) +public {contextTypeName}({JsonSerializerOptionsTypeRef} options) : base(options) {{ }} @@ -1184,11 +1198,6 @@ private string GetLogicForDefaultSerializerOptionsInit() private string GetFetchLogicForRuntimeSpecifiedCustomConverter() { - if (_currentContext.GenerationOptions.IgnoreRuntimeCustomConverters) - { - return ""; - } - // TODO (https://github.com/dotnet/runtime/issues/52218): use a dictionary if count > ~15. return @$"private {JsonConverterTypeRef}? {RuntimeCustomConverterFetchingMethodName}({TypeTypeRef} type) {{ diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs index 9ba0372893c3fd..e5cb105b39d6e4 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs @@ -25,9 +25,12 @@ private sealed class Parser private const string SystemTextJsonNamespace = "System.Text.Json"; private const string JsonConverterAttributeFullName = "System.Text.Json.Serialization.JsonConverterAttribute"; private const string JsonConverterFactoryFullName = "System.Text.Json.Serialization.JsonConverterFactory"; + private const string JsonArrayFullName = "System.Text.Json.Nodes.JsonArray"; private const string JsonElementFullName = "System.Text.Json.JsonElement"; private const string JsonExtensionDataAttributeFullName = "System.Text.Json.Serialization.JsonExtensionDataAttribute"; + private const string JsonNodeFullName = "System.Text.Json.Nodes.JsonNode"; private const string JsonObjectFullName = "System.Text.Json.Nodes.JsonObject"; + private const string JsonValueFullName = "System.Text.Json.Nodes.JsonValue"; private const string JsonIgnoreAttributeFullName = "System.Text.Json.Serialization.JsonIgnoreAttribute"; private const string JsonIgnoreConditionFullName = "System.Text.Json.Serialization.JsonIgnoreCondition"; private const string JsonIncludeAttributeFullName = "System.Text.Json.Serialization.JsonIncludeAttribute"; @@ -80,8 +83,11 @@ private sealed class Parser private readonly Type? _guidType; private readonly Type? _uriType; private readonly Type? _versionType; + private readonly Type? _jsonArrayType; private readonly Type? _jsonElementType; + private readonly Type? _jsonNodeType; private readonly Type? _jsonObjectType; + private readonly Type? _jsonValueType; // Unsupported types private readonly Type _typeType; @@ -193,8 +199,11 @@ public Parser(Compilation compilation, in JsonSourceGenerationContext sourceGene _guidType = _metadataLoadContext.Resolve(typeof(Guid)); _uriType = _metadataLoadContext.Resolve(typeof(Uri)); _versionType = _metadataLoadContext.Resolve(typeof(Version)); + _jsonArrayType = _metadataLoadContext.Resolve(JsonArrayFullName); _jsonElementType = _metadataLoadContext.Resolve(JsonElementFullName); + _jsonNodeType = _metadataLoadContext.Resolve(JsonNodeFullName); _jsonObjectType = _metadataLoadContext.Resolve(JsonObjectFullName); + _jsonValueType = _metadataLoadContext.Resolve(JsonValueFullName); // Unsupported types. _typeType = _metadataLoadContext.Resolve(typeof(Type)); @@ -565,14 +574,6 @@ private static bool TryGetClassDeclarationList(INamedTypeSymbol typeSymbol, [Not } } break; - case nameof(JsonSourceGenerationOptionsAttribute.IgnoreRuntimeCustomConverters): - { - if (bool.TryParse(propertyValueStr, out bool value)) - { - options.IgnoreRuntimeCustomConverters = value; - } - } - break; case nameof(JsonSourceGenerationOptionsAttribute.IncludeFields): { if (bool.TryParse(propertyValueStr, out bool value)) @@ -640,7 +641,7 @@ private TypeGenerationSpec GetOrAddTypeGenerationSpec(Type type, JsonSourceGener string? converterInstatiationLogic = null; bool implementsIJsonOnSerialized = false; bool implementsIJsonOnSerializing = false; - bool hasEncounteredInitOnlyProperties = false; + bool hasInitOnlyProperties = false; bool hasTypeFactoryConverter = false; bool hasPropertyFactoryConverters = false; @@ -972,10 +973,10 @@ void CacheMemberHelper() _implicitlyRegisteredTypes.Add(dataExtensionPropGenSpec); } - if (!hasEncounteredInitOnlyProperties && spec.CanUseSetter && spec.IsInitOnlySetter) + if (!hasInitOnlyProperties && spec.CanUseSetter && spec.IsInitOnlySetter) { _sourceGenerationContext.ReportDiagnostic(Diagnostic.Create(InitOnlyPropertyDeserializationNotSupported, Location.None, new string[] { type.Name })); - hasEncounteredInitOnlyProperties = true; + hasInitOnlyProperties = true; } if (spec.HasJsonInclude && (!spec.CanUseGetter || !spec.CanUseSetter || !spec.IsPublic)) @@ -1454,8 +1455,11 @@ private void PopulateKnownTypes() AddTypeIfNotNull(_knownTypes, _guidType); AddTypeIfNotNull(_knownTypes, _uriType); AddTypeIfNotNull(_knownTypes, _versionType); + AddTypeIfNotNull(_knownTypes, _jsonArrayType); AddTypeIfNotNull(_knownTypes, _jsonElementType); + AddTypeIfNotNull(_knownTypes, _jsonNodeType); AddTypeIfNotNull(_knownTypes, _jsonObjectType); + AddTypeIfNotNull(_knownTypes, _jsonValueType); _knownUnsupportedTypes.Add(_typeType); _knownUnsupportedTypes.Add(_serializationInfoType); diff --git a/src/libraries/System.Text.Json/gen/TypeGenerationSpec.cs b/src/libraries/System.Text.Json/gen/TypeGenerationSpec.cs index 02d51cdef6a48c..1b83543694361a 100644 --- a/src/libraries/System.Text.Json/gen/TypeGenerationSpec.cs +++ b/src/libraries/System.Text.Json/gen/TypeGenerationSpec.cs @@ -72,15 +72,6 @@ internal class TypeGenerationSpec public bool HasPropertyFactoryConverters { get; private set; } public bool HasTypeFactoryConverter { get; private set; } - public string FastPathSerializeMethodName - { - get - { - Debug.Assert(GenerateSerializationLogic); - return $"{TypeInfoPropertyName}Serialize"; - } - } - public string? ImmutableCollectionBuilderName { get diff --git a/src/libraries/System.Text.Json/ref/System.Text.Json.cs b/src/libraries/System.Text.Json/ref/System.Text.Json.cs index 23b632240cabe5..77d6c85b4376fe 100644 --- a/src/libraries/System.Text.Json/ref/System.Text.Json.cs +++ b/src/libraries/System.Text.Json/ref/System.Text.Json.cs @@ -798,7 +798,7 @@ public abstract partial class JsonConverter internal JsonConverter() { } public abstract bool CanConvert(System.Type typeToConvert); } - [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Interface | System.AttributeTargets.Enum | System.AttributeTargets.Field | System.AttributeTargets.Property | System.AttributeTargets.Struct, AllowMultiple=false)] + [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Interface | System.AttributeTargets.Enum | System.AttributeTargets.Field | System.AttributeTargets.Property | System.AttributeTargets.Struct, AllowMultiple = false)] public partial class JsonConverterAttribute : System.Text.Json.Serialization.JsonAttribute { protected JsonConverterAttribute() { } @@ -890,7 +890,8 @@ public JsonSerializableAttribute(System.Type type) { } } public abstract partial class JsonSerializerContext { - protected JsonSerializerContext(System.Text.Json.JsonSerializerOptions? instanceOptions, System.Text.Json.JsonSerializerOptions? defaultOptions) { } + protected JsonSerializerContext(System.Text.Json.JsonSerializerOptions? options) { } + protected abstract System.Text.Json.JsonSerializerOptions? GeneratedSerializerOptions { get; } public System.Text.Json.JsonSerializerOptions Options { get { throw null; } } public abstract System.Text.Json.Serialization.Metadata.JsonTypeInfo? GetTypeInfo(System.Type type); } @@ -901,11 +902,10 @@ public JsonSourceGenerationOptionsAttribute() { } public System.Text.Json.Serialization.JsonIgnoreCondition DefaultIgnoreCondition { get { throw null; } set { } } public bool IgnoreReadOnlyFields { get { throw null; } set { } } public bool IgnoreReadOnlyProperties { get { throw null; } set { } } - public bool IgnoreRuntimeCustomConverters { get { throw null; } set { } } public bool IncludeFields { get { throw null; } set { } } public System.Text.Json.Serialization.JsonKnownNamingPolicy PropertyNamingPolicy { get { throw null; } set { } } public bool WriteIndented { get { throw null; } set { } } - public JsonSourceGenerationMode GenerationMode { get { throw null; } set { } } + public System.Text.Json.Serialization.JsonSourceGenerationMode GenerationMode { get { throw null; } set { } } } [System.FlagsAttribute] public enum JsonSourceGenerationMode @@ -948,6 +948,15 @@ protected ReferenceResolver() { } } namespace System.Text.Json.Serialization.Metadata { + public sealed partial class JsonCollectionInfoValues + { + public JsonCollectionInfoValues() { } + public System.Func? ObjectCreator { get { throw null; } init { } } + public System.Text.Json.Serialization.Metadata.JsonTypeInfo ElementInfo { get { throw null; } init { } } + public System.Text.Json.Serialization.Metadata.JsonTypeInfo? KeyInfo { get { throw null; } init { } } + public System.Text.Json.Serialization.JsonNumberHandling NumberHandling { get { throw null; } init { } } + public System.Action? SerializeHandler { get { throw null; } init { } } + } public static partial class JsonMetadataServices { public static System.Text.Json.Serialization.JsonConverter BooleanConverter { get { throw null; } } @@ -962,8 +971,11 @@ public static partial class JsonMetadataServices public static System.Text.Json.Serialization.JsonConverter Int16Converter { get { throw null; } } public static System.Text.Json.Serialization.JsonConverter Int32Converter { get { throw null; } } public static System.Text.Json.Serialization.JsonConverter Int64Converter { get { throw null; } } + public static System.Text.Json.Serialization.JsonConverter JsonArrayConverter { get { throw null; } } public static System.Text.Json.Serialization.JsonConverter JsonElementConverter { get { throw null; } } + public static System.Text.Json.Serialization.JsonConverter JsonNodeConverter { get { throw null; } } public static System.Text.Json.Serialization.JsonConverter JsonObjectConverter { get { throw null; } } + public static System.Text.Json.Serialization.JsonConverter JsonValueConverter { get { throw null; } } public static System.Text.Json.Serialization.JsonConverter ObjectConverter { get { throw null; } } [System.CLSCompliantAttribute(false)] public static System.Text.Json.Serialization.JsonConverter SByteConverter { get { throw null; } } @@ -978,27 +990,28 @@ public static partial class JsonMetadataServices public static System.Text.Json.Serialization.JsonConverter UInt64Converter { get { throw null; } } public static System.Text.Json.Serialization.JsonConverter UriConverter { get { throw null; } } public static System.Text.Json.Serialization.JsonConverter VersionConverter { get { throw null; } } - public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateArrayInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) { throw null; } - public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateConcurrentQueueInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Concurrent.ConcurrentQueue { throw null; } - public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateConcurrentStackInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Concurrent.ConcurrentStack { throw null; } - public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateDictionaryInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo keyInfo, System.Text.Json.Serialization.Metadata.JsonTypeInfo valueInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Generic.Dictionary where TKey : notnull { throw null; } - public static JsonTypeInfo CreateICollectionInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Generic.ICollection { throw null; } - public static JsonTypeInfo CreateIDictionaryInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, JsonTypeInfo stringInfo, JsonTypeInfo objectInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.IDictionary { throw null; } - public static JsonTypeInfo CreateIDictionaryInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, JsonTypeInfo keyInfo, JsonTypeInfo valueInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Generic.IDictionary where TKey : notnull { throw null; } - public static JsonTypeInfo CreateIEnumerableInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.IEnumerable { throw null; } - public static JsonTypeInfo CreateIEnumerableInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Generic.IEnumerable { throw null; } - public static JsonTypeInfo CreateIListInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo objectInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.IList { throw null; } - public static JsonTypeInfo CreateIListInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Generic.IList { throw null; } - public static JsonTypeInfo CreateImmutableDictionaryInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, JsonTypeInfo keyInfo, JsonTypeInfo valueInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc, System.Func>, TCollection> createRangeFunc) where TCollection : System.Collections.Generic.IReadOnlyDictionary where TKey : notnull { throw null; } - public static JsonTypeInfo CreateImmutableEnumerableInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc, System.Func, TCollection> createRangeFunc) where TCollection : System.Collections.Generic.IEnumerable { throw null; } - public static JsonTypeInfo CreateIReadOnlyDictionaryInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, JsonTypeInfo keyInfo, JsonTypeInfo valueInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Generic.IReadOnlyDictionary where TKey : notnull { throw null; } - public static JsonTypeInfo CreateISetInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Generic.ISet { throw null; } - public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateListInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Generic.List { throw null; } + public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateArrayInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.Metadata.JsonCollectionInfoValues collectionInfo) { throw null; } + public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateConcurrentQueueInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.Metadata.JsonCollectionInfoValues collectionInfo) where TCollection : System.Collections.Concurrent.ConcurrentQueue { throw null; } + public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateConcurrentStackInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.Metadata.JsonCollectionInfoValues collectionInfo) where TCollection : System.Collections.Concurrent.ConcurrentStack { throw null; } + public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateDictionaryInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.Metadata.JsonCollectionInfoValues collectionInfo) where TCollection : System.Collections.Generic.Dictionary where TKey : notnull { throw null; } + public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateICollectionInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.Metadata.JsonCollectionInfoValues collectionInfo) where TCollection : System.Collections.Generic.ICollection { throw null; } + public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateIDictionaryInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.Metadata.JsonCollectionInfoValues collectionInfo) where TCollection : System.Collections.IDictionary { throw null; } + public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateIDictionaryInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.Metadata.JsonCollectionInfoValues collectionInfo) where TCollection : System.Collections.Generic.IDictionary where TKey : notnull { throw null; } + public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateIEnumerableInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.Metadata.JsonCollectionInfoValues collectionInfo) where TCollection : System.Collections.IEnumerable { throw null; } + public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateIEnumerableInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.Metadata.JsonCollectionInfoValues collectionInfo) where TCollection : System.Collections.Generic.IEnumerable { throw null; } + public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateIListInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.Metadata.JsonCollectionInfoValues collectionInfo) where TCollection : System.Collections.IList { throw null; } + public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateIListInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.Metadata.JsonCollectionInfoValues collectionInfo) where TCollection : System.Collections.Generic.IList { throw null; } + public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateImmutableDictionaryInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.Metadata.JsonCollectionInfoValues collectionInfo, System.Func>, TCollection> createRangeFunc) where TCollection : System.Collections.Generic.IReadOnlyDictionary where TKey : notnull { throw null; } + public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateImmutableEnumerableInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.Metadata.JsonCollectionInfoValues collectionInfo, System.Func, TCollection> createRangeFunc) where TCollection : System.Collections.Generic.IEnumerable { throw null; } + public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateIReadOnlyDictionaryInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.Metadata.JsonCollectionInfoValues collectionInfo) where TCollection : System.Collections.Generic.IReadOnlyDictionary where TKey : notnull { throw null; } + public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateISetInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.Metadata.JsonCollectionInfoValues collectionInfo) where TCollection : System.Collections.Generic.ISet { throw null; } + public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateListInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.Metadata.JsonCollectionInfoValues collectionInfo) where TCollection : System.Collections.Generic.List { throw null; } public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateObjectInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.Metadata.JsonObjectInfoValues objectInfo) where T : notnull { throw null; } - public static System.Text.Json.Serialization.Metadata.JsonPropertyInfo CreatePropertyInfo(System.Text.Json.JsonSerializerOptions options, bool isProperty, bool isPublic, bool isVirtual, System.Type declaringType, System.Text.Json.Serialization.Metadata.JsonTypeInfo propertyTypeInfo, System.Text.Json.Serialization.JsonConverter? converter, System.Func? getter, System.Action? setter, System.Text.Json.Serialization.JsonIgnoreCondition? ignoreCondition, bool hasJsonInclude, bool isExtensionData, System.Text.Json.Serialization.JsonNumberHandling? numberHandling, string propertyName, string? jsonPropertyName) { throw null; } - public static JsonTypeInfo CreateQueueInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Generic.Queue { throw null; } - public static JsonTypeInfo CreateStackInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Generic.Stack { throw null; } - public static JsonTypeInfo CreateStackOrQueueInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc, System.Action addFunc) where TCollection : System.Collections.IEnumerable { throw null; } + public static System.Text.Json.Serialization.Metadata.JsonPropertyInfo CreatePropertyInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues propertyInfo) { throw null; } + public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateQueueInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.Metadata.JsonCollectionInfoValues collectionInfo, System.Action addFunc) where TCollection : System.Collections.IEnumerable { throw null; } + public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateQueueInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.Metadata.JsonCollectionInfoValues collectionInfo) where TCollection : System.Collections.Generic.Queue { throw null; } + public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateStackInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.Metadata.JsonCollectionInfoValues collectionInfo, System.Action addFunc) where TCollection : System.Collections.IEnumerable { throw null; } + public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateStackInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.Metadata.JsonCollectionInfoValues collectionInfo) where TCollection : System.Collections.Generic.Stack { throw null; } public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateValueInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.JsonConverter converter) { throw null; } public static System.Text.Json.Serialization.JsonConverter GetUnsupportedTypeConverter() { throw null; } public static System.Text.Json.Serialization.JsonConverter GetEnumConverter(System.Text.Json.JsonSerializerOptions options) where T : struct { throw null; } @@ -1006,15 +1019,17 @@ public static partial class JsonMetadataServices } public sealed partial class JsonObjectInfoValues { - public System.Func? ConstructorParameterMetadataInitializer { get; init; } - public System.Text.Json.Serialization.JsonNumberHandling NumberHandling { get; init; } - public System.Func? ObjectCreator { get; init; } - public System.Func? ObjectWithParameterizedConstructorCreator { get; init; } - public System.Func? PropertyMetadataInitializer { get; init; } - public System.Action? SerializeHandler { get; init; } + public JsonObjectInfoValues() { } + public System.Func? ConstructorParameterMetadataInitializer { get { throw null; } init { } } + public System.Text.Json.Serialization.JsonNumberHandling NumberHandling { get { throw null; } init { } } + public System.Func? ObjectCreator { get { throw null; } init { } } + public System.Func? ObjectWithParameterizedConstructorCreator { get { throw null; } init { } } + public System.Func? PropertyMetadataInitializer { get { throw null; } init { } } + public System.Action? SerializeHandler { get { throw null; } init { } } } - public sealed class JsonParameterInfoValues + public sealed partial class JsonParameterInfoValues { + public JsonParameterInfoValues() { } public object? DefaultValue { get { throw null; } init { } } public bool HasDefaultValue { get { throw null; } init { } } public string Name { get { throw null; } init { } } @@ -1025,6 +1040,24 @@ public abstract partial class JsonPropertyInfo { internal JsonPropertyInfo() { } } + public sealed partial class JsonPropertyInfoValues + { + public JsonPropertyInfoValues() { } + public System.Text.Json.Serialization.JsonConverter? Converter { get { throw null; } init { } } + public System.Type DeclaringType { get { throw null; } init { } } + public System.Func? Getter { get { throw null; } init { } } + public System.Text.Json.Serialization.JsonIgnoreCondition? IgnoreCondition { get { throw null; } init { } } + public bool HasJsonInclude { get { throw null; } init { } } + public bool IsExtensionData { get { throw null; } init { } } + public bool IsProperty { get { throw null; } init { } } + public bool IsPublic { get { throw null; } init { } } + public bool IsVirtual { get { throw null; } init { } } + public string? JsonPropertyName { get { throw null; } init { } } + public System.Text.Json.Serialization.JsonNumberHandling? NumberHandling { get { throw null; } init { } } + public string PropertyName { get { throw null; } init { } } + public System.Text.Json.Serialization.Metadata.JsonTypeInfo PropertyTypeInfo { get { throw null; } init { } } + public System.Action? Setter { get { throw null; } init { } } + } public partial class JsonTypeInfo { internal JsonTypeInfo() { } @@ -1032,6 +1065,6 @@ internal JsonTypeInfo() { } public abstract partial class JsonTypeInfo : System.Text.Json.Serialization.Metadata.JsonTypeInfo { internal JsonTypeInfo() { } - public System.Action? Serialize { get { throw null; } } + public System.Action? SerializeHandler { get { throw null; } } } } diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.csproj b/src/libraries/System.Text.Json/src/System.Text.Json.csproj index 79eb67ccc4ec12..4b7f0bc75cee0c 100644 --- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj +++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj @@ -117,13 +117,6 @@ System.Text.Json.Utf8JsonReader - - - - - - - @@ -230,14 +223,23 @@ System.Text.Json.Utf8JsonReader + + + + + + - + + + + 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 9bc2f3b2ba9b1c..5ee913f18d9482 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 @@ -8,7 +8,7 @@ namespace System.Text.Json.Serialization.Converters { /// /// Provides a mechanism to invoke "fast-path" serialization logic via - /// . This type holds an optional + /// . This type holds an optional /// reference to an actual for the type /// , to provide a fallback when the fast path cannot be used. /// @@ -75,10 +75,10 @@ internal override bool OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializer if (!state.SupportContinuation && jsonTypeInfo is JsonTypeInfo info && - info.Serialize != null && + info.SerializeHandler != null && info.Options._context?.CanUseSerializationLogic == true) { - info.Serialize(writer, value); + info.SerializeHandler(writer, value); return true; } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonNodeConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonNodeConverter.cs index 8d0f7bb437f10d..5dd032cc743e03 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonNodeConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonNodeConverter.cs @@ -11,7 +11,7 @@ namespace System.Text.Json.Serialization.Converters /// Converter for JsonNode-derived types. The {T} value must be Object and not JsonNode /// since we allow Object-declared members\variables to deserialize as {JsonNode}. /// - internal sealed class JsonNodeConverter : JsonConverter + internal sealed class JsonNodeConverter : JsonConverter { private static JsonNodeConverter? s_nodeConverter; private static JsonArrayConverter? s_arrayConverter; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs index 4f100523bf486c..761798f14d921d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs @@ -43,8 +43,8 @@ private static void WriteUsingGeneratedSerializer(Utf8JsonWriter writer, jsonTypeInfo is JsonTypeInfo typedInfo && typedInfo.Options._context?.CanUseSerializationLogic == true) { - Debug.Assert(typedInfo.Serialize != null); - typedInfo.Serialize(writer, value); + Debug.Assert(typedInfo.SerializeHandler != null); + typedInfo.SerializeHandler(writer, value); writer.Flush(); } else diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerContext.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerContext.cs index 688fd91a3b91cb..0ce18180072c9e 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerContext.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerContext.cs @@ -13,10 +13,30 @@ namespace System.Text.Json.Serialization public abstract partial class JsonSerializerContext { private bool? _canUseSerializationLogic; - private JsonSerializerOptions? _defaultOptions; internal JsonSerializerOptions? _options; + /// + /// Gets the run time specified options of the context. If no options were passed + /// when instanciating the context, then a new instance is bound and returned. + /// + /// + /// The instance cannot be mutated once it is bound with the context instance. + /// + public JsonSerializerOptions Options + { + get + { + if (_options == null) + { + _options = new JsonSerializerOptions(); + _options._context = this; + } + + return _options; + } + } + /// /// Indicates whether pre-generated serialization logic for types in the context /// is compatible with the run time specified . @@ -27,7 +47,7 @@ internal bool CanUseSerializationLogic { if (!_canUseSerializationLogic.HasValue) { - if (_defaultOptions == null) + if (GeneratedSerializerOptions == null) { _canUseSerializationLogic = false; } @@ -46,13 +66,13 @@ internal bool CanUseSerializationLogic #pragma warning restore SYSLIB0020 // Ensure options values are consistent with expected defaults. - Options.DefaultIgnoreCondition == _defaultOptions.DefaultIgnoreCondition && - Options.IgnoreReadOnlyFields == _defaultOptions.IgnoreReadOnlyFields && - Options.IgnoreReadOnlyProperties == _defaultOptions.IgnoreReadOnlyProperties && - Options.IncludeFields == _defaultOptions.IncludeFields && - Options.PropertyNamingPolicy == _defaultOptions.PropertyNamingPolicy && - Options.DictionaryKeyPolicy == _defaultOptions.DictionaryKeyPolicy && - Options.WriteIndented == _defaultOptions.WriteIndented; + Options.DefaultIgnoreCondition == GeneratedSerializerOptions.DefaultIgnoreCondition && + Options.IgnoreReadOnlyFields == GeneratedSerializerOptions.IgnoreReadOnlyFields && + Options.IgnoreReadOnlyProperties == GeneratedSerializerOptions.IgnoreReadOnlyProperties && + Options.IncludeFields == GeneratedSerializerOptions.IncludeFields && + Options.PropertyNamingPolicy == GeneratedSerializerOptions.PropertyNamingPolicy && + Options.DictionaryKeyPolicy == GeneratedSerializerOptions.DictionaryKeyPolicy && + Options.WriteIndented == GeneratedSerializerOptions.WriteIndented; } } @@ -61,49 +81,30 @@ internal bool CanUseSerializationLogic } /// - /// Gets the run time specified options of the context. If no options were passed - /// when instanciating the context, then a new instance is bound and returned. + /// The default run time options for the context. Its values are defined at design-time via . /// - /// - /// The instance cannot be mutated once it is bound with the context instance. - /// - public JsonSerializerOptions Options - { - get - { - if (_options == null) - { - _options = new JsonSerializerOptions(); - _options._context = this; - } - - return _options; - } - } + protected abstract JsonSerializerOptions? GeneratedSerializerOptions { get; } /// /// Creates an instance of and binds it with the indicated . /// - /// The run time provided options for the context instance. - /// The default run time options for the context. Its values are defined at design-time via . + /// The run time provided options for the context instance. /// /// If no instance options are passed, then no options are set until the context is bound using , /// or until is called, where a new options instance is created and bound. /// - protected JsonSerializerContext(JsonSerializerOptions? instanceOptions, JsonSerializerOptions? defaultOptions) + protected JsonSerializerContext(JsonSerializerOptions? options) { - if (instanceOptions != null) + if (options != null) { - if (instanceOptions._context != null) + if (options._context != null) { ThrowHelper.ThrowInvalidOperationException_JsonSerializerOptionsAlreadyBoundToContext(); } - _options = instanceOptions; - instanceOptions._context = this; + _options = options; + options._context = this; } - - _defaultOptions = defaultOptions; } /// diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonCollectionInfoValuesOfTCollection.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonCollectionInfoValuesOfTCollection.cs new file mode 100644 index 00000000000000..b1fef6a4c934f2 --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonCollectionInfoValuesOfTCollection.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Text.Json.Serialization.Metadata +{ + /// + /// Provides serialization metadata about a collection type. + /// + /// The collection type. + public sealed class JsonCollectionInfoValues + { + /// + /// A to create an instance of the collection when deserializing. + /// + public Func? ObjectCreator { get; init; } + + /// + /// If a dictionary type, the instance representing the key type. + /// + public JsonTypeInfo? KeyInfo { get; init; } + + /// + /// A instance representing the element type. + /// + public JsonTypeInfo ElementInfo { get; init; } = null!; + + /// + /// The option to apply to number collection elements. + /// + public JsonNumberHandling NumberHandling { get; init; } + + /// + /// An optimized serialization implementation assuming pre-determined defaults. + /// + public Action? SerializeHandler { get; init; } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs index 71f48ad41b0eff..4b1708652961cf 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs @@ -11,552 +11,368 @@ namespace System.Text.Json.Serialization.Metadata public static partial class JsonMetadataServices { /// - /// Creates metadata for an array. + /// Creates serialization metadata for an array. /// /// The generic definition of the element type. /// The to use. - /// A instance representing the element type. - /// The option to apply to number collection elements. - /// An optimized serialization implementation assuming pre-determined defaults. - /// - public static JsonTypeInfo CreateArrayInfo( - JsonSerializerOptions options, - JsonTypeInfo elementInfo, - JsonNumberHandling numberHandling, - Action? serializeFunc) + /// Provides serialization metadata about the collection type. + /// Serialization metadata for the given type. + public static JsonTypeInfo CreateArrayInfo(JsonSerializerOptions options, JsonCollectionInfoValues collectionInfo) => new JsonTypeInfoInternal( options, - createObjectFunc: null, - () => new ArrayConverter(), - elementInfo, - numberHandling, - serializeFunc, - typeof(TElement)); + collectionInfo, + () => new ArrayConverter()); /// - /// Creates metadata for types assignable to . + /// Creates serialization metadata for types assignable to . /// /// The generic definition of the type. /// The generic definition of the element type. - /// - /// A to create an instance of the list when deserializing. - /// A instance representing the element type. - /// The option to apply to number collection elements. - /// An optimized serialization implementation assuming pre-determined defaults. - /// + /// The to use. + /// Provides serialization metadata about the collection type. + /// Serialization metadata for the given type. public static JsonTypeInfo CreateListInfo( JsonSerializerOptions options, - Func? createObjectFunc, - JsonTypeInfo elementInfo, - JsonNumberHandling numberHandling, - Action? serializeFunc) + JsonCollectionInfoValues collectionInfo) where TCollection : List => new JsonTypeInfoInternal( options, - createObjectFunc, - () => new ListOfTConverter(), - elementInfo, - numberHandling, - serializeFunc, - typeof(TElement)); + collectionInfo, + () => new ListOfTConverter()); /// - /// Creates metadata for types assignable to . + /// Creates serialization metadata for types assignable to . /// /// The generic definition of the type. /// The generic definition of the key type. /// The generic definition of the value type. /// - /// A to create an instance of the list when deserializing. - /// A instance representing the key type. - /// A instance representing the value type. - /// The option to apply to number collection elements. - /// An optimized serialization implementation assuming pre-determined defaults. - /// + /// Provides serialization metadata about the collection type. + /// Serialization metadata for the given type. public static JsonTypeInfo CreateDictionaryInfo( JsonSerializerOptions options, - Func? createObjectFunc, - JsonTypeInfo keyInfo, - JsonTypeInfo valueInfo, - JsonNumberHandling numberHandling, - Action? serializeFunc) + JsonCollectionInfoValues collectionInfo) where TCollection : Dictionary where TKey : notnull => new JsonTypeInfoInternal( options, - createObjectFunc, - () => new DictionaryOfTKeyTValueConverter(), - keyInfo, - valueInfo, - numberHandling, - serializeFunc, - typeof(TKey), - typeof(TValue)); + collectionInfo, + () => new DictionaryOfTKeyTValueConverter()); #pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved /// - /// Creates metadata for and + /// Creates serialization metadata for and /// types assignable to . /// /// The generic definition of the type. /// The generic definition of the key type. /// The generic definition of the value type. /// - /// A to create an instance of the list when deserializing. - /// A instance representing the key type. - /// A instance representing the value type. - /// The option to apply to number collection elements. - /// An optimized serialization implementation assuming pre-determined defaults. + /// Provides serialization metadata about the collection type. /// A method to create an immutable dictionary instance. - /// + /// Serialization metadata for the given type. #pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved public static JsonTypeInfo CreateImmutableDictionaryInfo( JsonSerializerOptions options, - Func? createObjectFunc, - JsonTypeInfo keyInfo, - JsonTypeInfo valueInfo, - JsonNumberHandling numberHandling, - Action? serializeFunc, + JsonCollectionInfoValues collectionInfo, Func>, TCollection> createRangeFunc) where TCollection : IReadOnlyDictionary where TKey : notnull => new JsonTypeInfoInternal( options, - createObjectFunc, + collectionInfo, () => new ImmutableDictionaryOfTKeyTValueConverter(), - keyInfo, - valueInfo, - numberHandling, - serializeFunc, - typeof(TKey), - typeof(TValue), - createRangeFunc ?? throw new ArgumentNullException(nameof(createRangeFunc))); + createObjectWithArgs: createRangeFunc ?? throw new ArgumentNullException(nameof(createRangeFunc))); /// - /// Creates metadata for types assignable to . + /// Creates serialization metadata for types assignable to . /// /// The generic definition of the type. /// The generic definition of the key type. /// The generic definition of the value type. /// - /// A to create an instance of the list when deserializing. - /// A instance representing the key type. - /// A instance representing the value type. - /// The option to apply to number collection elements. - /// An optimized serialization implementation assuming pre-determined defaults. - /// + /// Provides serialization metadata about the collection type. + /// Serialization metadata for the given type. public static JsonTypeInfo CreateIDictionaryInfo( JsonSerializerOptions options, - Func? createObjectFunc, - JsonTypeInfo keyInfo, - JsonTypeInfo valueInfo, - JsonNumberHandling numberHandling, - Action? serializeFunc) + JsonCollectionInfoValues collectionInfo) where TCollection : IDictionary where TKey : notnull => new JsonTypeInfoInternal( options, - createObjectFunc, - () => new IDictionaryOfTKeyTValueConverter(), - keyInfo, - valueInfo, - numberHandling, - serializeFunc, - typeof(TKey), - typeof(TValue)); + collectionInfo, + () => new IDictionaryOfTKeyTValueConverter()); /// - /// Creates metadata for types assignable to . + /// Creates serialization metadata for types assignable to . /// /// The generic definition of the type. /// The generic definition of the key type. /// The generic definition of the value type. /// - /// A to create an instance of the list when deserializing. - /// A instance representing the key type. - /// A instance representing the value type. - /// The option to apply to number collection elements. - /// An optimized serialization implementation assuming pre-determined defaults. - /// + /// Provides serialization metadata about the collection type. + /// Serialization metadata for the given type. public static JsonTypeInfo CreateIReadOnlyDictionaryInfo( JsonSerializerOptions options, - Func? createObjectFunc, - JsonTypeInfo keyInfo, - JsonTypeInfo valueInfo, - JsonNumberHandling numberHandling, - Action? serializeFunc) + JsonCollectionInfoValues collectionInfo) where TCollection : IReadOnlyDictionary where TKey : notnull => new JsonTypeInfoInternal( options, - createObjectFunc, - () => new IReadOnlyDictionaryOfTKeyTValueConverter(), - keyInfo, - valueInfo, - numberHandling, - serializeFunc, - typeof(TKey), - typeof(TValue)); + collectionInfo, + () => new IReadOnlyDictionaryOfTKeyTValueConverter()); #pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved /// - /// Creates metadata for non-dictionary immutable collection types. + /// Creates serialization metadata for non-dictionary immutable collection types. /// /// The generic definition of the type. /// The generic definition of the element type. /// - /// A to create an instance of the list when deserializing. - /// A instance representing the element type. - /// The option to apply to number collection elements. - /// An optimized serialization implementation assuming pre-determined defaults. + /// Provides serialization metadata about the collection type. /// A method to create an immutable dictionary instance. - /// + /// Serialization metadata for the given type. #pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved public static JsonTypeInfo CreateImmutableEnumerableInfo( JsonSerializerOptions options, - Func? createObjectFunc, - JsonTypeInfo elementInfo, - JsonNumberHandling numberHandling, - Action? serializeFunc, + JsonCollectionInfoValues collectionInfo, Func, TCollection> createRangeFunc) where TCollection : IEnumerable => new JsonTypeInfoInternal( options, - createObjectFunc, + collectionInfo, () => new ImmutableEnumerableOfTConverter(), - elementInfo, - numberHandling, - serializeFunc, - typeof(TElement), - createRangeFunc ?? throw new ArgumentNullException(nameof(createRangeFunc))); + createObjectWithArgs: createRangeFunc ?? throw new ArgumentNullException(nameof(createRangeFunc))); /// - /// Creates metadata for types assignable to . + /// Creates serialization metadata for types assignable to . /// /// The generic definition of the type. /// - /// A to create an instance of the list when deserializing. - /// A instance representing the element type. - /// The option to apply to number collection elements. - /// An optimized serialization implementation assuming pre-determined defaults. - /// + /// Provides serialization metadata about the collection type. + /// Serialization metadata for the given type. public static JsonTypeInfo CreateIListInfo( JsonSerializerOptions options, - Func? createObjectFunc, - JsonTypeInfo objectInfo, - JsonNumberHandling numberHandling, - Action? serializeFunc) + JsonCollectionInfoValues collectionInfo) where TCollection : IList => new JsonTypeInfoInternal( options, - createObjectFunc, - () => new IListConverter(), - objectInfo, - numberHandling, - serializeFunc, - typeof(object)); + collectionInfo, + () => new IListConverter()); /// - /// Creates metadata for types assignable to . + /// Creates serialization metadata for types assignable to . /// /// The generic definition of the type. /// The generic definition of the element type. /// - /// A to create an instance of the list when deserializing. - /// A instance representing the element type. - /// The option to apply to number collection elements. - /// An optimized serialization implementation assuming pre-determined defaults. - /// + /// Provides serialization metadata about the collection type. + /// Serialization metadata for the given type. public static JsonTypeInfo CreateIListInfo( JsonSerializerOptions options, - Func? createObjectFunc, - JsonTypeInfo elementInfo, - JsonNumberHandling numberHandling, - Action? serializeFunc) + JsonCollectionInfoValues collectionInfo) where TCollection : IList => new JsonTypeInfoInternal( options, - createObjectFunc, - () => new IListOfTConverter(), - elementInfo, - numberHandling, - serializeFunc, - typeof(TElement)); + collectionInfo, + () => new IListOfTConverter()); /// - /// Creates metadata for types assignable to . + /// Creates serialization metadata for types assignable to . /// /// The generic definition of the type. /// The generic definition of the element type. /// - /// A to create an instance of the list when deserializing. - /// A instance representing the element type. - /// The option to apply to number collection elements. - /// An optimized serialization implementation assuming pre-determined defaults. - /// + /// Provides serialization metadata about the collection type. + /// Serialization metadata for the given type. public static JsonTypeInfo CreateISetInfo( JsonSerializerOptions options, - Func? createObjectFunc, - JsonTypeInfo elementInfo, - JsonNumberHandling numberHandling, - Action? serializeFunc) + JsonCollectionInfoValues collectionInfo) where TCollection : ISet => new JsonTypeInfoInternal( options, - createObjectFunc, - () => new ISetOfTConverter(), - elementInfo, - numberHandling, - serializeFunc, - typeof(TElement)); + collectionInfo, + () => new ISetOfTConverter()); /// - /// Creates metadata for types assignable to . + /// Creates serialization metadata for types assignable to . /// /// The generic definition of the type. /// The generic definition of the element type. /// - /// A to create an instance of the list when deserializing. - /// A instance representing the element type. - /// The option to apply to number collection elements. - /// An optimized serialization implementation assuming pre-determined defaults. - /// + /// Provides serialization metadata about the collection type. + /// Serialization metadata for the given type. public static JsonTypeInfo CreateICollectionInfo( JsonSerializerOptions options, - Func? createObjectFunc, - JsonTypeInfo elementInfo, - JsonNumberHandling numberHandling, - Action? serializeFunc) + JsonCollectionInfoValues collectionInfo) where TCollection : ICollection => new JsonTypeInfoInternal( options, - createObjectFunc, - () => new ICollectionOfTConverter(), - elementInfo, - numberHandling, - serializeFunc, - typeof(TElement)); + collectionInfo, + () => new ICollectionOfTConverter()); /// - /// Creates metadata for types assignable to . + /// Creates serialization metadata for types assignable to . /// /// The generic definition of the type. /// The generic definition of the element type. /// - /// A to create an instance of the list when deserializing. - /// A instance representing the element type. - /// The option to apply to number collection elements. - /// An optimized serialization implementation assuming pre-determined defaults. - /// + /// Provides serialization metadata about the collection type. + /// Serialization metadata for the given type. public static JsonTypeInfo CreateStackInfo( JsonSerializerOptions options, - Func? createObjectFunc, - JsonTypeInfo elementInfo, - JsonNumberHandling numberHandling, - Action? serializeFunc) + JsonCollectionInfoValues collectionInfo) where TCollection : Stack => new JsonTypeInfoInternal( options, - createObjectFunc, - () => new StackOfTConverter(), - elementInfo, - numberHandling, - serializeFunc, - typeof(TElement)); + collectionInfo, + () => new StackOfTConverter()); /// - /// Creates metadata for types assignable to . + /// Creates serialization metadata for types assignable to . /// /// The generic definition of the type. /// The generic definition of the element type. /// - /// A to create an instance of the list when deserializing. - /// A instance representing the element type. - /// The option to apply to number collection elements. - /// An optimized serialization implementation assuming pre-determined defaults. - /// + /// Provides serialization metadata about the collection type. + /// Serialization metadata for the given type. public static JsonTypeInfo CreateQueueInfo( JsonSerializerOptions options, - Func? createObjectFunc, - JsonTypeInfo elementInfo, - JsonNumberHandling numberHandling, - Action? serializeFunc) + JsonCollectionInfoValues collectionInfo) where TCollection : Queue => new JsonTypeInfoInternal( options, - createObjectFunc, - () => new QueueOfTConverter(), - elementInfo, - numberHandling, - serializeFunc, - typeof(TElement)); + collectionInfo, + () => new QueueOfTConverter()); /// - /// Creates metadata for types assignable to . + /// Creates serialization metadata for types assignable to . /// /// The generic definition of the type. /// The generic definition of the element type. /// - /// A to create an instance of the list when deserializing. - /// A instance representing the element type. - /// The option to apply to number collection elements. - /// An optimized serialization implementation assuming pre-determined defaults. - /// + /// Provides serialization metadata about the collection type. + /// Serialization metadata for the given type. public static JsonTypeInfo CreateConcurrentStackInfo( JsonSerializerOptions options, - Func? createObjectFunc, - JsonTypeInfo elementInfo, - JsonNumberHandling numberHandling, - Action? serializeFunc) + JsonCollectionInfoValues collectionInfo) where TCollection : ConcurrentStack => new JsonTypeInfoInternal( options, - createObjectFunc, - () => new ConcurrentStackOfTConverter(), - elementInfo, - numberHandling, - serializeFunc, - typeof(TElement)); + collectionInfo, + () => new ConcurrentStackOfTConverter()); /// - /// Creates metadata for types assignable to . + /// Creates serialization metadata for types assignable to . /// /// The generic definition of the type. /// The generic definition of the element type. /// - /// A to create an instance of the list when deserializing. - /// A instance representing the element type. - /// The option to apply to number collection elements. - /// An optimized serialization implementation assuming pre-determined defaults. - /// + /// Provides serialization metadata about the collection type. + /// Serialization metadata for the given type. public static JsonTypeInfo CreateConcurrentQueueInfo( JsonSerializerOptions options, - Func? createObjectFunc, - JsonTypeInfo elementInfo, - JsonNumberHandling numberHandling, - Action? serializeFunc) + JsonCollectionInfoValues collectionInfo) where TCollection : ConcurrentQueue => new JsonTypeInfoInternal( options, - createObjectFunc, - () => new ConcurrentQueueOfTConverter(), - elementInfo, - numberHandling, - serializeFunc, - typeof(TElement)); + collectionInfo, + () => new ConcurrentQueueOfTConverter()); /// - /// Creates metadata for types assignable to . + /// Creates serialization metadata for types assignable to . /// /// The generic definition of the type. /// The generic definition of the element type. /// - /// A to create an instance of the list when deserializing. - /// A instance representing the element type. - /// The option to apply to number collection elements. - /// An optimized serialization implementation assuming pre-determined defaults. - /// + /// Provides serialization metadata about the collection type. + /// Serialization metadata for the given type. public static JsonTypeInfo CreateIEnumerableInfo( JsonSerializerOptions options, - Func? createObjectFunc, - JsonTypeInfo elementInfo, - JsonNumberHandling numberHandling, - Action? serializeFunc) + JsonCollectionInfoValues collectionInfo) where TCollection : IEnumerable => new JsonTypeInfoInternal( options, - createObjectFunc, - () => new IEnumerableOfTConverter(), - elementInfo, - numberHandling, - serializeFunc, - typeof(TElement)); + collectionInfo, + () => new IEnumerableOfTConverter()); /// - /// Creates metadata for types assignable to . + /// Creates serialization metadata for types assignable to . /// /// The generic definition of the type. /// - /// A to create an instance of the list when deserializing. - /// A instance representing instances. - /// A instance representing instances. - /// The option to apply to number collection elements. - /// An optimized serialization implementation assuming pre-determined defaults. - /// + /// Provides serialization metadata about the collection type. + /// Serialization metadata for the given type. public static JsonTypeInfo CreateIDictionaryInfo( JsonSerializerOptions options, - Func? createObjectFunc, - JsonTypeInfo stringInfo, - JsonTypeInfo objectInfo, - JsonNumberHandling numberHandling, - Action? serializeFunc) + JsonCollectionInfoValues collectionInfo) where TCollection : IDictionary => new JsonTypeInfoInternal( options, - createObjectFunc, - () => new IDictionaryConverter(), - keyInfo: stringInfo, - valueInfo: objectInfo, - numberHandling, - serializeFunc, - typeof(string), - typeof(object)); + collectionInfo, + () => new IDictionaryConverter()); + +#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved + /// + /// Creates serialization metadata for types. + /// + /// The generic definition of the type. + /// + /// Provides serialization metadata about the collection type. + /// A method for adding elements to the collection when using the serializer's code-paths. + /// Serialization metadata for the given type. +#pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved + public static JsonTypeInfo CreateStackInfo( + JsonSerializerOptions options, + JsonCollectionInfoValues collectionInfo, + Action addFunc) + where TCollection : IEnumerable + => CreateStackOrQueueInfo(options, collectionInfo, addFunc); +#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved /// - /// Creates metadata for types assignable to . + /// Creates serialization metadata for types. /// /// The generic definition of the type. /// - /// A to create an instance of the list when deserializing. - /// A instance representing the element type. - /// The option to apply to number collection elements. - /// An optimized serialization implementation assuming pre-determined defaults. + /// Provides serialization metadata about the collection type. /// A method for adding elements to the collection when using the serializer's code-paths. - /// - public static JsonTypeInfo CreateStackOrQueueInfo( + /// Serialization metadata for the given type. +#pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved + public static JsonTypeInfo CreateQueueInfo( + JsonSerializerOptions options, + JsonCollectionInfoValues collectionInfo, + Action addFunc) + where TCollection : IEnumerable + => CreateStackOrQueueInfo(options, collectionInfo, addFunc); + + private static JsonTypeInfo CreateStackOrQueueInfo( JsonSerializerOptions options, - Func? createObjectFunc, - JsonTypeInfo elementInfo, - JsonNumberHandling numberHandling, - Action? serializeFunc, + JsonCollectionInfoValues collectionInfo, Action addFunc) where TCollection : IEnumerable => new JsonTypeInfoInternal( options, - createObjectFunc, + collectionInfo, () => new StackOrQueueConverter(), - elementInfo, - numberHandling, - serializeFunc, - typeof(object), createObjectWithArgs: null, addFunc: addFunc ?? throw new ArgumentNullException(nameof(addFunc))); /// - /// Creates metadata for types assignable to . + /// Creates serialization metadata for types assignable to . /// /// The generic definition of the type. /// - /// A to create an instance of the list when deserializing. - /// A instance representing the element type. - /// The option to apply to number collection elements. - /// An optimized serialization implementation assuming pre-determined defaults. - /// + /// Provides serialization metadata about the collection type. + /// Serialization metadata for the given type. public static JsonTypeInfo CreateIEnumerableInfo( JsonSerializerOptions options, - Func? createObjectFunc, - JsonTypeInfo elementInfo, - JsonNumberHandling numberHandling, - Action? serializeFunc) + JsonCollectionInfoValues collectionInfo) where TCollection : IEnumerable => new JsonTypeInfoInternal( options, - createObjectFunc, - () => new IEnumerableConverter(), - elementInfo, - numberHandling, - serializeFunc, - typeof(object)); + collectionInfo, + () => new IEnumerableConverter()); } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Converters.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Converters.cs index 20d5b1f7ed1fbe..5cf7292e2b4551 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Converters.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Converters.cs @@ -81,18 +81,36 @@ public static partial class JsonMetadataServices public static JsonConverter Int64Converter => s_int64Converter ??= new Int64Converter(); private static JsonConverter? s_int64Converter; + /// + /// Returns a instance that converts values. + /// + public static JsonConverter JsonArrayConverter => s_jsonArrayConverter ??= new JsonArrayConverter(); + private static JsonConverter? s_jsonArrayConverter; + /// /// Returns a instance that converts values. /// public static JsonConverter JsonElementConverter => s_jsonElementConverter ??= new JsonElementConverter(); private static JsonConverter? s_jsonElementConverter; + /// + /// Returns a instance that converts values. + /// + public static JsonConverter JsonNodeConverter => s_jsonNodeConverter ??= new JsonNodeConverter(); + private static JsonConverter? s_jsonNodeConverter; + /// /// Returns a instance that converts values. /// public static JsonConverter JsonObjectConverter => s_jsonObjectConverter ??= new JsonObjectConverter(); private static JsonConverter? s_jsonObjectConverter; + /// + /// Returns a instance that converts values. + /// + public static JsonConverter JsonValueConverter => s_jsonValueConverter ??= new JsonValueConverter(); + private static JsonConverter? s_jsonValueConverter; + /// /// Returns a instance that converts values. /// @@ -161,7 +179,7 @@ public static partial class JsonMetadataServices /// Creates a instance that throws . /// /// The generic definition for the type. - /// + /// A instance that throws public static JsonConverter GetUnsupportedTypeConverter() => new UnsupportedTypeConverter(); @@ -170,7 +188,7 @@ public static JsonConverter GetUnsupportedTypeConverter() /// /// The generic definition for the enum type. /// The to use for serialization and deserialization. - /// + /// A instance that converts values. public static JsonConverter GetEnumConverter(JsonSerializerOptions options) where T : struct, Enum => new EnumConverter(EnumConverterOptions.AllowNumbers, options ?? throw new ArgumentNullException(nameof(options))); @@ -179,7 +197,7 @@ public static JsonConverter GetEnumConverter(JsonSerializerOptions options /// /// The generic definition for the underlying nullable type. /// Serialization metadata for the underlying nullable type. - /// + /// A instance that converts values public static JsonConverter GetNullableConverter(JsonTypeInfo underlyingTypeInfo) where T : struct { if (underlyingTypeInfo == null) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.cs index f7cd608313407a..e9b0578b863d92 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.cs @@ -16,58 +16,39 @@ public static partial class JsonMetadataServices /// /// The type that the converter for the property returns or accepts when converting JSON data. /// The to initialize the metadata with. - /// Whether the CLR member is a property or field. - /// Whether the CLR member is public. - /// Whether the CLR member is a virtual property. - /// The declaring type of the property or field. - /// The info for the property or field's type. - /// A for the property or field, specified by . - /// Provides a mechanism to get the property or field's value. - /// Provides a mechanism to set the property or field's value. - /// Specifies a condition for the property to be ignored. - /// If the property or field is a number, specifies how it should processed when serializing and deserializing. - /// Whether the property was annotated with . - /// Whether the property was annotated with . - /// The CLR name of the property or field. - /// The name to be used when processing the property or field, specified by . + /// Provides serialization metadata about the property or field. /// A instance intialized with the provided metadata. - public static JsonPropertyInfo CreatePropertyInfo( - JsonSerializerOptions options, - bool isProperty, - bool isPublic, - bool isVirtual, - Type declaringType, - JsonTypeInfo propertyTypeInfo, - JsonConverter? converter, - Func? getter, - Action? setter, - JsonIgnoreCondition? ignoreCondition, - bool hasJsonInclude, - bool isExtensionData, - JsonNumberHandling? numberHandling, - string propertyName, - string? jsonPropertyName) + public static JsonPropertyInfo CreatePropertyInfo(JsonSerializerOptions options, JsonPropertyInfoValues propertyInfo) { if (options == null) { throw new ArgumentNullException(nameof(options)); } + if (propertyInfo == null) + { + throw new ArgumentNullException(nameof(propertyInfo)); + } + + Type? declaringType = propertyInfo.DeclaringType; if (declaringType == null) { - throw new ArgumentNullException(nameof(declaringType)); + throw new ArgumentException(nameof(propertyInfo.DeclaringType)); } + JsonTypeInfo? propertyTypeInfo = propertyInfo.PropertyTypeInfo; if (propertyTypeInfo == null) { - throw new ArgumentNullException(nameof(propertyTypeInfo)); + throw new ArgumentException(nameof(propertyInfo.PropertyTypeInfo)); } + string? propertyName = propertyInfo.PropertyName; if (propertyName == null) { - throw new ArgumentNullException(nameof(propertyName)); + throw new ArgumentException(nameof(propertyInfo.PropertyName)); } + JsonConverter? converter = propertyInfo.Converter; if (converter == null) { converter = propertyTypeInfo.PropertyInfoForTypeInfo.ConverterBase as JsonConverter; @@ -77,28 +58,13 @@ public static JsonPropertyInfo CreatePropertyInfo( } } - if (!isProperty && isVirtual) + if (!propertyInfo.IsProperty && propertyInfo.IsVirtual) { - throw new InvalidOperationException(SR.Format(SR.FieldCannotBeVirtual, nameof(isProperty), nameof(isVirtual))); + throw new InvalidOperationException(SR.Format(SR.FieldCannotBeVirtual, nameof(propertyInfo.IsProperty), nameof(propertyInfo.IsVirtual))); } JsonPropertyInfo jsonPropertyInfo = new JsonPropertyInfo(); - jsonPropertyInfo.InitializeForSourceGen( - options, - isProperty, - isPublic, - declaringType, - propertyTypeInfo, - converter, - getter, - setter, - ignoreCondition, - hasJsonInclude, - isExtensionData, - numberHandling, - propertyName, - jsonPropertyName); - + jsonPropertyInfo.InitializeForSourceGen(options, propertyInfo); return jsonPropertyInfo; } @@ -122,7 +88,7 @@ public static JsonTypeInfo CreateObjectInfo(JsonSerializerOptions options, /// A instance representing the type. public static JsonTypeInfo CreateValueInfo(JsonSerializerOptions options, JsonConverter converter) { - JsonTypeInfo info = new JsonTypeInfoInternal(options, ConverterStrategy.Value); + JsonTypeInfo info = new JsonTypeInfoInternal(options); info.PropertyInfoForTypeInfo = CreateJsonPropertyInfoForClassInfo(typeof(T), info, converter, options); return info; } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonObjectInfoValues.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonObjectInfoValuesOfT.cs similarity index 100% rename from src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonObjectInfoValues.cs rename to src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonObjectInfoValuesOfT.cs 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 71363ca678c4e6..f60f0450701b9d 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 @@ -116,29 +116,15 @@ internal override void Initialize( GetPolicies(ignoreCondition, parentTypeNumberHandling); } - internal void InitializeForSourceGen( - JsonSerializerOptions options, - bool isProperty, - bool isPublic, - Type declaringType, - JsonTypeInfo typeInfo, - JsonConverter converter, - Func? getter, - Action? setter, - JsonIgnoreCondition? ignoreCondition, - bool hasJsonInclude, - bool isExtensionData, - JsonNumberHandling? numberHandling, - string propertyName, - string? jsonPropertyName) + internal void InitializeForSourceGen(JsonSerializerOptions options, JsonPropertyInfoValues propertyInfo) { Options = options; - ClrName = propertyName; + ClrName = propertyInfo.PropertyName; // Property name settings. - if (jsonPropertyName != null) + if (propertyInfo.JsonPropertyName != null) { - NameAsString = jsonPropertyName; + NameAsString = propertyInfo.JsonPropertyName; } else if (options.PropertyNamingPolicy == null) { @@ -155,14 +141,27 @@ internal void InitializeForSourceGen( NameAsUtf8Bytes ??= Encoding.UTF8.GetBytes(NameAsString!); EscapedNameSection ??= JsonHelpers.GetEscapedPropertyNameSection(NameAsUtf8Bytes, Options.Encoder); - - SrcGen_IsPublic = isPublic; - SrcGen_HasJsonInclude = hasJsonInclude; - SrcGen_IsExtensionData = isExtensionData; + SrcGen_IsPublic = propertyInfo.IsPublic; + SrcGen_HasJsonInclude = propertyInfo.HasJsonInclude; + SrcGen_IsExtensionData = propertyInfo.IsExtensionData; DeclaredPropertyType = typeof(T); + + JsonTypeInfo propertyTypeInfo = propertyInfo.PropertyTypeInfo; + Type declaringType = propertyInfo.DeclaringType; + + JsonConverter? converter = propertyInfo.Converter; + if (converter == null) + { + converter = propertyTypeInfo.PropertyInfoForTypeInfo.ConverterBase as JsonConverter; + if (converter == null) + { + throw new InvalidOperationException(SR.Format(SR.ConverterForPropertyMustBeValid, declaringType, ClrName, typeof(T))); + } + } + ConverterBase = converter; - if (ignoreCondition == JsonIgnoreCondition.Always) + if (propertyInfo.IgnoreCondition == JsonIgnoreCondition.Always) { IsIgnored = true; Debug.Assert(!ShouldSerialize); @@ -170,23 +169,23 @@ internal void InitializeForSourceGen( } else { - Get = getter!; - Set = setter; + Get = propertyInfo.Getter!; + Set = propertyInfo.Setter; HasGetter = Get != null; HasSetter = Set != null; - RuntimeTypeInfo = typeInfo; + RuntimeTypeInfo = propertyTypeInfo; DeclaringType = declaringType; - IgnoreCondition = ignoreCondition; - MemberType = isProperty ? MemberTypes.Property : MemberTypes.Field; + IgnoreCondition = propertyInfo.IgnoreCondition; + MemberType = propertyInfo.IsProperty ? MemberTypes.Property : MemberTypes.Field; - _converterIsExternalAndPolymorphic = !converter.IsInternalConverter && DeclaredPropertyType != converter.TypeToConvert; + _converterIsExternalAndPolymorphic = !ConverterBase.IsInternalConverter && DeclaredPropertyType != ConverterBase.TypeToConvert; PropertyTypeCanBeNull = typeof(T).CanBeNull(); - _propertyTypeEqualsTypeToConvert = converter.TypeToConvert == typeof(T); + _propertyTypeEqualsTypeToConvert = ConverterBase.TypeToConvert == typeof(T); ConverterStrategy = Converter!.ConverterStrategy; RuntimePropertyType = DeclaredPropertyType; DetermineIgnoreCondition(IgnoreCondition); // TODO: this method needs to also take the number handling option for the declaring type. - DetermineNumberHandlingForProperty(numberHandling, declaringTypeNumberHandling: null); + DetermineNumberHandlingForProperty(propertyInfo.NumberHandling, declaringTypeNumberHandling: null); DetermineSerializationCapabilities(IgnoreCondition); } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoValuesOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoValuesOfT.cs new file mode 100644 index 00000000000000..ed7fffe7124c7b --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoValuesOfT.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. + +namespace System.Text.Json.Serialization.Metadata +{ + /// + /// Provides serialization metadata about a property or field. + /// + /// The type to convert of the for the property. + public sealed class JsonPropertyInfoValues + { + /// + /// If , indicates that the member is a property, otherwise indicates the member is a field. + /// + public bool IsProperty { get; init; } + + /// + /// Whether the property or field is public. + /// + public bool IsPublic { get; init; } + + /// + /// Whether the property or field is a virtual property. + /// + public bool IsVirtual { get; init; } + + /// + /// The declaring type of the property or field. + /// + public Type DeclaringType { get; init; } = null!; + + /// + /// The info for the property or field's type. + /// + public JsonTypeInfo PropertyTypeInfo { get; init; } = null!; + + /// + /// A for the property or field, specified by . + /// + public JsonConverter? Converter { get; init; } + + /// + /// Provides a mechanism to get the property or field's value. + /// + public Func? Getter { get; init; } + + /// + /// Provides a mechanism to set the property or field's value. + /// + public Action? Setter { get; init; } + + /// + /// Specifies a condition for the member to be ignored. + /// + public JsonIgnoreCondition? IgnoreCondition { get; init; } + + /// + /// Whether the property was annotated with . + /// + public bool HasJsonInclude { get; init; } + + /// + /// Whether the property was annotated with . + /// + public bool IsExtensionData { get; init; } + + /// + /// If the property or field is a number, specifies how it should processed when serializing and deserializing. + /// + public JsonNumberHandling? NumberHandling { get; init; } + + /// + /// The name of the property or field. + /// + public string PropertyName { get; init; } = null!; + + /// + /// The name to be used when processing the property or field, specified by . + /// + public string? JsonPropertyName { get; init; } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs index cafba2fe577e98..55dd81410ce3a1 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs @@ -147,7 +147,7 @@ internal JsonTypeInfo() Debug.Assert(false, "This constructor should not be called."); } - internal JsonTypeInfo(Type type, JsonSerializerOptions options, ConverterStrategy converterStrategy) + internal JsonTypeInfo(Type type, JsonSerializerOptions options, bool dummy) { Type = type; Options = options ?? throw new ArgumentNullException(nameof(options)); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoInternalOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoInternalOfT.cs index e7e5dc7067bf52..552b49658dddd2 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoInternalOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoInternalOfT.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; using System.Text.Json.Serialization.Converters; namespace System.Text.Json.Serialization.Metadata @@ -13,17 +12,17 @@ namespace System.Text.Json.Serialization.Metadata internal sealed class JsonTypeInfoInternal : JsonTypeInfo { /// - /// Creates serialization metadata given JsonSerializerOptions and a ConverterStrategy. + /// Creates serialization metadata for a type using a simple converter. /// - public JsonTypeInfoInternal(JsonSerializerOptions options, ConverterStrategy converterStrategy) - : base(typeof(T), options, converterStrategy) + public JsonTypeInfoInternal(JsonSerializerOptions options) + : base(typeof(T), options) { } /// /// Creates serialization metadata for an object. /// - public JsonTypeInfoInternal(JsonSerializerOptions options, JsonObjectInfoValues objectInfo) : base(typeof(T), options, ConverterStrategy.Object) + public JsonTypeInfoInternal(JsonSerializerOptions options, JsonObjectInfoValues objectInfo) : base(typeof(T), options) { #pragma warning disable CS8714 // The type cannot be used as type parameter in the generic type or method. @@ -46,66 +45,40 @@ public JsonTypeInfoInternal(JsonSerializerOptions options, JsonObjectInfoValues< #pragma warning restore CS8714 PropInitFunc = objectInfo.PropertyMetadataInitializer; - Serialize = objectInfo.SerializeHandler; + SerializeHandler = objectInfo.SerializeHandler; PropertyInfoForTypeInfo = JsonMetadataServices.CreateJsonPropertyInfoForClassInfo(typeof(T), this, converter, Options); NumberHandling = objectInfo.NumberHandling; } /// - /// Creates serialization metadata for a . + /// Creates serialization metadata for a collection. /// public JsonTypeInfoInternal( JsonSerializerOptions options, - Func? createObjectFunc, + JsonCollectionInfoValues collectionInfo, Func> converterCreator, - JsonTypeInfo? elementInfo, - JsonNumberHandling numberHandling, - Action? serializeFunc, - Type elementType, object? createObjectWithArgs = null, object? addFunc = null) - : base(typeof(T), options, ConverterStrategy.Enumerable) + : base(typeof(T), options) { - JsonConverter converter = new JsonMetadataServicesConverter(converterCreator, ConverterStrategy.Enumerable); - - ElementType = converter.ElementType; - ElementTypeInfo = elementInfo ?? throw new ArgumentNullException(nameof(elementInfo)); - NumberHandling = numberHandling; - PropertyInfoForTypeInfo = JsonMetadataServices.CreateJsonPropertyInfoForClassInfo(typeof(T), this, converter, options); - Serialize = serializeFunc; - CreateObjectWithArgs = createObjectWithArgs; - AddMethodDelegate = addFunc; - SetCreateObjectFunc(createObjectFunc); - } + if (collectionInfo == null) + { + throw new ArgumentNullException(nameof(collectionInfo)); + } - /// - /// Creates serialization metadata for a . - /// - public JsonTypeInfoInternal( - JsonSerializerOptions options, - Func? createObjectFunc, - Func> converterCreator, - JsonTypeInfo? keyInfo, - JsonTypeInfo? valueInfo, - JsonNumberHandling numberHandling, - Action? serializeFunc, - Type keyType, - Type elementType, - object? createObjectWithArgs = null) - : base(typeof(T), options, ConverterStrategy.Dictionary) - { - JsonConverter converter = new JsonMetadataServicesConverter(converterCreator, ConverterStrategy.Dictionary); + ConverterStrategy strategy = collectionInfo.KeyInfo == null ? ConverterStrategy.Enumerable : ConverterStrategy.Dictionary; + JsonConverter converter = new JsonMetadataServicesConverter(converterCreator, strategy); KeyType = converter.KeyType; ElementType = converter.ElementType; - KeyTypeInfo = keyInfo ?? throw new ArgumentNullException(nameof(keyInfo)); - ElementType = converter.ElementType; - ElementTypeInfo = valueInfo ?? throw new ArgumentNullException(nameof(valueInfo)); - NumberHandling = numberHandling; + KeyTypeInfo = collectionInfo.KeyInfo; + ElementTypeInfo = collectionInfo.ElementInfo ?? throw new ArgumentNullException(nameof(collectionInfo.ElementInfo)); + NumberHandling = collectionInfo.NumberHandling; PropertyInfoForTypeInfo = JsonMetadataServices.CreateJsonPropertyInfoForClassInfo(typeof(T), this, converter, options); - Serialize = serializeFunc; + SerializeHandler = collectionInfo.SerializeHandler; CreateObjectWithArgs = createObjectWithArgs; - SetCreateObjectFunc(createObjectFunc); + AddMethodDelegate = addFunc; + SetCreateObjectFunc(collectionInfo.ObjectCreator); } private void SetCreateObjectFunc(Func? createObjectFunc) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.cs index 337fd7cf1a3ca9..e09f840c2c0f50 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.cs @@ -15,8 +15,8 @@ public abstract class JsonTypeInfo : JsonTypeInfo { private Action? _serialize; - internal JsonTypeInfo(Type type, JsonSerializerOptions options, ConverterStrategy converterStrategy) : - base(type, options, converterStrategy) + internal JsonTypeInfo(Type type, JsonSerializerOptions options) : + base(type, options, dummy: false) { } internal JsonTypeInfo() @@ -25,10 +25,11 @@ internal JsonTypeInfo() } /// - /// A method that serializes an instance of using + /// Serializes an instance of using /// values specified at design time. /// - public Action? Serialize + /// The writer is not flushed after writing. + public Action? SerializeHandler { get { diff --git a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Dictionary.NonStringKey.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Dictionary.NonStringKey.cs index 177b42fae56e6e..2c2ee556fbb991 100644 --- a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Dictionary.NonStringKey.cs +++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Dictionary.NonStringKey.cs @@ -665,23 +665,26 @@ static void ValidateException(NotSupportedException ex) public class CustomInt32ConverterSerializerContext : JsonSerializerContext { - public CustomInt32ConverterSerializerContext() : base(null, null) { } + public CustomInt32ConverterSerializerContext() : base(null) { } public override JsonTypeInfo? GetTypeInfo(Type _) => throw new NotImplementedException(); public JsonTypeInfo> DictionaryInt32String => _dictionaryInt32String ??= CreateDictionaryConverter(); private JsonTypeInfo>? _dictionaryInt32String; + protected override JsonSerializerOptions? GeneratedSerializerOptions => null; + private JsonTypeInfo> CreateDictionaryConverter() { JsonTypeInfo keyInfo = JsonMetadataServices.CreateValueInfo(Options, new ConverterForInt32()); JsonTypeInfo valueInfo = JsonMetadataServices.CreateValueInfo(Options, JsonMetadataServices.StringConverter); - return JsonMetadataServices.CreateDictionaryInfo, int, string>( - Options, - createObjectFunc: () => new(), - keyInfo, valueInfo, - numberHandling: default, - serializeFunc: null - ); + JsonCollectionInfoValues> info = new() + { + ObjectCreator = () => new(), + KeyInfo = keyInfo, + ElementInfo = valueInfo, + }; + + return JsonMetadataServices.CreateDictionaryInfo, int, string>(Options, info); } } diff --git a/src/libraries/System.Text.Json/tests/Common/JsonTestHelper.cs b/src/libraries/System.Text.Json/tests/Common/JsonTestHelper.cs index 528ac2903e4057..1f1c05c9a8aece 100644 --- a/src/libraries/System.Text.Json/tests/Common/JsonTestHelper.cs +++ b/src/libraries/System.Text.Json/tests/Common/JsonTestHelper.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Text.Json.Serialization.Tests; +using System.Text.Json.Serialization.Tests.Schemas.OrderPayload; using System.Text.RegularExpressions; using System.Threading.Tasks; using Xunit; @@ -84,5 +86,187 @@ public static async Task> ToListAsync(this IAsyncEnumerable source public static string StripWhitespace(this string value) => s_stripWhitespace.Replace(value, string.Empty); + + internal static List PopulateLargeObject(int size) + { + List orders = new List(size); + for (int i = 0; i < size; i++) + { + Order order = new Order + { + OrderNumber = i, + Customer = new User + { + UserId = "222ffbbb888kkk", + Name = "John Doe", + Username = "johndoe", + CreatedAt = new DateTime(), + ImageId = string.Empty, + UserType = UserType.Customer, + UpdatedAt = new DateTime(), + TwitterId = string.Empty, + FacebookId = "9988998877662222111", + SubscriptionType = 2, + IsNew = true, + IsEmployee = false + }, + ShippingInfo = new List + { + new ShippingInfo() + { + OrderNumber = i, + Employee = new User + { + UserId = "222ffbbb888" + i, + Name = "Shipping Coordinator", + Username = "coordinator" + i, + CreatedAt = new DateTime(), + ImageId = string.Empty, + UserType = UserType.Employee, + UpdatedAt = new DateTime(), + TwitterId = string.Empty, + SubscriptionType = 0, + IsEmployee = true + }, + CarrierId = "TTT123999MMM", + ShippingType = "Ground", + EstimatedDelivery = new DateTime(), + Tracking = new Uri("http://TestShipCompany.test/track/123" + i), + CarrierName = "TestShipCompany", + HandlingInstruction = "Do cats eat bats? Do cats eat bats. Do cats eat bats? Do cats eat bats. Do cats eat bats? Do cats eat bats. Do cats eat bats? Do cats eat bats", + CurrentStatus = "Out for delivery", + IsDangerous = false + } + }, + OneTime = true, + Cancelled = false, + IsGift = i % 2 == 0, + IsGPickUp = i % 5 == 0, + ShippingAddress = new Address() + { + City = "Redmond" + }, + PickupAddress = new Address + { + City = "Bellevue" + }, + Coupon = SampleEnumInt64.Max, + UserInteractions = new List + { + new Comment + { + Id = 200 + i, + OrderNumber = i, + Customer = new User + { + UserId = "222ffbbb888kkk", + Name = "John Doe", + Username = "johndoe", + CreatedAt = new DateTime(), + ImageId = string.Empty, + UserType = UserType.Customer, + UpdatedAt = new DateTime(), + TwitterId = "twitterId" + i, + FacebookId = "9988998877662222111", + SubscriptionType = 2, + IsNew = true, + IsEmployee = false + }, + Title = "Green Field", + Message = "Down, down, down. Would the fall never come to an end! 'I wonder how many miles I've fallen by this time. I think-' (for, you see, Alice had learnt several things of this sort in her lessons in the schoolroom, and though this was not a very good opportunity for showing off her knowledge, as there was no one to listen to her, still it was good practice to say it over) '-yes, that's about the right distance-but then I wonder what Latitude or Longitude I've got to", + Responses = new List() + } + }, + Created = new DateTime(2019, 11, 10), + Confirmed = new DateTime(2019, 11, 11), + ShippingDate = new DateTime(2019, 11, 12), + EstimatedDelivery = new DateTime(2019, 11, 15), + ReviewedBy = new User() + { + UserId = "222ffbbb888" + i, + Name = "Shipping Coordinator", + Username = "coordinator" + i, + CreatedAt = new DateTime(), + ImageId = string.Empty, + UserType = UserType.Employee, + UpdatedAt = new DateTime(), + TwitterId = string.Empty, + SubscriptionType = 0, + IsEmployee = true + } + }; + List products = new List(); + for (int j = 0; j < i % 4; j++) + { + Product product = new Product() + { + ProductId = Guid.NewGuid(), + Name = "Surface Pro", + SKU = "LL123" + j, + Brand = new TestClassWithInitializedProperties(), + ProductCategory = new SimpleTestClassWithNonGenericCollectionWrappers(), + Description = "Down, down, down. Would the fall never come to an end! 'I wonder how many miles I've fallen by this time. I think-' (for, you see, Alice had learnt several things of this sort in her lessons in the schoolroom, and though this was not a very good opportunity for showing off her knowledge, as there was no one to listen to her, still it was good practice to say it over) '-yes, that's about the right distance-but then I wonder what Latitude or Longitude I've got to", + Created = new DateTime(2000, 10, 12), + Title = "Surface Pro 6 for Business - 512GB", + Price = new Price(), + BestChoice = true, + AverageStars = 4.8f, + Featured = true, + ProductRestrictions = new TestClassWithInitializedProperties(), + SalesInfo = new SimpleTestClassWithGenericCollectionWrappers(), + Origin = SampleEnum.One, + Manufacturer = new BasicCompany(), + Fragile = true, + DetailsUrl = new Uri("http://dotnet.test/link/entries/entry/1"), + NetWeight = 2.7m, + GrossWeight = 3.3m, + Length = i, + Height = i + 1, + Width = i + 2, + FeaturedImage = new FeaturedImage(), + PreviewImage = new PreviewImage(), + KeyWords = new List { "surface", "pro", "laptop" }, + RelatedImages = new List(), + RelatedVideo = new Uri("http://dotnet.test/link/entries/entry/2"), + GuaranteeStartsAt = new DateTime(), + GuaranteeEndsAt = new DateTime(), + IsActive = true, + RelatedProducts = new List() + }; + product.SalesInfo.Initialize(); + List reviews = new List(); + for (int k = 0; k < i % 3; k++) + { + + Review review = new Review + { + Customer = new User + { + UserId = "333344445555", + Name = "Customer" + i + k, + Username = "cust" + i + k, + CreatedAt = new DateTime(), + ImageId = string.Empty, + UserType = UserType.Customer, + SubscriptionType = k + }, + ProductSku = product.SKU, + CustomerName = "Customer" + i + k, + Stars = j + k, + Title = $"Title {i}{j}{k}", + Comment = "", + Images = new List { new Uri($"http://dotnet.test/link/images/image/{k}"), new Uri($"http://dotnet.test/link/images/image/{j}") }, + ReviewId = i + j + k + }; + reviews.Add(review); + } + product.Reviews = reviews; + products.Add(product); + } + order.Products = products; + orders.Add(order); + } + return orders; + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/SerializerInteropTests.cs b/src/libraries/System.Text.Json/tests/Common/NodeInteropTests.cs similarity index 65% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/SerializerInteropTests.cs rename to src/libraries/System.Text.Json/tests/Common/NodeInteropTests.cs index 2d325eac994f1d..20562ef04fbc10 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/SerializerInteropTests.cs +++ b/src/libraries/System.Text.Json/tests/Common/NodeInteropTests.cs @@ -2,20 +2,24 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Text.Json.Nodes; using System.Text.Json.Serialization.Tests.Schemas.OrderPayload; +using System.Threading.Tasks; using Xunit; -namespace System.Text.Json.Nodes.Tests +namespace System.Text.Json.Serialization.Tests { - public static class SerializerInteropTests + public abstract class NodeInteropTests : SerializerTests { + public NodeInteropTests(JsonSerializerWrapperForString serializerWrapper) : base(serializerWrapper) { } + [Fact] - public static void CompareResultsAgainstSerializer() + public async Task CompareResultsAgainstSerializer() { - List obj = Serialization.Tests.StreamTests.PopulateLargeObject(2); - string expected = JsonSerializer.Serialize(obj); + List obj = JsonTestHelper.PopulateLargeObject(2); + string expected = await JsonSerializerWrapperForString.SerializeWrapper(obj); - JsonArray jArray = JsonSerializer.Deserialize(expected); + JsonArray jArray = await JsonSerializerWrapperForString.DeserializeWrapper(expected); string actual = jArray.ToJsonString(); Assert.Equal(expected, actual); @@ -24,7 +28,7 @@ public static void CompareResultsAgainstSerializer() Assert.Equal(expected, actual); } - private class Poco + public class Poco { public string MyString { get; set; } public JsonNode Node { get; set; } @@ -35,7 +39,7 @@ private class Poco } [Fact] - public static void NodesAsPocoProperties() + public async Task NodesAsPocoProperties() { const string Expected = "{\"MyString\":null,\"Node\":42,\"Array\":[43],\"Value\":44,\"IntValue\":45,\"Object\":{\"Property\":46}}"; @@ -51,10 +55,10 @@ public static void NodesAsPocoProperties() } }; - string json = JsonSerializer.Serialize(poco); + string json = await JsonSerializerWrapperForString.SerializeWrapper(poco); Assert.Equal(Expected, json); - poco = JsonSerializer.Deserialize(json); + poco = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Equal(42, (int)poco.Node); Assert.Equal(43, (int)poco.Array[0]); Assert.Equal(44, (int)poco.Value); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/SampleTestData.OrderPayload.cs b/src/libraries/System.Text.Json/tests/Common/SampleTestData.OrderPayload.cs similarity index 100% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/SampleTestData.OrderPayload.cs rename to src/libraries/System.Text.Json/tests/Common/SampleTestData.OrderPayload.cs diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/ContextClasses.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/ContextClasses.cs index f78c0a47eadaa8..682323f2e4a4be 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/ContextClasses.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/ContextClasses.cs @@ -57,14 +57,16 @@ internal partial class JsonContext : JsonSerializerContext private static JsonContext s_defaultContext; public static JsonContext Default => s_defaultContext ??= new JsonContext(new JsonSerializerOptions(s_defaultOptions)); - public JsonContext() : base(null, s_defaultOptions) + public JsonContext() : base(null) { } - public JsonContext(JsonSerializerOptions options) : base(options, s_defaultOptions) + public JsonContext(JsonSerializerOptions options) : base(options) { } + protected override JsonSerializerOptions? GeneratedSerializerOptions => s_defaultOptions; + public override JsonTypeInfo GetTypeInfo(global::System.Type type) { if (type == typeof(JsonMessage)) diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataAndSerializationContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataAndSerializationContextTests.cs index a74dd32819c191..e204e89f6c392a 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataAndSerializationContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataAndSerializationContextTests.cs @@ -50,28 +50,28 @@ public sealed class MetadataAndSerializationContextTests : RealWorldContextTests [Fact] public override void EnsureFastPathGeneratedAsExpected() { - Assert.NotNull(MetadataAndSerializationContext.Default.Location.Serialize); - Assert.NotNull(MetadataAndSerializationContext.Default.NumberTypes.Serialize); - Assert.NotNull(MetadataAndSerializationContext.Default.RepeatedLocation.Serialize); - Assert.NotNull(MetadataAndSerializationContext.Default.ActiveOrUpcomingEvent.Serialize); - Assert.NotNull(MetadataAndSerializationContext.Default.CampaignSummaryViewModel.Serialize); - Assert.NotNull(MetadataAndSerializationContext.Default.IndexViewModel.Serialize); - Assert.NotNull(MetadataAndSerializationContext.Default.WeatherForecastWithPOCOs.Serialize); - Assert.NotNull(MetadataAndSerializationContext.Default.EmptyPoco.Serialize); - Assert.NotNull(MetadataAndSerializationContext.Default.HighLowTemps.Serialize); - Assert.NotNull(MetadataAndSerializationContext.Default.MyType.Serialize); - Assert.NotNull(MetadataAndSerializationContext.Default.MyType2.Serialize); - Assert.NotNull(MetadataAndSerializationContext.Default.MyTypeWithCallbacks.Serialize); - Assert.NotNull(MetadataAndSerializationContext.Default.MyTypeWithPropertyOrdering.Serialize); - Assert.NotNull(MetadataAndSerializationContext.Default.MyIntermediateType.Serialize); - Assert.NotNull(MetadataAndSerializationContext.Default.HighLowTempsImmutable.Serialize); - Assert.NotNull(MetadataAndSerializationContext.Default.MyNestedClass.Serialize); - Assert.NotNull(MetadataAndSerializationContext.Default.MyNestedNestedClass.Serialize); - Assert.Null(MetadataAndSerializationContext.Default.ObjectArray.Serialize); - Assert.Null(MetadataAndSerializationContext.Default.SampleEnum.Serialize); - Assert.Null(MetadataAndSerializationContext.Default.String.Serialize); - Assert.NotNull(MetadataAndSerializationContext.Default.ValueTupleStringInt32Boolean.Serialize); - Assert.NotNull(MetadataAndSerializationContext.Default.ClassWithEnumAndNullable.Serialize); + Assert.NotNull(MetadataAndSerializationContext.Default.Location.SerializeHandler); + Assert.NotNull(MetadataAndSerializationContext.Default.NumberTypes.SerializeHandler); + Assert.NotNull(MetadataAndSerializationContext.Default.RepeatedLocation.SerializeHandler); + Assert.NotNull(MetadataAndSerializationContext.Default.ActiveOrUpcomingEvent.SerializeHandler); + Assert.NotNull(MetadataAndSerializationContext.Default.CampaignSummaryViewModel.SerializeHandler); + Assert.NotNull(MetadataAndSerializationContext.Default.IndexViewModel.SerializeHandler); + Assert.NotNull(MetadataAndSerializationContext.Default.WeatherForecastWithPOCOs.SerializeHandler); + Assert.NotNull(MetadataAndSerializationContext.Default.EmptyPoco.SerializeHandler); + Assert.NotNull(MetadataAndSerializationContext.Default.HighLowTemps.SerializeHandler); + Assert.NotNull(MetadataAndSerializationContext.Default.MyType.SerializeHandler); + Assert.NotNull(MetadataAndSerializationContext.Default.MyType2.SerializeHandler); + Assert.NotNull(MetadataAndSerializationContext.Default.MyTypeWithCallbacks.SerializeHandler); + Assert.NotNull(MetadataAndSerializationContext.Default.MyTypeWithPropertyOrdering.SerializeHandler); + Assert.NotNull(MetadataAndSerializationContext.Default.MyIntermediateType.SerializeHandler); + Assert.NotNull(MetadataAndSerializationContext.Default.HighLowTempsImmutable.SerializeHandler); + Assert.NotNull(MetadataAndSerializationContext.Default.MyNestedClass.SerializeHandler); + Assert.NotNull(MetadataAndSerializationContext.Default.MyNestedNestedClass.SerializeHandler); + Assert.Null(MetadataAndSerializationContext.Default.ObjectArray.SerializeHandler); + Assert.Null(MetadataAndSerializationContext.Default.SampleEnum.SerializeHandler); + Assert.Null(MetadataAndSerializationContext.Default.String.SerializeHandler); + Assert.NotNull(MetadataAndSerializationContext.Default.ValueTupleStringInt32Boolean.SerializeHandler); + Assert.NotNull(MetadataAndSerializationContext.Default.ClassWithEnumAndNullable.SerializeHandler); Assert.NotNull(MetadataAndSerializationContext.Default.ClassWithCustomConverter); Assert.NotNull(MetadataAndSerializationContext.Default.StructWithCustomConverter); Assert.NotNull(MetadataAndSerializationContext.Default.ClassWithCustomConverterFactory); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs index d9fb76625c1829..3b9a83751da611 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs @@ -49,36 +49,36 @@ public sealed class MetadataWithPerTypeAttributeContextTests : RealWorldContextT [Fact] public override void EnsureFastPathGeneratedAsExpected() { - Assert.Null(MetadataWithPerTypeAttributeContext.Default.Location.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.RepeatedLocation.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.NumberTypes.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.ActiveOrUpcomingEvent.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.CampaignSummaryViewModel.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.IndexViewModel.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.WeatherForecastWithPOCOs.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.EmptyPoco.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.HighLowTemps.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.MyType.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.MyType2.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.MyIntermediateType.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.HighLowTempsImmutable.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.MyNestedClass.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.MyNestedNestedClass.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.ObjectArray.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.SampleEnum.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.String.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.ValueTupleStringInt32Boolean.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.ClassWithEnumAndNullable.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.ClassWithCustomConverter.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.StructWithCustomConverter.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.ClassWithCustomConverterFactory.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.StructWithCustomConverterFactory.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.ClassWithCustomConverterProperty.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.StructWithCustomConverterProperty.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.ClassWithCustomConverterPropertyFactory.Serialize); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.StructWithCustomConverterPropertyFactory.Serialize); - Assert.Throws(() => MetadataWithPerTypeAttributeContext.Default.ClassWithBadCustomConverter.Serialize); - Assert.Throws(() => MetadataWithPerTypeAttributeContext.Default.StructWithBadCustomConverter.Serialize); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.Location.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.RepeatedLocation.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.NumberTypes.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.ActiveOrUpcomingEvent.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.CampaignSummaryViewModel.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.IndexViewModel.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.WeatherForecastWithPOCOs.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.EmptyPoco.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.HighLowTemps.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.MyType.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.MyType2.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.MyIntermediateType.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.HighLowTempsImmutable.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.MyNestedClass.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.MyNestedNestedClass.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.ObjectArray.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.SampleEnum.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.String.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.ValueTupleStringInt32Boolean.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.ClassWithEnumAndNullable.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.ClassWithCustomConverter.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.StructWithCustomConverter.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.ClassWithCustomConverterFactory.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.StructWithCustomConverterFactory.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.ClassWithCustomConverterProperty.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.StructWithCustomConverterProperty.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.ClassWithCustomConverterPropertyFactory.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.StructWithCustomConverterPropertyFactory.SerializeHandler); + Assert.Throws(() => MetadataWithPerTypeAttributeContext.Default.ClassWithBadCustomConverter.SerializeHandler); + Assert.Throws(() => MetadataWithPerTypeAttributeContext.Default.StructWithBadCustomConverter.SerializeHandler); } } @@ -147,38 +147,38 @@ public sealed class MetadataContextTests : RealWorldContextTests [Fact] public override void EnsureFastPathGeneratedAsExpected() { - Assert.Null(MetadataContext.Default.Location.Serialize); - Assert.Null(MetadataContext.Default.RepeatedLocation.Serialize); - Assert.Null(MetadataContext.Default.NumberTypes.Serialize); - Assert.Null(MetadataContext.Default.ActiveOrUpcomingEvent.Serialize); - Assert.Null(MetadataContext.Default.CampaignSummaryViewModel.Serialize); - Assert.Null(MetadataContext.Default.IndexViewModel.Serialize); - Assert.Null(MetadataContext.Default.WeatherForecastWithPOCOs.Serialize); - Assert.Null(MetadataContext.Default.EmptyPoco.Serialize); - Assert.Null(MetadataContext.Default.HighLowTemps.Serialize); - Assert.Null(MetadataContext.Default.MyType.Serialize); - Assert.Null(MetadataContext.Default.MyType2.Serialize); - Assert.Null(MetadataContext.Default.MyTypeWithCallbacks.Serialize); - Assert.Null(MetadataContext.Default.MyTypeWithPropertyOrdering.Serialize); - Assert.Null(MetadataContext.Default.MyIntermediateType.Serialize); - Assert.Null(MetadataContext.Default.HighLowTempsImmutable.Serialize); - Assert.Null(MetadataContext.Default.MyNestedClass.Serialize); - Assert.Null(MetadataContext.Default.MyNestedNestedClass.Serialize); - Assert.Null(MetadataContext.Default.ObjectArray.Serialize); - Assert.Null(MetadataContext.Default.SampleEnum.Serialize); - Assert.Null(MetadataContext.Default.String.Serialize); - Assert.Null(MetadataContext.Default.ValueTupleStringInt32Boolean.Serialize); - Assert.Null(MetadataContext.Default.ClassWithEnumAndNullable.Serialize); - Assert.Null(MetadataContext.Default.ClassWithCustomConverter.Serialize); - Assert.Null(MetadataContext.Default.StructWithCustomConverter.Serialize); - Assert.Null(MetadataContext.Default.ClassWithCustomConverterFactory.Serialize); - Assert.Null(MetadataContext.Default.StructWithCustomConverterFactory.Serialize); - Assert.Null(MetadataContext.Default.ClassWithCustomConverterProperty.Serialize); - Assert.Null(MetadataContext.Default.StructWithCustomConverterProperty.Serialize); - Assert.Null(MetadataContext.Default.ClassWithCustomConverterPropertyFactory.Serialize); - Assert.Null(MetadataContext.Default.StructWithCustomConverterPropertyFactory.Serialize); - Assert.Throws(() => MetadataContext.Default.ClassWithBadCustomConverter.Serialize); - Assert.Throws(() => MetadataContext.Default.StructWithBadCustomConverter.Serialize); + Assert.Null(MetadataContext.Default.Location.SerializeHandler); + Assert.Null(MetadataContext.Default.RepeatedLocation.SerializeHandler); + Assert.Null(MetadataContext.Default.NumberTypes.SerializeHandler); + Assert.Null(MetadataContext.Default.ActiveOrUpcomingEvent.SerializeHandler); + Assert.Null(MetadataContext.Default.CampaignSummaryViewModel.SerializeHandler); + Assert.Null(MetadataContext.Default.IndexViewModel.SerializeHandler); + Assert.Null(MetadataContext.Default.WeatherForecastWithPOCOs.SerializeHandler); + Assert.Null(MetadataContext.Default.EmptyPoco.SerializeHandler); + Assert.Null(MetadataContext.Default.HighLowTemps.SerializeHandler); + Assert.Null(MetadataContext.Default.MyType.SerializeHandler); + Assert.Null(MetadataContext.Default.MyType2.SerializeHandler); + Assert.Null(MetadataContext.Default.MyTypeWithCallbacks.SerializeHandler); + Assert.Null(MetadataContext.Default.MyTypeWithPropertyOrdering.SerializeHandler); + Assert.Null(MetadataContext.Default.MyIntermediateType.SerializeHandler); + Assert.Null(MetadataContext.Default.HighLowTempsImmutable.SerializeHandler); + Assert.Null(MetadataContext.Default.MyNestedClass.SerializeHandler); + Assert.Null(MetadataContext.Default.MyNestedNestedClass.SerializeHandler); + Assert.Null(MetadataContext.Default.ObjectArray.SerializeHandler); + Assert.Null(MetadataContext.Default.SampleEnum.SerializeHandler); + Assert.Null(MetadataContext.Default.String.SerializeHandler); + Assert.Null(MetadataContext.Default.ValueTupleStringInt32Boolean.SerializeHandler); + Assert.Null(MetadataContext.Default.ClassWithEnumAndNullable.SerializeHandler); + Assert.Null(MetadataContext.Default.ClassWithCustomConverter.SerializeHandler); + Assert.Null(MetadataContext.Default.StructWithCustomConverter.SerializeHandler); + Assert.Null(MetadataContext.Default.ClassWithCustomConverterFactory.SerializeHandler); + Assert.Null(MetadataContext.Default.StructWithCustomConverterFactory.SerializeHandler); + Assert.Null(MetadataContext.Default.ClassWithCustomConverterProperty.SerializeHandler); + Assert.Null(MetadataContext.Default.StructWithCustomConverterProperty.SerializeHandler); + Assert.Null(MetadataContext.Default.ClassWithCustomConverterPropertyFactory.SerializeHandler); + Assert.Null(MetadataContext.Default.StructWithCustomConverterPropertyFactory.SerializeHandler); + Assert.Throws(() => MetadataContext.Default.ClassWithBadCustomConverter.SerializeHandler); + Assert.Throws(() => MetadataContext.Default.StructWithBadCustomConverter.SerializeHandler); } [Fact] diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs index 82b82880156ef9..ede23fcf3a6e8b 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs @@ -50,37 +50,37 @@ public sealed class MixedModeContextTests : RealWorldContextTests [Fact] public override void EnsureFastPathGeneratedAsExpected() { - Assert.Null(MixedModeContext.Default.Location.Serialize); - Assert.NotNull(MixedModeContext.Default.RepeatedLocation.Serialize); - Assert.Null(MixedModeContext.Default.NumberTypes.Serialize); - Assert.NotNull(MixedModeContext.Default.CampaignSummaryViewModel.Serialize); - Assert.Null(MixedModeContext.Default.IndexViewModel.Serialize); - Assert.Null(MixedModeContext.Default.WeatherForecastWithPOCOs.Serialize); - Assert.NotNull(MixedModeContext.Default.EmptyPoco.Serialize); - Assert.NotNull(MixedModeContext.Default.HighLowTemps.Serialize); - Assert.NotNull(MixedModeContext.Default.MyType.Serialize); - Assert.NotNull(MixedModeContext.Default.MyType2.Serialize); - Assert.NotNull(MixedModeContext.Default.MyTypeWithCallbacks.Serialize); - Assert.NotNull(MixedModeContext.Default.MyTypeWithPropertyOrdering.Serialize); - Assert.NotNull(MixedModeContext.Default.MyIntermediateType.Serialize); - Assert.Null(MixedModeContext.Default.HighLowTempsImmutable.Serialize); - Assert.NotNull(MixedModeContext.Default.MyNestedClass.Serialize); - Assert.NotNull(MixedModeContext.Default.MyNestedNestedClass.Serialize); - Assert.Null(MixedModeContext.Default.ObjectArray.Serialize); - Assert.Null(MixedModeContext.Default.SampleEnum.Serialize); - Assert.Null(MixedModeContext.Default.String.Serialize); - Assert.NotNull(MixedModeContext.Default.ValueTupleStringInt32Boolean.Serialize); - Assert.NotNull(MixedModeContext.Default.ClassWithEnumAndNullable.Serialize); - Assert.Null(MixedModeContext.Default.ClassWithCustomConverter.Serialize); - Assert.Null(MixedModeContext.Default.StructWithCustomConverter.Serialize); - Assert.Null(MixedModeContext.Default.ClassWithCustomConverterFactory.Serialize); - Assert.Null(MixedModeContext.Default.StructWithCustomConverterFactory.Serialize); - Assert.Null(MixedModeContext.Default.ClassWithCustomConverterProperty.Serialize); - Assert.Null(MixedModeContext.Default.StructWithCustomConverterProperty.Serialize); - Assert.Null(MixedModeContext.Default.ClassWithCustomConverterPropertyFactory.Serialize); - Assert.Null(MixedModeContext.Default.StructWithCustomConverterPropertyFactory.Serialize); - Assert.Throws(() => MixedModeContext.Default.ClassWithBadCustomConverter.Serialize); - Assert.Throws(() => MixedModeContext.Default.StructWithBadCustomConverter.Serialize); + Assert.Null(MixedModeContext.Default.Location.SerializeHandler); + Assert.NotNull(MixedModeContext.Default.RepeatedLocation.SerializeHandler); + Assert.Null(MixedModeContext.Default.NumberTypes.SerializeHandler); + Assert.NotNull(MixedModeContext.Default.CampaignSummaryViewModel.SerializeHandler); + Assert.Null(MixedModeContext.Default.IndexViewModel.SerializeHandler); + Assert.Null(MixedModeContext.Default.WeatherForecastWithPOCOs.SerializeHandler); + Assert.NotNull(MixedModeContext.Default.EmptyPoco.SerializeHandler); + Assert.NotNull(MixedModeContext.Default.HighLowTemps.SerializeHandler); + Assert.NotNull(MixedModeContext.Default.MyType.SerializeHandler); + Assert.NotNull(MixedModeContext.Default.MyType2.SerializeHandler); + Assert.NotNull(MixedModeContext.Default.MyTypeWithCallbacks.SerializeHandler); + Assert.NotNull(MixedModeContext.Default.MyTypeWithPropertyOrdering.SerializeHandler); + Assert.NotNull(MixedModeContext.Default.MyIntermediateType.SerializeHandler); + Assert.Null(MixedModeContext.Default.HighLowTempsImmutable.SerializeHandler); + Assert.NotNull(MixedModeContext.Default.MyNestedClass.SerializeHandler); + Assert.NotNull(MixedModeContext.Default.MyNestedNestedClass.SerializeHandler); + Assert.Null(MixedModeContext.Default.ObjectArray.SerializeHandler); + Assert.Null(MixedModeContext.Default.SampleEnum.SerializeHandler); + Assert.Null(MixedModeContext.Default.String.SerializeHandler); + Assert.NotNull(MixedModeContext.Default.ValueTupleStringInt32Boolean.SerializeHandler); + Assert.NotNull(MixedModeContext.Default.ClassWithEnumAndNullable.SerializeHandler); + Assert.Null(MixedModeContext.Default.ClassWithCustomConverter.SerializeHandler); + Assert.Null(MixedModeContext.Default.StructWithCustomConverter.SerializeHandler); + Assert.Null(MixedModeContext.Default.ClassWithCustomConverterFactory.SerializeHandler); + Assert.Null(MixedModeContext.Default.StructWithCustomConverterFactory.SerializeHandler); + Assert.Null(MixedModeContext.Default.ClassWithCustomConverterProperty.SerializeHandler); + Assert.Null(MixedModeContext.Default.StructWithCustomConverterProperty.SerializeHandler); + Assert.Null(MixedModeContext.Default.ClassWithCustomConverterPropertyFactory.SerializeHandler); + Assert.Null(MixedModeContext.Default.StructWithCustomConverterPropertyFactory.SerializeHandler); + Assert.Throws(() => MixedModeContext.Default.ClassWithBadCustomConverter.SerializeHandler); + Assert.Throws(() => MixedModeContext.Default.StructWithBadCustomConverter.SerializeHandler); } [Fact] diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs index 8681cc711348f2..520be63598f441 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs @@ -198,7 +198,7 @@ public virtual void RoundtripWithCustomConverterProperty_Class() }; // Types with properties in custom converters do not support fast path serialization. - Assert.True(DefaultContext.ClassWithCustomConverterProperty.Serialize is null); + Assert.True(DefaultContext.ClassWithCustomConverterProperty.SerializeHandler is null); if (DefaultContext.JsonSourceGenerationMode == JsonSourceGenerationMode.Serialization) { @@ -225,7 +225,7 @@ public virtual void RoundtripWithCustomConverterProperty_Struct() }; // Types with properties in custom converters do not support fast path serialization. - Assert.True(DefaultContext.StructWithCustomConverterProperty.Serialize is null); + Assert.True(DefaultContext.StructWithCustomConverterProperty.SerializeHandler is null); if (DefaultContext.JsonSourceGenerationMode == JsonSourceGenerationMode.Serialization) { @@ -757,13 +757,15 @@ public void GetTypeInfoCalledDuringPolymorphicSerialization() internal class CustomContext : JsonSerializerContext { - public CustomContext(JsonSerializerOptions options) : base(options, null) { } + public CustomContext(JsonSerializerOptions options) : base(options) { } private JsonTypeInfo _object; public JsonTypeInfo Object => _object ??= JsonMetadataServices.CreateValueInfo(Options, JsonMetadataServices.ObjectConverter); private JsonTypeInfo _objectArray; - public JsonTypeInfo ObjectArray => _objectArray ??= JsonMetadataServices.CreateArrayInfo(Options, Object, default, serializeFunc: null); + public JsonTypeInfo ObjectArray => _objectArray ??= JsonMetadataServices.CreateArrayInfo(Options, new JsonCollectionInfoValues { ElementInfo = Object }); + + protected override JsonSerializerOptions? GeneratedSerializerOptions => null; public override JsonTypeInfo GetTypeInfo(Type type) { @@ -780,7 +782,7 @@ protected static void AssertFastPathLogicCorrect(string expectedJson, T value { using MemoryStream ms = new(); using Utf8JsonWriter writer = new(ms); - typeInfo.Serialize!(writer, value); + typeInfo.SerializeHandler!(writer, value); writer.Flush(); JsonTestHelper.AssertJsonEqual(expectedJson, Encoding.UTF8.GetString(ms.ToArray())); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/NodeInteropTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/NodeInteropTests.cs new file mode 100644 index 00000000000000..4a27daaf2ebc5b --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/NodeInteropTests.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.Collections.Generic; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Tests; +using System.Text.Json.Serialization.Tests.Schemas.OrderPayload; + +namespace System.Text.Json.SourceGeneration.Tests +{ + public sealed partial class NodeInteropTests_Metadata : NodeInteropTests + { + public NodeInteropTests_Metadata() + : base(new StringSerializerWrapper(NodeInteropTestsContext_Metadata.Default, (options) => new NodeInteropTestsContext_Metadata(options))) + { + } + + [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)] + [JsonSerializable(typeof(List))] + [JsonSerializable(typeof(JsonArray))] + [JsonSerializable(typeof(Poco))] + [JsonSerializable(typeof(string))] + internal sealed partial class NodeInteropTestsContext_Metadata : JsonSerializerContext + { + } + } + + public sealed partial class NodeInteropTests_Default : NodeInteropTests + { + public NodeInteropTests_Default() + : base(new StringSerializerWrapper(NodeInteropTestsContext_Default.Default, (options) => new NodeInteropTestsContext_Default(options))) + { + } + + [JsonSerializable(typeof(List))] + [JsonSerializable(typeof(JsonArray))] + [JsonSerializable(typeof(Poco))] + [JsonSerializable(typeof(string))] + internal sealed partial class NodeInteropTestsContext_Default : JsonSerializerContext + { + } + } +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs index 639f380d97fcfa..5f4b0dc8362372 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs @@ -128,35 +128,35 @@ internal SerializationContextTests(ITestContext defaultContext, Func(() => SerializationContext.Default.ClassWithBadCustomConverter.Serialize); - Assert.Throws(() => SerializationContext.Default.StructWithBadCustomConverter.Serialize); + Assert.NotNull(SerializationContext.Default.Location.SerializeHandler); + Assert.NotNull(SerializationContext.Default.RepeatedLocation.SerializeHandler); + Assert.NotNull(SerializationContext.Default.NumberTypes.SerializeHandler); + Assert.NotNull(SerializationContext.Default.ActiveOrUpcomingEvent.SerializeHandler); + Assert.NotNull(SerializationContext.Default.CampaignSummaryViewModel.SerializeHandler); + Assert.NotNull(SerializationContext.Default.IndexViewModel.SerializeHandler); + Assert.NotNull(SerializationContext.Default.WeatherForecastWithPOCOs.SerializeHandler); + Assert.NotNull(SerializationContext.Default.WeatherForecastWithPOCOs.SerializeHandler); + Assert.NotNull(SerializationContext.Default.HighLowTemps.SerializeHandler); + Assert.NotNull(SerializationContext.Default.MyType.SerializeHandler); + Assert.NotNull(SerializationContext.Default.MyType2.SerializeHandler); + Assert.NotNull(SerializationContext.Default.MyTypeWithCallbacks.SerializeHandler); + Assert.NotNull(SerializationContext.Default.MyTypeWithPropertyOrdering.SerializeHandler); + Assert.NotNull(SerializationContext.Default.MyIntermediateType.SerializeHandler); + Assert.NotNull(SerializationContext.Default.HighLowTempsImmutable.SerializeHandler); + Assert.NotNull(SerializationContext.Default.MyNestedClass.SerializeHandler); + Assert.NotNull(SerializationContext.Default.MyNestedNestedClass.SerializeHandler); + Assert.Null(SerializationContext.Default.ObjectArray.SerializeHandler); + Assert.Null(SerializationContext.Default.String.SerializeHandler); + Assert.NotNull(SerializationContext.Default.ValueTupleStringInt32Boolean.SerializeHandler); + Assert.NotNull(SerializationContext.Default.ClassWithEnumAndNullable.SerializeHandler); + Assert.Null(SerializationContext.Default.ClassWithCustomConverter.SerializeHandler); + Assert.Null(SerializationContext.Default.StructWithCustomConverter.SerializeHandler); + Assert.Null(SerializationContext.Default.ClassWithCustomConverterFactory.SerializeHandler); + Assert.Null(SerializationContext.Default.StructWithCustomConverterFactory.SerializeHandler); + Assert.Null(SerializationContext.Default.ClassWithCustomConverterProperty.SerializeHandler); + Assert.Null(SerializationContext.Default.StructWithCustomConverterProperty.SerializeHandler); + Assert.Throws(() => SerializationContext.Default.ClassWithBadCustomConverter.SerializeHandler); + Assert.Throws(() => SerializationContext.Default.StructWithBadCustomConverter.SerializeHandler); } [Fact] @@ -398,35 +398,35 @@ public sealed class SerializationWithPerTypeAttributeContextTests : Serializatio [Fact] public override void EnsureFastPathGeneratedAsExpected() { - Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.Location.Serialize); - Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.RepeatedLocation.Serialize); - Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.ActiveOrUpcomingEvent.Serialize); - Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.CampaignSummaryViewModel.Serialize); - Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.IndexViewModel.Serialize); - Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.WeatherForecastWithPOCOs.Serialize); - Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.WeatherForecastWithPOCOs.Serialize); - Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.HighLowTemps.Serialize); - Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.MyType.Serialize); - Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.MyType2.Serialize); - Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.MyIntermediateType.Serialize); - Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.HighLowTempsImmutable.Serialize); - Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.MyNestedClass.Serialize); - Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.MyNestedNestedClass.Serialize); - Assert.Null(SerializationWithPerTypeAttributeContext.Default.ObjectArray.Serialize); - Assert.Null(SerializationWithPerTypeAttributeContext.Default.SampleEnum.Serialize); - Assert.Null(SerializationWithPerTypeAttributeContext.Default.String.Serialize); - Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.ValueTupleStringInt32Boolean.Serialize); - Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.ClassWithEnumAndNullable.Serialize); - Assert.Null(SerializationWithPerTypeAttributeContext.Default.ClassWithCustomConverter.Serialize); - Assert.Null(SerializationWithPerTypeAttributeContext.Default.StructWithCustomConverter.Serialize); - Assert.Null(SerializationWithPerTypeAttributeContext.Default.ClassWithCustomConverterFactory.Serialize); - Assert.Null(SerializationWithPerTypeAttributeContext.Default.StructWithCustomConverterFactory.Serialize); - Assert.Null(SerializationWithPerTypeAttributeContext.Default.ClassWithCustomConverterProperty.Serialize); - Assert.Null(SerializationWithPerTypeAttributeContext.Default.StructWithCustomConverterProperty.Serialize); - Assert.Null(SerializationWithPerTypeAttributeContext.Default.ClassWithCustomConverterPropertyFactory.Serialize); - Assert.Null(SerializationWithPerTypeAttributeContext.Default.StructWithCustomConverterPropertyFactory.Serialize); - Assert.Throws(() => SerializationWithPerTypeAttributeContext.Default.ClassWithBadCustomConverter.Serialize); - Assert.Throws(() => SerializationWithPerTypeAttributeContext.Default.StructWithBadCustomConverter.Serialize); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.Location.SerializeHandler); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.RepeatedLocation.SerializeHandler); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.ActiveOrUpcomingEvent.SerializeHandler); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.CampaignSummaryViewModel.SerializeHandler); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.IndexViewModel.SerializeHandler); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.WeatherForecastWithPOCOs.SerializeHandler); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.WeatherForecastWithPOCOs.SerializeHandler); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.HighLowTemps.SerializeHandler); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.MyType.SerializeHandler); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.MyType2.SerializeHandler); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.MyIntermediateType.SerializeHandler); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.HighLowTempsImmutable.SerializeHandler); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.MyNestedClass.SerializeHandler); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.MyNestedNestedClass.SerializeHandler); + Assert.Null(SerializationWithPerTypeAttributeContext.Default.ObjectArray.SerializeHandler); + Assert.Null(SerializationWithPerTypeAttributeContext.Default.SampleEnum.SerializeHandler); + Assert.Null(SerializationWithPerTypeAttributeContext.Default.String.SerializeHandler); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.ValueTupleStringInt32Boolean.SerializeHandler); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.ClassWithEnumAndNullable.SerializeHandler); + Assert.Null(SerializationWithPerTypeAttributeContext.Default.ClassWithCustomConverter.SerializeHandler); + Assert.Null(SerializationWithPerTypeAttributeContext.Default.StructWithCustomConverter.SerializeHandler); + Assert.Null(SerializationWithPerTypeAttributeContext.Default.ClassWithCustomConverterFactory.SerializeHandler); + Assert.Null(SerializationWithPerTypeAttributeContext.Default.StructWithCustomConverterFactory.SerializeHandler); + Assert.Null(SerializationWithPerTypeAttributeContext.Default.ClassWithCustomConverterProperty.SerializeHandler); + Assert.Null(SerializationWithPerTypeAttributeContext.Default.StructWithCustomConverterProperty.SerializeHandler); + Assert.Null(SerializationWithPerTypeAttributeContext.Default.ClassWithCustomConverterPropertyFactory.SerializeHandler); + Assert.Null(SerializationWithPerTypeAttributeContext.Default.StructWithCustomConverterPropertyFactory.SerializeHandler); + Assert.Throws(() => SerializationWithPerTypeAttributeContext.Default.ClassWithBadCustomConverter.SerializeHandler); + Assert.Throws(() => SerializationWithPerTypeAttributeContext.Default.StructWithBadCustomConverter.SerializeHandler); } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationLogicTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationLogicTests.cs index a086659da54779..2bfb07086eecd9 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationLogicTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationLogicTests.cs @@ -52,11 +52,11 @@ public static void SerializationFuncNotInvokedWhenNotSupported(JsonSerializerOpt [Fact] public static void DictionaryFastPathPrimitiveValueSupported() { - Assert.NotNull(DictionaryTypeContext.Default.DictionaryStringString.Serialize); - Assert.NotNull(DictionaryTypeContext.Default.DictionaryStringJsonMessage.Serialize); - Assert.NotNull(DictionaryTypeContext.Default.JsonMessage.Serialize); - Assert.Null(DictionaryTypeContext.Default.String.Serialize); - Assert.Null(DictionaryTypeContext.Default.Int32.Serialize); + Assert.NotNull(DictionaryTypeContext.Default.DictionaryStringString.SerializeHandler); + Assert.NotNull(DictionaryTypeContext.Default.DictionaryStringJsonMessage.SerializeHandler); + Assert.NotNull(DictionaryTypeContext.Default.JsonMessage.SerializeHandler); + Assert.Null(DictionaryTypeContext.Default.String.SerializeHandler); + Assert.Null(DictionaryTypeContext.Default.Int32.SerializeHandler); } // Options with features that apply only to deserialization. diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets index 71681de2554820..b16e2ccc8f23e2 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets @@ -40,10 +40,12 @@ + + @@ -75,6 +77,7 @@ + diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerApiValidation.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerApiValidation.cs index aeb4c30fd2cabd..515579fc26ec85 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerApiValidation.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerApiValidation.cs @@ -61,9 +61,10 @@ private class MyPoco { } internal partial class MyDummyContext : JsonSerializerContext { - public MyDummyContext() : base(new JsonSerializerOptions(), new JsonSerializerOptions()) { } - public MyDummyContext(JsonSerializerOptions options) : base(options, new JsonSerializerOptions()) { } + public MyDummyContext() : base(new JsonSerializerOptions()) { } + public MyDummyContext(JsonSerializerOptions options) : base(options) { } public override JsonTypeInfo? GetTypeInfo(Type type) => throw new NotImplementedException(); + protected override JsonSerializerOptions? GeneratedSerializerOptions => null; } private JsonTypeInfo myDummyTypeInfo = GetTypeInfo(); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.Options.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.Options.cs index c25e7803f9220d..567a2776b1e0c8 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.Options.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.Options.cs @@ -89,17 +89,20 @@ public void OptionsImmutableAfterBinding() private class MyJsonContext : JsonSerializerContext { - public MyJsonContext() : base(null, null) { } + public MyJsonContext() : base(null) { } - public MyJsonContext(JsonSerializerOptions options) : base(options, null) { } + public MyJsonContext(JsonSerializerOptions options) : base(options) { } public override JsonTypeInfo? GetTypeInfo(Type type) => throw new NotImplementedException(); + + protected override JsonSerializerOptions? GeneratedSerializerOptions => null; } private class MyJsonContextThatSetsOptionsInParameterlessCtor : JsonSerializerContext { - public MyJsonContextThatSetsOptionsInParameterlessCtor() : base(new JsonSerializerOptions(), null) { } + public MyJsonContextThatSetsOptionsInParameterlessCtor() : base(new JsonSerializerOptions()) { } public override JsonTypeInfo? GetTypeInfo(Type type) => throw new NotImplementedException(); + protected override JsonSerializerOptions? GeneratedSerializerOptions => null; } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/NodeInteropTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/NodeInteropTests.cs new file mode 100644 index 00000000000000..a424cb884f5605 --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/NodeInteropTests.cs @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Text.Json.Serialization.Tests +{ + public sealed partial class NodeInteropTestsDynamic : NodeInteropTests + { + public NodeInteropTestsDynamic() : base(JsonSerializerWrapperForString.StringSerializer) { } + } +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.WriteTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.WriteTests.cs index 35d96103569f37..d7e6e92ba90e02 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.WriteTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.WriteTests.cs @@ -289,7 +289,7 @@ public async Task LargeJsonFile(int bufferSize) [InlineData(1000, false, false)] public async Task VeryLargeJsonFileTest(int payloadSize, bool ignoreNull, bool writeIndented) { - List list = PopulateLargeObject(payloadSize); + List list = JsonTestHelper.PopulateLargeObject(payloadSize); JsonSerializerOptions options = new JsonSerializerOptions { @@ -337,10 +337,10 @@ public async Task DeepNestedJsonFileTest(int depthFactor, bool ignoreNull, bool int length = ListLength * depthFactor; List[] orders = new List[length]; - orders[0] = PopulateLargeObject(1); + orders[0] = JsonTestHelper.PopulateLargeObject(1); for (int i = 1; i < length; i++ ) { - orders[i] = PopulateLargeObject(1); + orders[i] = JsonTestHelper.PopulateLargeObject(1); orders[i - 1][0].RelatedOrder = orders[i]; } @@ -381,10 +381,10 @@ public async Task NestedJsonFileCircularDependencyTest(int depthFactor) int length = ListLength * depthFactor; List[] orders = new List[length]; - orders[0] = PopulateLargeObject(1000); + orders[0] = JsonTestHelper.PopulateLargeObject(1000); for (int i = 1; i < length; i++) { - orders[i] = PopulateLargeObject(1); + orders[i] = JsonTestHelper.PopulateLargeObject(1); orders[i - 1][0].RelatedOrder = orders[i]; } @@ -464,188 +464,6 @@ private static List GenerateListOfSize(int size) } return list; } - - internal static List PopulateLargeObject(int size) - { - List orders = new List(size); - for (int i = 0; i < size; i++) - { - Order order = new Order - { - OrderNumber = i, - Customer = new User - { - UserId = "222ffbbb888kkk", - Name = "John Doe", - Username = "johndoe", - CreatedAt = new DateTime(), - ImageId = string.Empty, - UserType = UserType.Customer, - UpdatedAt = new DateTime(), - TwitterId = string.Empty, - FacebookId = "9988998877662222111", - SubscriptionType = 2, - IsNew = true, - IsEmployee = false - }, - ShippingInfo = new List - { - new ShippingInfo() - { - OrderNumber = i, - Employee = new User - { - UserId = "222ffbbb888" + i, - Name = "Shipping Coordinator", - Username = "coordinator" + i, - CreatedAt = new DateTime(), - ImageId = string.Empty, - UserType = UserType.Employee, - UpdatedAt = new DateTime(), - TwitterId = string.Empty, - SubscriptionType = 0, - IsEmployee = true - }, - CarrierId = "TTT123999MMM", - ShippingType = "Ground", - EstimatedDelivery = new DateTime(), - Tracking = new Uri("http://TestShipCompany.test/track/123" + i), - CarrierName = "TestShipCompany", - HandlingInstruction = "Do cats eat bats? Do cats eat bats. Do cats eat bats? Do cats eat bats. Do cats eat bats? Do cats eat bats. Do cats eat bats? Do cats eat bats", - CurrentStatus = "Out for delivery", - IsDangerous = false - } - }, - OneTime = true, - Cancelled = false, - IsGift = i % 2 == 0, - IsGPickUp = i % 5 == 0, - ShippingAddress = new Address() - { - City = "Redmond" - }, - PickupAddress = new Address - { - City = "Bellevue" - }, - Coupon = SampleEnumInt64.Max, - UserInteractions = new List - { - new Comment - { - Id = 200 + i, - OrderNumber = i, - Customer = new User - { - UserId = "222ffbbb888kkk", - Name = "John Doe", - Username = "johndoe", - CreatedAt = new DateTime(), - ImageId = string.Empty, - UserType = UserType.Customer, - UpdatedAt = new DateTime(), - TwitterId = "twitterId" + i, - FacebookId = "9988998877662222111", - SubscriptionType = 2, - IsNew = true, - IsEmployee = false - }, - Title = "Green Field", - Message = "Down, down, down. Would the fall never come to an end! 'I wonder how many miles I've fallen by this time. I think-' (for, you see, Alice had learnt several things of this sort in her lessons in the schoolroom, and though this was not a very good opportunity for showing off her knowledge, as there was no one to listen to her, still it was good practice to say it over) '-yes, that's about the right distance-but then I wonder what Latitude or Longitude I've got to", - Responses = new List() - } - }, - Created = new DateTime(2019, 11, 10), - Confirmed = new DateTime(2019, 11, 11), - ShippingDate = new DateTime(2019, 11, 12), - EstimatedDelivery = new DateTime(2019, 11, 15), - ReviewedBy = new User() - { - UserId = "222ffbbb888" + i, - Name = "Shipping Coordinator", - Username = "coordinator" + i, - CreatedAt = new DateTime(), - ImageId = string.Empty, - UserType = UserType.Employee, - UpdatedAt = new DateTime(), - TwitterId = string.Empty, - SubscriptionType = 0, - IsEmployee = true - } - }; - List products = new List(); - for (int j = 0; j < i % 4; j++) - { - Product product = new Product() - { - ProductId = Guid.NewGuid(), - Name = "Surface Pro", - SKU = "LL123" + j, - Brand = new TestClassWithInitializedProperties(), - ProductCategory = new SimpleTestClassWithNonGenericCollectionWrappers(), - Description = "Down, down, down. Would the fall never come to an end! 'I wonder how many miles I've fallen by this time. I think-' (for, you see, Alice had learnt several things of this sort in her lessons in the schoolroom, and though this was not a very good opportunity for showing off her knowledge, as there was no one to listen to her, still it was good practice to say it over) '-yes, that's about the right distance-but then I wonder what Latitude or Longitude I've got to", - Created = new DateTime(2000, 10, 12), - Title = "Surface Pro 6 for Business - 512GB", - Price = new Price(), - BestChoice = true, - AverageStars = 4.8f, - Featured = true, - ProductRestrictions = new TestClassWithInitializedProperties(), - SalesInfo = new SimpleTestClassWithGenericCollectionWrappers(), - Origin = SampleEnum.One, - Manufacturer = new BasicCompany(), - Fragile = true, - DetailsUrl = new Uri("http://dotnet.test/link/entries/entry/1"), - NetWeight = 2.7m, - GrossWeight = 3.3m, - Length = i, - Height = i + 1, - Width = i + 2, - FeaturedImage = new FeaturedImage(), - PreviewImage = new PreviewImage(), - KeyWords = new List { "surface", "pro", "laptop" }, - RelatedImages = new List(), - RelatedVideo = new Uri("http://dotnet.test/link/entries/entry/2"), - GuaranteeStartsAt = new DateTime(), - GuaranteeEndsAt = new DateTime(), - IsActive = true, - RelatedProducts = new List() - }; - product.SalesInfo.Initialize(); - List reviews = new List(); - for (int k = 0; k < i % 3; k++) - { - - Review review = new Review - { - Customer = new User - { - UserId = "333344445555", - Name = "Customer" + i + k, - Username = "cust" + i + k, - CreatedAt = new DateTime(), - ImageId = string.Empty, - UserType = UserType.Customer, - SubscriptionType = k - }, - ProductSku = product.SKU, - CustomerName = "Customer" + i + k, - Stars = j + k, - Title = $"Title {i}{j}{k}", - Comment = "", - Images = new List{ new Uri($"http://dotnet.test/link/images/image/{k}"), new Uri($"http://dotnet.test/link/images/image/{j}")}, - ReviewId = i + j +k - }; - reviews.Add(review); - } - product.Reviews = reviews; - products.Add(product); - } - order.Products = products; - orders.Add(order); - } - return orders; - } } public sealed class TestStream : Stream diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj index 9311481bcc3113..475d0b30ee3bfb 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj @@ -44,10 +44,12 @@ + + @@ -88,7 +90,6 @@ - @@ -155,6 +156,7 @@ + @@ -173,7 +175,6 @@ - diff --git a/src/libraries/System.Threading.Tasks.Parallel/src/System/Threading/Tasks/Parallel.ForEachAsync.cs b/src/libraries/System.Threading.Tasks.Parallel/src/System/Threading/Tasks/Parallel.ForEachAsync.cs index ee56e0d1a18d15..3a7d40b7d0489b 100644 --- a/src/libraries/System.Threading.Tasks.Parallel/src/System/Threading/Tasks/Parallel.ForEachAsync.cs +++ b/src/libraries/System.Threading.Tasks.Parallel/src/System/Threading/Tasks/Parallel.ForEachAsync.cs @@ -480,18 +480,10 @@ public void Complete() } else { - // Fault with all of the received exceptions, but filter out those due to inner cancellation, - // as they're effectively an implementation detail and stem from the original exception. - Debug.Assert(_exceptions.Count > 0, "If _exceptions was created, it should have also been populated."); - for (int i = 0; i < _exceptions.Count; i++) - { - if (_exceptions[i] is OperationCanceledException oce && oce.CancellationToken == Cancellation.Token) - { - _exceptions[i] = null!; - } - } - _exceptions.RemoveAll(e => e is null); - Debug.Assert(_exceptions.Count > 0, "Since external cancellation wasn't requested, there should have been a non-cancellation exception that triggered internal cancellation."); + // Fail the task with the resulting exceptions. The first should be the initial + // exception that triggered the operation to shut down. The others, if any, may + // include cancellation exceptions from other concurrent operations being canceled + // in response to the primary exception. taskSet = TrySetException(_exceptions); } diff --git a/src/libraries/System.Threading.Tasks.Parallel/tests/ParallelForEachAsyncTests.cs b/src/libraries/System.Threading.Tasks.Parallel/tests/ParallelForEachAsyncTests.cs index 91747d3828b622..97ac99bc159600 100644 --- a/src/libraries/System.Threading.Tasks.Parallel/tests/ParallelForEachAsyncTests.cs +++ b/src/libraries/System.Threading.Tasks.Parallel/tests/ParallelForEachAsyncTests.cs @@ -618,6 +618,64 @@ static async IAsyncEnumerable Iterate() Assert.True(t.IsCanceled); } + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + [InlineData(false)] + [InlineData(true)] + public async Task Cancellation_FaultsForOceForNonCancellation(bool internalToken) + { + static async IAsyncEnumerable Iterate() + { + int counter = 0; + while (true) + { + await Task.Yield(); + yield return counter++; + } + } + + var cts = new CancellationTokenSource(); + + Task t = Parallel.ForEachAsync(Iterate(), new ParallelOptions { CancellationToken = cts.Token }, (item, cancellationToken) => + { + throw new OperationCanceledException(internalToken ? cancellationToken : cts.Token); + }); + + await Assert.ThrowsAnyAsync(() => t); + Assert.True(t.IsFaulted); + } + + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + [InlineData(0, 4)] + [InlineData(1, 4)] + [InlineData(2, 4)] + [InlineData(3, 4)] + [InlineData(4, 4)] + public async Task Cancellation_InternalCancellationExceptionsArentFilteredOut(int numThrowingNonCanceledOce, int total) + { + var cts = new CancellationTokenSource(); + + var barrier = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + int remainingCount = total; + + Task t = Parallel.ForEachAsync(Enumerable.Range(0, total), new ParallelOptions { CancellationToken = cts.Token, MaxDegreeOfParallelism = total }, async (item, cancellationToken) => + { + // Wait for all operations to be started + if (Interlocked.Decrement(ref remainingCount) == 0) + { + barrier.SetResult(); + } + await barrier.Task; + + throw item < numThrowingNonCanceledOce ? + new OperationCanceledException(cancellationToken) : + throw new FormatException(); + }); + + await Assert.ThrowsAnyAsync(() => t); + Assert.Equal(total, t.Exception.InnerExceptions.Count); + Assert.Equal(numThrowingNonCanceledOce, t.Exception.InnerExceptions.Count(e => e is OperationCanceledException)); + } + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void Exception_FromGetEnumerator_Sync() { @@ -672,7 +730,6 @@ static IEnumerable Iterate() Task t = Parallel.ForEachAsync(Iterate(), (item, cancellationToken) => default); await Assert.ThrowsAsync(() => t); Assert.True(t.IsFaulted); - Assert.Equal(1, t.Exception.InnerExceptions.Count); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] @@ -694,7 +751,6 @@ static async IAsyncEnumerable Iterate() Task t = Parallel.ForEachAsync(Iterate(), (item, cancellationToken) => default); await Assert.ThrowsAsync(() => t); Assert.True(t.IsFaulted); - Assert.Equal(1, t.Exception.InnerExceptions.Count); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] @@ -771,7 +827,6 @@ public async Task Exception_FromDispose_Sync() Task t = Parallel.ForEachAsync((IEnumerable)new ThrowsExceptionFromDispose(), (item, cancellationToken) => default); await Assert.ThrowsAsync(() => t); Assert.True(t.IsFaulted); - Assert.Equal(1, t.Exception.InnerExceptions.Count); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] @@ -780,7 +835,6 @@ public async Task Exception_FromDispose_Async() Task t = Parallel.ForEachAsync((IAsyncEnumerable)new ThrowsExceptionFromDispose(), (item, cancellationToken) => default); await Assert.ThrowsAsync(() => t); Assert.True(t.IsFaulted); - Assert.Equal(1, t.Exception.InnerExceptions.Count); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 621abed1cef9cb..5b42461a64c944 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -196,6 +196,10 @@ + + + + diff --git a/src/mono/CMakeLists.txt b/src/mono/CMakeLists.txt index bd636b92e284ea..bbd8de5308a45a 100644 --- a/src/mono/CMakeLists.txt +++ b/src/mono/CMakeLists.txt @@ -245,11 +245,16 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") add_compile_options(/EHsc) # set exception handling behavior add_compile_options(/FC) # use full pathnames in diagnostics if(CMAKE_BUILD_TYPE STREQUAL "Release") - add_compile_options(/Oi) # enable intrinsics - add_compile_options(/GF) # enable string pooling - add_compile_options(/Zi) # enable debugging information - add_compile_options(/GL) # whole program optimization - add_link_options(/LTCG) # link-time code generation + add_compile_options(/Oi) # enable intrinsics + add_compile_options(/GF) # enable string pooling + add_compile_options(/Zi) # enable debugging information + add_compile_options(/GL) # whole program optimization + add_link_options(/LTCG) # link-time code generation + add_link_options(/DEBUG) # enable debugging information + add_link_options(/OPT:REF) # optimize: remove unreferenced functions & data + add_link_options(/OPT:ICF) # optimize: enable COMDAT folding + # the combination of /Zi compiler flag and /DEBUG /OPT:REF /OPT:ICF + # linker flags is needed to create .pdb output on release builds endif() elseif(CMAKE_SYSTEM_NAME STREQUAL "SunOS") set(HOST_SOLARIS 1) diff --git a/src/mono/mono.proj b/src/mono/mono.proj index 9b4d254d42598a..c4bd405f0e606b 100644 --- a/src/mono/mono.proj +++ b/src/mono/mono.proj @@ -36,6 +36,8 @@ $(LibPrefix)$(MonoLibName)$(StaticLibExt) $(MonoStaticLibFileName) $(MonoSharedLibFileName) + mono-aot-cross$(ExeExt) + mono-aot-cross.pdb $(Configuration) $(Configuration) $([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'tests', 'coreclr', '$(TargetOS).$(Platform).$(CoreClrTestConfig)', 'Tests', 'Core_Root')) @@ -672,6 +674,8 @@ + + @@ -745,7 +749,8 @@ <_MonoIncludeIcuFiles Condition="'$(TargetsiOS)' == 'true' or '$(TargetstvOS)' == 'true' or '$(TargetsMacCatalyst)' == 'true'">true - <_MonoAotCrossFilePath>$(MonoObjCrossDir)out\bin\mono-sgen$(ExeExt) + <_MonoAotCrossFilePath>$(MonoObjCrossDir)out\bin\$(MonoAotCrossFileName) + <_MonoAotCrossPdbFilePath>$(MonoObjCrossDir)out\bin\$(MonoAotCrossPdbFileName) @@ -774,7 +779,10 @@ $(RuntimeBinDir)%(_MonoRuntimeComponentsSharedFilePath.Filename)%(_MonoRuntimeComponentsSharedFilePath.Extension) <_MonoRuntimeArtifacts Include="$(_MonoAotCrossFilePath)"> - $(RuntimeBinDir)cross\$(PackageRID)\mono-aot-cross$(ExeExt) + $(RuntimeBinDir)cross\$(PackageRID)\$(MonoAotCrossFileName) + + <_MonoRuntimeArtifacts Include="$(_MonoAotCrossPdbFilePath)" Condition="Exists('$(_MonoAotCrossPdbFilePath)')"> + $(RuntimeBinDir)cross\$(PackageRID)\$(MonoAotCrossPdbFileName) <_MonoRuntimeArtifacts Condition="'$(MonoBundleLLVMOptimizer)' == 'true'" Include="$(MonoLLVMDir)\bin\llc$(ExeExt)"> $(RuntimeBinDir)\llc$(ExeExt) diff --git a/src/mono/mono/mini/CMakeLists.txt b/src/mono/mono/mini/CMakeLists.txt index 3e9ca497336abf..1760e0982cc24a 100644 --- a/src/mono/mono/mini/CMakeLists.txt +++ b/src/mono/mono/mini/CMakeLists.txt @@ -473,6 +473,9 @@ if(NOT DISABLE_EXECUTABLES) set(sgen_sources "${sgen_sources};${PROJECT_BINARY_DIR}/../../NativeVersion.rc") endif() add_executable(mono-sgen "${sgen_sources}") + if(MONO_CROSS_COMPILE_EXECUTABLE_NAME) + set_target_properties(mono-sgen PROPERTIES OUTPUT_NAME mono-aot-cross) + endif() target_link_libraries(mono-sgen PRIVATE monosgen-static ${OS_LIBS} ${ICONV_LIB} ${LLVM_LIBS} ${ICU_LIBS}) if(NOT DISABLE_COMPONENTS AND STATIC_COMPONENTS AND NOT DISABLE_LINK_STATIC_COMPONENTS) # if components are built statically, link them into runtime. @@ -492,4 +495,7 @@ if(NOT DISABLE_EXECUTABLES) set_property(TARGET mono-sgen APPEND_STRING PROPERTY LINK_FLAGS " ${ICU_LDFLAGS}") endif() install(TARGETS mono-sgen RUNTIME) + if(HOST_WIN32) + install(FILES $ DESTINATION bin OPTIONAL) + endif() endif() diff --git a/src/native/corehost/bundle/info.cpp b/src/native/corehost/bundle/info.cpp index 4d58a00ae647be..fd4a7d3f3b08b7 100644 --- a/src/native/corehost/bundle/info.cpp +++ b/src/native/corehost/bundle/info.cpp @@ -108,7 +108,14 @@ char* info_t::config_t::map(const pal::string_t& path, const location_t* &locati // * There is no performance limitation due to a larger sized mapping, since we actually only read the pages with relevant contents. // * Files that are too large to be mapped (ex: that exhaust 32-bit virtual address space) are not supported. +#ifdef _WIN32 + // Since we can't use in-situ parsing on Windows, as JSON data is encoded in + // UTF-8 and the host expects wide strings. + // We do not need COW and read-only mapping will be enough. + char* addr = (char*)pal::mmap_read(app->m_bundle_path); +#else // _WIN32 char* addr = (char*)pal::mmap_copy_on_write(app->m_bundle_path); +#endif // _WIN32 if (addr == nullptr) { trace::error(_X("Failure processing application bundle.")); diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.csproj b/src/tasks/AotCompilerTask/MonoAOTCompiler.csproj index 6371df56223568..840115ca71b909 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.csproj +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.csproj @@ -36,6 +36,7 @@ <_PublishFramework Include="$(TargetFrameworks)" /> + diff --git a/src/tasks/ILStripTask/ILStrip.csproj b/src/tasks/ILStripTask/ILStrip.csproj index 879c56f26ae262..bb9c41f692e1cc 100644 --- a/src/tasks/ILStripTask/ILStrip.csproj +++ b/src/tasks/ILStripTask/ILStrip.csproj @@ -27,6 +27,7 @@ <_PublishFramework Include="$(TargetFrameworks)" /> + diff --git a/src/tasks/JsonToItemsTaskFactory/JsonToItemsTaskFactory.csproj b/src/tasks/JsonToItemsTaskFactory/JsonToItemsTaskFactory.csproj index f68810fc3f1c26..17799fbf6570c1 100644 --- a/src/tasks/JsonToItemsTaskFactory/JsonToItemsTaskFactory.csproj +++ b/src/tasks/JsonToItemsTaskFactory/JsonToItemsTaskFactory.csproj @@ -32,6 +32,7 @@ <_PublishFramework Include="$(TargetFrameworks)" /> + diff --git a/src/tasks/RuntimeConfigParser/RuntimeConfigParser.csproj b/src/tasks/RuntimeConfigParser/RuntimeConfigParser.csproj index 2fb35e649eed73..c07e367def62ec 100644 --- a/src/tasks/RuntimeConfigParser/RuntimeConfigParser.csproj +++ b/src/tasks/RuntimeConfigParser/RuntimeConfigParser.csproj @@ -33,6 +33,7 @@ <_PublishFramework Include="$(TargetFrameworks)" /> +