730 lines
28 KiB
C#
730 lines
28 KiB
C#
//
|
|
// Copyright (c) 2012-2021 Antmicro
|
|
//
|
|
// This file is licensed under the MIT License.
|
|
// Full license text is available in the LICENSE file.
|
|
|
|
using System;
|
|
using System.IO;
|
|
using System.Runtime.Serialization;
|
|
using System.Linq;
|
|
using System.Collections.Generic;
|
|
using System.Collections;
|
|
using Antmicro.Migrant.Hooks;
|
|
using Antmicro.Migrant.Utilities;
|
|
using Antmicro.Migrant.Generators;
|
|
using Antmicro.Migrant.VersionTolerance;
|
|
using Antmicro.Migrant.Customization;
|
|
|
|
namespace Antmicro.Migrant
|
|
{
|
|
internal class ObjectReader
|
|
{
|
|
public ObjectReader(Stream stream, Serializer.ReadMethods readMethods, SwapList objectsForSurrogates = null, Action<object> postDeserializationCallback = null,
|
|
bool treatCollectionAsUserObject = false,
|
|
VersionToleranceLevel versionToleranceLevel = 0, bool useBuffering = true, bool disableStamping = false,
|
|
ReferencePreservation referencePreservation = ReferencePreservation.Preserve,
|
|
bool forceStampVerification = false)
|
|
{
|
|
this.readMethods = readMethods;
|
|
this.postDeserializationCallback = postDeserializationCallback;
|
|
this.treatCollectionAsUserObject = treatCollectionAsUserObject;
|
|
this.referencePreservation = referencePreservation;
|
|
this.objectsForSurrogates = objectsForSurrogates ?? new SwapList();
|
|
|
|
VersionToleranceLevel = versionToleranceLevel;
|
|
types = new List<TypeDescriptor>();
|
|
Methods = new IdentifiedElementsList<MethodDescriptor>(this);
|
|
Assemblies = new IdentifiedElementsList<AssemblyDescriptor>(this);
|
|
Modules = new IdentifiedElementsList<ModuleDescriptor>(this);
|
|
latePostDeserializationHooks = new List<Action>();
|
|
|
|
reader = new PrimitiveReader(stream, useBuffering);
|
|
surrogatesWhileReading = new OneToOneMap<int, object>();
|
|
|
|
readTypeMethod = disableStamping ? (Func<TypeDescriptor>)ReadSimpleTypeDescriptor : ReadFullTypeDescriptor;
|
|
ForceStampVerification = forceStampVerification;
|
|
}
|
|
|
|
public void ReuseWithNewStream(Stream stream)
|
|
{
|
|
deserializedObjects.Clear();
|
|
types.Clear();
|
|
Methods.Clear();
|
|
Assemblies.Clear();
|
|
Modules.Clear();
|
|
reader = new PrimitiveReader(stream, reader.IsBuffered);
|
|
}
|
|
|
|
public T ReadObject<T>()
|
|
{
|
|
if(soFarDeserialized != null)
|
|
{
|
|
deserializedObjects = new AutoResizingList<object>(soFarDeserialized.Length);
|
|
for(var i = 0; i < soFarDeserialized.Length; i++)
|
|
{
|
|
deserializedObjects[i] = soFarDeserialized[i].Target;
|
|
}
|
|
}
|
|
if(deserializedObjects == null)
|
|
{
|
|
deserializedObjects = new AutoResizingList<object>(InitialCapacity);
|
|
}
|
|
|
|
objectsWrittenInlineCount = 0;
|
|
var before = deserializedObjects.Count;
|
|
var theRefId = ReadAndTouchReference();
|
|
if(theRefId == before)
|
|
{
|
|
var after = deserializedObjects.Count;
|
|
if(before < after - objectsWrittenInlineCount)
|
|
{
|
|
int prevRefId;
|
|
int refId = -1;
|
|
do
|
|
{
|
|
prevRefId = refId;
|
|
refId = reader.ReadInt32();
|
|
var type = GetObjectByReferenceId(refId).GetType();
|
|
readMethods.readMethodsProvider.GetOrCreate(type)(this, type, refId);
|
|
// this is to compensate objects written inline that has been already counted
|
|
objectsWrittenInlineCount -= (refId - prevRefId - 1);
|
|
}
|
|
while(deserializedObjects.Count - before - refId - objectsWrittenInlineCount > 1);
|
|
}
|
|
}
|
|
|
|
var obj = deserializedObjects[theRefId];
|
|
try
|
|
{
|
|
for(var i = deserializedObjects.Count - 1; i >= 0; i--)
|
|
{
|
|
Completed(i);
|
|
}
|
|
|
|
if(!(obj is T))
|
|
{
|
|
throw new InvalidDataException(
|
|
string.Format("Type {0} requested to be deserialized, however type {1} encountered in the stream.",
|
|
typeof(T), obj.GetType()));
|
|
}
|
|
|
|
PrepareForNextRead();
|
|
foreach(var hook in latePostDeserializationHooks)
|
|
{
|
|
hook();
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
latePostDeserializationHooks.Clear();
|
|
}
|
|
return (T)obj;
|
|
}
|
|
|
|
public void Flush()
|
|
{
|
|
reader.Dispose();
|
|
}
|
|
|
|
private void PrepareForNextRead()
|
|
{
|
|
if(referencePreservation == ReferencePreservation.UseWeakReference)
|
|
{
|
|
soFarDeserialized = new WeakReference[deserializedObjects.Count];
|
|
for(var i = 0; i < soFarDeserialized.Length; i++)
|
|
{
|
|
soFarDeserialized[i] = new WeakReference(deserializedObjects[i]);
|
|
}
|
|
}
|
|
if(referencePreservation != ReferencePreservation.Preserve)
|
|
{
|
|
deserializedObjects = null;
|
|
}
|
|
}
|
|
|
|
internal static bool HasSpecialReadMethod(Type type)
|
|
{
|
|
return type == typeof(string) || typeof(ISpeciallySerializable).IsAssignableFrom(type) || Helpers.IsTransient(type);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads the object using reflection.
|
|
///
|
|
/// REMARK: this method is not thread-safe
|
|
/// </summary>
|
|
/// <param name="objectReader">Object reader</param>
|
|
/// <param name="actualType">Type of object to deserialize</param>
|
|
/// <param name="objectId">Identifier of object to deserialize</param>
|
|
internal static void ReadObjectInnerUsingReflection(ObjectReader objectReader, Type actualType, int objectId)
|
|
{
|
|
objectReader.TryTouchObject(actualType, objectId);
|
|
|
|
switch(GetCreationWay(actualType, objectReader.treatCollectionAsUserObject))
|
|
{
|
|
case CreationWay.Null:
|
|
objectReader.ReadNotPrecreated(actualType, objectId);
|
|
break;
|
|
case CreationWay.DefaultCtor:
|
|
objectReader.UpdateElements(actualType, objectId);
|
|
break;
|
|
case CreationWay.Uninitialized:
|
|
objectReader.UpdateFields(actualType, objectReader.GetObjectByReferenceId(objectId));
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void UpdateFields(Type actualType, object target)
|
|
{
|
|
var fieldOrTypeInfos = ((TypeFullDescriptor)actualType).FieldsToDeserialize;
|
|
foreach(var fieldOrTypeInfo in fieldOrTypeInfos)
|
|
{
|
|
if(fieldOrTypeInfo.Field == null)
|
|
{
|
|
ReadField(fieldOrTypeInfo.TypeToOmit);
|
|
continue;
|
|
}
|
|
var field = fieldOrTypeInfo.Field;
|
|
if(field.IsDefined(typeof(TransientAttribute), false))
|
|
{
|
|
if(field.IsDefined(typeof(ConstructorAttribute), false))
|
|
{
|
|
var ctorAttribute = (ConstructorAttribute)field.GetCustomAttributes(false).First(x => x is ConstructorAttribute);
|
|
field.SetValue(target, Activator.CreateInstance(field.FieldType, ctorAttribute.Parameters));
|
|
}
|
|
continue;
|
|
}
|
|
var value = ReadField(field.FieldType);
|
|
field.SetValue(target, value);
|
|
}
|
|
}
|
|
|
|
internal void Completed(int refId)
|
|
{
|
|
var currentObject = GetObjectByReferenceId(refId);
|
|
if(currentObject == null)
|
|
{
|
|
// this may happen with empty delegates
|
|
return;
|
|
}
|
|
var type = currentObject.GetType();
|
|
readMethods.completeMethodsProvider.GetOrCreate(type)(this, refId);
|
|
}
|
|
|
|
internal void CompletedInnerUsingReflection(int refId)
|
|
{
|
|
var obj = GetObjectByReferenceId(refId);
|
|
var factoryId = objectsForSurrogates.FindMatchingIndex(obj.GetType());
|
|
|
|
// (1) call post-deserialization hooks on it
|
|
Helpers.InvokeAttribute(typeof(PostDeserializationAttribute), obj);
|
|
var postHook = Helpers.GetDelegateWithAttribute(typeof(LatePostDeserializationAttribute), obj);
|
|
if(postHook != null)
|
|
{
|
|
if(factoryId != -1)
|
|
{
|
|
throw new InvalidOperationException(string.Format(LateHookAndSurrogateError, obj.GetType()));
|
|
}
|
|
latePostDeserializationHooks.Add(postHook);
|
|
}
|
|
if(postDeserializationCallback != null)
|
|
{
|
|
postDeserializationCallback(obj);
|
|
}
|
|
|
|
// (2) de-surrogate it if needed & clone the content
|
|
if(factoryId != -1)
|
|
{
|
|
var desurrogated = objectsForSurrogates.GetByIndex(factoryId).DynamicInvoke(new[] { obj });
|
|
// clone value of de-surrogated objects to final objects
|
|
CloneContentUsingReflection(desurrogated, deserializedObjects[refId]);
|
|
obj = deserializedObjects[refId];
|
|
surrogatesWhileReading.Remove(refId);
|
|
}
|
|
}
|
|
|
|
internal static void CloneContentUsingReflection(object source, object destination)
|
|
{
|
|
var sourceType = source.GetType();
|
|
var destinationType = destination.GetType();
|
|
|
|
if(sourceType != destinationType)
|
|
{
|
|
throw new ArgumentException("Source and destination types mismatched.");
|
|
}
|
|
|
|
foreach(var field in source.GetType().GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance))
|
|
{
|
|
var srcValue = field.GetValue(source);
|
|
field.SetValue(destination, srcValue == source ? destination : srcValue);
|
|
}
|
|
}
|
|
|
|
private void ReadNotPrecreated(Type type, int objectId)
|
|
{
|
|
if(type.IsValueType)
|
|
{
|
|
// a boxed value type
|
|
SetObjectByReferenceId(objectId, ReadField(type));
|
|
}
|
|
else if(typeof(MulticastDelegate).IsAssignableFrom(type))
|
|
{
|
|
ReadDelegate(type, objectId);
|
|
}
|
|
else if(type.IsArray)
|
|
{
|
|
ReadArray(type.GetElementType(), objectId);
|
|
}
|
|
else
|
|
{
|
|
throw new InvalidOperationException(InternalErrorMessage);
|
|
}
|
|
}
|
|
|
|
private void UpdateElements(Type type, int objectId)
|
|
{
|
|
var obj = GetObjectByReferenceId(objectId);
|
|
var speciallyDeserializable = obj as ISpeciallySerializable;
|
|
if(speciallyDeserializable != null)
|
|
{
|
|
LoadAndVerifySpeciallySerializableAndVerify(speciallyDeserializable, reader);
|
|
return;
|
|
}
|
|
CollectionMetaToken token;
|
|
if(!CollectionMetaToken.TryGetCollectionMetaToken(type, out token))
|
|
{
|
|
throw new InvalidOperationException(InternalErrorMessage);
|
|
}
|
|
|
|
// so we can assume it is ICollection<T> or ICollection
|
|
FillCollection(token.FormalElementType, objectId);
|
|
}
|
|
|
|
internal object GetObjectByReferenceId(int refId, bool forceDeserializedObject = false)
|
|
{
|
|
object obj;
|
|
if(!forceDeserializedObject && surrogatesWhileReading.TryGetValue(refId, out obj))
|
|
{
|
|
return obj;
|
|
}
|
|
return deserializedObjects[refId];
|
|
}
|
|
|
|
internal void SetObjectByReferenceId(int refId, object obj)
|
|
{
|
|
if(surrogatesWhileReading.ContainsKey(refId))
|
|
{
|
|
surrogatesWhileReading[refId] = obj;
|
|
}
|
|
else
|
|
{
|
|
deserializedObjects[refId] = obj;
|
|
}
|
|
}
|
|
|
|
internal int ReadAndTouchReference()
|
|
{
|
|
var refId = reader.ReadInt32();
|
|
if(refId == Consts.NullObjectId)
|
|
{
|
|
// there was an explicit 'null' stored in stream
|
|
return Consts.NullObjectId;
|
|
}
|
|
if(refId >= deserializedObjects.Count)
|
|
{
|
|
var type = ReadType();
|
|
var isSurrogated = reader.ReadBoolean();
|
|
if(isSurrogated)
|
|
{
|
|
var objectTypeAfterDesurrogation = ReadType();
|
|
// using formatter service here is enough, as the whole content of an object will be cloned later
|
|
deserializedObjects[refId] = FormatterServices.GetUninitializedObject(objectTypeAfterDesurrogation.UnderlyingType);
|
|
var surrogate = readMethods.createObjectMethodsProvider.GetOrCreate(type.UnderlyingType)();
|
|
surrogatesWhileReading.Add(refId, surrogate);
|
|
}
|
|
|
|
readMethods.touchInlinedObjectMethodsProvider.GetOrCreate(type.UnderlyingType)(this, refId);
|
|
}
|
|
|
|
return refId;
|
|
}
|
|
|
|
internal void TryTouchInlinedObjectUsingReflection(Type type, int refId)
|
|
{
|
|
if(!TryTouchObject(type, refId))
|
|
{
|
|
if(!TryRecreateObjectUsingAdditionalMetadata(type, refId))
|
|
{
|
|
ReadNotPrecreated(type, refId);
|
|
objectsWrittenInlineCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
private object ReadField(Type formalType)
|
|
{
|
|
if(Helpers.IsTransient(formalType))
|
|
{
|
|
return Helpers.GetDefaultValue(formalType);
|
|
}
|
|
|
|
if(!formalType.IsValueType)
|
|
{
|
|
var refId = ReadAndTouchReference();
|
|
if(refId == Consts.NullObjectId)
|
|
{
|
|
// there was an explicit 'null' stored in stream
|
|
return null;
|
|
}
|
|
return GetObjectByReferenceId(refId, true);
|
|
}
|
|
if(formalType.IsEnum)
|
|
{
|
|
var value = ReadField(Enum.GetUnderlyingType(formalType));
|
|
return Enum.ToObject(formalType, value);
|
|
}
|
|
var nullableActualType = Nullable.GetUnderlyingType(formalType);
|
|
if(nullableActualType != null)
|
|
{
|
|
var isNotNull = reader.ReadBoolean();
|
|
return isNotNull ? ReadField(nullableActualType) : null;
|
|
}
|
|
if(Helpers.IsWriteableByPrimitiveWriter(formalType))
|
|
{
|
|
var methodName = string.Concat("Read", formalType.Name);
|
|
var readMethod = typeof(PrimitiveReader).GetMethod(methodName);
|
|
return readMethod.Invoke(reader, Type.EmptyTypes);
|
|
}
|
|
|
|
var returnedObject = Activator.CreateInstance(formalType);
|
|
// here we have a boxed struct which we put to struct reference list
|
|
UpdateFields(formalType, returnedObject);
|
|
// if this is subtype
|
|
return returnedObject;
|
|
}
|
|
|
|
private bool TryRecreateObjectUsingAdditionalMetadata(Type type, int objectId)
|
|
{
|
|
if(type.IsArray)
|
|
{
|
|
ReadMetadataAndTouchArray(type.GetElementType(), objectId);
|
|
return true;
|
|
}
|
|
if(type == typeof(string))
|
|
{
|
|
var result = reader.ReadString();
|
|
SetObjectByReferenceId(objectId, result);
|
|
objectsWrittenInlineCount++;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private void FillCollection(Type elementFormalType, int objectId)
|
|
{
|
|
var obj = GetObjectByReferenceId(objectId);
|
|
var collectionType = obj.GetType();
|
|
var count = reader.ReadInt32();
|
|
|
|
if(collectionType == typeof(Stack) ||
|
|
(collectionType.IsGenericType && collectionType.GetGenericTypeDefinition() == typeof(Stack<>)))
|
|
{
|
|
var stack = (dynamic)obj;
|
|
var temp = new dynamic[count];
|
|
for(int i = 0; i < count; i++)
|
|
{
|
|
temp[i] = ReadField(elementFormalType);
|
|
}
|
|
for(int i = count - 1; i >= 0; --i)
|
|
{
|
|
stack.Push(temp[i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var addMethod = collectionType.GetMethod("Add", new[] { elementFormalType }) ??
|
|
collectionType.GetMethod("Enqueue", new[] { elementFormalType }) ??
|
|
collectionType.GetMethod("Push", new[] { elementFormalType });
|
|
if(addMethod == null)
|
|
{
|
|
throw new InvalidOperationException(string.Format(CouldNotFindAddErrorMessage,
|
|
collectionType));
|
|
}
|
|
Type delegateType;
|
|
if(addMethod.ReturnType == typeof(void))
|
|
{
|
|
delegateType = typeof(Action<>).MakeGenericType(new[] { elementFormalType });
|
|
}
|
|
else
|
|
{
|
|
delegateType = typeof(Func<,>).MakeGenericType(new[] {
|
|
elementFormalType,
|
|
addMethod.ReturnType
|
|
});
|
|
}
|
|
|
|
var addDelegate = Delegate.CreateDelegate(delegateType, obj, addMethod);
|
|
for(var i = 0; i < count; i++)
|
|
{
|
|
var value = ReadField(elementFormalType);
|
|
addDelegate.DynamicInvoke(value);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ReadMetadataAndTouchArray(Type elementFormalType, int objectId)
|
|
{
|
|
var rank = reader.ReadInt32();
|
|
var lengths = new int[rank];
|
|
for(var i = 0; i < rank; i++)
|
|
{
|
|
lengths[i] = reader.ReadInt32();
|
|
}
|
|
var array = Array.CreateInstance(elementFormalType, lengths);
|
|
// we should update the array object as soon as we can
|
|
// why? because it can have the reference to itself (what a corner case!)
|
|
SetObjectByReferenceId(objectId, array);
|
|
}
|
|
|
|
private void ReadArray(Type elementFormalType, int objectId)
|
|
{
|
|
var array = (Array)GetObjectByReferenceId(objectId);
|
|
var position = new int[array.Rank];
|
|
FillArrayRowRecursive(array, 0, position, elementFormalType);
|
|
}
|
|
|
|
private void ReadDelegate(Type type, int objectId)
|
|
{
|
|
var invocationListLength = reader.ReadInt32();
|
|
if(invocationListLength == 0)
|
|
{
|
|
return;
|
|
}
|
|
Delegate result = null;
|
|
for(var i = 0; i < invocationListLength; i++)
|
|
{
|
|
var target = ReadField(typeof(object));
|
|
var method = Methods.Read();
|
|
var del = Delegate.CreateDelegate(type, target, method.UnderlyingMethod);
|
|
result = Delegate.Combine(result, del);
|
|
}
|
|
SetObjectByReferenceId(objectId, result);
|
|
}
|
|
|
|
private void FillArrayRowRecursive(Array array, int currentDimension, int[] position, Type elementFormalType)
|
|
{
|
|
var length = array.GetLength(currentDimension);
|
|
for(var i = 0; i < length; i++)
|
|
{
|
|
if(currentDimension == array.Rank - 1)
|
|
{
|
|
var value = ReadField(elementFormalType);
|
|
array.SetValue(value, position);
|
|
}
|
|
else
|
|
{
|
|
FillArrayRowRecursive(array, currentDimension + 1, position, elementFormalType);
|
|
}
|
|
position[currentDimension]++;
|
|
for(var j = currentDimension + 1; j < array.Rank; j++)
|
|
{
|
|
position[j] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal static void LoadAndVerifySpeciallySerializableAndVerify(ISpeciallySerializable obj, PrimitiveReader reader)
|
|
{
|
|
var beforePosition = reader.Position;
|
|
obj.Load(reader);
|
|
var afterPosition = reader.Position;
|
|
var serializedLength = reader.ReadInt64();
|
|
if(serializedLength + beforePosition != afterPosition)
|
|
{
|
|
throw new InvalidOperationException(string.Format(
|
|
"Stream corruption by '{0}', incorrent magic {1} when {2} expected.", obj.GetType(), serializedLength, afterPosition - beforePosition));
|
|
}
|
|
}
|
|
|
|
internal TypeDescriptor ReadType()
|
|
{
|
|
return readTypeMethod();
|
|
}
|
|
|
|
private TypeSimpleDescriptor ReadSimpleTypeDescriptor()
|
|
{
|
|
TypeSimpleDescriptor type;
|
|
var typeId = reader.ReadInt32();
|
|
if(typeId == Consts.NullObjectId)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if(types.Count > typeId)
|
|
{
|
|
type = (TypeSimpleDescriptor)types[typeId];
|
|
}
|
|
else
|
|
{
|
|
type = new TypeSimpleDescriptor();
|
|
types.Add(type);
|
|
|
|
type.Read(this);
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
private TypeFullDescriptor ReadFullTypeDescriptor()
|
|
{
|
|
TypeFullDescriptor type;
|
|
var typeIdOrPosition = reader.ReadInt32();
|
|
if(typeIdOrPosition == Consts.NullObjectId)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var isGenericParameter = reader.ReadBoolean();
|
|
if(isGenericParameter)
|
|
{
|
|
var genericType = ReadFullTypeDescriptor().UnderlyingType;
|
|
type = (TypeFullDescriptor)genericType.GetGenericArguments()[typeIdOrPosition];
|
|
}
|
|
else
|
|
{
|
|
if(types.Count > typeIdOrPosition)
|
|
{
|
|
type = (TypeFullDescriptor)types[typeIdOrPosition];
|
|
}
|
|
else
|
|
{
|
|
type = new TypeFullDescriptor();
|
|
types.Add(type);
|
|
|
|
type.Read(this);
|
|
}
|
|
}
|
|
|
|
if(type.UnderlyingType.IsGenericType)
|
|
{
|
|
var containsAnyFixedGenericArguments = reader.ReadBoolean();
|
|
if(containsAnyFixedGenericArguments)
|
|
{
|
|
var args = new Type[type.UnderlyingType.GetGenericArguments().Count()];
|
|
for(int i = 0; i < args.Length; i++)
|
|
{
|
|
args[i] = ReadFullTypeDescriptor().UnderlyingType;
|
|
}
|
|
|
|
type = (TypeFullDescriptor)type.UnderlyingType.MakeGenericType(args);
|
|
}
|
|
}
|
|
|
|
if(Helpers.ContainsGenericArguments(type.UnderlyingType))
|
|
{
|
|
var ranks = reader.ReadArray();
|
|
if(ranks.Length > 0)
|
|
{
|
|
var arrayDescriptor = new ArrayDescriptor(type.UnderlyingType, ranks);
|
|
type = (TypeFullDescriptor)arrayDescriptor.BuildArrayType();
|
|
}
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
private bool TryTouchObject(Type actualType, int refId)
|
|
{
|
|
if(refId < deserializedObjects.Count)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
var created = CreateObjectUsingReflection(actualType, treatCollectionAsUserObject);
|
|
if(created != null)
|
|
{
|
|
SetObjectByReferenceId(refId, created);
|
|
}
|
|
return created != null;
|
|
}
|
|
|
|
internal static object CreateObjectUsingReflection(Type type, bool treatCollectionAsUserObject)
|
|
{
|
|
object result = null;
|
|
switch(GetCreationWay(type, treatCollectionAsUserObject))
|
|
{
|
|
case CreationWay.Null:
|
|
break;
|
|
case CreationWay.DefaultCtor:
|
|
result = Activator.CreateInstance(type, true);
|
|
break;
|
|
case CreationWay.Uninitialized:
|
|
result = FormatterServices.GetUninitializedObject(type);
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
internal static CreationWay GetCreationWay(Type actualType, bool treatCollectionAsUserObject)
|
|
{
|
|
if(Helpers.CanBeCreatedWithDataOnly(actualType))
|
|
{
|
|
return CreationWay.Null;
|
|
}
|
|
if(!treatCollectionAsUserObject && CollectionMetaToken.IsCollection(actualType))
|
|
{
|
|
return CreationWay.DefaultCtor;
|
|
}
|
|
if(typeof(ISpeciallySerializable).IsAssignableFrom(actualType))
|
|
{
|
|
return CreationWay.DefaultCtor;
|
|
}
|
|
return CreationWay.Uninitialized;
|
|
}
|
|
|
|
internal bool TreatCollectionAsUserObject { get { return treatCollectionAsUserObject; } }
|
|
internal PrimitiveReader PrimitiveReader { get { return reader; } }
|
|
internal IdentifiedElementsList<ModuleDescriptor> Modules { get; private set; }
|
|
internal IdentifiedElementsList<AssemblyDescriptor> Assemblies { get; private set; }
|
|
internal IdentifiedElementsList<MethodDescriptor> Methods { get; private set; }
|
|
internal VersionToleranceLevel VersionToleranceLevel { get; private set; }
|
|
internal bool ForceStampVerification { get; private set; }
|
|
|
|
internal const string LateHookAndSurrogateError = "Type {0}: late post deserialization callback cannot be used in conjunction with surrogates.";
|
|
|
|
internal readonly Action<object> postDeserializationCallback;
|
|
internal readonly List<Action> latePostDeserializationHooks;
|
|
internal readonly SwapList objectsForSurrogates;
|
|
internal AutoResizingList<object> deserializedObjects;
|
|
internal readonly OneToOneMap<int, object> surrogatesWhileReading;
|
|
internal readonly Serializer.ReadMethods readMethods;
|
|
internal int objectsWrittenInlineCount;
|
|
|
|
private readonly Func<TypeDescriptor> readTypeMethod;
|
|
private List<TypeDescriptor> types;
|
|
private WeakReference[] soFarDeserialized;
|
|
private readonly bool treatCollectionAsUserObject;
|
|
private ReferencePreservation referencePreservation;
|
|
private PrimitiveReader reader;
|
|
|
|
private const int InitialCapacity = 128;
|
|
private const string InternalErrorMessage = "Internal error: should not reach here.";
|
|
private const string CouldNotFindAddErrorMessage = "Could not find suitable Add method for the type {0}.";
|
|
|
|
internal enum CreationWay
|
|
{
|
|
Uninitialized,
|
|
DefaultCtor,
|
|
Null
|
|
}
|
|
}
|
|
|
|
internal delegate void ReadMethodDelegate(ObjectReader reader, Type type, int objectId);
|
|
internal delegate void CompleteMethodDelegate(ObjectReader reader, int objectId);
|
|
internal delegate void TouchInlinedObjectMethodDelegate(ObjectReader reader, int objectId);
|
|
internal delegate object CreateObjectMethodDelegate();
|
|
|
|
internal delegate void CloneMethodDelegate(object src, object dst);
|
|
}
|
|
|