Skip to content

Commit 4a7ac0e

Browse files
authored
Move the working BinaryFormatter implementation to a NuGet package
This change makes System.Runtime.Serialization.Formatters.dll build for both NetCoreAppMinimum and NetCoreAppCurrent. * The NetCoreAppCurrent version has a copy of BinaryFormatter that unconditionally throws PNSE from calls to Serialize or Deserialize. * It is included in the shared runtime * It is permanently AssemblyVersion locked to 8.1 ("bigger than 8.0, smaller than 9.0") * It is not included in the new NuGet package. * The NetCoreAppMinimum version(s) are exactly what this library was in .NET 8 * The AppContext switch is still required to use it * These TFMs are included in a resurrected System.Runtime.Serialization.Formatters nupkg * The AssemblyVersion floats with the repository (so 9.0 now, and we could stop producing the package in 10... or make a 10.0 then)
1 parent 93ac9b6 commit 4a7ac0e

22 files changed

+260
-115
lines changed

src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs

+32-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,9 @@ public static int SlowRuntimeTimeoutModifier
135135
public static bool IsThreadingSupported => (!IsWasi && !IsBrowser) || IsWasmThreadingSupported;
136136
public static bool IsWasmThreadingSupported => IsBrowser && IsEnvironmentVariableTrue("IsBrowserThreadingSupported");
137137
public static bool IsNotWasmThreadingSupported => !IsWasmThreadingSupported;
138-
public static bool IsBinaryFormatterSupported => IsNotMobile && !IsNativeAot;
138+
139+
private static readonly Lazy<bool> s_isBinaryFormatterSupported = new Lazy<bool>(DetermineBinaryFormatterSupport);
140+
public static bool IsBinaryFormatterSupported => s_isBinaryFormatterSupported.Value;
139141

140142
public static bool IsStartingProcessesSupported => !IsiOS && !IstvOS;
141143

@@ -722,5 +724,34 @@ private static bool GetSupportsSha3()
722724

723725
return false;
724726
}
727+
728+
private static bool DetermineBinaryFormatterSupport()
729+
{
730+
if (IsNetFramework)
731+
{
732+
return true;
733+
}
734+
else if (IsNativeAot)
735+
{
736+
return false;
737+
}
738+
739+
Assembly assembly = typeof(System.Runtime.Serialization.Formatters.Binary.BinaryFormatter).Assembly;
740+
AssemblyName name = assembly.GetName();
741+
Version assemblyVersion = name.Version;
742+
743+
bool isSupported = true;
744+
745+
// Version 8.1 is the version in the shared runtime (.NET 9+) that has the type disabled with no config.
746+
// Assembly versions beyond 8.1 are the fully functional version from NuGet.
747+
// Assembly versions before 8.1 probably won't be encountered, since that's the past.
748+
749+
if (assemblyVersion.Major == 8 && assemblyVersion.Minor == 1)
750+
{
751+
isSupported = false;
752+
}
753+
754+
return isSupported;
755+
}
725756
}
726757
}

src/libraries/System.Data.Common/tests/System/Data/DataSetTest.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1591,7 +1591,10 @@ public void SerializationFormat_Binary_does_not_work_by_default()
15911591
#pragma warning restore SYSLIB0038
15921592
}
15931593

1594-
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
1594+
public static bool RemoteExecutorBinaryFormatter =>
1595+
RemoteExecutor.IsSupported && PlatformDetection.IsBinaryFormatterSupported;
1596+
1597+
[ConditionalFact(nameof(RemoteExecutorBinaryFormatter))]
15951598
public void SerializationFormat_Binary_works_with_appconfig_switch()
15961599
{
15971600
RemoteExecutor.Invoke(RunTest).Dispose();

src/libraries/System.Data.Common/tests/System/Data/DataTableTest.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,10 @@ public void SerializationFormat_Binary_does_not_work_by_default()
387387
#pragma warning restore SYSLIB0038
388388
}
389389

