File: System\Private\Windows\Core\BinaryFormat\Deserializer\ClassRecordSerializationInfoDeserializer.cs
Web Access
Project: src\src\System.Private.Windows.Core\src\System.Private.Windows.Core.csproj (System.Private.Windows.Core)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Runtime.Serialization;
using System.Formats.Nrbf;
 
namespace System.Private.Windows.Core.BinaryFormat;
 
#pragma warning disable SYSLIB0050 // Type or member is obsolete
 
/// <summary>
///  Deserializer for <see cref="ClassRecord"/>s that use <see cref="SerializationInfo"/> to initialize class state.
/// </summary>
/// <remarks>
///  <para>
///   This is used either because the class implements <see cref="ISerializable"/> or because a surrogate was used.
///  </para>
/// </remarks>
internal sealed class ClassRecordSerializationInfoDeserializer : ClassRecordDeserializer
{
    private readonly ClassRecord _classRecord;
    private readonly SerializationInfo _serializationInfo;
    private readonly ISerializationSurrogate? _surrogate;
    private readonly IEnumerator<string> _memberNamesIterator;
    private bool _canIterate;
 
    internal ClassRecordSerializationInfoDeserializer(
        ClassRecord classRecord,
        object @object,
        Type type,
        ISerializationSurrogate? surrogate,
        IDeserializer deserializer) : base(classRecord, @object, deserializer)
    {
        _classRecord = classRecord;
        _surrogate = surrogate;
        _serializationInfo = new(type, DefaultConverter);
        _memberNamesIterator = _classRecord.MemberNames.GetEnumerator();
        _canIterate = _memberNamesIterator.MoveNext(); // start the iterator
    }
 
    internal override SerializationRecordId Continue()
    {
        if (_canIterate)
        {
            do
            {
                string memberName = _memberNamesIterator.Current;
                (object? memberValue, SerializationRecordId reference) = UnwrapMemberValue(_classRecord.GetRawValue(memberName));
 
                if (s_missingValueSentinel == memberValue)
                {
                    // Record has not been encountered yet, need to pend iteration.
                    return reference;
                }
 
                if (memberValue is not null && DoesValueNeedUpdated(memberValue, reference))
                {
                    Deserializer.PendValueUpdater(new SerializationInfoValueUpdater(
                        _classRecord.Id,
                        reference,
                        _serializationInfo,
                        memberName));
                }
 
                _serializationInfo.AddValue(memberName, memberValue);
            }
            while (_memberNamesIterator.MoveNext());
 
            _canIterate = false;
        }
 
        // We can't complete these in the same way we do with direct field sets as user code can dereference the
        // reference type members from the SerializationInfo that aren't fully completed (due to cycles). With direct
        // field sets it doesn't matter if the referenced object isn't fully completed. Waiting until the graph is
        // fully parsed to allow cycles the best chance to resolve as much as possible without having to walk the
        // entire graph from this point to make a determination.
        //
        // The same issue applies to "complete" events, which is why we pend them as well.
        //
        // If we were confident that there were no cycles in the graph to this point we could apply directly
        // if there were no pending value types (which should also not happen if there are no cycles).
 
        PendingSerializationInfo pending = new(_classRecord.Id, _serializationInfo, _surrogate);
        Deserializer.PendSerializationInfo(pending);
 
        // No more missing member refs.
        return default;
    }
}
 
#pragma warning restore SYSLIB0050 // Type or member is obsolete