private AnyClass ReadInstance(int index) { Debug.Assert(index > 0); if (index > 1) { if (_instanceMap != null && _instanceMap.Count > index - 2) { return(_instanceMap[index - 2]); } throw new InvalidDataException($"could not find index {index} in {nameof(_instanceMap)}"); } if (++_classGraphDepth > Communicator.ClassGraphDepthMax) { throw new InvalidDataException("maximum class graph depth reached"); } InstanceData?previousCurrent = Push(InstanceType.Class); Debug.Assert(_current != null); AnyClass?instance = null; do { // Read the slice header. string?typeIdOpt = ReadSliceHeaderIntoCurrent(); // We cannot read the indirection table at this point as it may reference the new instance that is // not created yet. IClassFactory?factory = null; if (typeIdOpt is string typeId) { Debug.Assert(_current.SliceCompactId == null); factory = Communicator.FindClassFactory(typeId); } else if (_current.SliceCompactId is int compactId) { factory = Communicator.FindClassFactory(compactId); } if (factory != null) { instance = factory.Read(this); } else if (SkipSlice()) // Slice off what we don't understand. { instance = new UnknownSlicedClass(this); } }while (instance == null); Pop(previousCurrent); --_classGraphDepth; return(instance); }
/// <summary>Reads a class instance from the stream.</summary> /// <param name="index">The index of the class instance. If greater than 1, it's a reference to a previously /// seen class; if 1, the class's bytes are next on the stream. Cannot be 0 or less.</param> /// <param name="formalTypeId">The type ID of the formal type of the parameter or data member being read. /// </param> private AnyClass ReadInstance(int index, string?formalTypeId) { Debug.Assert(Communicator != null); Debug.Assert(index > 0); if (index > 1) { if (_instanceMap != null && _instanceMap.Count > index - 2) { return(_instanceMap[index - 2]); } throw new InvalidDataException($"could not find index {index} in {nameof(_instanceMap)}"); } if (++_classGraphDepth > Communicator.ClassGraphMaxDepth) { throw new InvalidDataException("maximum class graph depth reached"); } // Save current in case we're reading a nested instance. InstanceData previousCurrent = _current; _current = default; _current.InstanceType = InstanceType.Class; AnyClass?instance = null; _instanceMap ??= new List <AnyClass>(); if (OldEncoding) { bool readIndirectionTable = true; do { // Read the slice header. (string?typeId, int?compactId) = ReadSliceHeaderIntoCurrent11(); // We cannot read the indirection table at this point as it may reference the new instance that is // not created yet. Func <AnyClass>?factory = null; if (typeId != null) { factory = Communicator.FindClassFactory(typeId); } else if (compactId is int compactIdValue) { factory = Communicator.FindClassFactory(compactIdValue); } if (factory != null) { instance = factory(); } else if (SkipSlice(typeId, compactId)) // Slice off what we don't understand. { instance = new UnknownSlicedClass(); // Don't read the indirection table as it's the last entry in DeferredIndirectionTableList11. readIndirectionTable = false; } }while (instance == null); // Add the instance to the map/list of instances. This must be done before reading the instances (for // circular references). _instanceMap.Add(instance); // Read all the deferred indirection tables now that the instance is inserted in _instanceMap. if (_current.DeferredIndirectionTableList11?.Count > 0) { int savedPos = Pos; Debug.Assert(_current.Slices?.Count == _current.DeferredIndirectionTableList11.Count); for (int i = 0; i < _current.DeferredIndirectionTableList11.Count; ++i) { int pos = _current.DeferredIndirectionTableList11[i]; if (pos > 0) { Pos = pos; _current.Slices[i].Instances = Array.AsReadOnly(ReadIndirectionTable()); } // else remains empty } Pos = savedPos; } if (readIndirectionTable) { ReadIndirectionTableIntoCurrent(); } } else { // With the 2.0 encoding, we don't need a DeferredIndirectionTableList because all the type IDs are // provided by the first slice (when using the sliced format). (string[]? allTypeIds, _, _) = ReadFirstSliceHeaderIntoCurrent20(); if (allTypeIds != null) { int skipCount = 0; foreach (string typeId in allTypeIds) { if (Communicator.FindClassFactory(typeId) is Func <AnyClass> factory) { instance = factory(); break; // foreach } else { skipCount++; } } instance ??= new UnknownSlicedClass(); _instanceMap.Add(instance); ReadIndirectionTableIntoCurrent(); // read the indirection table immediately for (int i = 0; i < skipCount; ++i) { // SkipSlice saves the slice data including the current indirection table, if any. if (SkipSlice(allTypeIds[i])) { if (i != skipCount - 1) { throw new InvalidDataException( "class slice marked as the last slice is not the last slice"); } break; } else { ReadNextSliceHeaderIntoCurrent(); ReadIndirectionTableIntoCurrent(); } } } else if (formalTypeId != null) { // received null and formalTypeId is not null, apply formal type optimization. if (Communicator.FindClassFactory(formalTypeId) is Func <AnyClass> factory) { instance = factory(); _instanceMap.Add(instance); ReadIndirectionTableIntoCurrent(); // Nothing to skip } else { throw new ArgumentException($"cannot find class factory for `{formalTypeId}'", nameof(formalTypeId)); } } else { throw new InvalidDataException( @$ "cannot read a class instance with no type ID; did you forget to specify the { nameof(formalTypeId)}?"); } }