Skip to content

Commit

Permalink
Revert "switch to the new API (57 compiler errors)"
Browse files Browse the repository at this point in the history
This reverts commit 4b92762.
  • Loading branch information
adamsitnik committed May 9, 2024
1 parent 4b92762 commit a385c85
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 95 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +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.Reflection.Metadata;

namespace System.Windows.Forms.BinaryFormat;

internal sealed partial class BinaryFormattedObject
Expand All @@ -15,9 +13,9 @@ internal interface ITypeResolver
/// <summary>
/// Resolves the given type name against the specified library.
/// </summary>
/// <param name="libraryName">The library id, or <see cref="Id.Null"/> for the "system" assembly.</param>
/// <param name="libraryId">The library id, or <see cref="Id.Null"/> for the "system" assembly.</param>
[RequiresUnreferencedCode("Calls System.Reflection.Assembly.GetType(String)")]
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
Type GetType(TypeName typeName, AssemblyNameInfo libraryName);
Type GetType(string typeName, Id libraryId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Reflection;
using System.Reflection.Metadata;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;

Expand All @@ -16,46 +15,71 @@ internal sealed class DefaultTypeResolver : ITypeResolver
{
private readonly FormatterAssemblyStyle _assemblyMatching;
private readonly SerializationBinder? _binder;
private readonly IReadOnlyRecordMap _recordMap;

private readonly Dictionary<string, Assembly> _assemblies = [];
private readonly Dictionary<(string TypeName, string LibraryId), Type> _types = [];
// This has to be Id as we use Id.Null for mscorlib types.
private readonly Dictionary<Id, Assembly> _assemblies = [];
private readonly Dictionary<(string TypeName, Id LibraryId), Type> _types = [];

internal DefaultTypeResolver(Options options)
private string? _lastTypeName;
private Id _lastLibraryId;

[AllowNull]
private Type _lastType;

internal DefaultTypeResolver(Options options, IReadOnlyRecordMap recordMap)
{
_assemblyMatching = options.AssemblyMatching;
_binder = options.Binder;
_assemblies[Id.Null] = TypeInfo.MscorlibAssembly;
_recordMap = recordMap;
}

/// <summary>
/// Resolves the given type name against the specified library.
/// </summary>
/// <param name="libraryName">The library id, or <see cref="Id.Null"/> for the "system" assembly.</param>
/// <param name="libraryId">The library id, or <see cref="Id.Null"/> for the "system" assembly.</param>
[RequiresUnreferencedCode("Calls System.Reflection.Assembly.GetType(String)")]
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
Type ITypeResolver.GetType(TypeName typeName, AssemblyNameInfo libraryName)
Type ITypeResolver.GetType(string typeName, Id libraryId)
{
if (_types.TryGetValue((typeName.FullName, libraryName.FullName), out Type? cachedType))
if (libraryId == _lastLibraryId && string.Equals(typeName, _lastTypeName))
{
Debug.Assert(_lastType is not null);
return _lastType;
}

_lastLibraryId = libraryId;
_lastTypeName = typeName;

if (_types.TryGetValue((typeName, libraryId), out Type? cachedType))
{
_lastType = cachedType;
return cachedType;
}

if (_binder?.BindToType(libraryName.FullName, typeName.FullName) is Type binderType)
string libraryName = libraryId.IsNull
? TypeInfo.MscorlibAssemblyName
: ((BinaryLibrary)_recordMap[libraryId]).LibraryName;

if (_binder?.BindToType(libraryName, typeName) is Type binderType)
{
// BinaryFormatter is inconsistent about what caching behavior you get with binders.
// It would always cache the last item from the binder, but wouldn't put the result
// in the type cache. This could lead to inconsistent results if the binder didn't
// always return the same result for a given set of strings. Choosing to always cache
// for performance.

_types[(typeName.FullName, libraryName.FullName)] = binderType;
_types[(typeName, libraryId)] = binderType;
_lastType = binderType;
return binderType;
}

if (!_assemblies.TryGetValue(libraryName.FullName, out Assembly? assembly))
if (!_assemblies.TryGetValue(libraryId, out Assembly? assembly))
{
Debug.Assert(libraryName.FullName != TypeInfo.MscorlibAssembly.FullName);
Debug.Assert(!libraryId.IsNull);

AssemblyName assemblyName = libraryName.ToAssemblyName();
AssemblyName assemblyName = new(libraryName);
try
{
assembly = Assembly.Load(assemblyName);
Expand All @@ -70,34 +94,35 @@ Type ITypeResolver.GetType(TypeName typeName, AssemblyNameInfo libraryName)
assembly = Assembly.Load(assemblyName.Name!);
}

_assemblies.Add(libraryName.FullName, assembly);
_assemblies.Add(libraryId, assembly);
}

Type? type = _assemblyMatching != FormatterAssemblyStyle.Simple
? assembly.GetType(typeName.FullName)
? assembly.GetType(typeName)
: GetSimplyNamedTypeFromAssembly(assembly, typeName);

_types[(typeName.FullName, libraryName.FullName)] = type ?? throw new SerializationException($"Could not find type '{typeName}'.");
return type;
_types[(typeName, libraryId)] = type ?? throw new SerializationException($"Could not find type '{typeName}'.");
_lastType = type;
return _lastType;
}

[RequiresUnreferencedCode("Calls System.Reflection.Assembly.GetType(String, Boolean, Boolean)")]
private static Type? GetSimplyNamedTypeFromAssembly(Assembly assembly, TypeName typeName)
private static Type? GetSimplyNamedTypeFromAssembly(Assembly assembly, string typeName)
{
// Catching any exceptions that could be thrown from a failure on assembly load
// This is necessary, for example, if there are generic parameters that are qualified
// with a version of the assembly that predates the one available.

try
{
return assembly.GetType(typeName.FullName, throwOnError: false, ignoreCase: false);
return assembly.GetType(typeName, throwOnError: false, ignoreCase: false);
}
catch (TypeLoadException) { }
catch (FileNotFoundException) { }
catch (FileLoadException) { }
catch (BadImageFormatException) { }

return Type.GetType(typeName.FullName, ResolveSimpleAssemblyName, new TopLevelAssemblyTypeResolver(assembly).ResolveType, throwOnError: false);
return Type.GetType(typeName, ResolveSimpleAssemblyName, new TopLevelAssemblyTypeResolver(assembly).ResolveType, throwOnError: false);

static Assembly? ResolveSimpleAssemblyName(AssemblyName assemblyName)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using System.Reflection;
using System.Runtime.ExceptionServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.BinaryFormat;
using System.Text;

namespace System.Windows.Forms.BinaryFormat;

Expand All @@ -29,20 +29,42 @@ internal sealed partial class BinaryFormattedObject
private static readonly Options s_defaultOptions = new();
private readonly Options _options;

private readonly RecordMap _recordMap = new();

private ITypeResolver? _typeResolver;
private ITypeResolver TypeResolver => _typeResolver ??= new DefaultTypeResolver(_options);
private ITypeResolver TypeResolver => _typeResolver ??= new DefaultTypeResolver(_options, _recordMap);

private readonly Id _rootRecord;

/// <summary>
/// Creates <see cref="BinaryFormattedObject"/> by parsing <paramref name="stream"/>.
/// </summary>
public BinaryFormattedObject(Stream stream, Options? options = null)
{
ArgumentNullException.ThrowIfNull(stream);
using BinaryReader reader = new(stream, Encoding.UTF8, leaveOpen: true);

_options = options ?? s_defaultOptions;

ParseState state = new(reader, this);

IRecord? currentRecord;

try
{
RootRecord = PayloadReader.Read(stream, out var readonlyRecordMap, leaveOpen: true);
RecordMap = readonlyRecordMap;
currentRecord = Record.ReadBinaryFormatRecord(state);
if (currentRecord is not SerializationHeader header)
{
throw new SerializationException("Did not find serialization header.");
}

_rootRecord = header.RootId;

do
{
currentRecord = Record.ReadBinaryFormatRecord(state);
}
while (currentRecord is not MessageEnd);
}
catch (Exception ex) when (ex is ArgumentException or InvalidCastException or ArithmeticException or IOException)
{
Expand All @@ -63,7 +85,7 @@ public object Deserialize()
{
try
{
return Deserializer.Deserializer.Deserialize(RootRecord.ObjectId, RecordMap, TypeResolver, _options);
return Deserializer.Deserializer.Deserialize(RootRecord.Id, _recordMap, TypeResolver, _options);
}
catch (Exception ex) when (ex is ArgumentException or InvalidCastException or ArithmeticException or IOException)
{
Expand All @@ -79,14 +101,13 @@ public object Deserialize()
/// <summary>
/// The Id of the root record of the object graph.
/// </summary>
public SerializationRecord RootRecord { get; }
public IRecord RootRecord => _recordMap[_rootRecord];

/// <summary>
/// Gets a record by it's identifier. Not all records have identifiers, only ones that
/// can be referenced by other records.
/// </summary>
public SerializationRecord this[Id id] => RecordMap[id];
public IRecord this[Id id] => _recordMap[id];

// adsitnik: do we need to expose both the map and the indexer?
public IReadOnlyDictionary<int, SerializationRecord> RecordMap { get; }
public IReadOnlyRecordMap RecordMap => _recordMap;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ internal abstract class ClassRecordDeserializer : ObjectRecordDeserializer
{
private readonly bool _onlyAllowPrimitives;

private protected ClassRecordDeserializer(Runtime.Serialization.BinaryFormat.ClassRecord classRecord, object @object, IDeserializer deserializer)
private protected ClassRecordDeserializer(ClassRecord classRecord, object @object, IDeserializer deserializer)
: base(classRecord, deserializer)
{
Object = @object;
Expand All @@ -26,9 +26,9 @@ private protected ClassRecordDeserializer(Runtime.Serialization.BinaryFormat.Cla
}

[RequiresUnreferencedCode("Calls System.Windows.Forms.BinaryFormat.BinaryFormattedObject.TypeResolver.GetType(String, Id)")]
internal static ObjectRecordDeserializer Create(Runtime.Serialization.BinaryFormat.ClassRecord classRecord, IDeserializer deserializer)
internal static ObjectRecordDeserializer Create(ClassRecord classRecord, IDeserializer deserializer)
{
Type type = deserializer.TypeResolver.GetType(classRecord.TypeName, classRecord.LibraryName);
Type type = deserializer.TypeResolver.GetType(classRecord.Name, classRecord.LibraryId);
Id id = classRecord.ObjectId;

ISerializationSurrogate? surrogate = deserializer.GetSurrogate(type);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ namespace System.Windows.Forms.BinaryFormat.Deserializer;
/// </summary>
internal sealed class ClassRecordFieldInfoDeserializer : ClassRecordDeserializer
{
private readonly Runtime.Serialization.BinaryFormat.ClassRecord _classRecord;
private readonly ClassRecord _classRecord;
private readonly MemberInfo[] _fieldInfo;
private int _currentFieldIndex;
private readonly bool _isValueType;
private bool _hasFixups;

internal ClassRecordFieldInfoDeserializer(
Runtime.Serialization.BinaryFormat.ClassRecord classRecord,
ClassRecord classRecord,
object @object,
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.PublicFields)]
Type type,
Expand Down Expand Up @@ -53,7 +53,7 @@ internal override Id Continue()
object? rawValue;
try
{
rawValue = _classRecord.GetObject(field.Name);
rawValue = _classRecord[field.Name];
}
catch (KeyNotFoundException)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ namespace System.Windows.Forms.BinaryFormat.Deserializer;
/// </remarks>
internal sealed class ClassRecordSerializationInfoDeserializer : ClassRecordDeserializer
{
private readonly Runtime.Serialization.BinaryFormat.ClassRecord _classRecord;
private readonly ClassRecord _classRecord;
private readonly SerializationInfo _serializationInfo;
private readonly ISerializationSurrogate? _surrogate;
private int _currentMemberIndex;

internal ClassRecordSerializationInfoDeserializer(
Runtime.Serialization.BinaryFormat.ClassRecord classRecord,
ClassRecord classRecord,
object @object,
Type type,
ISerializationSurrogate? surrogate,
Expand Down
Loading

0 comments on commit a385c85

Please sign in to comment.