Skip to content

Commit

Permalink
Move the working BinaryFormatter implementation to a NuGet package
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
bartonjs committed Jun 17, 2024
1 parent 93ac9b6 commit 4a7ac0e
Show file tree
Hide file tree
Showing 22 changed files with 260 additions and 115 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,9 @@ public static int SlowRuntimeTimeoutModifier
public static bool IsThreadingSupported => (!IsWasi && !IsBrowser) || IsWasmThreadingSupported;
public static bool IsWasmThreadingSupported => IsBrowser && IsEnvironmentVariableTrue("IsBrowserThreadingSupported");
public static bool IsNotWasmThreadingSupported => !IsWasmThreadingSupported;
public static bool IsBinaryFormatterSupported => IsNotMobile && !IsNativeAot;

private static readonly Lazy<bool> s_isBinaryFormatterSupported = new Lazy<bool>(DetermineBinaryFormatterSupport);
public static bool IsBinaryFormatterSupported => s_isBinaryFormatterSupported.Value;

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

Expand Down Expand Up @@ -722,5 +724,34 @@ private static bool GetSupportsSha3()

return false;
}

private static bool DetermineBinaryFormatterSupport()
{
if (IsNetFramework)
{
return true;
}
else if (IsNativeAot)
{
return false;
}

Assembly assembly = typeof(System.Runtime.Serialization.Formatters.Binary.BinaryFormatter).Assembly;
AssemblyName name = assembly.GetName();
Version assemblyVersion = name.Version;

bool isSupported = true;

// Version 8.1 is the version in the shared runtime (.NET 9+) that has the type disabled with no config.
// Assembly versions beyond 8.1 are the fully functional version from NuGet.
// Assembly versions before 8.1 probably won't be encountered, since that's the past.

if (assemblyVersion.Major == 8 && assemblyVersion.Minor == 1)
{
isSupported = false;
}

return isSupported;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1591,7 +1591,10 @@ public void SerializationFormat_Binary_does_not_work_by_default()
#pragma warning restore SYSLIB0038
}

