|
// 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
|