|
// 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.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Threading;
namespace System.Runtime.Serialization.Formatters.Binary
{
// This class contains information about an object. It is used so that
// the rest of the Formatter routines can use a common interface for
// a normal object, an ISerializable object, and a surrogate object
internal sealed class WriteObjectInfo
{
internal int _objectInfoId;
internal object? _obj;
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
internal Type? _objectType;
internal bool _isSi;
internal bool _isNamed;
internal bool _isArray;
internal SerializationInfo? _si;
internal SerObjectInfoCache _cache = null!; // Initiated before use
internal object?[]? _memberData;
internal ISerializationSurrogate? _serializationSurrogate;
internal StreamingContext _context;
internal SerObjectInfoInit? _serObjectInfoInit;
// Writing and Parsing information
internal long _objectId;
internal long _assemId;
// Binder information
private string? _binderTypeName;
private string? _binderAssemblyString;
internal WriteObjectInfo() { }
internal void ObjectEnd()
{
Debug.Assert(_serObjectInfoInit != null);
PutObjectInfo(_serObjectInfoInit, this);
}
private void InternalInit()
{
_obj = null;
_objectType = null;
_isSi = false;
_isNamed = false;
_isArray = false;
_si = null;
_cache = null!;
_memberData = null;
// Writing and Parsing information
_objectId = 0;
_assemId = 0;
// Binder information
_binderTypeName = null;
_binderAssemblyString = null;
}
[RequiresUnreferencedCode("It isn't possible to statically get the Type of object")]
internal static WriteObjectInfo Serialize(object obj, ISurrogateSelector? surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder? binder)
{
WriteObjectInfo woi = GetObjectInfo(serObjectInfoInit);
woi.InitSerialize(obj, surrogateSelector, context, serObjectInfoInit, converter, objectWriter, binder);
return woi;
}
// Write constructor
[RequiresUnreferencedCode("It isn't possible to statically get the Type of object")]
internal void InitSerialize(object obj, ISurrogateSelector? surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder? binder)
{
_context = context;
_obj = obj;
_serObjectInfoInit = serObjectInfoInit;
_objectType = obj.GetType();
if (_objectType.IsArray)
{
_isArray = true;
InitNoMembers();
return;
}
InvokeSerializationBinder(binder);
objectWriter.ObjectManager.RegisterObject(obj);
if (surrogateSelector != null && (_serializationSurrogate = surrogateSelector.GetSurrogate(_objectType, context, out _)) != null)
{
_si = new SerializationInfo(_objectType, converter);
if (!_objectType.IsPrimitive)
{
_serializationSurrogate.GetObjectData(obj, _si, context);
}
InitSiWrite();
}
else if (obj is ISerializable)
{
if (!_objectType.IsSerializable)
{
throw new SerializationException(SR.Format(SR.Serialization_NonSerType, _objectType.FullName, _objectType.Assembly.FullName));
}
_si = new SerializationInfo(_objectType, converter);
((ISerializable)obj).GetObjectData(_si, context);
InitSiWrite();
CheckTypeForwardedFrom(_cache, _objectType, _binderAssemblyString);
}
else
{
InitMemberInfo();
CheckTypeForwardedFrom(_cache, _objectType, _binderAssemblyString);
}
}
internal static WriteObjectInfo Serialize(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type objectType,
ISurrogateSelector? surrogateSelector,
StreamingContext context,
SerObjectInfoInit serObjectInfoInit,
IFormatterConverter converter,
SerializationBinder? binder)
{
WriteObjectInfo woi = GetObjectInfo(serObjectInfoInit);
woi.InitSerialize(objectType, surrogateSelector, context, serObjectInfoInit, converter, binder);
return woi;
}
// Write Constructor used for array types or null members
internal void InitSerialize(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type objectType,
ISurrogateSelector? surrogateSelector,
StreamingContext context,
SerObjectInfoInit serObjectInfoInit,
IFormatterConverter converter,
SerializationBinder? binder)
{
_objectType = objectType;
_context = context;
_serObjectInfoInit = serObjectInfoInit;
if (objectType.IsArray)
{
InitNoMembers();
return;
}
InvokeSerializationBinder(binder);
if (surrogateSelector != null)
{
_serializationSurrogate = surrogateSelector.GetSurrogate(objectType, context, out _);
}
if (_serializationSurrogate != null)
{
// surrogate does not have this problem since user has pass in through the BF's ctor
_si = new SerializationInfo(objectType, converter);
_cache = new SerObjectInfoCache(objectType);
_isSi = true;
}
else if (!ReferenceEquals(objectType, Converter.s_typeofObject) && Converter.s_typeofISerializable.IsAssignableFrom(objectType))
{
_si = new SerializationInfo(objectType, converter);
_cache = new SerObjectInfoCache(objectType);
CheckTypeForwardedFrom(_cache, objectType, _binderAssemblyString);
_isSi = true;
}
if (!_isSi)
{
InitMemberInfo();
CheckTypeForwardedFrom(_cache, objectType, _binderAssemblyString);
}
}
private void InitSiWrite()
{
SerializationInfoEnumerator? siEnum;
_isSi = true;
Debug.Assert(_si != null);
int infoLength;
infoLength = _si.MemberCount;
int count = infoLength;
// For ISerializable cache cannot be saved because each object instance can have different values
// BinaryWriter only puts the map on the wire if the ISerializable map cannot be reused.
TypeInformation? typeInformation = null;
string fullTypeName = _si.FullTypeName;
string assemblyString = _si.AssemblyName;
bool hasTypeForwardedFrom = false;
if (!_si.IsFullTypeNameSetExplicit)
{
typeInformation = BinaryFormatter.GetTypeInformation(_si.ObjectType);
fullTypeName = typeInformation.FullTypeName;
hasTypeForwardedFrom = typeInformation.HasTypeForwardedFrom;
}
if (!_si.IsAssemblyNameSetExplicit)
{
typeInformation ??= BinaryFormatter.GetTypeInformation(_si.ObjectType);
assemblyString = typeInformation.AssemblyString;
hasTypeForwardedFrom = typeInformation.HasTypeForwardedFrom;
}
_cache = new SerObjectInfoCache(fullTypeName, assemblyString, hasTypeForwardedFrom);
_cache._memberNames = new string[count];
_cache._memberTypes = new Type[count];
_memberData = new object[count];
siEnum = _si.GetEnumerator();
for (int i = 0; siEnum.MoveNext(); i++)
{
_cache._memberNames[i] = siEnum.Name;
_cache._memberTypes[i] = siEnum.ObjectType;
_memberData[i] = siEnum.Value;
}
_isNamed = true;
}
private static void CheckTypeForwardedFrom(SerObjectInfoCache? cache, Type objectType, string? binderAssemblyString)
{
// nop
}
private void InitNoMembers()
{
Debug.Assert(_serObjectInfoInit != null && _objectType != null);
if (!_serObjectInfoInit._seenBeforeTable.TryGetValue(_objectType, out _cache!))
{
_cache = new SerObjectInfoCache(_objectType);
_serObjectInfoInit._seenBeforeTable.Add(_objectType, _cache);
}
}
private void InitMemberInfo()
{
Debug.Assert(_serObjectInfoInit != null && _objectType != null);
if (!_serObjectInfoInit._seenBeforeTable.TryGetValue(_objectType, out _cache!))
{
_cache = new SerObjectInfoCache(_objectType);
_cache._memberInfos = FormatterServices.GetSerializableMembers(_objectType, _context);
int count = _cache._memberInfos.Length;
_cache._memberNames = new string[count];
_cache._memberTypes = new Type[count];
// Calculate new arrays
for (int i = 0; i < count; i++)
{
_cache._memberNames[i] = _cache._memberInfos[i].Name;
_cache._memberTypes[i] = ((FieldInfo)_cache._memberInfos[i]).FieldType;
}
_serObjectInfoInit._seenBeforeTable.Add(_objectType, _cache);
}
if (_obj != null)
{
_memberData = FormatterServices.GetObjectData(_obj, _cache._memberInfos!);
}
_isNamed = true;
}
internal string GetTypeFullName() => _binderTypeName ?? _cache._fullTypeName;
internal string GetAssemblyString() => _binderAssemblyString ?? _cache._assemblyString;
private void InvokeSerializationBinder(SerializationBinder? binder)
{
BinaryFormatterEventSource.Log.SerializingObject(_objectType!);
binder?.BindToName(_objectType!, out _binderAssemblyString, out _binderTypeName);
}
internal void GetMemberInfo(out string[]? outMemberNames, out Type[]? outMemberTypes, out object?[]? outMemberData)
{
outMemberNames = _cache._memberNames;
outMemberTypes = _cache._memberTypes;
outMemberData = _memberData;
if (_isSi && !_isNamed)
{
throw new SerializationException(SR.Serialization_ISerializableMemberInfo);
}
}
private static WriteObjectInfo GetObjectInfo(SerObjectInfoInit serObjectInfoInit)
{
WriteObjectInfo objectInfo;
if (!serObjectInfoInit._oiPool.IsEmpty())
{
objectInfo = (WriteObjectInfo)serObjectInfoInit._oiPool.Pop()!;
objectInfo.InternalInit();
}
else
{
objectInfo = new WriteObjectInfo();
objectInfo._objectInfoId = serObjectInfoInit._objectInfoIdCount++;
}
return objectInfo;
}
private static void PutObjectInfo(SerObjectInfoInit serObjectInfoInit, WriteObjectInfo objectInfo) =>
serObjectInfoInit._oiPool.Push(objectInfo);
}
internal sealed class ReadObjectInfo
{
internal int _objectInfoId;
internal static int _readObjectInfoCounter;
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
internal Type? _objectType;
internal ObjectManager? _objectManager;
internal int _count;
internal bool _isSi;
internal bool _isTyped;
internal bool _isSimpleAssembly;
internal SerObjectInfoCache? _cache;
internal string[]? _wireMemberNames;
internal Type[]? _wireMemberTypes;
private int _lastPosition;
internal ISerializationSurrogate? _serializationSurrogate;
internal StreamingContext _context;
// Si Read
internal List<Type>? _memberTypesList;
internal SerObjectInfoInit? _serObjectInfoInit;
internal IFormatterConverter? _formatterConverter;
internal ReadObjectInfo() { }
internal void ObjectEnd() { }
internal void PrepareForReuse()
{
_lastPosition = 0;
}
internal static ReadObjectInfo Create(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type objectType,
ISurrogateSelector? surrogateSelector,
StreamingContext context,
ObjectManager? objectManager,
SerObjectInfoInit? serObjectInfoInit,
IFormatterConverter? converter,
bool bSimpleAssembly)
{
ReadObjectInfo roi = GetObjectInfo(serObjectInfoInit);
roi.Init(objectType, surrogateSelector, context, objectManager, serObjectInfoInit, converter, bSimpleAssembly);
return roi;
}
internal void Init(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type objectType,
ISurrogateSelector? surrogateSelector,
StreamingContext context,
ObjectManager? objectManager,
SerObjectInfoInit? serObjectInfoInit,
IFormatterConverter? converter,
bool bSimpleAssembly)
{
_objectType = objectType;
_objectManager = objectManager;
_context = context;
_serObjectInfoInit = serObjectInfoInit;
_formatterConverter = converter;
_isSimpleAssembly = bSimpleAssembly;
InitReadConstructor(objectType, surrogateSelector, context);
}
internal static ReadObjectInfo Create(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? objectType,
string[] memberNames,
Type[]? memberTypes,
ISurrogateSelector? surrogateSelector,
StreamingContext context,
ObjectManager? objectManager,
SerObjectInfoInit? serObjectInfoInit,
IFormatterConverter? converter,
bool bSimpleAssembly)
{
ReadObjectInfo roi = GetObjectInfo(serObjectInfoInit);
roi.Init(objectType, memberNames, memberTypes, surrogateSelector, context, objectManager, serObjectInfoInit, converter, bSimpleAssembly);
return roi;
}
internal void Init(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? objectType,
string[] memberNames,
Type[]? memberTypes,
ISurrogateSelector? surrogateSelector,
StreamingContext context,
ObjectManager? objectManager,
SerObjectInfoInit? serObjectInfoInit,
IFormatterConverter? converter,
bool bSimpleAssembly)
{
_objectType = objectType;
_objectManager = objectManager;
_wireMemberNames = memberNames;
_wireMemberTypes = memberTypes;
_context = context;
_serObjectInfoInit = serObjectInfoInit;
_formatterConverter = converter;
_isSimpleAssembly = bSimpleAssembly;
if (memberTypes != null)
{
_isTyped = true;
}
if (objectType != null)
{
InitReadConstructor(objectType, surrogateSelector, context);
}
}
private void InitReadConstructor(Type objectType, ISurrogateSelector? surrogateSelector, StreamingContext context)
{
BinaryFormatterEventSource.Log.DeserializingObject(objectType);
if (objectType.IsArray)
{
InitNoMembers();
return;
}
if (surrogateSelector != null)
{
_serializationSurrogate = surrogateSelector.GetSurrogate(objectType, context, out _);
}
if (_serializationSurrogate != null)
{
_isSi = true;
}
else if (!ReferenceEquals(objectType, Converter.s_typeofObject) && Converter.s_typeofISerializable.IsAssignableFrom(objectType))
{
_isSi = true;
}
if (_isSi)
{
InitSiRead();
}
else
{
InitMemberInfo();
}
}
private void InitSiRead()
{
if (_memberTypesList != null)
{
_memberTypesList = new List<Type>(20);
}
}
private void InitNoMembers()
{
_cache = new SerObjectInfoCache(_objectType!);
}
private void InitMemberInfo()
{
_cache = new SerObjectInfoCache(_objectType!);
_cache._memberInfos = FormatterServices.GetSerializableMembers(_objectType!, _context);
_count = _cache._memberInfos.Length;
_cache._memberNames = new string[_count];
_cache._memberTypes = new Type[_count];
// Calculate new arrays
for (int i = 0; i < _count; i++)
{
_cache._memberNames[i] = _cache._memberInfos[i].Name;
_cache._memberTypes[i] = GetMemberType(_cache._memberInfos[i]);
}
_isTyped = true;
}
// Get the memberInfo for a memberName
internal MemberInfo? GetMemberInfo(string? name)
{
if (_cache == null)
{
return null;
}
if (_isSi)
{
throw new SerializationException(SR.Format(SR.Serialization_MemberInfo, _objectType + " " + name));
}
if (_cache._memberInfos == null)
{
throw new SerializationException(SR.Format(SR.Serialization_NoMemberInfo, _objectType + " " + name));
}
int position = Position(name);
return position != -1 ? _cache._memberInfos[position] : null;
}
// Get the ObjectType for a memberName
internal Type? GetType(string name)
{
int position = Position(name);
if (position == -1)
{
return null;
}
Type type = _isTyped ? _cache!._memberTypes![position] : _memberTypesList![position];
if (type == null)
{
throw new SerializationException(SR.Format(SR.Serialization_ISerializableTypes, _objectType + " " + name));
}
return type;
}
// Adds the value for a memberName
internal void AddValue(string name, object? value, ref SerializationInfo? si, ref object?[]? memberData)
{
if (_isSi)
{
Debug.Assert(si != null);
si.AddValue(name, value);
}
else
{
// If a member in the stream is not found, ignore it
int position = Position(name);
if (position != -1)
{
Debug.Assert(memberData != null);
memberData[position] = value;
}
}
}
internal void InitDataStore(ref SerializationInfo? si, ref object?[]? memberData)
{
if (_isSi)
{
if (si == null)
{
Debug.Assert(_objectType != null);
si = new SerializationInfo(_objectType, _formatterConverter!);
}
}
else
{
if (memberData == null && _cache != null)
{
Debug.Assert(_cache._memberNames != null);
memberData = new object[_cache._memberNames.Length];
}
}
}
// Records an objectId in a member when the actual object for that member is not yet known
internal void RecordFixup(long objectId, string name, long idRef)
{
if (_isSi)
{
if (_objectManager == null)
{
throw new SerializationException(SR.Serialization_CorruptedStream);
}
_objectManager.RecordDelayedFixup(objectId, name, idRef);
}
else
{
int position = Position(name);
if (position != -1)
{
if (_objectManager == null)
{
throw new SerializationException(SR.Serialization_CorruptedStream);
}
Debug.Assert(_cache != null && _cache._memberInfos != null);
_objectManager.RecordFixup(objectId, _cache._memberInfos[position], idRef);
}
}
}
// Fills in the values for an object
internal void PopulateObjectMembers(object obj, object?[]? memberData)
{
if (!_isSi && memberData != null)
{
Debug.Assert(_cache != null && _cache._memberInfos != null);
FormatterServices.PopulateObjectMembers(obj, _cache._memberInfos, memberData);
}
}
// Specifies the position in the memberNames array of this name
private int Position(string? name)
{
if (_cache == null)
{
return -1;
}
Debug.Assert(_cache._memberNames != null);
if (_cache._memberNames.Length > 0 && _cache._memberNames[_lastPosition].Equals(name))
{
return _lastPosition;
}
else if ((++_lastPosition < _cache._memberNames.Length) && (_cache._memberNames[_lastPosition].Equals(name)))
{
return _lastPosition;
}
else
{
// Search for name
for (int i = 0; i < _cache._memberNames.Length; i++)
{
if (_cache._memberNames[i].Equals(name))
{
_lastPosition = i;
return _lastPosition;
}
}
_lastPosition = 0;
return -1;
}
}
// Return the member Types in order of memberNames
internal Type[]? GetMemberTypes(string[] inMemberNames, Type? objectType)
{
if (_isSi)
{
throw new SerializationException(SR.Format(SR.Serialization_ISerializableTypes, objectType));
}
if (_cache == null)
{
return null;
}
Debug.Assert(_cache._memberInfos != null);
if (_cache._memberTypes == null)
{
_cache._memberTypes = new Type[_count];
for (int i = 0; i < _count; i++)
{
_cache._memberTypes[i] = GetMemberType(_cache._memberInfos[i]);
}
}
bool memberMissing = false;
if (inMemberNames.Length < _cache._memberInfos.Length)
{
memberMissing = true;
}
Type[] outMemberTypes = new Type[_cache._memberInfos.Length];
bool isFound;
for (int i = 0; i < _cache._memberInfos.Length; i++)
{
if (!memberMissing && inMemberNames[i].Equals(_cache._memberInfos[i].Name))
{
outMemberTypes[i] = _cache._memberTypes[i];
}
else
{
// MemberNames on wire in different order then memberInfos returned by reflection
isFound = false;
for (int j = 0; j < inMemberNames.Length; j++)
{
if (_cache._memberInfos[i].Name.Equals(inMemberNames[j]))
{
outMemberTypes[i] = _cache._memberTypes[i];
isFound = true;
break;
}
}
if (!isFound)
{
// A field on the type isn't found. See if the field has OptionalFieldAttribute. We only throw
// when the assembly format is set appropriately.
if (!_isSimpleAssembly &&
_cache._memberInfos[i].GetCustomAttribute<OptionalFieldAttribute>(inherit: false) == null)
{
Debug.Assert(_cache._memberNames != null);
throw new SerializationException(SR.Format(SR.Serialization_MissingMember, _cache._memberNames[i], objectType, typeof(OptionalFieldAttribute).FullName));
}
}
}
}
return outMemberTypes;
}
// Retrieves the member type from the MemberInfo
internal Type GetMemberType(MemberInfo objMember)
{
if (objMember is FieldInfo)
{
return ((FieldInfo)objMember).FieldType;
}
throw new SerializationException(SR.Format(SR.Serialization_SerMemberInfo, objMember.GetType()));
}
private static ReadObjectInfo GetObjectInfo(SerObjectInfoInit? serObjectInfoInit)
{
ReadObjectInfo roi = new ReadObjectInfo();
roi._objectInfoId = Interlocked.Increment(ref _readObjectInfoCounter);
return roi;
}
}
internal sealed class SerObjectInfoInit
{
internal readonly Dictionary<Type, SerObjectInfoCache> _seenBeforeTable = new Dictionary<Type, SerObjectInfoCache>();
internal int _objectInfoIdCount = 1;
internal SerStack _oiPool = new SerStack("SerObjectInfo Pool");
}
internal sealed class SerObjectInfoCache
{
internal readonly string _fullTypeName;
internal readonly string _assemblyString;
internal readonly bool _hasTypeForwardedFrom;
internal MemberInfo[]? _memberInfos;
internal string[]? _memberNames;
internal Type[]? _memberTypes;
internal SerObjectInfoCache(string typeName, string assemblyName, bool hasTypeForwardedFrom)
{
_fullTypeName = typeName;
_assemblyString = assemblyName;
_hasTypeForwardedFrom = hasTypeForwardedFrom;
}
internal SerObjectInfoCache(Type type)
{
TypeInformation typeInformation = BinaryFormatter.GetTypeInformation(type);
_fullTypeName = typeInformation.FullTypeName;
_assemblyString = typeInformation.AssemblyString;
_hasTypeForwardedFrom = typeInformation.HasTypeForwardedFrom;
}
}
internal sealed class TypeInformation
{
internal TypeInformation(string fullTypeName, string assemblyString, bool hasTypeForwardedFrom)
{
FullTypeName = fullTypeName;
AssemblyString = assemblyString;
HasTypeForwardedFrom = hasTypeForwardedFrom;
}
internal string FullTypeName { get; }
internal string AssemblyString { get; }
internal bool HasTypeForwardedFrom { get; }
}
}
|