|
// 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;
using System.Runtime.Serialization;
using System.Formats.Nrbf;
using System.Private.Windows.Core.Resources;
namespace System.Private.Windows.Core.BinaryFormat;
#pragma warning disable SYSLIB0050 // Type or member is obsolete
/// <summary>
/// Deserializer for <see cref="ClassRecord"/>s that directly set fields.
/// </summary>
internal sealed class ClassRecordFieldInfoDeserializer : ClassRecordDeserializer
{
private readonly ClassRecord _classRecord;
private readonly MemberInfo[] _fieldInfo;
private int _currentFieldIndex;
private readonly bool _isValueType;
private bool _hasFixups;
internal ClassRecordFieldInfoDeserializer(
ClassRecord classRecord,
object @object,
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
Type type,
IDeserializer deserializer)
: base(classRecord, @object, deserializer)
{
_classRecord = classRecord;
_fieldInfo = FormatterServices.GetSerializableMembers(type);
_isValueType = type.IsValueType;
}
internal override SerializationRecordId Continue()
{
// When directly setting fields we need to populate fields with primitive types before we
// can add the object to the deserialized object list to handle value types. This ensures
// partialially filled boxed value types in the collection are assigned (and unboxed) in
// this path (non-ISerializable) with nothing directly pending other than reference types.
Debug.Assert(_fieldInfo is not null);
// Note that while fields must have member data, fields are not required for all member data.
while (_currentFieldIndex < _fieldInfo.Length)
{
// FormatterServices *never* returns anything but fields.
FieldInfo field = (FieldInfo)_fieldInfo[_currentFieldIndex];
if (!_classRecord.HasMember(field.Name))
{
if (Deserializer.Options.SimpleAssemblyMatching
|| field.GetCustomAttribute<OptionalFieldAttribute>() is not null)
{
_currentFieldIndex++;
continue;
}
throw new SerializationException(string.Format(SR.Serialization_MissingField, field.Name, field.DeclaringType!.Name));
}
(object? memberValue, SerializationRecordId reference) = UnwrapMemberValue(_classRecord.GetRawValue(field.Name));
if (s_missingValueSentinel == memberValue)
{
// Record has not been encountered yet, need to pend iteration.
return reference;
}
field.SetValue(Object, memberValue);
if (memberValue is not null && DoesValueNeedUpdated(memberValue, reference))
{
// Need to track a fixup for this field.
_hasFixups = true;
Deserializer.PendValueUpdater(new FieldValueUpdater(_classRecord.Id, reference, field));
}
_currentFieldIndex++;
}
if (!_hasFixups || !_isValueType)
{
// We can be used for completion even with fixups if we're not a value type as our fixups won't need to be
// copied to propogate them. Note that surrogates cannot replace our created instance for reference types.
Deserializer.CompleteObject(_classRecord.Id);
}
// No more missing member refs.
return default;
}
}
#pragma warning restore SYSLIB0050 // Type or member is obsolete
|