1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Diagnostics;
6 using System.Reflection;
7 using System.Globalization;
8 
9 namespace System.Runtime.Serialization
10 {
11     public class ObjectManager
12     {
13         private const int DefaultInitialSize = 16;
14         private const int MaxArraySize = 0x100000; //MUST BE A POWER OF 2!
15         private const int ArrayMask = MaxArraySize - 1;
16         private const int MaxReferenceDepth = 100;
17 
18         private DeserializationEventHandler _onDeserializationHandler;
19         private SerializationEventHandler _onDeserializedHandler;
20 
21         internal ObjectHolder[] _objects;
22         internal object _topObject = null;
23         internal ObjectHolderList _specialFixupObjects; //This is IObjectReference, ISerializable, or has a Surrogate.
24         internal long _fixupCount;
25         internal readonly ISurrogateSelector _selector;
26         internal readonly StreamingContext _context;
27         private readonly bool _isCrossAppDomain;
28 
ObjectManager(ISurrogateSelector selector, StreamingContext context)29         public ObjectManager(ISurrogateSelector selector, StreamingContext context) : this(selector, context, true, false)
30         {
31         }
32 
ObjectManager(ISurrogateSelector selector, StreamingContext context, bool checkSecurity, bool isCrossAppDomain)33         internal ObjectManager(ISurrogateSelector selector, StreamingContext context, bool checkSecurity, bool isCrossAppDomain)
34         {
35             _objects = new ObjectHolder[DefaultInitialSize];
36             _selector = selector;
37             _context = context;
38             _isCrossAppDomain = isCrossAppDomain;
39         }
40 
41         private bool CanCallGetType(object obj) => true;
42 
43         internal object TopObject
44         {
45             set { _topObject = value; }
46             get { return _topObject; }
47         }
48 
49         internal ObjectHolderList SpecialFixupObjects =>
50             _specialFixupObjects ?? (_specialFixupObjects = new ObjectHolderList());
51 
FindObjectHolder(long objectID)52         internal ObjectHolder FindObjectHolder(long objectID)
53         {
54             // The  index of the bin in which we live is rightmost n bits of the objectID.
55             int index = (int)(objectID & ArrayMask);
56             if (index >= _objects.Length)
57             {
58                 return null;
59             }
60 
61             // Find the bin in which we live.
62             ObjectHolder temp = _objects[index];
63 
64             // Walk the chain in that bin.  Return the ObjectHolder if we find it, otherwise
65             // return null.
66             while (temp != null)
67             {
68                 if (temp._id == objectID)
69                 {
70                     return temp;
71                 }
72                 temp = temp._next;
73             }
74 
75             return temp;
76         }
77 
FindOrCreateObjectHolder(long objectID)78         internal ObjectHolder FindOrCreateObjectHolder(long objectID)
79         {
80             ObjectHolder holder;
81             holder = FindObjectHolder(objectID);
82             if (holder == null)
83             {
84                 holder = new ObjectHolder(objectID);
85                 AddObjectHolder(holder);
86             }
87             return holder;
88         }
89 
AddObjectHolder(ObjectHolder holder)90         private void AddObjectHolder(ObjectHolder holder)
91         {
92             Debug.Assert(holder != null, "holder!=null");
93             Debug.Assert(holder._id >= 0, "holder.m_id>=0");
94 
95             //If the id that we need to place is greater than our current length, and less
96             //than the maximum allowable size of the array.  We need to double the size
97             //of the array.  If the array has already reached it's maximum allowable size,
98             //we chain elements off of the buckets.
99             if (holder._id >= _objects.Length && _objects.Length != MaxArraySize)
100             {
101                 int newSize = MaxArraySize;
102 
103                 if (holder._id < (MaxArraySize / 2))
104                 {
105                     newSize = (_objects.Length * 2);
106 
107                     //Keep doubling until we're larger than our target size.
108                     //We could also do this with log operations, but that would
109                     //be slower than the brute force approach.
110                     while (newSize <= holder._id && newSize < MaxArraySize)
111                     {
112                         newSize *= 2;
113                     }
114 
115                     if (newSize > MaxArraySize)
116                     {
117                         newSize = MaxArraySize;
118                     }
119                 }
120 
121                 ObjectHolder[] temp = new ObjectHolder[newSize];
122                 Array.Copy(_objects, 0, temp, 0, _objects.Length);
123                 _objects = temp;
124             }
125 
126             //Find the bin in which we live and make this new element the first element in the bin.
127             int index = (int)(holder._id & ArrayMask);
128 
129             ObjectHolder tempHolder = _objects[index];
130             holder._next = tempHolder;
131             _objects[index] = holder;
132         }
133 
GetCompletionInfo(FixupHolder fixup, out ObjectHolder holder, out object member, bool bThrowIfMissing)134         private bool GetCompletionInfo(FixupHolder fixup, out ObjectHolder holder, out object member, bool bThrowIfMissing)
135         {
136             //Set the member id (String or MemberInfo) for the member being fixed up.
137             member = fixup._fixupInfo;
138 
139             //Find the object required for the fixup.  Throw if we can't find it.
140             holder = FindObjectHolder(fixup._id);
141 
142             // CompletelyFixed is our poorly named property which indicates if something requires a SerializationInfo fixup
143             // or is an incomplete object reference.  We have this particular branch to handle valuetypes which implement
144             // ISerializable.  In that case, we can't do any fixups on them later, so we need to delay the fixups further.
145             if (!holder.CompletelyFixed)
146             {
147                 if (holder.ObjectValue != null && holder.ObjectValue is ValueType)
148                 {
149                     SpecialFixupObjects.Add(holder);
150                     return false;
151                 }
152             }
153 
154             if (holder == null || holder.CanObjectValueChange || holder.ObjectValue == null)
155             {
156                 if (bThrowIfMissing)
157                 {
158                     if (holder == null)
159                     {
160                         throw new SerializationException(SR.Format(SR.Serialization_NeverSeen, fixup._id));
161                     }
162                     if (holder.IsIncompleteObjectReference)
163                     {
164                         throw new SerializationException(SR.Format(SR.Serialization_IORIncomplete, fixup._id));
165                     }
166                     throw new SerializationException(SR.Format(SR.Serialization_ObjectNotSupplied, fixup._id));
167                 }
168                 return false;
169             }
170             return true;
171         }
172 
FixupSpecialObject(ObjectHolder holder)173         private void FixupSpecialObject(ObjectHolder holder)
174         {
175             ISurrogateSelector uselessSelector = null;
176 
177             Debug.Assert(holder.RequiresSerInfoFixup, "[ObjectManager.FixupSpecialObject]holder.HasSurrogate||holder.HasISerializable");
178             if (holder.HasSurrogate)
179             {
180                 ISerializationSurrogate surrogate = holder.Surrogate;
181                 Debug.Assert(surrogate != null, "surrogate!=null");
182                 object returnValue = surrogate.SetObjectData(holder.ObjectValue, holder.SerializationInfo, _context, uselessSelector);
183                 if (returnValue != null)
184                 {
185                     if (!holder.CanSurrogatedObjectValueChange && returnValue != holder.ObjectValue)
186                     {
187                         throw new SerializationException(string.Format(CultureInfo.CurrentCulture, SR.Serialization_NotCyclicallyReferenceableSurrogate, surrogate.GetType().FullName));
188                     }
189                     holder.SetObjectValue(returnValue, this);
190                 }
191                 holder._surrogate = null;
192                 holder.SetFlags();
193             }
194             else
195             {
196                 //Set the object data
197                 Debug.Assert(holder.ObjectValue is ISerializable, "holder.m_object is ISerializable");
198                 CompleteISerializableObject(holder.ObjectValue, holder.SerializationInfo, _context);
199             }
200             //Clear anything that we know that we're not going to need.
201             holder.SerializationInfo = null;
202             holder.RequiresSerInfoFixup = false;
203 
204             // For value types, fixups would have been done. So the newly fixed object must be copied
205             // to its container.
206             if (holder.RequiresValueTypeFixup && holder.ValueTypeFixupPerformed)
207             {
208                 DoValueTypeFixup(null, holder, holder.ObjectValue);
209             }
210             DoNewlyRegisteredObjectFixups(holder);
211         }
212 
213         /// <summary>
214         /// Unfortunately, an ObjectReference could actually be a reference to another
215         /// object reference and we don't know how far we have to tunnel until we can find the real object.  While
216         /// we're still getting instances of IObjectReference back and we're still getting new objects, keep calling
217         /// GetRealObject.  Once we've got the new object, take care of all of the fixups
218         /// that we can do now that we've got it.
219         /// </summary>
220         /// <param name="holder"></param>
ResolveObjectReference(ObjectHolder holder)221         private bool ResolveObjectReference(ObjectHolder holder)
222         {
223             object tempObject;
224             Debug.Assert(holder.IsIncompleteObjectReference, "holder.IsIncompleteObjectReference");
225 
226             //In the pathological case, an Object implementing IObjectReference could return a reference
227             //to a different object which implements IObjectReference.  This makes us vulnerable to a
228             //denial of service attack and stack overflow.  If the depthCount becomes greater than
229             //MaxReferenceDepth, we'll throw a SerializationException.
230             int depthCount = 0;
231 
232             //We wrap this in a try/catch block to handle the case where we're trying to resolve a chained
233             //list of object reference (e.g. an IObjectReference can't resolve itself without some information
234             //that's currently missing from the graph).  We'll catch the NullReferenceException and come back
235             //and try again later.  The downside of this scheme is that if the object actually needed to throw
236             //a NullReferenceException, it's being caught and turned into a SerializationException with a
237             //fairly cryptic message.
238             try
239             {
240                 do
241                 {
242                     tempObject = holder.ObjectValue;
243                     holder.SetObjectValue(((IObjectReference)(holder.ObjectValue)).GetRealObject(_context), this);
244                     //The object didn't yet have enough information to resolve the reference, so we'll
245                     //return false and the graph walker should call us back again after more objects have
246                     //been resolved.
247                     if (holder.ObjectValue == null)
248                     {
249                         holder.SetObjectValue(tempObject, this);
250                         return false;
251                     }
252                     if (depthCount++ == MaxReferenceDepth)
253                     {
254                         throw new SerializationException(SR.Serialization_TooManyReferences);
255                     }
256                 } while ((holder.ObjectValue is IObjectReference) && (tempObject != holder.ObjectValue));
257             }
258             catch (NullReferenceException)
259             {
260                 return false;
261             }
262 
263             holder.IsIncompleteObjectReference = false;
264             DoNewlyRegisteredObjectFixups(holder);
265             return true;
266         }
267 
268         /*===============================DoValueTypeFixup===============================
269         **Arguments:
270         ** memberToFix -- the member in the object contained in holder being fixed up.
271         ** holder -- the ObjectHolder for the object (a value type in this case) being completed.
272         ** value  -- the data to set into the field.
273         ==============================================================================*/
DoValueTypeFixup(FieldInfo memberToFix, ObjectHolder holder, object value)274         private bool DoValueTypeFixup(FieldInfo memberToFix, ObjectHolder holder, object value)
275         {
276             var fieldsTemp = new FieldInfo[4];
277             FieldInfo[] fields = null;
278             int currentFieldIndex = 0;
279             int[] arrayIndex = null;
280             ValueTypeFixupInfo currFixup = null;
281             object fixupObj = holder.ObjectValue;
282             ObjectHolder originalHolder = holder;
283 
284             Debug.Assert(holder != null, "[TypedReferenceBuilder.ctor]holder!=null");
285             Debug.Assert(holder.RequiresValueTypeFixup, "[TypedReferenceBuilder.ctor]holder.RequiresValueTypeFixup");
286 
287             //In order to get a TypedReference, we need to get a list of all of the FieldInfos to
288             //create the path from our outermost containing object down to the actual field which
289             //we'd like to set.  This loop is used to build up that list.
290             while (holder.RequiresValueTypeFixup)
291             {
292                 //Enlarge the array if required (this is actually fairly unlikely as it would require that we
293                 //be nested more than 4 deep.
294                 if ((currentFieldIndex + 1) >= fieldsTemp.Length)
295                 {
296                     var temp = new FieldInfo[fieldsTemp.Length * 2];
297                     Array.Copy(fieldsTemp, 0, temp, 0, fieldsTemp.Length);
298                     fieldsTemp = temp;
299                 }
300 
301                 //Get the fixup information.  If we have data for our parent field, add it to our list
302                 //and continue the walk up to find the next outermost containing object.  We cache the
303                 //object that we have.  In most cases, we could have just grabbed it after this loop finished.
304                 //However, if the outermost containing object is an array, we need the object one further
305                 //down the chain, so we have to do a lot of caching.
306                 currFixup = holder.ValueFixup;
307                 fixupObj = holder.ObjectValue;  //Save the most derived
308                 if (currFixup.ParentField != null)
309                 {
310                     FieldInfo parentField = currFixup.ParentField;
311 
312                     ObjectHolder tempHolder = FindObjectHolder(currFixup.ContainerID);
313                     if (tempHolder.ObjectValue == null)
314                     {
315                         break;
316                     }
317                     if (Nullable.GetUnderlyingType(parentField.FieldType) != null)
318                     {
319                         fieldsTemp[currentFieldIndex] = parentField.FieldType.GetField("value", BindingFlags.NonPublic | BindingFlags.Instance);
320                         currentFieldIndex++;
321                     }
322 
323                     fieldsTemp[currentFieldIndex] = parentField;
324                     holder = tempHolder;
325                     currentFieldIndex++;
326                 }
327                 else
328                 {
329                     //If we find an index into an array, save that information.
330                     Debug.Assert(currFixup.ParentIndex != null, "[ObjectManager.DoValueTypeFixup]currFixup.ParentIndex!=null");
331                     holder = FindObjectHolder(currFixup.ContainerID); //find the array to fix.
332                     arrayIndex = currFixup.ParentIndex;
333                     break;
334                 }
335             }
336 
337             //If the outermost container isn't an array, we need to grab it.  Otherwise, we just need to hang onto
338             //the boxed object that we already grabbed.  We'll assign the boxed object back into the array as the
339             //last step.
340             if (!(holder.ObjectValue is Array) && holder.ObjectValue != null)
341             {
342                 fixupObj = holder.ObjectValue;
343                 Debug.Assert(fixupObj != null, "[ObjectManager.DoValueTypeFixup]FixupObj!=null");
344             }
345 
346             if (currentFieldIndex != 0)
347             {
348                 //MakeTypedReference requires an array of exactly the correct size that goes from the outermost object
349                 //in to the innermost field.  We currently have an array of arbitrary size that goes from the innermost
350                 //object outwards.  We create an array of the right size and do the copy.
351                 fields = new FieldInfo[currentFieldIndex];
352                 for (int i = 0; i < currentFieldIndex; i++)
353                 {
354                     FieldInfo fieldInfo = fieldsTemp[(currentFieldIndex - 1 - i)];
355                     SerializationFieldInfo serInfo = fieldInfo as SerializationFieldInfo;
356                     fields[i] = serInfo == null ? fieldInfo : serInfo.FieldInfo;
357                 }
358 
359                 Debug.Assert(fixupObj != null, "[ObjectManager.DoValueTypeFixup]fixupObj!=null");
360                 //Make the TypedReference and use it to set the value.
361                 TypedReference typedRef = TypedReference.MakeTypedReference(fixupObj, fields);
362                 if (memberToFix != null)
363                 {
364                     memberToFix.SetValueDirect(typedRef, value);
365                 }
366                 else
367                 {
368                     TypedReference.SetTypedReference(typedRef, value);
369                 }
370             }
371             else if (memberToFix != null)
372             {
373                 FormatterServices.SerializationSetValue(memberToFix, fixupObj, value);
374             }
375 
376             //If we have an array index, it means that our outermost container was an array.  We don't have
377             //any way to build a TypedReference into an array, so we'll use the array functions to set the value.
378             if (arrayIndex != null && holder.ObjectValue != null)
379             {
380                 ((Array)(holder.ObjectValue)).SetValue(fixupObj, arrayIndex);
381             }
382 
383             return true;
384         }
385 
CompleteObject(ObjectHolder holder, bool bObjectFullyComplete)386         internal void CompleteObject(ObjectHolder holder, bool bObjectFullyComplete)
387         {
388             FixupHolderList fixups = holder._missingElements;
389             FixupHolder currentFixup;
390             SerializationInfo si;
391             object fixupInfo = null;
392             ObjectHolder tempObjectHolder = null;
393             int fixupsPerformed = 0;
394 
395             Debug.Assert(holder != null, "[ObjectManager.CompleteObject]holder.m_object!=null");
396             if (holder.ObjectValue == null)
397             {
398                 throw new SerializationException(SR.Format(SR.Serialization_MissingObject, holder._id));
399             }
400 
401             if (fixups == null)
402             {
403                 return;
404             }
405             //If either one of these conditions is true, we need to update the data in the
406             //SerializationInfo before calling SetObjectData.
407             if (holder.HasSurrogate || holder.HasISerializable)
408             {
409                 si = holder._serInfo;
410                 if (si == null)
411                 {
412                     throw new SerializationException(SR.Serialization_InvalidFixupDiscovered);
413                 }
414 
415                 //Walk each of the fixups and complete the name-value pair in the SerializationInfo.
416                 if (fixups != null)
417                 {
418                     for (int i = 0; i < fixups._count; i++)
419                     {
420                         if (fixups._values[i] == null)
421                         {
422                             continue;
423                         }
424                         Debug.Assert(fixups._values[i]._fixupType == FixupHolder.DelayedFixup, "fixups.m_values[i].m_fixupType==FixupHolder.DelayedFixup");
425                         if (GetCompletionInfo(fixups._values[i], out tempObjectHolder, out fixupInfo, bObjectFullyComplete))
426                         {
427                             //Walk the SerializationInfo and find the member needing completion.  All we have to do
428                             //at this point is set the member into the Object
429                             object holderValue = tempObjectHolder.ObjectValue;
430                             if (CanCallGetType(holderValue))
431                             {
432                                 si.UpdateValue((string)fixupInfo, holderValue, holderValue.GetType());
433                             }
434                             else
435                             {
436                                 si.UpdateValue((string)fixupInfo, holderValue, typeof(MarshalByRefObject));
437                             }
438                             //Decrement our total number of fixups left to do.
439                             fixupsPerformed++;
440                             fixups._values[i] = null;
441                             if (!bObjectFullyComplete)
442                             {
443                                 holder.DecrementFixupsRemaining(this);
444                                 tempObjectHolder.RemoveDependency(holder._id);
445                             }
446                         }
447                     }
448                 }
449             }
450             else
451             {
452                 for (int i = 0; i < fixups._count; i++)
453                 {
454                     currentFixup = fixups._values[i];
455                     if (currentFixup == null)
456                     {
457                         continue;
458                     }
459                     if (GetCompletionInfo(currentFixup, out tempObjectHolder, out fixupInfo, bObjectFullyComplete))
460                     {
461                         // Check to make sure we are not both reachable from the topObject
462                         // and there was a typeloadexception
463                         if (tempObjectHolder.TypeLoadExceptionReachable)
464                         {
465                             holder.TypeLoadException = tempObjectHolder.TypeLoadException;
466                             // If the holder is both reachable and typeloadexceptionreachable
467                             // throw an exception with the type name
468                             if (holder.Reachable)
469                             {
470                                 throw new SerializationException(SR.Format(SR.Serialization_TypeLoadFailure, holder.TypeLoadException.TypeName));
471                             }
472                         }
473 
474                         // If the current holder is reachable, mark the dependant reachable as well
475                         if (holder.Reachable)
476                         {
477                             tempObjectHolder.Reachable = true;
478                         }
479 
480                         //There are two types of fixups that we could be doing: array or member.
481                         //Delayed Fixups should be handled by the above branch.
482                         switch (currentFixup._fixupType)
483                         {
484                             case FixupHolder.ArrayFixup:
485                                 Debug.Assert(holder.ObjectValue is Array, "holder.ObjectValue is Array");
486                                 if (holder.RequiresValueTypeFixup)
487                                 {
488                                     throw new SerializationException(SR.Serialization_ValueTypeFixup);
489                                 }
490                                 else
491                                 {
492                                     ((Array)(holder.ObjectValue)).SetValue(tempObjectHolder.ObjectValue, ((int[])fixupInfo));
493                                 }
494                                 break;
495                             case FixupHolder.MemberFixup:
496                                 Debug.Assert(fixupInfo is MemberInfo, "fixupInfo is MemberInfo");
497                                 //Fixup the member directly.
498                                 MemberInfo tempMember = (MemberInfo)fixupInfo;
499                                 if (tempMember is FieldInfo)
500                                 {
501                                     // If we have a valuetype that's been boxed to an object and requires a fixup,
502                                     // there are two possible states:
503                                     // (a)The valuetype has never been fixed up into it's container.  In this case, we should
504                                     // just fix up the boxed valuetype.  The task of pushing that valuetype into it's container
505                                     // will be handled later.  This case is handled by the else clause of the following statement.
506                                     // (b)The valuetype has already been inserted into it's container.  In that case, we need
507                                     // to go through the more complicated path laid out in DoValueTypeFixup. We can tell that the
508                                     // valuetype has already been inserted into it's container because we set ValueTypeFixupPerformed
509                                     // to true when we do this.
510                                     if (holder.RequiresValueTypeFixup && holder.ValueTypeFixupPerformed)
511                                     {
512                                         if (!DoValueTypeFixup((FieldInfo)tempMember, holder, tempObjectHolder.ObjectValue))
513                                         {
514                                             throw new SerializationException(SR.Serialization_PartialValueTypeFixup);
515                                         }
516                                     }
517                                     else
518                                     {
519                                         FormatterServices.SerializationSetValue(tempMember, holder.ObjectValue, tempObjectHolder.ObjectValue);
520                                     }
521                                     if (tempObjectHolder.RequiresValueTypeFixup)
522                                     {
523                                         tempObjectHolder.ValueTypeFixupPerformed = true;
524                                     }
525                                 }
526                                 else
527                                 {
528                                     throw new SerializationException(SR.Serialization_UnableToFixup);
529                                 }
530                                 break;
531                             default:
532                                 throw new SerializationException(SR.Serialization_UnableToFixup);
533                         }
534                         //Decrement our total number of fixups left to do.
535                         fixupsPerformed++;
536                         fixups._values[i] = null;
537                         if (!bObjectFullyComplete)
538                         {
539                             holder.DecrementFixupsRemaining(this);
540                             tempObjectHolder.RemoveDependency(holder._id);
541                         }
542                     }
543                 }
544             }
545 
546             _fixupCount -= fixupsPerformed;
547 
548             if (fixups._count == fixupsPerformed)
549             {
550                 holder._missingElements = null;
551             }
552         }
553 
554         /// <summary>
555         /// This is called immediately after we register a new object.  Walk that objects
556         /// dependency list (if it has one) and decrement the counters on each object for
557         /// the number of unsatisfiable references.  If the count reaches 0, go ahead
558         /// and process the object.
559         /// </summary>
560         /// <param name="holder">dependencies The list of dependent objects</param>
DoNewlyRegisteredObjectFixups(ObjectHolder holder)561         private void DoNewlyRegisteredObjectFixups(ObjectHolder holder)
562         {
563             if (holder.CanObjectValueChange)
564             {
565                 return;
566             }
567 
568             //If we don't have any dependencies, we're done.
569             LongList dependencies = holder.DependentObjects;
570             if (dependencies == null)
571             {
572                 return;
573             }
574 
575             //Walk all of the dependencies and decrement the counter on each of uncompleted objects.
576             //If one of the counters reaches 0, all of it's fields have been completed and we should
577             //go take care of its fixups.
578             dependencies.StartEnumeration();
579             while (dependencies.MoveNext())
580             {
581                 ObjectHolder temp = FindObjectHolder(dependencies.Current);
582                 Debug.Assert(temp.DirectlyDependentObjects > 0, "temp.m_missingElementsRemaining>0");
583                 temp.DecrementFixupsRemaining(this);
584                 if (((temp.DirectlyDependentObjects)) == 0)
585                 {
586                     // If this is null, we have the case where a fixup was registered for a child, the object
587                     // required by the fixup was provided, and the object to be fixed hasn't yet been seen.
588                     if (temp.ObjectValue != null)
589                     {
590                         CompleteObject(temp, true);
591                     }
592                     else
593                     {
594                         temp.MarkForCompletionWhenAvailable();
595                     }
596                 }
597             }
598         }
599 
GetObject(long objectID)600         public virtual object GetObject(long objectID)
601         {
602             if (objectID <= 0)
603             {
604                 throw new ArgumentOutOfRangeException(nameof(objectID), SR.ArgumentOutOfRange_ObjectID);
605             }
606 
607             //Find the bin in which we're interested.  IObjectReference's shouldn't be returned -- the graph
608             //needs to link to the objects to which they refer, not to the references themselves.
609             ObjectHolder holder = FindObjectHolder(objectID);
610             if (holder == null || holder.CanObjectValueChange)
611             {
612                 return null;
613             }
614 
615             return holder.ObjectValue;
616         }
617 
RegisterObject(object obj, long objectID)618         public virtual void RegisterObject(object obj, long objectID)
619         {
620             RegisterObject(obj, objectID, null, 0, null);
621         }
622 
RegisterObject(object obj, long objectID, SerializationInfo info)623         public void RegisterObject(object obj, long objectID, SerializationInfo info)
624         {
625             RegisterObject(obj, objectID, info, 0, null);
626         }
627 
RegisterObject(object obj, long objectID, SerializationInfo info, long idOfContainingObj, MemberInfo member)628         public void RegisterObject(object obj, long objectID, SerializationInfo info, long idOfContainingObj, MemberInfo member)
629         {
630             RegisterObject(obj, objectID, info, idOfContainingObj, member, null);
631         }
632 
RegisterString(string obj, long objectID, SerializationInfo info, long idOfContainingObj, MemberInfo member)633         internal void RegisterString(string obj, long objectID, SerializationInfo info, long idOfContainingObj, MemberInfo member)
634         {
635             ObjectHolder temp;
636             Debug.Assert(member == null || member is FieldInfo, "RegisterString - member is FieldInfo");
637 
638             temp = new ObjectHolder(obj, objectID, info, null, idOfContainingObj, (FieldInfo)member, null);
639             AddObjectHolder(temp);
640             return;
641         }
642 
RegisterObject(object obj, long objectID, SerializationInfo info, long idOfContainingObj, MemberInfo member, int[] arrayIndex)643         public void RegisterObject(object obj, long objectID, SerializationInfo info, long idOfContainingObj, MemberInfo member, int[] arrayIndex)
644         {
645             if (obj == null)
646             {
647                 throw new ArgumentNullException(nameof(obj));
648             }
649             if (objectID <= 0)
650             {
651                 throw new ArgumentOutOfRangeException(nameof(objectID), SR.ArgumentOutOfRange_ObjectID);
652             }
653             if (member != null && !(member is FieldInfo)) // desktop checks specifically for RuntimeFieldInfo and SerializationFieldInfo, but the former is an implementation detail in corelib
654             {
655                 throw new SerializationException(SR.Serialization_UnknownMemberInfo);
656             }
657 
658             ObjectHolder temp;
659             ISerializationSurrogate surrogate = null;
660             ISurrogateSelector useless;
661 
662             if (_selector != null)
663             {
664                 Type selectorType = CanCallGetType(obj) ?
665                     obj.GetType() :
666                     typeof(MarshalByRefObject);
667 
668                 //If we need a surrogate for this object, lets find it now.
669                 surrogate = _selector.GetSurrogate(selectorType, _context, out useless);
670             }
671 
672             //The object is interested in DeserializationEvents so lets register it.
673             if (obj is IDeserializationCallback)
674             {
675                 DeserializationEventHandler d = new DeserializationEventHandler(((IDeserializationCallback)obj).OnDeserialization);
676                 AddOnDeserialization(d);
677             }
678 
679             //Formatter developers may cache and reuse arrayIndex in their code.
680             //So that we don't get bitten by this, take a copy up front.
681             if (arrayIndex != null)
682             {
683                 arrayIndex = (int[])arrayIndex.Clone();
684             }
685 
686             //This is the first time which we've seen the object, we need to create a new holder.
687             temp = FindObjectHolder(objectID);
688             if (temp == null)
689             {
690                 temp = new ObjectHolder(obj, objectID, info, surrogate, idOfContainingObj, (FieldInfo)member, arrayIndex);
691                 AddObjectHolder(temp);
692                 if (temp.RequiresDelayedFixup)
693                 {
694                     SpecialFixupObjects.Add(temp);
695                 }
696 
697                 // We cannot compute whether this has any fixups required or not
698                 AddOnDeserialized(obj);
699                 return;
700             }
701 
702             //If the object isn't null, we've registered this before.  Not good.
703             if (temp.ObjectValue != null)
704             {
705                 throw new SerializationException(SR.Serialization_RegisterTwice);
706             }
707 
708             //Complete the data in the ObjectHolder
709             temp.UpdateData(obj, info, surrogate, idOfContainingObj, (FieldInfo)member, arrayIndex, this);
710 
711             // The following case will only be true when somebody has registered a fixup on an object before
712             // registering the object itself.  I don't believe that most well-behaved formatters will do this,
713             // but we need to allow it anyway.  We will walk the list of fixups which have been recorded on
714             // the new object and fix those that we can.  Because the user could still register later fixups
715             // on this object, we won't call any implementations of ISerializable now.  If that's required,
716             // it will have to be handled by the code in DoFixups.
717             // README README: We have to do the UpdateData before
718             if (temp.DirectlyDependentObjects > 0)
719             {
720                 CompleteObject(temp, false);
721             }
722 
723             if (temp.RequiresDelayedFixup)
724             {
725                 SpecialFixupObjects.Add(temp);
726             }
727 
728             if (temp.CompletelyFixed)
729             {
730                 //Here's where things get tricky.  If this isn't an instance of IObjectReference, we need to walk it's fixup
731                 //chain and decrement the counters on anything that has reached 0.  Once we've notified all of the dependencies,
732                 //we can simply clear the list of dependent objects.
733                 DoNewlyRegisteredObjectFixups(temp);
734                 temp.DependentObjects = null;
735             }
736 
737             //Register the OnDeserialized methods to be invoked after deserialization is complete
738             if (temp.TotalDependentObjects > 0)
739             {
740                 AddOnDeserialized(obj);
741             }
742             else
743             {
744                 RaiseOnDeserializedEvent(obj);
745             }
746         }
747 
748         /// <summary>
749         /// Completes an object implementing ISerializable.  This will involve calling that
750         /// objects constructor which takes an instance of ISerializable and a StreamingContext.
751         /// </summary>
752         /// <param name="obj">The object to be completed.</param>
753         /// <param name="info">The SerializationInfo containing all info for obj.</param>
754         /// <param name="context">The streaming context in which the serialization is taking place.</param>
CompleteISerializableObject(object obj, SerializationInfo info, StreamingContext context)755         internal void CompleteISerializableObject(object obj, SerializationInfo info, StreamingContext context)
756         {
757             if (obj == null)
758             {
759                 throw new ArgumentNullException(nameof(obj));
760             }
761             if (!(obj is ISerializable))
762             {
763                 throw new ArgumentException(SR.Serialization_NotISer);
764             }
765 
766             ConstructorInfo constInfo = null;
767             Type t = obj.GetType();
768             try
769             {
770                 constInfo = GetDeserializationConstructor(t);
771             }
772             catch (Exception e)
773             {
774                 throw new SerializationException(SR.Format(SR.Serialization_ConstructorNotFound, t), e);
775             }
776 
777             constInfo.Invoke(obj, new object[] { info, context });
778         }
779 
GetDeserializationConstructor(Type t)780         internal static ConstructorInfo GetDeserializationConstructor(Type t)
781         {
782             // TODO #10530: Use Type.GetConstructor that takes BindingFlags when it's available
783             foreach (ConstructorInfo ci in t.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly))
784             {
785                 ParameterInfo[] parameters = ci.GetParameters();
786                 if (parameters.Length == 2 &&
787                     parameters[0].ParameterType == typeof(SerializationInfo) &&
788                     parameters[1].ParameterType == typeof(StreamingContext))
789                 {
790                     return ci;
791                 }
792             }
793 
794             throw new SerializationException(SR.Format(SR.Serialization_ConstructorNotFound, t.FullName));
795         }
796 
DoFixups()797         public virtual void DoFixups()
798         {
799             ObjectHolder temp;
800             int fixupCount = -1;
801 
802             //The first thing that we need to do is fixup all of the objects which implement
803             //IObjectReference.  This is complicated by the fact that we need to deal with IReferenceObjects
804             //objects that have a reference to an object implementing IObjectReference.  We continually
805             //walk over the list of objects until we've completed all of the object references or until
806             //we can't resolve any more (which may happen if we have two objects implementing IObjectReference
807             //which have a circular dependency on each other).  We don't explicitly catch the later case here,
808             //it will be caught when we try to do the rest of the fixups and discover that we have some that
809             //can't be completed.
810             while (fixupCount != 0)
811             {
812                 fixupCount = 0;
813                 //Walk all of the IObjectReferences and ensure that they've been properly completed.
814                 ObjectHolderListEnumerator fixupObjectsEnum = SpecialFixupObjects.GetFixupEnumerator();
815                 while (fixupObjectsEnum.MoveNext())
816                 {
817                     temp = fixupObjectsEnum.Current;
818                     if (temp.ObjectValue == null)
819                     {
820                         throw new SerializationException(SR.Format(SR.Serialization_ObjectNotSupplied, temp._id));
821                     }
822                     if (temp.TotalDependentObjects == 0)
823                     {
824                         if (temp.RequiresSerInfoFixup)
825                         {
826                             FixupSpecialObject(temp);
827                             fixupCount++;
828                         }
829                         else if (!temp.IsIncompleteObjectReference)
830                         {
831                             CompleteObject(temp, true);
832                         }
833 
834                         if (temp.IsIncompleteObjectReference && ResolveObjectReference(temp))
835                         {
836                             fixupCount++;
837                         }
838                     }
839                 }
840             }
841 
842             Debug.Assert(_fixupCount >= 0, "[ObjectManager.DoFixups]m_fixupCount>=0");
843 
844             //If our count is 0, we're done and should just return
845             if (_fixupCount == 0)
846             {
847                 if (TopObject is TypeLoadExceptionHolder)
848                 {
849                     throw new SerializationException(SR.Format(SR.Serialization_TypeLoadFailure, ((TypeLoadExceptionHolder)TopObject).TypeName));
850                 }
851                 return;
852             }
853 
854             //If our count isn't 0, we had at least one case where an object referenced another object twice.
855             //Walk the entire list until the count is 0 or until we find an object which we can't complete.
856             for (int i = 0; i < _objects.Length; i++)
857             {
858                 temp = _objects[i];
859                 while (temp != null)
860                 {
861                     if (temp.TotalDependentObjects > 0 /*|| temp.m_missingElements!=null*/)
862                     {
863                         CompleteObject(temp, true);
864                     }
865                     temp = temp._next;
866                 }
867                 if (_fixupCount == 0)
868                 {
869                     return;
870                 }
871             }
872 
873             // this assert can be trigered by user code that manages fixups manually
874             throw new SerializationException(SR.Serialization_IncorrectNumberOfFixups);
875         }
876 
877         /// <summary>
878         /// Do the actual grunt work of recording a fixup and registering the dependency.
879         /// Create the necessary ObjectHolders and use them to do the addition.
880         /// </summary>
881         /// <param name="fixup">The FixupHolder to be added.</param>
882         /// <param name="objectRequired">The id of the object required to do the fixup.</param>
883         /// <param name="objectToBeFixed">The id of the object requiring the fixup.</param>
RegisterFixup(FixupHolder fixup, long objectToBeFixed, long objectRequired)884         private void RegisterFixup(FixupHolder fixup, long objectToBeFixed, long objectRequired)
885         {
886             //Record the fixup with the object that needs it.
887             ObjectHolder ohToBeFixed = FindOrCreateObjectHolder(objectToBeFixed);
888             ObjectHolder ohRequired;
889 
890             if (ohToBeFixed.RequiresSerInfoFixup && fixup._fixupType == FixupHolder.MemberFixup)
891             {
892                 throw new SerializationException(SR.Serialization_InvalidFixupType);
893             }
894 
895             //Add the fixup to the list.
896             ohToBeFixed.AddFixup(fixup, this);
897 
898             //Find the object on which we're dependent and note the dependency.
899             //These dependencies will be processed when the object is supplied.
900             ohRequired = FindOrCreateObjectHolder(objectRequired);
901 
902             ohRequired.AddDependency(objectToBeFixed);
903 
904             _fixupCount++;
905         }
906 
RecordFixup(long objectToBeFixed, MemberInfo member, long objectRequired)907         public virtual void RecordFixup(long objectToBeFixed, MemberInfo member, long objectRequired)
908         {
909             //Verify our arguments
910             if (objectToBeFixed <= 0 || objectRequired <= 0)
911             {
912                 throw new ArgumentOutOfRangeException(objectToBeFixed <= 0 ? nameof(objectToBeFixed) : nameof(objectRequired), SR.Serialization_IdTooSmall);
913             }
914             if (member == null)
915             {
916                 throw new ArgumentNullException(nameof(member));
917             }
918             if (!(member is FieldInfo)) // desktop checks specifically for RuntimeFieldInfo and SerializationFieldInfo, but the former is an implementation detail in corelib
919             {
920                 throw new SerializationException(SR.Format(SR.Serialization_InvalidType, member.GetType().ToString()));
921             }
922 
923             //Create a new fixup holder
924             FixupHolder fixup = new FixupHolder(objectRequired, member, FixupHolder.MemberFixup);
925             RegisterFixup(fixup, objectToBeFixed, objectRequired);
926         }
927 
RecordDelayedFixup(long objectToBeFixed, string memberName, long objectRequired)928         public virtual void RecordDelayedFixup(long objectToBeFixed, string memberName, long objectRequired)
929         {
930             //Verify our arguments
931             if (objectToBeFixed <= 0 || objectRequired <= 0)
932             {
933                 throw new ArgumentOutOfRangeException(objectToBeFixed <= 0 ? nameof(objectToBeFixed) : nameof(objectRequired), SR.Serialization_IdTooSmall);
934             }
935             if (memberName == null)
936             {
937                 throw new ArgumentNullException(nameof(memberName));
938             }
939 
940             //Create a new fixup holder
941             FixupHolder fixup = new FixupHolder(objectRequired, memberName, FixupHolder.DelayedFixup);
942             RegisterFixup(fixup, objectToBeFixed, objectRequired);
943         }
944 
RecordArrayElementFixup(long arrayToBeFixed, int index, long objectRequired)945         public virtual void RecordArrayElementFixup(long arrayToBeFixed, int index, long objectRequired)
946         {
947             int[] indexArray = new int[1];
948             indexArray[0] = index;
949             RecordArrayElementFixup(arrayToBeFixed, indexArray, objectRequired);
950         }
951 
RecordArrayElementFixup(long arrayToBeFixed, int[] indices, long objectRequired)952         public virtual void RecordArrayElementFixup(long arrayToBeFixed, int[] indices, long objectRequired)
953         {
954             //Verify our arguments
955             if (arrayToBeFixed <= 0 || objectRequired <= 0)
956             {
957                 throw new ArgumentOutOfRangeException(arrayToBeFixed <= 0 ? nameof(arrayToBeFixed) : nameof(objectRequired), SR.Serialization_IdTooSmall);
958             }
959             if (indices == null)
960             {
961                 throw new ArgumentNullException(nameof(indices));
962             }
963 
964             FixupHolder fixup = new FixupHolder(objectRequired, indices, FixupHolder.ArrayFixup);
965             RegisterFixup(fixup, arrayToBeFixed, objectRequired);
966         }
967 
RaiseDeserializationEvent()968         public virtual void RaiseDeserializationEvent()
969         {
970             // Invoke OnDerserialized event if applicable
971             _onDeserializedHandler?.Invoke(_context);
972             _onDeserializationHandler?.Invoke(null);
973         }
974 
AddOnDeserialization(DeserializationEventHandler handler)975         internal virtual void AddOnDeserialization(DeserializationEventHandler handler)
976         {
977             _onDeserializationHandler = (DeserializationEventHandler)Delegate.Combine(_onDeserializationHandler, handler);
978         }
979 
AddOnDeserialized(object obj)980         internal virtual void AddOnDeserialized(object obj)
981         {
982             SerializationEvents cache = SerializationEventsCache.GetSerializationEventsForType(obj.GetType());
983             _onDeserializedHandler = cache.AddOnDeserialized(obj, _onDeserializedHandler);
984         }
985 
RaiseOnDeserializedEvent(object obj)986         internal virtual void RaiseOnDeserializedEvent(object obj)
987         {
988             SerializationEvents cache = SerializationEventsCache.GetSerializationEventsForType(obj.GetType());
989             cache.InvokeOnDeserialized(obj, _context);
990         }
991 
RaiseOnDeserializingEvent(object obj)992         public void RaiseOnDeserializingEvent(object obj)
993         {
994             // Run the OnDeserializing methods
995             SerializationEvents cache = SerializationEventsCache.GetSerializationEventsForType(obj.GetType());
996             cache.InvokeOnDeserializing(obj, _context);
997         }
998     }
999 
1000     internal sealed class ObjectHolder
1001     {
1002         internal const int IncompleteObjectReference = 0x0001;
1003         internal const int HAS_ISERIALIZABLE = 0x0002;
1004         internal const int HAS_SURROGATE = 0x0004;
1005         internal const int REQUIRES_VALUETYPE_FIXUP = 0x0008;
1006         internal const int REQUIRES_DELAYED_FIXUP = HAS_ISERIALIZABLE | HAS_SURROGATE | IncompleteObjectReference;
1007         internal const int SER_INFO_FIXED = 0x4000;
1008         internal const int VALUETYPE_FIXUP_PERFORMED = 0x8000;
1009 
1010         private object _object;
1011         internal readonly long _id;
1012         private int _missingElementsRemaining;
1013         private int _missingDecendents;
1014         internal SerializationInfo _serInfo;
1015         internal ISerializationSurrogate _surrogate;
1016         internal FixupHolderList _missingElements;
1017         internal LongList _dependentObjects;
1018         internal ObjectHolder _next;
1019         internal int _flags;
1020         private bool _markForFixupWhenAvailable;
1021         private ValueTypeFixupInfo _valueFixup;
1022         private TypeLoadExceptionHolder _typeLoad = null;
1023         private bool _reachable = false;
1024 
ObjectHolder(long objID)1025         internal ObjectHolder(long objID) : this(null, objID, null, null, 0, null, null)
1026         {
1027         }
1028 
ObjectHolder( object obj, long objID, SerializationInfo info, ISerializationSurrogate surrogate, long idOfContainingObj, FieldInfo field, int[] arrayIndex)1029         internal ObjectHolder(
1030             object obj, long objID, SerializationInfo info, ISerializationSurrogate surrogate,
1031             long idOfContainingObj, FieldInfo field, int[] arrayIndex)
1032         {
1033             Debug.Assert(objID >= 0, "objID>=0");
1034 
1035             _object = obj; //May be null;
1036             _id = objID;
1037 
1038             _flags = 0;
1039             _missingElementsRemaining = 0;
1040             _missingDecendents = 0;
1041             _dependentObjects = null;
1042             _next = null;
1043 
1044             _serInfo = info;
1045             _surrogate = surrogate;
1046             _markForFixupWhenAvailable = false;
1047 
1048             if (obj is TypeLoadExceptionHolder)
1049             {
1050                 _typeLoad = (TypeLoadExceptionHolder)obj;
1051             }
1052 
1053             if (idOfContainingObj != 0 && ((field != null && field.FieldType.IsValueType) || arrayIndex != null))
1054             {
1055                 if (idOfContainingObj == objID)
1056                 {
1057                     throw new SerializationException(SR.Serialization_ParentChildIdentical);
1058                 }
1059 
1060                 _valueFixup = new ValueTypeFixupInfo(idOfContainingObj, field, arrayIndex);
1061             }
1062 
1063             SetFlags();
1064         }
1065 
ObjectHolder( string obj, long objID, SerializationInfo info, ISerializationSurrogate surrogate, long idOfContainingObj, FieldInfo field, int[] arrayIndex)1066         internal ObjectHolder(
1067             string obj, long objID, SerializationInfo info, ISerializationSurrogate surrogate,
1068             long idOfContainingObj, FieldInfo field, int[] arrayIndex)
1069         {
1070             Debug.Assert(objID >= 0, "objID>=0");
1071 
1072             _object = obj; //May be null;
1073             _id = objID;
1074 
1075             _flags = 0;
1076             _missingElementsRemaining = 0;
1077             _missingDecendents = 0;
1078             _dependentObjects = null;
1079             _next = null;
1080 
1081             _serInfo = info;
1082             _surrogate = surrogate;
1083             _markForFixupWhenAvailable = false;
1084 
1085             if (idOfContainingObj != 0 && arrayIndex != null)
1086             {
1087                 _valueFixup = new ValueTypeFixupInfo(idOfContainingObj, field, arrayIndex);
1088             }
1089 
1090             if (_valueFixup != null)
1091             {
1092                 _flags |= REQUIRES_VALUETYPE_FIXUP;
1093             }
1094         }
1095 
IncrementDescendentFixups(int amount)1096         private void IncrementDescendentFixups(int amount) => _missingDecendents += amount;
1097 
DecrementFixupsRemaining(ObjectManager manager)1098         internal void DecrementFixupsRemaining(ObjectManager manager)
1099         {
1100             _missingElementsRemaining--;
1101             if (RequiresValueTypeFixup)
1102             {
1103                 UpdateDescendentDependencyChain(-1, manager);
1104             }
1105         }
1106 
1107         /// <summary>
1108         /// Removes a dependency of the object represented in this holder.
1109         /// This is normally the result of the dependency having been filled when
1110         /// the object is going to be only partially completed.  If we plan to fully
1111         /// update the object, we do not take the work to do this.
1112         /// </summary>
1113         /// <param name="id">The id of the object for which to remove the dependency.</param>
RemoveDependency(long id)1114         internal void RemoveDependency(long id)
1115         {
1116             Debug.Assert(_dependentObjects != null, "[ObjectHolder.RemoveDependency]m_dependentObjects!=null");
1117             Debug.Assert(id >= 0, "[ObjectHolder.RemoveDependency]id>=0");
1118             _dependentObjects.RemoveElement(id);
1119         }
1120 
1121         /// <summary>
1122         /// Note a fixup that has to be done before this object can be completed.
1123         /// Fixups are things that need to happen when other objects in the graph
1124         /// are added.  Dependencies are things that need to happen when this object
1125         /// is added.
1126         /// </summary>
1127         /// <param name="fixup">The fixup holder containing enough information to complete the fixup.</param>
AddFixup(FixupHolder fixup, ObjectManager manager)1128         internal void AddFixup(FixupHolder fixup, ObjectManager manager)
1129         {
1130             if (_missingElements == null)
1131             {
1132                 _missingElements = new FixupHolderList();
1133             }
1134             _missingElements.Add(fixup);
1135             _missingElementsRemaining++;
1136 
1137             if (RequiresValueTypeFixup)
1138             {
1139                 UpdateDescendentDependencyChain(1, manager);
1140             }
1141         }
1142 
1143         /// <summary>
1144         /// Updates the total list of dependencies to account for a fixup being added
1145         /// or completed in a child value class.  This will update all value classes
1146         /// containing that child and the object which contains all of them.
1147         /// </summary>
1148         /// <param name="amount">the amount by which to increment (or decrement) the dependency chain.</param>
1149         /// <param name="manager">The ObjectManager used to lookup other objects up the chain.</param>
UpdateDescendentDependencyChain(int amount, ObjectManager manager)1150         private void UpdateDescendentDependencyChain(int amount, ObjectManager manager)
1151         {
1152             ObjectHolder holder = this;
1153 
1154             //This loop walks one more object up the chain than there are valuetypes.  This
1155             //is because we need to increment the TotalFixups in the holders as well.
1156             do
1157             {
1158                 holder = manager.FindOrCreateObjectHolder(holder.ContainerID);
1159                 Debug.Assert(holder != null, "[ObjectHolder.UpdateTotalDependencyChain]holder!=null");
1160                 holder.IncrementDescendentFixups(amount);
1161             } while (holder.RequiresValueTypeFixup);
1162         }
1163 
1164         /// <summary>
1165         /// Note an object which is dependent on the one which will be contained in
1166         /// this ObjectHolder.  Dependencies should only be added if the object hasn't
1167         /// yet been added.  NB: An incomplete object counts as having no object.
1168         /// </summary>
1169         /// <param name="dependentObject">the id of the object which is dependent on this object being provided.</param>
AddDependency(long dependentObject)1170         internal void AddDependency(long dependentObject)
1171         {
1172             if (_dependentObjects == null)
1173             {
1174                 _dependentObjects = new LongList();
1175             }
1176             _dependentObjects.Add(dependentObject);
1177         }
1178 
1179         /// <summary>
1180         /// Update the data in the object holder.  This should be called when the object
1181         /// is finally registered.  Presumably the ObjectHolder was created to track
1182         /// some dependencies or preregistered fixups and we now need to actually record the
1183         /// object and other associated data.  We take this opportunity to set the flags
1184         /// so that we can do some faster processing in the future.
1185         /// </summary>
1186         /// <param name="obj">The object being held by this object holder. (This should no longer be null).</param>
1187         /// <param name="field">The SerializationInfo associated with this object, only required if we're doing delayed fixups.</param>
1188         /// <param name="manager">the ObjectManager being used to track these ObjectHolders.</param>
1189         /// <param name="surrogate">The surrogate handling this object.  May be null.</param>
1190         /// <param name="idOfContainer">The id of the object containing this one if this is a valuetype.</param>
UpdateData( object obj, SerializationInfo info, ISerializationSurrogate surrogate, long idOfContainer, FieldInfo field, int[] arrayIndex, ObjectManager manager)1191         internal void UpdateData(
1192             object obj, SerializationInfo info, ISerializationSurrogate surrogate, long idOfContainer,
1193             FieldInfo field, int[] arrayIndex, ObjectManager manager)
1194         {
1195             Debug.Assert(obj != null, "obj!=null");
1196             Debug.Assert(_id > 0, "m_id>0");
1197 
1198             //Record the fields that we can.
1199             SetObjectValue(obj, manager);
1200             _serInfo = info;
1201             _surrogate = surrogate;
1202 
1203             if (idOfContainer != 0 && ((field != null && field.FieldType.IsValueType) || arrayIndex != null))
1204             {
1205                 if (idOfContainer == _id)
1206                 {
1207                     throw new SerializationException(SR.Serialization_ParentChildIdentical);
1208                 }
1209                 _valueFixup = new ValueTypeFixupInfo(idOfContainer, field, arrayIndex);
1210             }
1211 
1212             SetFlags();
1213 
1214             if (RequiresValueTypeFixup)
1215             {
1216                 UpdateDescendentDependencyChain(_missingElementsRemaining, manager);
1217             }
1218         }
1219 
MarkForCompletionWhenAvailable()1220         internal void MarkForCompletionWhenAvailable() => _markForFixupWhenAvailable = true;
1221 
1222         /// <summary>
1223         /// An internal-only routine to set the flags based upon the data contained in
1224         /// the ObjectHolder
1225         /// </summary>
SetFlags()1226         internal void SetFlags()
1227         {
1228             if (_object is IObjectReference)
1229             {
1230                 _flags |= IncompleteObjectReference;
1231             }
1232 
1233             _flags &= ~(HAS_ISERIALIZABLE | HAS_SURROGATE);
1234             if (_surrogate != null)
1235             {
1236                 _flags |= HAS_SURROGATE;
1237             }
1238             else if (_object is ISerializable)
1239             {
1240                 _flags |= HAS_ISERIALIZABLE;
1241             }
1242 
1243             if (_valueFixup != null)
1244             {
1245                 _flags |= REQUIRES_VALUETYPE_FIXUP;
1246             }
1247         }
1248 
1249         internal bool IsIncompleteObjectReference
1250         {
1251             get { return (_flags & IncompleteObjectReference) != 0; }
1252             set
1253             {
1254                 if (value)
1255                 {
1256                     _flags |= IncompleteObjectReference;
1257                 }
1258                 else
1259                 {
1260                     _flags &= ~IncompleteObjectReference;
1261                 }
1262             }
1263         }
1264 
1265         internal bool RequiresDelayedFixup => (_flags & REQUIRES_DELAYED_FIXUP) != 0;
1266 
1267         internal bool RequiresValueTypeFixup => (_flags & REQUIRES_VALUETYPE_FIXUP) != 0;
1268 
1269         // ValueTypes which require fixups are initially handed to the ObjectManager
1270         // as boxed objects.  When they're still boxed objects, we should just do fixups
1271         // on them like we would any other object.  As soon as they're pushed into their
1272         // containing object we set ValueTypeFixupPerformed to true and have to go through
1273         // a more complicated path to set fixed up valuetype objects.
1274         // We check whether or not there are any dependent objects.
1275         internal bool ValueTypeFixupPerformed
1276         {
1277             get
1278             {
1279                 return (((_flags & VALUETYPE_FIXUP_PERFORMED) != 0) ||
1280                         (_object != null && ((_dependentObjects == null) || _dependentObjects.Count == 0)));
1281             }
1282             set
1283             {
1284                 if (value)
1285                 {
1286                     _flags |= VALUETYPE_FIXUP_PERFORMED;
1287                 }
1288             }
1289         }
1290 
1291         internal bool HasISerializable => (_flags & HAS_ISERIALIZABLE) != 0;
1292 
1293         internal bool HasSurrogate => (_flags & HAS_SURROGATE) != 0;
1294 
1295         internal bool CanSurrogatedObjectValueChange =>
1296             (_surrogate == null || _surrogate.GetType() != typeof(SurrogateForCyclicalReference));
1297 
1298         internal bool CanObjectValueChange =>
1299             IsIncompleteObjectReference ? true :
1300             HasSurrogate ? CanSurrogatedObjectValueChange :
1301             false;
1302 
1303         internal int DirectlyDependentObjects => _missingElementsRemaining;
1304 
1305         internal int TotalDependentObjects => _missingElementsRemaining + _missingDecendents;
1306 
1307         internal bool Reachable { get { return _reachable; } set { _reachable = value; } }
1308 
1309         internal bool TypeLoadExceptionReachable => _typeLoad != null;
1310 
1311         internal TypeLoadExceptionHolder TypeLoadException { get { return _typeLoad; } set { _typeLoad = value; } }
1312 
1313         internal object ObjectValue => _object;
1314 
SetObjectValue(object obj, ObjectManager manager)1315         internal void SetObjectValue(object obj, ObjectManager manager)
1316         {
1317             _object = obj;
1318             if (obj == manager.TopObject)
1319             {
1320                 _reachable = true;
1321             }
1322             if (obj is TypeLoadExceptionHolder)
1323             {
1324                 _typeLoad = (TypeLoadExceptionHolder)obj;
1325             }
1326 
1327             if (_markForFixupWhenAvailable)
1328             {
1329                 manager.CompleteObject(this, true);
1330             }
1331         }
1332 
1333         internal SerializationInfo SerializationInfo { get { return _serInfo; } set { _serInfo = value; } }
1334 
1335         internal ISerializationSurrogate Surrogate => _surrogate;
1336 
1337         internal LongList DependentObjects { get { return _dependentObjects; } set { _dependentObjects = value; } }
1338 
1339         internal bool RequiresSerInfoFixup
1340         {
1341             get
1342             {
1343                 if (((_flags & HAS_SURROGATE) == 0) && ((_flags & HAS_ISERIALIZABLE) == 0))
1344                 {
1345                     return false;
1346                 }
1347 
1348                 return (_flags & SER_INFO_FIXED) == 0;
1349             }
1350             set
1351             {
1352                 if (!value)
1353                 {
1354                     _flags |= SER_INFO_FIXED;
1355                 }
1356                 else
1357                 {
1358                     _flags &= ~SER_INFO_FIXED;
1359                 }
1360             }
1361         }
1362 
1363         internal ValueTypeFixupInfo ValueFixup => _valueFixup;
1364 
1365         internal bool CompletelyFixed => !RequiresSerInfoFixup && !IsIncompleteObjectReference;
1366 
1367         internal long ContainerID => _valueFixup != null ? _valueFixup.ContainerID : 0;
1368     }
1369 
1370     [Serializable]
1371     internal sealed class FixupHolder
1372     {
1373         internal const int ArrayFixup = 0x1;
1374         internal const int MemberFixup = 0x2;
1375         internal const int DelayedFixup = 0x4;
1376 
1377         internal long _id;
1378         internal object _fixupInfo; //This is either an array index, a String, or a MemberInfo
1379         internal readonly int _fixupType;
1380 
FixupHolder(long id, object fixupInfo, int fixupType)1381         internal FixupHolder(long id, object fixupInfo, int fixupType)
1382         {
1383             Debug.Assert(id > 0, "id>0");
1384             Debug.Assert(fixupInfo != null, "fixupInfo!=null");
1385             Debug.Assert(fixupType == ArrayFixup || fixupType == MemberFixup || fixupType == DelayedFixup, "fixupType==ArrayFixup || fixupType == MemberFixup || fixupType==DelayedFixup");
1386 
1387             _id = id;
1388             _fixupInfo = fixupInfo;
1389             _fixupType = fixupType;
1390         }
1391     }
1392 
1393     [Serializable]
1394     internal sealed class FixupHolderList
1395     {
1396         internal const int InitialSize = 2;
1397 
1398         internal FixupHolder[] _values;
1399         internal int _count;
1400 
FixupHolderList()1401         internal FixupHolderList() : this(InitialSize)
1402         {
1403         }
1404 
FixupHolderList(int startingSize)1405         internal FixupHolderList(int startingSize)
1406         {
1407             _count = 0;
1408             _values = new FixupHolder[startingSize];
1409         }
1410 
Add(FixupHolder fixup)1411         internal void Add(FixupHolder fixup)
1412         {
1413             if (_count == _values.Length)
1414             {
1415                 EnlargeArray();
1416             }
1417             _values[_count++] = fixup;
1418         }
1419 
EnlargeArray()1420         private void EnlargeArray()
1421         {
1422             int newLength = _values.Length * 2;
1423             if (newLength < 0)
1424             {
1425                 if (newLength == int.MaxValue)
1426                 {
1427                     throw new SerializationException(SR.Serialization_TooManyElements);
1428                 }
1429                 newLength = int.MaxValue;
1430             }
1431 
1432             FixupHolder[] temp = new FixupHolder[newLength];
1433             Array.Copy(_values, 0, temp, 0, _count);
1434             _values = temp;
1435         }
1436     }
1437 
1438     [Serializable]
1439     internal sealed class LongList
1440     {
1441         private const int InitialSize = 2;
1442 
1443         private long[] _values;
1444         private int _count; //The total number of valid items still in the list;
1445         private int _totalItems; //The total number of allocated entries. This includes space for items which have been marked as deleted.
1446         private int _currentItem; //Used when doing an enumeration over the list.
1447 
1448         // An m_currentItem of -1 indicates that the enumeration hasn't been started.
1449         // An m_values[xx] of -1 indicates that the item has been deleted.
LongList()1450         internal LongList() : this(InitialSize)
1451         {
1452         }
1453 
LongList(int startingSize)1454         internal LongList(int startingSize)
1455         {
1456             _count = 0;
1457             _totalItems = 0;
1458             _values = new long[startingSize];
1459         }
1460 
Add(long value)1461         internal void Add(long value)
1462         {
1463             if (_totalItems == _values.Length)
1464             {
1465                 EnlargeArray();
1466             }
1467             _values[_totalItems++] = value;
1468             _count++;
1469         }
1470 
1471         internal int Count => _count;
1472 
StartEnumeration()1473         internal void StartEnumeration() => _currentItem = -1;
1474 
MoveNext()1475         internal bool MoveNext()
1476         {
1477             while (++_currentItem < _totalItems && _values[_currentItem] == -1) ;
1478             return _currentItem != _totalItems;
1479         }
1480 
1481         internal long Current
1482         {
1483             get
1484             {
1485                 Debug.Assert(_currentItem != -1, "[LongList.Current]m_currentItem!=-1");
1486                 Debug.Assert(_values[_currentItem] != -1, "[LongList.Current]m_values[m_currentItem]!=-1");
1487                 return _values[_currentItem];
1488             }
1489         }
1490 
RemoveElement(long value)1491         internal bool RemoveElement(long value)
1492         {
1493             int i;
1494             for (i = 0; i < _totalItems; i++)
1495             {
1496                 if (_values[i] == value)
1497                 {
1498                     break;
1499                 }
1500             }
1501             if (i == _totalItems)
1502             {
1503                 return false;
1504             }
1505             _values[i] = -1;
1506             return true;
1507         }
1508 
EnlargeArray()1509         private void EnlargeArray()
1510         {
1511             int newLength = _values.Length * 2;
1512             if (newLength < 0)
1513             {
1514                 if (newLength == int.MaxValue)
1515                 {
1516                     throw new SerializationException(SR.Serialization_TooManyElements);
1517                 }
1518                 newLength = int.MaxValue;
1519             }
1520 
1521             long[] temp = new long[newLength];
1522             Array.Copy(_values, 0, temp, 0, _count);
1523             _values = temp;
1524         }
1525     }
1526 
1527     internal sealed class ObjectHolderList
1528     {
1529         internal const int DefaultInitialSize = 8;
1530 
1531         internal ObjectHolder[] _values;
1532         internal int _count;
1533 
ObjectHolderList()1534         internal ObjectHolderList() : this(DefaultInitialSize)
1535         {
1536         }
1537 
ObjectHolderList(int startingSize)1538         internal ObjectHolderList(int startingSize)
1539         {
1540             Debug.Assert(startingSize > 0 && startingSize < 0x1000, "startingSize>0 && startingSize<0x1000");
1541             _count = 0;
1542             _values = new ObjectHolder[startingSize];
1543         }
1544 
Add(ObjectHolder value)1545         internal void Add(ObjectHolder value)
1546         {
1547             if (_count == _values.Length)
1548             {
1549                 EnlargeArray();
1550             }
1551             _values[_count++] = value;
1552         }
1553 
GetFixupEnumerator()1554         internal ObjectHolderListEnumerator GetFixupEnumerator() => new ObjectHolderListEnumerator(this, true);
1555 
EnlargeArray()1556         private void EnlargeArray()
1557         {
1558             int newLength = _values.Length * 2;
1559             if (newLength < 0)
1560             {
1561                 if (newLength == int.MaxValue)
1562                 {
1563                     throw new SerializationException(SR.Serialization_TooManyElements);
1564                 }
1565                 newLength = int.MaxValue;
1566             }
1567 
1568             ObjectHolder[] temp = new ObjectHolder[newLength];
1569             Array.Copy(_values, 0, temp, 0, _count);
1570             _values = temp;
1571         }
1572 
1573         internal int Version => _count;
1574 
1575         internal int Count => _count;
1576     }
1577 
1578     internal sealed class ObjectHolderListEnumerator
1579     {
1580         private readonly bool _isFixupEnumerator;
1581         private readonly ObjectHolderList _list;
1582         private readonly int _startingVersion;
1583         private int _currPos;
1584 
ObjectHolderListEnumerator(ObjectHolderList list, bool isFixupEnumerator)1585         internal ObjectHolderListEnumerator(ObjectHolderList list, bool isFixupEnumerator)
1586         {
1587             Debug.Assert(list != null, "[ObjectHolderListEnumerator.ctor]list!=null");
1588             _list = list;
1589             _startingVersion = _list.Version;
1590             _currPos = -1;
1591             _isFixupEnumerator = isFixupEnumerator;
1592         }
1593 
MoveNext()1594         internal bool MoveNext()
1595         {
1596             Debug.Assert(_startingVersion == _list.Version, "[ObjectHolderListEnumerator.MoveNext]m_startingVersion==m_list.Version");
1597             if (_isFixupEnumerator)
1598             {
1599                 while (++_currPos < _list.Count && _list._values[_currPos].CompletelyFixed) ;
1600                 return _currPos != _list.Count;
1601             }
1602             else
1603             {
1604                 _currPos++;
1605                 return _currPos != _list.Count;
1606             }
1607         }
1608 
1609         internal ObjectHolder Current
1610         {
1611             get
1612             {
1613                 Debug.Assert(_currPos != -1, "[ObjectHolderListEnumerator.Current]m_currPos!=-1");
1614                 Debug.Assert(_currPos < _list.Count, "[ObjectHolderListEnumerator.Current]m_currPos<m_list.Count");
1615                 Debug.Assert(_startingVersion == _list.Version, "[ObjectHolderListEnumerator.Current]m_startingVersion==m_list.Version");
1616                 return _list._values[_currPos];
1617             }
1618         }
1619     }
1620 
1621     public sealed class TypeLoadExceptionHolder
1622     {
TypeLoadExceptionHolder(string typeName)1623         internal TypeLoadExceptionHolder(string typeName)
1624         {
1625             TypeName = typeName;
1626         }
1627 
1628         internal string TypeName { get; }
1629     }
1630 
1631     // TODO: Temporary workaround.  Remove this once SerializationInfo.UpdateValue is exposed
1632     // from coreclr for use by ObjectManager.
1633     internal static class SerializationInfoExtensions
1634     {
1635         private static readonly Action<SerializationInfo, string, object, Type> s_updateValue =
1636             (Action<SerializationInfo, string, object, Type>)typeof(SerializationInfo)
1637             .GetMethod("UpdateValue", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
1638             .CreateDelegate(typeof(Action<SerializationInfo, string, object, Type>));
1639 
UpdateValue(this SerializationInfo si, string name, object value, Type type)1640         public static void UpdateValue(this SerializationInfo si, string name, object value, Type type) =>
1641             s_updateValue(si, name, value, type);
1642     }
1643 }
1644