[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public static bool RemoteExecutorBinaryFormatter =>
RemoteExecutor.IsSupported && PlatformDetection.IsBinaryFormatterSupported;

[ConditionalFact(nameof(RemoteExecutorBinaryFormatter))]
public void SerializationFormat_Binary_works_with_appconfig_switch()
{
RemoteExecutor.Invoke(RunTest).Dispose();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,10 @@ public void SerializationFormat_Binary_does_not_work_by_default()
#pragma warning restore SYSLIB0038
}

[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public static bool RemoteExecutorBinaryFormatter =>
RemoteExecutor.IsSupported && PlatformDetection.IsBinaryFormatterSupported;

[ConditionalFact(nameof(RemoteExecutorBinaryFormatter))]
public void SerializationFormat_Binary_works_with_appconfig_switch()
{
RemoteExecutor.Invoke(RunTest).Dispose();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
</PropertyGroup>

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

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project>
<Import Project="..\Directory.Build.targets" />
<PropertyGroup>

<!--
The real implementation of this library is built for NetCoreAppMinimum,
the NetCoreAppCurrent build has a non-functional copy of BinaryFormatter.
The NetCoreAppCurrent build is only included in the shared runtime,
and should always lose to the package for unification; so it is pinned
at an assembly version that will always lose.
-->
<AssemblyVersion Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">8.1.0.0</AssemblyVersion>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
<TargetFrameworks>$(NetCoreAppMinimum);$(NetCoreAppCurrent)</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Compile Include="System.Runtime.Serialization.Formatters.cs" />
<Compile Include="System.Runtime.Serialization.Formatters.Forwards.cs" />
</ItemGroup>
<ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">
<ProjectReference Include="..\..\System.Collections.NonGeneric\ref\System.Collections.NonGeneric.csproj" />
<ProjectReference Include="..\..\System.Runtime\ref\System.Runtime.csproj" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
## About

Provides the legacy BinaryFormatter class for workloads which still require it.

## Main Types

The main types provided by this library are:

* `System.Runtime.Serialization.Formatters.Binary.BinaryFormatter`

## Additional Documentation

* [Obsoletion Notice](https://aka.ms/binaryformatter)

## Feedback & Contributing

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.
Original file line number Diff line number Diff line change
Expand Up @@ -252,4 +252,7 @@
<data name="BinaryFormatter_SerializationNotSupportedOnThisPlatform" xml:space="preserve">
<value>BinaryFormatter serialization and deserialization are not supported on this platform. See https://aka.ms/binaryformatter for more information.</value>
</data>
<data name="BinaryFormatter_Removed" xml:space="preserve">
<value>BinaryFormatter serialization and deserialization have been removed. See https://aka.ms/binaryformatter for more information.</value>
</data>
</root>
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent);$(NetCoreAppCurrent)-browser;$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent)-android</TargetFrameworks>
<TargetFrameworks>$(NetCoreAppCurrent);$(NetCoreAppMinimum)</TargetFrameworks>
<UseCompilerGeneratedDocXmlFile>false</UseCompilerGeneratedDocXmlFile>
<IsPackable>true</IsPackable>
<!-- TODO: Enable validation after the .NET 9 package release -->
<EnablePackageValidation>false</EnablePackageValidation>
</PropertyGroup>

<!-- DesignTimeBuild requires all the TargetFramework Derived Properties to not be present in the first property group. -->
<PropertyGroup>
<TargetPlatformIdentifier Condition="$(TargetFramework.Contains('-'))">$(TargetFramework.SubString($([MSBuild]::Add($(TargetFramework.IndexOf('-')), 1))))</TargetPlatformIdentifier>
<!-- TargetsMobile: When we replace implementations with PNSE, need to suppress some "field is never assigned to" warnings. -->
<NoWarn Condition="'$(TargetPlatformIdentifier)' != ''">$(NoWarn);CS0649</NoWarn>
<NoWarn>$(NoWarn);CA1822;IDE0060</NoWarn>

<FunctioningBinaryFormatter Condition="'$(TargetFramework)' == '$(NetCoreAppMinimum)'">true</FunctioningBinaryFormatter>
<!-- The GenerateNuspec target fails if this property is keyed off of FunctioningBinaryFormatter, so keep it logically in sync -->
<IncludeBuildOutput Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">false</IncludeBuildOutput>
<SuppressDependenciesWhenPacking Condition="'$(FunctioningBinaryFormatter)' != 'true'">true</SuppressDependenciesWhenPacking>
</PropertyGroup>

<ItemGroup>
<ILLinkSubstitutionsXmls Condition="'$(TargetPlatformIdentifier)' == ''" Include="$(ILLinkDirectory)ILLink.Substitutions.NonMobile.xml" />
<ItemGroup Condition="'$(FunctioningBinaryFormatter)' == 'true'">
<ILLinkSubstitutionsXmls Include="$(ILLinkDirectory)ILLink.Substitutions.xml" />
</ItemGroup>

<ItemGroup>
Expand All @@ -37,55 +42,55 @@
<Compile Include="System\Runtime\Serialization\ValueTypeFixupInfo.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\CommonEnums.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\IFieldInfo.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatter.cs" />
<Compile Include="$(CommonPath)System\Obsoletions.cs"
Link="Common\System\Obsoletions.cs" />
<Compile Include="$(CoreLibSharedDir)System\Collections\HashHelpers.cs"
Link="Common\System\Collections\HashHelpers.cs" />
</ItemGroup>

<ItemGroup Condition="'$(FunctioningBinaryFormatter)' == 'true'">
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryArray.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryAssembly.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryAssemblyInfo.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryTypeConverter.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryCrossAppDomainAssembly.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryCrossAppDomainMap.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryCrossAppDomainString.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryEnums.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatter.Core.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatterEventSource.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatterWriter.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryObject.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryObjectInfo.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryObjectReader.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryObjectString.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryObjectWithMap.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryObjectWithMapTyped.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryObjectWriter.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryParser.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryTypeConverter.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryUtilClasses.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\Converter.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\IStreamable.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\MemberPrimitiveTyped.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\MemberPrimitiveUntyped.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\MemberReference.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\MessageEnd.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\ObjectMap.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\ObjectNull.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\SerializationHeaderRecord.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\ObjectProgress.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\Converter.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryEnums.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatter.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatterEventSource.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatterWriter.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryObjectInfo.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryObjectReader.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryObjectWriter.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryParser.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryUtilClasses.cs" />
<Compile Include="$(CommonPath)System\Obsoletions.cs"
Link="Common\System\Obsoletions.cs" />
<Compile Include="$(CoreLibSharedDir)System\Collections\HashHelpers.cs"
Link="Common\System\Collections\HashHelpers.cs" />
</ItemGroup>

<ItemGroup Condition="'$(TargetPlatformIdentifier)' == ''">
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatter.Core.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\SerializationHeaderRecord.cs" />
<Compile Include="System\Runtime\Serialization\LocalAppContextSwitches.cs" />
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs">
<Link>Common\System\LocalAppContextSwitches.Common.cs</Link>
</Compile>
</ItemGroup>

<ItemGroup Condition="'$(TargetPlatformIdentifier)' != ''">
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatter.PlatformNotSupported.cs" />
<ItemGroup Condition="'$(FunctioningBinaryFormatter)' != 'true'">
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatter.Removed.cs" />
</ItemGroup>

<ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">
<ProjectReference Include="$(CoreLibProject)" />
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime\src\System.Runtime.csproj" />
<ProjectReference Include="$(LibrariesProjectRoot)System.Collections\src\System.Collections.csproj" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using System.IO;

namespace System.Runtime.Serialization.Formatters.Binary
{
public sealed partial class BinaryFormatter : IFormatter
{
private static readonly ConcurrentDictionary<Type, TypeInformation> s_typeNameCache = new ConcurrentDictionary<Type, TypeInformation>();

internal object[]? _crossAppDomainArray;

[RequiresDynamicCode(IFormatter.RequiresDynamicCodeMessage)]
[RequiresUnreferencedCode(IFormatter.RequiresUnreferencedCodeMessage)]
public object Deserialize(Stream serializationStream)
Expand Down Expand Up @@ -88,5 +93,12 @@ public void Serialize(Stream serializationStream, object graph)
BinaryFormatterEventSource.Log.SerializationStop();
}
}

internal static TypeInformation GetTypeInformation(Type type) =>
s_typeNameCache.GetOrAdd(type, t =>
{
string assemblyName = FormatterServices.GetClrAssemblyName(t, out bool hasTypeForwardedFrom);
return new TypeInformation(FormatterServices.GetClrTypeFullName(t), assemblyName, hasTypeForwardedFrom);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ public sealed partial class BinaryFormatter : IFormatter
[RequiresDynamicCode(IFormatter.RequiresDynamicCodeMessage)]
[RequiresUnreferencedCode(IFormatter.RequiresUnreferencedCodeMessage)]
public object Deserialize(Stream serializationStream)
=> throw new PlatformNotSupportedException(SR.BinaryFormatter_SerializationNotSupportedOnThisPlatform);
=> throw new PlatformNotSupportedException(SR.BinaryFormatter_Removed);

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

using System.Collections.Concurrent;

namespace System.Runtime.Serialization.Formatters.Binary
{
[Obsolete(Obsoletions.BinaryFormatterMessage, DiagnosticId = Obsoletions.BinaryFormatterDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
public sealed partial class BinaryFormatter : IFormatter
{
private static readonly ConcurrentDictionary<Type, TypeInformation> s_typeNameCache = new ConcurrentDictionary<Type, TypeInformation>();

internal ISurrogateSelector? _surrogates;
internal StreamingContext _context;
internal SerializationBinder? _binder;
internal FormatterTypeStyle _typeFormat = FormatterTypeStyle.TypesAlways; // For version resiliency, always put out types
internal FormatterAssemblyStyle _assemblyFormat = FormatterAssemblyStyle.Simple;
internal TypeFilterLevel _securityLevel = TypeFilterLevel.Full;
internal object[]? _crossAppDomainArray;

public FormatterTypeStyle TypeFormat { get { return _typeFormat; } set { _typeFormat = value; } }
public FormatterAssemblyStyle AssemblyFormat { get { return _assemblyFormat; } set { _assemblyFormat = value; } }
Expand All @@ -34,12 +29,5 @@ public BinaryFormatter(ISurrogateSelector? selector, StreamingContext context)
_surrogates = selector;
_context = context;
}

internal static TypeInformation GetTypeInformation(Type type) =>
s_typeNameCache.GetOrAdd(type, t =>
{
string assemblyName = FormatterServices.GetClrAssemblyName(t, out bool hasTypeForwardedFrom);
return new TypeInformation(FormatterServices.GetClrTypeFullName(t), assemblyName, hasTypeForwardedFrom);
});
}
}
Loading

0 comments on commit 4a7ac0e

Please sign in to comment.