390-
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
390+
public static bool RemoteExecutorBinaryFormatter =>
391+
RemoteExecutor.IsSupported && PlatformDetection.IsBinaryFormatterSupported;
392+
393+
[ConditionalFact(nameof(RemoteExecutorBinaryFormatter))]
391394
public void SerializationFormat_Binary_works_with_appconfig_switch()
392395
{
393396
RemoteExecutor.Invoke(RunTest).Dispose();

src/libraries/System.Resources.Extensions/tests/BinaryFormatTests/System.Resources.Extensions.BinaryFormat.Tests.csproj

+2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
</PropertyGroup>
1515

1616
<ItemGroup>
17+
<!-- TODO 103447: These tests all require BinaryFormatter, which won't work until there's a published NuGet package.
1718
<Compile Include="**\*.cs" />
19+
-->
1820
</ItemGroup>
1921

2022
<ItemGroup>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project>
2+
<Import Project="..\Directory.Build.targets" />
3+
<PropertyGroup>
4+
5+
<!--
6+
The real implementation of this library is built for NetCoreAppMinimum,
7+
the NetCoreAppCurrent build has a non-functional copy of BinaryFormatter.
8+
The NetCoreAppCurrent build is only included in the shared runtime,
9+
and should always lose to the package for unification; so it is pinned
10+
at an assembly version that will always lose.
11+
-->
12+
<AssemblyVersion Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">8.1.0.0</AssemblyVersion>
13+
</PropertyGroup>
14+
</Project>

src/libraries/System.Runtime.Serialization.Formatters/ref/System.Runtime.Serialization.Formatters.csproj

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
3-
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
3+
<TargetFrameworks>$(NetCoreAppMinimum);$(NetCoreAppCurrent)</TargetFrameworks>
44
</PropertyGroup>
55
<ItemGroup>
66
<Compile Include="System.Runtime.Serialization.Formatters.cs" />
77
<Compile Include="System.Runtime.Serialization.Formatters.Forwards.cs" />
88
</ItemGroup>
9-
<ItemGroup>
9+
<ItemGroup Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">
1010
<ProjectReference Include="..\..\System.Collections.NonGeneric\ref\System.Collections.NonGeneric.csproj" />
1111
<ProjectReference Include="..\..\System.Runtime\ref\System.Runtime.csproj" />
1212
</ItemGroup>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
## About
2+
3+
Provides the legacy BinaryFormatter class for workloads which still require it.
4+
5+
## Main Types
6+
7+
The main types provided by this library are:
8+
9+
* `System.Runtime.Serialization.Formatters.Binary.BinaryFormatter`
10+
11+
## Additional Documentation
12+
13+
* [Obsoletion Notice](https://aka.ms/binaryformatter)
14+
15+
## Feedback & Contributing
16+
17+
System.Runtime.Serialization.Formatters is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports are welcome at [the GitHub repository](https://github.com/dotnet/runtime). This package is considered legacy, and we only consider low-risk, high-impact fixes that are necessary to maintain functionality.

src/libraries/System.Runtime.Serialization.Formatters/src/Resources/Strings.resx

+3
Original file line numberDiff line numberDiff line change
@@ -252,4 +252,7 @@
252252
<data name="BinaryFormatter_SerializationNotSupportedOnThisPlatform" xml:space="preserve">
253253
<value>BinaryFormatter serialization and deserialization are not supported on this platform. See https://aka.ms/binaryformatter for more information.</value>
254254
</data>
255+
<data name="BinaryFormatter_Removed" xml:space="preserve">
256+
<value>BinaryFormatter serialization and deserialization have been removed. See https://aka.ms/binaryformatter for more information.</value>
257+
</data>
255258
</root>

src/libraries/System.Runtime.Serialization.Formatters/src/System.Runtime.Serialization.Formatters.csproj

+34-29
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,25 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>$(NetCoreAppCurrent);$(NetCoreAppCurrent)-browser;$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent)-android</TargetFrameworks>
4+
<TargetFrameworks>$(NetCoreAppCurrent);$(NetCoreAppMinimum)</TargetFrameworks>
55
<UseCompilerGeneratedDocXmlFile>false</UseCompilerGeneratedDocXmlFile>
6+
<IsPackable>true</IsPackable>
7+
<!-- TODO: Enable validation after the .NET 9 package release -->
8+
<EnablePackageValidation>false</EnablePackageValidation>
69
</PropertyGroup>
710

811
<!-- DesignTimeBuild requires all the TargetFramework Derived Properties to not be present in the first property group. -->
912
<PropertyGroup>
10-
<TargetPlatformIdentifier Condition="$(TargetFramework.Contains('-'))">$(TargetFramework.SubString($([MSBuild]::Add($(TargetFramework.IndexOf('-')), 1))))</TargetPlatformIdentifier>
11-
<!-- TargetsMobile: When we replace implementations with PNSE, need to suppress some "field is never assigned to" warnings. -->
12-
<NoWarn Condition="'$(TargetPlatformIdentifier)' != ''">$(NoWarn);CS0649</NoWarn>
1313
<NoWarn>$(NoWarn);CA1822;IDE0060</NoWarn>
14+
15+
<FunctioningBinaryFormatter Condition="'$(TargetFramework)' == '$(NetCoreAppMinimum)'">true</FunctioningBinaryFormatter>
16+
<!-- The GenerateNuspec target fails if this property is keyed off of FunctioningBinaryFormatter, so keep it logically in sync -->
17+
<IncludeBuildOutput Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">false</IncludeBuildOutput>
18+
<SuppressDependenciesWhenPacking Condition="'$(FunctioningBinaryFormatter)' != 'true'">true</SuppressDependenciesWhenPacking>
1419
</PropertyGroup>
1520

16-
<ItemGroup>
17-
<ILLinkSubstitutionsXmls Condition="'$(TargetPlatformIdentifier)' == ''" Include="$(ILLinkDirectory)ILLink.Substitutions.NonMobile.xml" />
21+
<ItemGroup Condition="'$(FunctioningBinaryFormatter)' == 'true'">
22+
<ILLinkSubstitutionsXmls Include="$(ILLinkDirectory)ILLink.Substitutions.xml" />
1823
</ItemGroup>
1924

2025
<ItemGroup>
@@ -37,55 +42,55 @@
3742
<Compile Include="System\Runtime\Serialization\ValueTypeFixupInfo.cs" />
3843
<Compile Include="System\Runtime\Serialization\Formatters\CommonEnums.cs" />
3944
<Compile Include="System\Runtime\Serialization\Formatters\IFieldInfo.cs" />
45+
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatter.cs" />
46+
<Compile Include="$(CommonPath)System\Obsoletions.cs"
47+
Link="Common\System\Obsoletions.cs" />
48+
<Compile Include="$(CoreLibSharedDir)System\Collections\HashHelpers.cs"
49+
Link="Common\System\Collections\HashHelpers.cs" />
50+
</ItemGroup>
51+
52+
<ItemGroup Condition="'$(FunctioningBinaryFormatter)' == 'true'">
4053
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryArray.cs" />
4154
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryAssembly.cs" />
4255
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryAssemblyInfo.cs" />
43-
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryTypeConverter.cs" />
4456
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryCrossAppDomainAssembly.cs" />
4557
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryCrossAppDomainMap.cs" />
4658
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryCrossAppDomainString.cs" />
59+
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryEnums.cs" />
60+
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatter.Core.cs" />
61+
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatterEventSource.cs" />
62+
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatterWriter.cs" />
4763
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryObject.cs" />
64+
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryObjectInfo.cs" />
65+
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryObjectReader.cs" />
4866
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryObjectString.cs" />
4967
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryObjectWithMap.cs" />
5068
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryObjectWithMapTyped.cs" />
69+
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryObjectWriter.cs" />
70+
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryParser.cs" />
71+
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryTypeConverter.cs" />
72+
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryUtilClasses.cs" />
73+
<Compile Include="System\Runtime\Serialization\Formatters\Binary\Converter.cs" />
5174
<Compile Include="System\Runtime\Serialization\Formatters\Binary\IStreamable.cs" />
5275
<Compile Include="System\Runtime\Serialization\Formatters\Binary\MemberPrimitiveTyped.cs" />
5376
<Compile Include="System\Runtime\Serialization\Formatters\Binary\MemberPrimitiveUntyped.cs" />
5477
<Compile Include="System\Runtime\Serialization\Formatters\Binary\MemberReference.cs" />
5578
<Compile Include="System\Runtime\Serialization\Formatters\Binary\MessageEnd.cs" />
5679
<Compile Include="System\Runtime\Serialization\Formatters\Binary\ObjectMap.cs" />
5780
<Compile Include="System\Runtime\Serialization\Formatters\Binary\ObjectNull.cs" />
58-
<Compile Include="System\Runtime\Serialization\Formatters\Binary\SerializationHeaderRecord.cs" />
5981
<Compile Include="System\Runtime\Serialization\Formatters\Binary\ObjectProgress.cs" />
60-
<Compile Include="System\Runtime\Serialization\Formatters\Binary\Converter.cs" />
61-
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryEnums.cs" />
62-
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatter.cs" />
63-
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatterEventSource.cs" />
64-
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatterWriter.cs" />
65-
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryObjectInfo.cs" />
66-
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryObjectReader.cs" />
67-
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryObjectWriter.cs" />
68-
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryParser.cs" />
69-
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryUtilClasses.cs" />
70-
<Compile Include="$(CommonPath)System\Obsoletions.cs"
71-
Link="Common\System\Obsoletions.cs" />
72-
<Compile Include="$(CoreLibSharedDir)System\Collections\HashHelpers.cs"
73-
Link="Common\System\Collections\HashHelpers.cs" />
74-
</ItemGroup>
75-
76-
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == ''">
77-
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatter.Core.cs" />
82+
<Compile Include="System\Runtime\Serialization\Formatters\Binary\SerializationHeaderRecord.cs" />
7883
<Compile Include="System\Runtime\Serialization\LocalAppContextSwitches.cs" />
7984
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs">
8085
<Link>Common\System\LocalAppContextSwitches.Common.cs</Link>
8186
</Compile>
8287
</ItemGroup>
8388

84-
<ItemGroup Condition="'$(TargetPlatformIdentifier)' != ''">
85-
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatter.PlatformNotSupported.cs" />
89+
<ItemGroup Condition="'$(FunctioningBinaryFormatter)' != 'true'">
90+
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatter.Removed.cs" />
8691
</ItemGroup>
8792

88-
<ItemGroup>
93+
<ItemGroup Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">
8994
<ProjectReference Include="$(CoreLibProject)" />
9095
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime\src\System.Runtime.csproj" />
9196
<ProjectReference Include="$(LibrariesProjectRoot)System.Collections\src\System.Collections.csproj" />

src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryFormatter.Core.cs

+12
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Collections.Concurrent;
45
using System.Diagnostics.CodeAnalysis;
56
using System.IO;
67

78
namespace System.Runtime.Serialization.Formatters.Binary
89
{
910
public sealed partial class BinaryFormatter : IFormatter
1011
{
12+
private static readonly ConcurrentDictionary<Type, TypeInformation> s_typeNameCache = new ConcurrentDictionary<Type, TypeInformation>();
13+
14+
internal object[]? _crossAppDomainArray;
15+
1116
[RequiresDynamicCode(IFormatter.RequiresDynamicCodeMessage)]
1217
[RequiresUnreferencedCode(IFormatter.RequiresUnreferencedCodeMessage)]
1318
public object Deserialize(Stream serializationStream)
@@ -88,5 +93,12 @@ public void Serialize(Stream serializationStream, object graph)
8893
BinaryFormatterEventSource.Log.SerializationStop();
8994
}
9095
}
96+
97+
internal static TypeInformation GetTypeInformation(Type type) =>
98+
s_typeNameCache.GetOrAdd(type, t =>
99+
{
100+
string assemblyName = FormatterServices.GetClrAssemblyName(t, out bool hasTypeForwardedFrom);
101+
return new TypeInformation(FormatterServices.GetClrTypeFullName(t), assemblyName, hasTypeForwardedFrom);
102+
});
91103
}
92104
}
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ public sealed partial class BinaryFormatter : IFormatter
1111
[RequiresDynamicCode(IFormatter.RequiresDynamicCodeMessage)]
1212
[RequiresUnreferencedCode(IFormatter.RequiresUnreferencedCodeMessage)]
1313
public object Deserialize(Stream serializationStream)
14-
=> throw new PlatformNotSupportedException(SR.BinaryFormatter_SerializationNotSupportedOnThisPlatform);
14+
=> throw new PlatformNotSupportedException(SR.BinaryFormatter_Removed);
1515

