|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
namespace System.Runtime.Serialization
{
[Obsolete(Obsoletions.LegacyFormatterMessage, DiagnosticId = Obsoletions.LegacyFormatterDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
public class ObjectManager
{
private const int DefaultInitialSize = 16;
private const int MaxArraySize = 0x100000; //MUST BE A POWER OF 2!
private const int ArrayMask = MaxArraySize - 1;
private const int MaxReferenceDepth = 100;
private const string ObjectManagerUnreferencedCodeMessage = "ObjectManager is not trim compatible because the type of objects being managed cannot be statically discovered.";
private static readonly FieldInfo s_nullableValueField = typeof(Nullable<>).GetField("value", BindingFlags.NonPublic | BindingFlags.Instance)!;
private DeserializationEventHandler? _onDeserializationHandler;
private SerializationEventHandler? _onDeserializedHandler;
internal ObjectHolder[] _objects;
internal object? _topObject;
internal ObjectHolderList? _specialFixupObjects; //This is IObjectReference, ISerializable, or has a Surrogate.
internal long _fixupCount;
internal readonly ISurrogateSelector? _selector;
internal readonly StreamingContext _context;
public ObjectManager(ISurrogateSelector? selector, StreamingContext context)
{
_objects = new ObjectHolder[DefaultInitialSize];
_selector = selector;
_context = context;
}
private bool CanCallGetType(object? obj) => true;
internal object? TopObject
{
get { return _topObject; }
set { _topObject = value; }
}
internal ObjectHolderList SpecialFixupObjects =>
_specialFixupObjects ??= new ObjectHolderList();
internal ObjectHolder? FindObjectHolder(long objectID)
{
// The index of the bin in which we live is rightmost n bits of the objectID.
int index = (int)(objectID & ArrayMask);
if (index >= _objects.Length)
{
return null;
}
// Find the bin in which we live.
ObjectHolder? temp = _objects[index];
// Walk the chain in that bin. Return the ObjectHolder if we find it, otherwise
// return null.
while (temp != null)
{
if (temp._id == objectID)
{
return temp;
}
temp = temp._next;
}
return temp;
}
internal ObjectHolder FindOrCreateObjectHolder(long objectID)
{
ObjectHolder? holder = FindObjectHolder(objectID);
if (holder == null)
{
holder = new ObjectHolder(objectID);
AddObjectHolder(holder);
}
return holder;
}
private void AddObjectHolder(ObjectHolder holder)
{
Debug.Assert(holder != null, "holder!=null");
Debug.Assert(holder._id >= 0, "holder.m_id>=0");
//If the id that we need to place is greater than our current length, and less
//than the maximum allowable size of the array. We need to double the size
//of the array. If the array has already reached it's maximum allowable size,
//we chain elements off of the buckets.
if (holder._id >= _objects.Length && _objects.Length != MaxArraySize)
{
int newSize = MaxArraySize;
if (holder._id < (MaxArraySize / 2))
{
newSize = (_objects.Length * 2);
//Keep doubling until we're larger than our target size.
//We could also do this with log operations, but that would
//be slower than the brute force approach.
while (newSize <= holder._id && newSize < MaxArraySize)
{
newSize *= 2;
}
if (newSize > MaxArraySize)
{
newSize = MaxArraySize;
}
}
ObjectHolder[] temp = new ObjectHolder[newSize];
Array.Copy(_objects, temp, _objects.Length);
_objects = temp;
}
//Find the bin in which we live and make this new element the first element in the bin.
int index = (int)(holder._id & ArrayMask);
ObjectHolder tempHolder = _objects[index];
holder._next = tempHolder;
_objects[index] = holder;
}
private bool GetCompletionInfo(FixupHolder fixup, [NotNullWhen(true)] out ObjectHolder? holder, out object member, bool bThrowIfMissing)
{
//Set the member id (String or MemberInfo) for the member being fixed up.
member = fixup._fixupInfo;
//Find the object required for the fixup. Throw if we can't find it.
holder = FindObjectHolder(fixup._id);
if (holder == null || holder.CanObjectValueChange || holder.ObjectValue == null)
{
if (bThrowIfMissing)
{
if (holder == null)
{
throw new SerializationException(SR.Format(SR.Serialization_NeverSeen, fixup._id));
}
if (holder.IsIncompleteObjectReference)
{
throw new SerializationException(SR.Format(SR.Serialization_IORIncomplete, fixup._id));
}
throw new SerializationException(SR.Format(SR.Serialization_ObjectNotSupplied, fixup._id));
}
return false;
}
// CompletelyFixed is our poorly named property which indicates if something requires a SerializationInfo fixup
// or is an incomplete object reference. We have this particular branch to handle valuetypes which implement
// ISerializable. In that case, we can't do any fixups on them later, so we need to delay the fixups further.
if (!holder.CompletelyFixed)
{
if (holder.ObjectValue != null && holder.ObjectValue is ValueType)
{
SpecialFixupObjects.Add(holder);
return false;
}
}
return true;
}
[RequiresUnreferencedCode(ObjectManagerUnreferencedCodeMessage)]
private void FixupSpecialObject(ObjectHolder holder)
{
ISurrogateSelector? uselessSelector = null;
Debug.Assert(holder.RequiresSerInfoFixup, "[ObjectManager.FixupSpecialObject]holder.HasSurrogate||holder.HasISerializable");
if (holder.HasSurrogate)
{
ISerializationSurrogate? surrogate = holder.Surrogate;
Debug.Assert(surrogate != null, "surrogate!=null");
Debug.Assert(holder.SerializationInfo != null);
object? returnValue = surrogate.SetObjectData(holder.ObjectValue!, holder.SerializationInfo, _context, uselessSelector);
if (returnValue != null)
{
if (!holder.CanSurrogatedObjectValueChange && returnValue != holder.ObjectValue)
{
throw new SerializationException(SR.Format(SR.Serialization_NotCyclicallyReferenceableSurrogate, surrogate.GetType().FullName));
}
holder.SetObjectValue(returnValue, this);
}
holder._surrogate = null;
holder.SetFlags();
}
else
{
//Set the object data
Debug.Assert(holder.ObjectValue is ISerializable, "holder.m_object is ISerializable");
CompleteISerializableObject(holder.ObjectValue, holder.SerializationInfo, _context);
}
//Clear anything that we know that we're not going to need.
holder.SerializationInfo = null;
holder.RequiresSerInfoFixup = false;
// For value types, fixups would have been done. So the newly fixed object must be copied
// to its container.
if (holder.RequiresValueTypeFixup && holder.ValueTypeFixupPerformed)
{
DoValueTypeFixup(null, holder, holder.ObjectValue);
}
DoNewlyRegisteredObjectFixups(holder);
}
/// <summary>
/// Unfortunately, an ObjectReference could actually be a reference to another
/// object reference and we don't know how far we have to tunnel until we can find the real object. While
/// we're still getting instances of IObjectReference back and we're still getting new objects, keep calling
/// GetRealObject. Once we've got the new object, take care of all of the fixups
/// that we can do now that we've got it.
/// </summary>
/// <param name="holder"></param>
private bool ResolveObjectReference(ObjectHolder holder)
{
object? tempObject;
Debug.Assert(holder.IsIncompleteObjectReference);
//In the pathological case, an Object implementing IObjectReference could return a reference
//to a different object which implements IObjectReference. This makes us vulnerable to a
//denial of service attack and stack overflow. If the depthCount becomes greater than
//MaxReferenceDepth, we'll throw a SerializationException.
int depthCount = 0;
//We wrap this in a try/catch block to handle the case where we're trying to resolve a chained
//list of object reference (e.g. an IObjectReference can't resolve itself without some information
//that's currently missing from the graph). We'll catch the NullReferenceException and come back
//and try again later. The downside of this scheme is that if the object actually needed to throw
//a NullReferenceException, it's being caught and turned into a SerializationException with a
//fairly cryptic message.
try
{
do
{
tempObject = holder.ObjectValue;
Debug.Assert(holder.ObjectValue != null);
holder.SetObjectValue(((IObjectReference)(holder.ObjectValue)).GetRealObject(_context), this);
//The object didn't yet have enough information to resolve the reference, so we'll
//return false and the graph walker should call us back again after more objects have
//been resolved.
if (holder.ObjectValue == null)
{
holder.SetObjectValue(tempObject, this);
return false;
}
if (depthCount++ == MaxReferenceDepth)
{
throw new SerializationException(SR.Serialization_TooManyReferences);
}
} while ((holder.ObjectValue is IObjectReference) && (tempObject != holder.ObjectValue));
}
catch (NullReferenceException)
{
return false;
}
holder.IsIncompleteObjectReference = false;
DoNewlyRegisteredObjectFixups(holder);
return true;
}
/*===============================DoValueTypeFixup===============================
**Arguments:
** memberToFix -- the member in the object contained in holder being fixed up.
** holder -- the ObjectHolder for the object (a value type in this case) being completed.
** value -- the data to set into the field.
==============================================================================*/
private bool DoValueTypeFixup(FieldInfo? memberToFix, ObjectHolder holder, object? value)
{
var fieldsTemp = new FieldInfo[4];
FieldInfo[] fields;
int currentFieldIndex = 0;
int[]? arrayIndex = null;
ValueTypeFixupInfo currFixup;
object? fixupObj = holder.ObjectValue;
Debug.Assert(holder != null, "[TypedReferenceBuilder.ctor]holder!=null");
Debug.Assert(holder.RequiresValueTypeFixup, "[TypedReferenceBuilder.ctor]holder.RequiresValueTypeFixup");
//In order to get a TypedReference, we need to get a list of all of the FieldInfos to
//create the path from our outermost containing object down to the actual field which
//we'd like to set. This loop is used to build up that list.
while (holder.RequiresValueTypeFixup)
{
//Enlarge the array if required (this is actually fairly unlikely as it would require that we
//be nested more than 4 deep.
if ((currentFieldIndex + 1) >= fieldsTemp.Length)
{
var temp = new FieldInfo[fieldsTemp.Length * 2];
Array.Copy(fieldsTemp, temp, fieldsTemp.Length);
fieldsTemp = temp;
}
//Get the fixup information. If we have data for our parent field, add it to our list
//and continue the walk up to find the next outermost containing object. We cache the
//object that we have. In most cases, we could have just grabbed it after this loop finished.
//However, if the outermost containing object is an array, we need the object one further
//down the chain, so we have to do a lot of caching.
currFixup = holder.ValueFixup!;
fixupObj = holder.ObjectValue; //Save the most derived
if (currFixup.ParentField != null)
{
FieldInfo parentField = currFixup.ParentField;
ObjectHolder? tempHolder = FindObjectHolder(currFixup.ContainerID);
Debug.Assert(tempHolder != null);
if (tempHolder.ObjectValue == null)
{
break;
}
FieldInfo? nullableValueField = GetNullableValueField(parentField.FieldType);
if (nullableValueField != null)
{
fieldsTemp[currentFieldIndex] = nullableValueField;
currentFieldIndex++;
}
fieldsTemp[currentFieldIndex] = parentField;
holder = tempHolder;
currentFieldIndex++;
}
else
{
//If we find an index into an array, save that information.
Debug.Assert(currFixup.ParentIndex != null, "[ObjectManager.DoValueTypeFixup]currFixup.ParentIndex!=null");
holder = FindObjectHolder(currFixup.ContainerID)!; //find the array to fix.
arrayIndex = currFixup.ParentIndex;
break;
}
}
//If the outermost container isn't an array, we need to grab it. Otherwise, we just need to hang onto
//the boxed object that we already grabbed. We'll assign the boxed object back into the array as the
//last step.
if (!(holder.ObjectValue is Array) && holder.ObjectValue != null)
{
fixupObj = holder.ObjectValue;
Debug.Assert(fixupObj != null, "[ObjectManager.DoValueTypeFixup]FixupObj!=null");
}
if (currentFieldIndex != 0)
{
//MakeTypedReference requires an array of exactly the correct size that goes from the outermost object
//in to the innermost field. We currently have an array of arbitrary size that goes from the innermost
//object outwards. We create an array of the right size and do the copy.
fields = new FieldInfo[currentFieldIndex];
for (int i = 0; i < currentFieldIndex; i++)
{
FieldInfo? fieldInfo = fieldsTemp[(currentFieldIndex - 1 - i)];
SerializationFieldInfo? serInfo = fieldInfo as SerializationFieldInfo;
fields[i] = serInfo == null ? fieldInfo : serInfo.FieldInfo;
}
Debug.Assert(fixupObj != null, "[ObjectManager.DoValueTypeFixup]fixupObj!=null");
//Make the TypedReference and use it to set the value.
TypedReference typedRef = TypedReference.MakeTypedReference(fixupObj, fields);
if (memberToFix != null)
{
memberToFix.SetValueDirect(typedRef, value!);
}
else
{
TypedReference.SetTypedReference(typedRef, value);
}
}
else if (memberToFix != null)
{
FormatterServices.SerializationSetValue(memberToFix, fixupObj, value);
}
//If we have an array index, it means that our outermost container was an array. We don't have
//any way to build a TypedReference into an array, so we'll use the array functions to set the value.
if (arrayIndex != null && holder.ObjectValue != null)
{
((Array)(holder.ObjectValue)).SetValue(fixupObj, arrayIndex);
}
return true;
}
private static FieldInfo? GetNullableValueField(Type type)
{
if (Nullable.GetUnderlyingType(type) != null)
{
return (FieldInfo)type.GetMemberWithSameMetadataDefinitionAs(s_nullableValueField);
}
return null;
}
internal void CompleteObject(ObjectHolder holder, bool bObjectFullyComplete)
{
FixupHolderList? fixups = holder._missingElements;
FixupHolder? currentFixup;
SerializationInfo? si;
object? fixupInfo;
ObjectHolder? tempObjectHolder;
int fixupsPerformed = 0;
Debug.Assert(holder != null, "[ObjectManager.CompleteObject]holder.m_object!=null");
if (holder.ObjectValue == null)
{
throw new SerializationException(SR.Format(SR.Serialization_MissingObject, holder._id));
}
if (fixups == null)
{
return;
}
//If either one of these conditions is true, we need to update the data in the
//SerializationInfo before calling SetObjectData.
if (holder.HasSurrogate || holder.HasISerializable)
{
si = holder._serInfo;
if (si == null)
{
throw new SerializationException(SR.Serialization_InvalidFixupDiscovered);
}
//Walk each of the fixups and complete the name-value pair in the SerializationInfo.
if (fixups != null)
{
for (int i = 0; i < fixups._count; i++)
{
if (fixups._values[i] == null)
{
continue;
}
Debug.Assert(fixups._values[i]!._fixupType == FixupHolder.DelayedFixup, "fixups.m_values[i].m_fixupType==FixupHolder.DelayedFixup");
if (GetCompletionInfo(fixups._values[i]!, out tempObjectHolder, out fixupInfo, bObjectFullyComplete))
{
//Walk the SerializationInfo and find the member needing completion. All we have to do
//at this point is set the member into the Object
object? holderValue = tempObjectHolder.ObjectValue;
Debug.Assert(holderValue != null);
if (CanCallGetType(holderValue))
{
si.UpdateValue((string)fixupInfo, holderValue, holderValue.GetType());
}
else
{
si.UpdateValue((string)fixupInfo, holderValue, typeof(MarshalByRefObject));
}
//Decrement our total number of fixups left to do.
fixupsPerformed++;
fixups._values[i] = null;
if (!bObjectFullyComplete)
{
holder.DecrementFixupsRemaining(this);
tempObjectHolder.RemoveDependency(holder._id);
}
}
}
}
}
else
{
for (int i = 0; i < fixups._count; i++)
{
currentFixup = fixups._values[i];
if (currentFixup == null)
{
continue;
}
if (GetCompletionInfo(currentFixup, out tempObjectHolder, out fixupInfo, bObjectFullyComplete))
{
// Check to make sure we are not both reachable from the topObject
// and there was a typeloadexception
if (tempObjectHolder.TypeLoadExceptionReachable)
{
holder.TypeLoadException = tempObjectHolder.TypeLoadException;
// If the holder is both reachable and typeloadexceptionreachable
// throw an exception with the type name
if (holder.Reachable)
{
throw new SerializationException(SR.Format(SR.Serialization_TypeLoadFailure, holder.TypeLoadException!.TypeName));
}
}
// If the current holder is reachable, mark the dependant reachable as well
if (holder.Reachable)
{
tempObjectHolder.Reachable = true;
}
//There are two types of fixups that we could be doing: array or member.
//Delayed Fixups should be handled by the above branch.
switch (currentFixup._fixupType)
{
case FixupHolder.ArrayFixup:
Debug.Assert(holder.ObjectValue is Array);
if (holder.RequiresValueTypeFixup)
{
throw new SerializationException(SR.Serialization_ValueTypeFixup);
}
else
{
((Array)(holder.ObjectValue)).SetValue(tempObjectHolder.ObjectValue, ((int[])fixupInfo));
}
break;
case FixupHolder.MemberFixup:
Debug.Assert(fixupInfo is MemberInfo);
//Fixup the member directly.
MemberInfo tempMember = (MemberInfo)fixupInfo;
if (tempMember is FieldInfo)
{
// If we have a valuetype that's been boxed to an object and requires a fixup,
// there are two possible states:
// (a)The valuetype has never been fixed up into it's container. In this case, we should
// just fix up the boxed valuetype. The task of pushing that valuetype into it's container
// will be handled later. This case is handled by the else clause of the following statement.
// (b)The valuetype has already been inserted into it's container. In that case, we need
// to go through the more complicated path laid out in DoValueTypeFixup. We can tell that the
// valuetype has already been inserted into it's container because we set ValueTypeFixupPerformed
// to true when we do this.
if (holder.RequiresValueTypeFixup && holder.ValueTypeFixupPerformed)
{
if (!DoValueTypeFixup((FieldInfo)tempMember, holder, tempObjectHolder.ObjectValue))
{
throw new SerializationException(SR.Serialization_PartialValueTypeFixup);
}
}
else
{
FormatterServices.SerializationSetValue(tempMember, holder.ObjectValue, tempObjectHolder.ObjectValue);
}
if (tempObjectHolder.RequiresValueTypeFixup)
{
tempObjectHolder.ValueTypeFixupPerformed = true;
}
}
else
{
throw new SerializationException(SR.Serialization_UnableToFixup);
}
break;
default:
throw new SerializationException(SR.Serialization_UnableToFixup);
}
//Decrement our total number of fixups left to do.
fixupsPerformed++;
fixups._values[i] = null;
if (!bObjectFullyComplete)
{
holder.DecrementFixupsRemaining(this);
tempObjectHolder.RemoveDependency(holder._id);
}
}
}
}
_fixupCount -= fixupsPerformed;
if (fixups!._count == fixupsPerformed)
{
holder._missingElements = null;
}
}
/// <summary>
/// This is called immediately after we register a new object. Walk that objects
/// dependency list (if it has one) and decrement the counters on each object for
/// the number of unsatisfiable references. If the count reaches 0, go ahead
/// and process the object.
/// </summary>
/// <param name="holder">dependencies The list of dependent objects</param>
private void DoNewlyRegisteredObjectFixups(ObjectHolder holder)
{
if (holder.CanObjectValueChange)
{
return;
}
//If we don't have any dependencies, we're done.
LongList? dependencies = holder.DependentObjects;
if (dependencies == null)
{
return;
}
//Walk all of the dependencies and decrement the counter on each of uncompleted objects.
//If one of the counters reaches 0, all of it's fields have been completed and we should
//go take care of its fixups.
dependencies.StartEnumeration();
while (dependencies.MoveNext())
{
ObjectHolder? temp = FindObjectHolder(dependencies.Current);
Debug.Assert(temp != null);
Debug.Assert(temp.DirectlyDependentObjects > 0, "temp.m_missingElementsRemaining>0");
temp.DecrementFixupsRemaining(this);
if (((temp.DirectlyDependentObjects)) == 0)
{
// If this is null, we have the case where a fixup was registered for a child, the object
// required by the fixup was provided, and the object to be fixed hasn't yet been seen.
if (temp.ObjectValue != null)
{
CompleteObject(temp, true);
}
else
{
temp.MarkForCompletionWhenAvailable();
}
}
}
}
public virtual object? GetObject(long objectID)
{
if (objectID <= 0)
{
throw new ArgumentOutOfRangeException(nameof(objectID), SR.ArgumentOutOfRange_ObjectID);
}
//Find the bin in which we're interested. IObjectReference's shouldn't be returned -- the graph
//needs to link to the objects to which they refer, not to the references themselves.
ObjectHolder? holder = FindObjectHolder(objectID);
if (holder == null || holder.CanObjectValueChange)
{
return null;
}
return holder.ObjectValue;
}
[RequiresUnreferencedCode(ObjectManagerUnreferencedCodeMessage)]
public virtual void RegisterObject(object obj, long objectID)
{
RegisterObject(obj, objectID, null, 0, null);
}
[RequiresUnreferencedCode(ObjectManagerUnreferencedCodeMessage)]
public void RegisterObject(object obj, long objectID, SerializationInfo info)
{
RegisterObject(obj, objectID, info, 0, null);
}
[RequiresUnreferencedCode(ObjectManagerUnreferencedCodeMessage)]
public void RegisterObject(object obj, long objectID, SerializationInfo? info, long idOfContainingObj, MemberInfo? member)
{
RegisterObject(obj, objectID, info, idOfContainingObj, member, null);
}
internal void RegisterString(string? obj, long objectID, SerializationInfo? info, long idOfContainingObj, MemberInfo? member)
{
ObjectHolder temp;
Debug.Assert(member == null || member is FieldInfo, "RegisterString - member is FieldInfo");
temp = new ObjectHolder(obj, objectID, info, null, idOfContainingObj, (FieldInfo?)member, null);
AddObjectHolder(temp);
return;
}
[RequiresUnreferencedCode(ObjectManagerUnreferencedCodeMessage)]
public void RegisterObject(object obj, long objectID, SerializationInfo? info, long idOfContainingObj, MemberInfo? member, int[]? arrayIndex)
{
ArgumentNullException.ThrowIfNull(obj);
if (objectID <= 0)
{
throw new ArgumentOutOfRangeException(nameof(objectID), SR.ArgumentOutOfRange_ObjectID);
}
if (member != null && !(member is FieldInfo)) // .NET Framework checks specifically for RuntimeFieldInfo and SerializationFieldInfo, but the former is an implementation detail in corelib
{
throw new SerializationException(SR.Serialization_UnknownMemberInfo);
}
ObjectHolder? temp;
ISerializationSurrogate? surrogate = null;
ISurrogateSelector useless;
if (_selector != null)
{
Type selectorType = CanCallGetType(obj) ?
obj.GetType() :
typeof(MarshalByRefObject);
//If we need a surrogate for this object, lets find it now.
surrogate = _selector.GetSurrogate(selectorType, _context, out useless);
}
//The object is interested in DeserializationEvents so lets register it.
if (obj is IDeserializationCallback)
{
DeserializationEventHandler d = new DeserializationEventHandler(((IDeserializationCallback)obj).OnDeserialization);
AddOnDeserialization(d);
}
//Formatter developers may cache and reuse arrayIndex in their code.
//So that we don't get bitten by this, take a copy up front.
if (arrayIndex != null)
{
arrayIndex = (int[])arrayIndex.Clone();
}
//This is the first time which we've seen the object, we need to create a new holder.
temp = FindObjectHolder(objectID);
if (temp == null)
{
temp = new ObjectHolder(obj, objectID, info, surrogate, idOfContainingObj, (FieldInfo?)member, arrayIndex);
AddObjectHolder(temp);
if (temp.RequiresDelayedFixup)
{
SpecialFixupObjects.Add(temp);
}
// We cannot compute whether this has any fixups required or not
AddOnDeserialized(obj);
return;
}
//If the object isn't null, we've registered this before. Not good.
if (temp.ObjectValue != null)
{
throw new SerializationException(SR.Serialization_RegisterTwice);
}
//Complete the data in the ObjectHolder
temp.UpdateData(obj, info, surrogate, idOfContainingObj, (FieldInfo?)member, arrayIndex, this);
// The following case will only be true when somebody has registered a fixup on an object before
// registering the object itself. I don't believe that most well-behaved formatters will do this,
// but we need to allow it anyway. We will walk the list of fixups which have been recorded on
// the new object and fix those that we can. Because the user could still register later fixups
// on this object, we won't call any implementations of ISerializable now. If that's required,
// it will have to be handled by the code in DoFixups.
// README README: We have to do the UpdateData before
if (temp.DirectlyDependentObjects > 0)
{
CompleteObject(temp, false);
}
if (temp.RequiresDelayedFixup)
{
SpecialFixupObjects.Add(temp);
}
if (temp.CompletelyFixed)
{
//Here's where things get tricky. If this isn't an instance of IObjectReference, we need to walk it's fixup
//chain and decrement the counters on anything that has reached 0. Once we've notified all of the dependencies,
//we can simply clear the list of dependent objects.
DoNewlyRegisteredObjectFixups(temp);
temp.DependentObjects = null;
}
//Register the OnDeserialized methods to be invoked after deserialization is complete
if (temp.TotalDependentObjects > 0)
{
AddOnDeserialized(obj);
}
else
{
RaiseOnDeserializedEvent(obj);
}
}
/// <summary>
/// Completes an object implementing ISerializable. This will involve calling that
/// objects constructor which takes an instance of ISerializable and a StreamingContext.
/// </summary>
/// <param name="obj">The object to be completed.</param>
/// <param name="info">The SerializationInfo containing all info for obj.</param>
/// <param name="context">The streaming context in which the serialization is taking place.</param>
[RequiresUnreferencedCode(ObjectManagerUnreferencedCodeMessage)]
internal void CompleteISerializableObject(object obj, SerializationInfo? info, StreamingContext context)
{
ArgumentNullException.ThrowIfNull(obj);
if (!(obj is ISerializable))
{
throw new ArgumentException(SR.Serialization_NotISer);
}
ConstructorInfo constInfo;
Type t = obj.GetType();
try
{
constInfo = GetDeserializationConstructor(t);
}
catch (Exception e)
{
throw new SerializationException(SR.Format(SR.Serialization_ConstructorNotFound, t), e);
}
constInfo.Invoke(obj, new object?[] { info, context });
}
internal static ConstructorInfo GetDeserializationConstructor(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type t)
{
foreach (ConstructorInfo ci in t.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly))
{
ParameterInfo[] parameters = ci.GetParameters();
if (parameters.Length == 2 &&
parameters[0].ParameterType == typeof(SerializationInfo) &&
parameters[1].ParameterType == typeof(StreamingContext))
{
return ci;
}
}
throw new SerializationException(SR.Format(SR.Serialization_ConstructorNotFound, t.FullName));
}
[RequiresUnreferencedCode(ObjectManagerUnreferencedCodeMessage)]
public virtual void DoFixups()
{
ObjectHolder? temp;
int fixupCount = -1;
//The first thing that we need to do is fixup all of the objects which implement
//IObjectReference. This is complicated by the fact that we need to deal with IReferenceObjects
//objects that have a reference to an object implementing IObjectReference. We continually
//walk over the list of objects until we've completed all of the object references or until
//we can't resolve any more (which may happen if we have two objects implementing IObjectReference
//which have a circular dependency on each other). We don't explicitly catch the later case here,
//it will be caught when we try to do the rest of the fixups and discover that we have some that
//can't be completed.
while (fixupCount != 0)
{
fixupCount = 0;
//Walk all of the IObjectReferences and ensure that they've been properly completed.
ObjectHolderListEnumerator fixupObjectsEnum = SpecialFixupObjects.GetFixupEnumerator();
while (fixupObjectsEnum.MoveNext())
{
temp = fixupObjectsEnum.Current;
if (temp.ObjectValue == null)
{
throw new SerializationException(SR.Format(SR.Serialization_ObjectNotSupplied, temp._id));
}
if (temp.TotalDependentObjects == 0)
{
if (temp.RequiresSerInfoFixup)
{
FixupSpecialObject(temp);
fixupCount++;
}
else if (!temp.IsIncompleteObjectReference)
{
CompleteObject(temp, true);
}
if (temp.IsIncompleteObjectReference && ResolveObjectReference(temp))
{
fixupCount++;
}
}
}
}
Debug.Assert(_fixupCount >= 0, "[ObjectManager.DoFixups]m_fixupCount>=0");
//If our count is 0, we're done and should just return
if (_fixupCount == 0)
{
if (TopObject is TypeLoadExceptionHolder)
{
throw new SerializationException(SR.Format(SR.Serialization_TypeLoadFailure, ((TypeLoadExceptionHolder)TopObject).TypeName));
}
return;
}
//If our count isn't 0, we had at least one case where an object referenced another object twice.
//Walk the entire list until the count is 0 or until we find an object which we can't complete.
for (int i = 0; i < _objects.Length; i++)
{
temp = _objects[i];
while (temp != null)
{
if (temp.TotalDependentObjects > 0 /*|| temp.m_missingElements!=null*/)
{
CompleteObject(temp, true);
}
temp = temp._next;
}
if (_fixupCount == 0)
{
return;
}
}
// this assert can be trigered by user code that manages fixups manually
throw new SerializationException(SR.Serialization_IncorrectNumberOfFixups);
}
/// <summary>
/// Do the actual grunt work of recording a fixup and registering the dependency.
/// Create the necessary ObjectHolders and use them to do the addition.
/// </summary>
/// <param name="fixup">The FixupHolder to be added.</param>
/// <param name="objectRequired">The id of the object required to do the fixup.</param>
/// <param name="objectToBeFixed">The id of the object requiring the fixup.</param>
private void RegisterFixup(FixupHolder fixup, long objectToBeFixed, long objectRequired)
{
//Record the fixup with the object that needs it.
ObjectHolder ohToBeFixed = FindOrCreateObjectHolder(objectToBeFixed);
ObjectHolder ohRequired;
if (ohToBeFixed.RequiresSerInfoFixup && fixup._fixupType == FixupHolder.MemberFixup)
{
throw new SerializationException(SR.Serialization_InvalidFixupType);
}
//Add the fixup to the list.
ohToBeFixed.AddFixup(fixup, this);
//Find the object on which we're dependent and note the dependency.
//These dependencies will be processed when the object is supplied.
ohRequired = FindOrCreateObjectHolder(objectRequired);
ohRequired.AddDependency(objectToBeFixed);
_fixupCount++;
}
public virtual void RecordFixup(long objectToBeFixed, MemberInfo member, long objectRequired)
{
//Verify our arguments
if (objectToBeFixed <= 0 || objectRequired <= 0)
{
throw new ArgumentOutOfRangeException(objectToBeFixed <= 0 ? nameof(objectToBeFixed) : nameof(objectRequired), SR.Serialization_IdTooSmall);
}
ArgumentNullException.ThrowIfNull(member);
if (!(member is FieldInfo)) // .NET Framework checks specifically for RuntimeFieldInfo and SerializationFieldInfo, but the former is an implementation detail in corelib
{
throw new SerializationException(SR.Format(SR.Serialization_InvalidType, member.GetType()));
}
//Create a new fixup holder
FixupHolder fixup = new FixupHolder(objectRequired, member, FixupHolder.MemberFixup);
RegisterFixup(fixup, objectToBeFixed, objectRequired);
}
public virtual void RecordDelayedFixup(long objectToBeFixed, string memberName, long objectRequired)
{
//Verify our arguments
if (objectToBeFixed <= 0 || objectRequired <= 0)
{
throw new ArgumentOutOfRangeException(objectToBeFixed <= 0 ? nameof(objectToBeFixed) : nameof(objectRequired), SR.Serialization_IdTooSmall);
}
ArgumentNullException.ThrowIfNull(memberName);
//Create a new fixup holder
FixupHolder fixup = new FixupHolder(objectRequired, memberName, FixupHolder.DelayedFixup);
RegisterFixup(fixup, objectToBeFixed, objectRequired);
}
public virtual void RecordArrayElementFixup(long arrayToBeFixed, int index, long objectRequired)
{
int[] indexArray = new int[1];
indexArray[0] = index;
RecordArrayElementFixup(arrayToBeFixed, indexArray, objectRequired);
}
public virtual void RecordArrayElementFixup(long arrayToBeFixed, int[] indices, long objectRequired)
{
//Verify our arguments
if (arrayToBeFixed <= 0 || objectRequired <= 0)
{
throw new ArgumentOutOfRangeException(arrayToBeFixed <= 0 ? nameof(arrayToBeFixed) : nameof(objectRequired), SR.Serialization_IdTooSmall);
}
ArgumentNullException.ThrowIfNull(indices);
FixupHolder fixup = new FixupHolder(objectRequired, indices, FixupHolder.ArrayFixup);
RegisterFixup(fixup, arrayToBeFixed, objectRequired);
}
public virtual void RaiseDeserializationEvent()
{
// Invoke OnDerserialized event if applicable
_onDeserializedHandler?.Invoke(_context);
_onDeserializationHandler?.Invoke(null);
}
internal virtual void AddOnDeserialization(DeserializationEventHandler handler)
{
_onDeserializationHandler = (DeserializationEventHandler)Delegate.Combine(_onDeserializationHandler, handler);
}
[RequiresUnreferencedCode(ObjectManagerUnreferencedCodeMessage)]
internal virtual void AddOnDeserialized(object obj)
{
SerializationEvents cache = SerializationEventsCache.GetSerializationEventsForType(obj.GetType());
_onDeserializedHandler = cache.AddOnDeserialized(obj, _onDeserializedHandler);
}
[RequiresUnreferencedCode(ObjectManagerUnreferencedCodeMessage)]
internal virtual void RaiseOnDeserializedEvent(object obj)
{
SerializationEvents cache = SerializationEventsCache.GetSerializationEventsForType(obj.GetType());
cache.InvokeOnDeserialized(obj, _context);
}
[RequiresUnreferencedCode(ObjectManagerUnreferencedCodeMessage)]
public void RaiseOnDeserializingEvent(object obj)
{
// Run the OnDeserializing methods
SerializationEvents cache = SerializationEventsCache.GetSerializationEventsForType(obj.GetType());
cache.InvokeOnDeserializing(obj, _context);
}
}
internal sealed class ObjectHolder
{
internal const int IncompleteObjectReference = 0x0001;
internal const int HAS_ISERIALIZABLE = 0x0002;
internal const int HAS_SURROGATE = 0x0004;
internal const int REQUIRES_VALUETYPE_FIXUP = 0x0008;
internal const int REQUIRES_DELAYED_FIXUP = HAS_ISERIALIZABLE | HAS_SURROGATE | IncompleteObjectReference;
internal const int SER_INFO_FIXED = 0x4000;
internal const int VALUETYPE_FIXUP_PERFORMED = 0x8000;
private object? _object;
internal readonly long _id;
private int _missingElementsRemaining;
private int _missingDecendents;
internal SerializationInfo? _serInfo;
internal ISerializationSurrogate? _surrogate;
internal FixupHolderList? _missingElements;
internal LongList? _dependentObjects;
internal ObjectHolder? _next;
internal int _flags;
private bool _markForFixupWhenAvailable;
private ValueTypeFixupInfo? _valueFixup;
private TypeLoadExceptionHolder? _typeLoad;
private bool _reachable;
internal ObjectHolder(long objID) : this(null, objID, null, null, 0, null, null)
{
}
internal ObjectHolder(
object? obj, long objID, SerializationInfo? info, ISerializationSurrogate? surrogate,
long idOfContainingObj, FieldInfo? field, int[]? arrayIndex)
{
Debug.Assert(objID >= 0, "objID>=0");
_object = obj; //May be null;
_id = objID;
_flags = 0;
_missingElementsRemaining = 0;
_missingDecendents = 0;
_dependentObjects = null;
_next = null;
_serInfo = info;
_surrogate = surrogate;
_markForFixupWhenAvailable = false;
if (obj is TypeLoadExceptionHolder)
{
_typeLoad = (TypeLoadExceptionHolder)obj;
}
if (idOfContainingObj != 0 && ((field != null && field.FieldType.IsValueType) || arrayIndex != null))
{
if (idOfContainingObj == objID)
{
throw new SerializationException(SR.Serialization_ParentChildIdentical);
}
_valueFixup = new ValueTypeFixupInfo(idOfContainingObj, field, arrayIndex);
}
SetFlags();
}
internal ObjectHolder(
string? obj, long objID, SerializationInfo? info, ISerializationSurrogate? surrogate,
long idOfContainingObj, FieldInfo? field, int[]? arrayIndex)
{
Debug.Assert(objID >= 0, "objID>=0");
_object = obj; //May be null;
_id = objID;
_flags = 0;
_missingElementsRemaining = 0;
_missingDecendents = 0;
_dependentObjects = null;
_next = null;
_serInfo = info;
_surrogate = surrogate;
_markForFixupWhenAvailable = false;
if (idOfContainingObj != 0 && arrayIndex != null)
{
_valueFixup = new ValueTypeFixupInfo(idOfContainingObj, field, arrayIndex);
}
if (_valueFixup != null)
{
_flags |= REQUIRES_VALUETYPE_FIXUP;
}
}
private void IncrementDescendentFixups(int amount) => _missingDecendents += amount;
internal void DecrementFixupsRemaining(ObjectManager manager)
{
_missingElementsRemaining--;
if (RequiresValueTypeFixup)
{
UpdateDescendentDependencyChain(-1, manager);
}
}
/// <summary>
/// Removes a dependency of the object represented in this holder.
/// This is normally the result of the dependency having been filled when
/// the object is going to be only partially completed. If we plan to fully
/// update the object, we do not take the work to do this.
/// </summary>
/// <param name="id">The id of the object for which to remove the dependency.</param>
internal void RemoveDependency(long id)
{
Debug.Assert(_dependentObjects != null, "[ObjectHolder.RemoveDependency]m_dependentObjects!=null");
Debug.Assert(id >= 0, "[ObjectHolder.RemoveDependency]id>=0");
_dependentObjects.RemoveElement(id);
}
/// <summary>
/// Note a fixup that has to be done before this object can be completed.
/// Fixups are things that need to happen when other objects in the graph
/// are added. Dependencies are things that need to happen when this object
/// is added.
/// </summary>
/// <param name="fixup">The fixup holder containing enough information to complete the fixup.</param>
/// <param name="manager">The associated object manager.</param>
internal void AddFixup(FixupHolder fixup, ObjectManager manager)
{
_missingElements ??= new FixupHolderList();
_missingElements.Add(fixup);
_missingElementsRemaining++;
if (RequiresValueTypeFixup)
{
UpdateDescendentDependencyChain(1, manager);
}
}
/// <summary>
/// Updates the total list of dependencies to account for a fixup being added
/// or completed in a child value class. This will update all value classes
/// containing that child and the object which contains all of them.
/// </summary>
/// <param name="amount">the amount by which to increment (or decrement) the dependency chain.</param>
/// <param name="manager">The ObjectManager used to lookup other objects up the chain.</param>
private void UpdateDescendentDependencyChain(int amount, ObjectManager manager)
{
ObjectHolder holder = this;
//This loop walks one more object up the chain than there are valuetypes. This
//is because we need to increment the TotalFixups in the holders as well.
do
{
holder = manager.FindOrCreateObjectHolder(holder.ContainerID);
Debug.Assert(holder != null, "[ObjectHolder.UpdateTotalDependencyChain]holder!=null");
holder.IncrementDescendentFixups(amount);
} while (holder.RequiresValueTypeFixup);
}
/// <summary>
/// Note an object which is dependent on the one which will be contained in
/// this ObjectHolder. Dependencies should only be added if the object hasn't
/// yet been added. NB: An incomplete object counts as having no object.
/// </summary>
/// <param name="dependentObject">the id of the object which is dependent on this object being provided.</param>
internal void AddDependency(long dependentObject)
{
_dependentObjects ??= new LongList();
_dependentObjects.Add(dependentObject);
}
/// <summary>
/// Update the data in the object holder. This should be called when the object
/// is finally registered. Presumably the ObjectHolder was created to track
/// some dependencies or preregistered fixups and we now need to actually record the
/// object and other associated data. We take this opportunity to set the flags
/// so that we can do some faster processing in the future.
/// </summary>
/// <param name="info">The optional serialization info.</param>
/// <param name="obj">The object being held by this object holder. (This should no longer be null).</param>
/// <param name="field">The SerializationInfo associated with this object, only required if we're doing delayed fixups.</param>
/// <param name="surrogate">The surrogate handling this object. May be null.</param>
/// <param name="idOfContainer">The id of the object containing this one if this is a valuetype.</param>
/// <param name="arrayIndex">The array index.</param>
/// <param name="manager">the ObjectManager being used to track these ObjectHolders.</param>
internal void UpdateData(
object obj, SerializationInfo? info, ISerializationSurrogate? surrogate, long idOfContainer,
FieldInfo? field, int[]? arrayIndex, ObjectManager manager)
{
Debug.Assert(obj != null, "obj!=null");
Debug.Assert(_id > 0, "m_id>0");
//Record the fields that we can.
SetObjectValue(obj, manager);
_serInfo = info;
_surrogate = surrogate;
if (idOfContainer != 0 && ((field != null && field.FieldType.IsValueType) || arrayIndex != null))
{
if (idOfContainer == _id)
{
throw new SerializationException(SR.Serialization_ParentChildIdentical);
}
_valueFixup = new ValueTypeFixupInfo(idOfContainer, field, arrayIndex);
}
SetFlags();
if (RequiresValueTypeFixup)
{
UpdateDescendentDependencyChain(_missingElementsRemaining, manager);
}
}
internal void MarkForCompletionWhenAvailable() => _markForFixupWhenAvailable = true;
/// <summary>
/// An internal-only routine to set the flags based upon the data contained in
/// the ObjectHolder
/// </summary>
internal void SetFlags()
{
if (_object is IObjectReference)
{
_flags |= IncompleteObjectReference;
}
_flags &= ~(HAS_ISERIALIZABLE | HAS_SURROGATE);
if (_surrogate != null)
{
_flags |= HAS_SURROGATE;
}
else if (_object is ISerializable)
{
_flags |= HAS_ISERIALIZABLE;
}
if (_valueFixup != null)
{
_flags |= REQUIRES_VALUETYPE_FIXUP;
}
}
internal bool IsIncompleteObjectReference
{
get { return (_flags & IncompleteObjectReference) != 0; }
set
{
if (value)
{
_flags |= IncompleteObjectReference;
}
else
{
_flags &= ~IncompleteObjectReference;
}
}
}
internal bool RequiresDelayedFixup => (_flags & REQUIRES_DELAYED_FIXUP) != 0;
internal bool RequiresValueTypeFixup => (_flags & REQUIRES_VALUETYPE_FIXUP) != 0;
// ValueTypes which require fixups are initially handed to the ObjectManager
// as boxed objects. When they're still boxed objects, we should just do fixups
// on them like we would any other object. As soon as they're pushed into their
// containing object we set ValueTypeFixupPerformed to true and have to go through
// a more complicated path to set fixed up valuetype objects.
// We check whether or not there are any dependent objects.
internal bool ValueTypeFixupPerformed
{
get
{
return (((_flags & VALUETYPE_FIXUP_PERFORMED) != 0) ||
(_object != null && ((_dependentObjects == null) || _dependentObjects.Count == 0)));
}
set
{
if (value)
{
_flags |= VALUETYPE_FIXUP_PERFORMED;
}
}
}
internal bool HasISerializable => (_flags & HAS_ISERIALIZABLE) != 0;
internal bool HasSurrogate => (_flags & HAS_SURROGATE) != 0;
internal bool CanSurrogatedObjectValueChange =>
(_surrogate == null || _surrogate.GetType() != typeof(SurrogateForCyclicalReference));
internal bool CanObjectValueChange =>
IsIncompleteObjectReference ? true :
HasSurrogate ? CanSurrogatedObjectValueChange :
false;
internal int DirectlyDependentObjects => _missingElementsRemaining;
internal int TotalDependentObjects => _missingElementsRemaining + _missingDecendents;
internal bool Reachable { get { return _reachable; } set { _reachable = value; } }
internal bool TypeLoadExceptionReachable => _typeLoad != null;
internal TypeLoadExceptionHolder? TypeLoadException { get { return _typeLoad; } set { _typeLoad = value; } }
internal object? ObjectValue => _object;
internal void SetObjectValue(object? obj, ObjectManager manager)
{
_object = obj;
if (obj == manager.TopObject)
{
_reachable = true;
}
if (obj is TypeLoadExceptionHolder)
{
_typeLoad = (TypeLoadExceptionHolder)obj;
}
if (_markForFixupWhenAvailable)
{
manager.CompleteObject(this, true);
}
}
internal SerializationInfo? SerializationInfo { get { return _serInfo; } set { _serInfo = value; } }
internal ISerializationSurrogate? Surrogate => _surrogate;
internal LongList? DependentObjects { get { return _dependentObjects; } set { _dependentObjects = value; } }
internal bool RequiresSerInfoFixup
{
get
{
if (((_flags & HAS_SURROGATE) == 0) && ((_flags & HAS_ISERIALIZABLE) == 0))
{
return false;
}
return (_flags & SER_INFO_FIXED) == 0;
}
set
{
if (!value)
{
_flags |= SER_INFO_FIXED;
}
else
{
_flags &= ~SER_INFO_FIXED;
}
}
}
internal ValueTypeFixupInfo? ValueFixup => _valueFixup;
internal bool CompletelyFixed => !RequiresSerInfoFixup && !IsIncompleteObjectReference;
internal long ContainerID => _valueFixup != null ? _valueFixup.ContainerID : 0;
}
internal sealed class FixupHolder
{
internal const int ArrayFixup = 0x1;
internal const int MemberFixup = 0x2;
internal const int DelayedFixup = 0x4;
internal long _id;
internal object _fixupInfo; //This is either an array index, a String, or a MemberInfo
internal readonly int _fixupType;
internal FixupHolder(long id, object fixupInfo, int fixupType)
{
Debug.Assert(id > 0, "id>0");
Debug.Assert(fixupInfo != null, "fixupInfo!=null");
Debug.Assert(fixupType == ArrayFixup || fixupType == MemberFixup || fixupType == DelayedFixup, "fixupType==ArrayFixup || fixupType == MemberFixup || fixupType==DelayedFixup");
_id = id;
_fixupInfo = fixupInfo;
_fixupType = fixupType;
}
}
internal sealed class FixupHolderList
{
internal const int InitialSize = 2;
internal FixupHolder?[] _values;
internal int _count;
internal FixupHolderList() : this(InitialSize)
{
}
internal FixupHolderList(int startingSize)
{
_count = 0;
_values = new FixupHolder[startingSize];
}
internal void Add(FixupHolder fixup)
{
if (_count == _values.Length)
{
EnlargeArray();
}
_values[_count++] = fixup;
}
private void EnlargeArray()
{
int newLength = _values.Length * 2;
if (newLength < 0)
{
newLength = int.MaxValue;
}
FixupHolder[] temp = new FixupHolder[newLength];
Array.Copy(_values, temp, _count);
_values = temp;
}
}
internal sealed class LongList
{
private const int InitialSize = 2;
private long[] _values;
private int _count; //The total number of valid items still in the list;
private int _totalItems; //The total number of allocated entries. This includes space for items which have been marked as deleted.
private int _currentItem; //Used when doing an enumeration over the list.
// An m_currentItem of -1 indicates that the enumeration hasn't been started.
// An m_values[xx] of -1 indicates that the item has been deleted.
internal LongList() : this(InitialSize)
{
}
internal LongList(int startingSize)
{
_count = 0;
_totalItems = 0;
_values = new long[startingSize];
}
internal void Add(long value)
{
if (_totalItems == _values.Length)
{
EnlargeArray();
}
_values[_totalItems++] = value;
_count++;
}
internal int Count => _count;
internal void StartEnumeration() => _currentItem = -1;
internal bool MoveNext()
{
while (++_currentItem < _totalItems && _values[_currentItem] == -1) ;
return _currentItem != _totalItems;
}
internal long Current
{
get
{
Debug.Assert(_currentItem != -1, "[LongList.Current]m_currentItem!=-1");
Debug.Assert(_values[_currentItem] != -1, "[LongList.Current]m_values[m_currentItem]!=-1");
return _values[_currentItem];
}
}
internal bool RemoveElement(long value)
{
int i;
for (i = 0; i < _totalItems; i++)
{
if (_values[i] == value)
{
break;
}
}
if (i == _totalItems)
{
return false;
}
_values[i] = -1;
return true;
}
private void EnlargeArray()
{
int newLength = _values.Length * 2;
if (newLength < 0)
{
newLength = int.MaxValue;
}
long[] temp = new long[newLength];
Array.Copy(_values, temp, _count);
_values = temp;
}
}
internal sealed class ObjectHolderList
{
internal const int DefaultInitialSize = 8;
internal ObjectHolder[] _values;
internal int _count;
internal ObjectHolderList() : this(DefaultInitialSize)
{
}
internal ObjectHolderList(int startingSize)
{
Debug.Assert(startingSize > 0 && startingSize < 0x1000, "startingSize>0 && startingSize<0x1000");
_count = 0;
_values = new ObjectHolder[startingSize];
}
internal void Add(ObjectHolder value)
{
if (_count == _values.Length)
{
EnlargeArray();
}
_values[_count++] = value;
}
internal ObjectHolderListEnumerator GetFixupEnumerator() => new ObjectHolderListEnumerator(this, true);
private void EnlargeArray()
{
int newLength = _values.Length * 2;
if (newLength < 0)
{
newLength = int.MaxValue;
}
ObjectHolder[] temp = new ObjectHolder[newLength];
Array.Copy(_values, temp, _count);
_values = temp;
}
internal int Version => _count;
internal int Count => _count;
}
internal sealed class ObjectHolderListEnumerator
{
private readonly bool _isFixupEnumerator;
private readonly ObjectHolderList _list;
private readonly int _startingVersion;
private int _currPos;
internal ObjectHolderListEnumerator(ObjectHolderList list, bool isFixupEnumerator)
{
Debug.Assert(list != null, "[ObjectHolderListEnumerator.ctor]list!=null");
_list = list;
_startingVersion = _list.Version;
_currPos = -1;
_isFixupEnumerator = isFixupEnumerator;
}
internal bool MoveNext()
{
Debug.Assert(_startingVersion == _list.Version, "[ObjectHolderListEnumerator.MoveNext]m_startingVersion==m_list.Version");
if (_isFixupEnumerator)
{
while (++_currPos < _list.Count && _list._values[_currPos].CompletelyFixed);
return _currPos != _list.Count;
}
else
{
_currPos++;
return _currPos != _list.Count;
}
}
internal ObjectHolder Current
{
get
{
Debug.Assert(_currPos != -1, "[ObjectHolderListEnumerator.Current]m_currPos!=-1");
Debug.Assert(_currPos < _list.Count, "[ObjectHolderListEnumerator.Current]m_currPos<m_list.Count");
Debug.Assert(_startingVersion == _list.Version, "[ObjectHolderListEnumerator.Current]m_startingVersion==m_list.Version");
return _list._values[_currPos];
}
}
}
public sealed class TypeLoadExceptionHolder
{
internal TypeLoadExceptionHolder(string? typeName)
{
TypeName = typeName;
}
internal string? TypeName { get; }
}
internal static class SerializationInfoExtensions
{
private static readonly Action<SerializationInfo, string, object, Type> s_updateValue =
typeof(SerializationInfo)
.GetMethod("UpdateValue", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)!
.CreateDelegate<Action<SerializationInfo, string, object, Type>>();
public static void UpdateValue(this SerializationInfo si, string name, object value, Type type) =>
s_updateValue(si, name, value, type);
}
}
|