1616
[RequiresUnreferencedCode(IFormatter.RequiresUnreferencedCodeMessage)]
1717
public void Serialize(Stream serializationStream, object graph)
18-
=> throw new PlatformNotSupportedException(SR.BinaryFormatter_SerializationNotSupportedOnThisPlatform);
18+
=> throw new PlatformNotSupportedException(SR.BinaryFormatter_Removed);
1919
}
2020
}
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,17 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.Collections.Concurrent;
5-
64
namespace System.Runtime.Serialization.Formatters.Binary
75
{
86
[Obsolete(Obsoletions.BinaryFormatterMessage, DiagnosticId = Obsoletions.BinaryFormatterDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
97
public sealed partial class BinaryFormatter : IFormatter
108
{
11-
private static readonly ConcurrentDictionary<Type, TypeInformation> s_typeNameCache = new ConcurrentDictionary<Type, TypeInformation>();
12-
139
internal ISurrogateSelector? _surrogates;
1410
internal StreamingContext _context;
1511
internal SerializationBinder? _binder;
1612
internal FormatterTypeStyle _typeFormat = FormatterTypeStyle.TypesAlways; // For version resiliency, always put out types
1713
internal FormatterAssemblyStyle _assemblyFormat = FormatterAssemblyStyle.Simple;
1814
internal TypeFilterLevel _securityLevel = TypeFilterLevel.Full;
19-
internal object[]? _crossAppDomainArray;
2015

2116
public FormatterTypeStyle TypeFormat { get { return _typeFormat; } set { _typeFormat = value; } }
2217
public FormatterAssemblyStyle AssemblyFormat { get { return _assemblyFormat; } set { _assemblyFormat = value; } }
@@ -34,12 +29,5 @@ public BinaryFormatter(ISurrogateSelector? selector, StreamingContext context)
3429
_surrogates = selector;
3530
_context = context;
3631
}
37-
38-
internal static TypeInformation GetTypeInformation(Type type) =>
39-
s_typeNameCache.GetOrAdd(type, t =>
40-
{
41-
string assemblyName = FormatterServices.GetClrAssemblyName(t, out bool hasTypeForwardedFrom);
42-
return new TypeInformation(FormatterServices.GetClrTypeFullName(t), assemblyName, hasTypeForwardedFrom);
43-
});
4432
}
4533
}

0 commit comments

Comments
 (0)