964 lines
44 KiB
C#
964 lines
44 KiB
C#
//-----------------------------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//-----------------------------------------------------------------------------
|
|
namespace System.Runtime.Serialization
|
|
{
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using System.Reflection.Emit;
|
|
using System.Runtime.Serialization.Diagnostics.Application;
|
|
using System.Security;
|
|
using System.Security.Permissions;
|
|
using System.Xml;
|
|
|
|
#if USE_REFEMIT
|
|
public delegate object XmlFormatClassReaderDelegate(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString[] memberNames, XmlDictionaryString[] memberNamespaces);
|
|
public delegate object XmlFormatCollectionReaderDelegate(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString itemName, XmlDictionaryString itemNamespace, CollectionDataContract collectionContract);
|
|
public delegate void XmlFormatGetOnlyCollectionReaderDelegate(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString itemName, XmlDictionaryString itemNamespace, CollectionDataContract collectionContract);
|
|
|
|
public sealed class XmlFormatReaderGenerator
|
|
#else
|
|
internal delegate object XmlFormatClassReaderDelegate(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString[] memberNames, XmlDictionaryString[] memberNamespaces);
|
|
internal delegate object XmlFormatCollectionReaderDelegate(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString itemName, XmlDictionaryString itemNamespace, CollectionDataContract collectionContract);
|
|
internal delegate void XmlFormatGetOnlyCollectionReaderDelegate(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString itemName, XmlDictionaryString itemNamespace, CollectionDataContract collectionContract);
|
|
|
|
internal sealed class XmlFormatReaderGenerator
|
|
#endif
|
|
{
|
|
[Fx.Tag.SecurityNote(Critical = "Holds instance of CriticalHelper which keeps state that was produced within an assert.")]
|
|
[SecurityCritical]
|
|
CriticalHelper helper;
|
|
|
|
[Fx.Tag.SecurityNote(Critical = "Initializes SecurityCritical field 'helper'.")]
|
|
[SecurityCritical]
|
|
public XmlFormatReaderGenerator()
|
|
{
|
|
helper = new CriticalHelper();
|
|
}
|
|
|
|
[Fx.Tag.SecurityNote(Critical = "Accesses SecurityCritical helper class 'CriticalHelper'.")]
|
|
[SecurityCritical]
|
|
public XmlFormatClassReaderDelegate GenerateClassReader(ClassDataContract classContract)
|
|
{
|
|
try
|
|
{
|
|
if (TD.DCGenReaderStartIsEnabled())
|
|
{
|
|
TD.DCGenReaderStart("Class", classContract.UnderlyingType.FullName);
|
|
}
|
|
|
|
return helper.GenerateClassReader(classContract);
|
|
}
|
|
finally
|
|
{
|
|
if (TD.DCGenReaderStopIsEnabled())
|
|
{
|
|
TD.DCGenReaderStop();
|
|
}
|
|
}
|
|
}
|
|
|
|
[Fx.Tag.SecurityNote(Critical = "Accesses SecurityCritical helper class 'CriticalHelper'.")]
|
|
[SecurityCritical]
|
|
public XmlFormatCollectionReaderDelegate GenerateCollectionReader(CollectionDataContract collectionContract)
|
|
{
|
|
try
|
|
{
|
|
if (TD.DCGenReaderStartIsEnabled())
|
|
{
|
|
TD.DCGenReaderStart("Collection", collectionContract.UnderlyingType.FullName);
|
|
}
|
|
|
|
return helper.GenerateCollectionReader(collectionContract);
|
|
}
|
|
finally
|
|
{
|
|
if (TD.DCGenReaderStopIsEnabled())
|
|
{
|
|
TD.DCGenReaderStop();
|
|
}
|
|
}
|
|
}
|
|
|
|
[Fx.Tag.SecurityNote(Critical = "Accesses SecurityCritical helper class 'CriticalHelper'.")]
|
|
[SecurityCritical]
|
|
public XmlFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader(CollectionDataContract collectionContract)
|
|
{
|
|
try
|
|
{
|
|
if (TD.DCGenReaderStartIsEnabled())
|
|
{
|
|
TD.DCGenReaderStart("GetOnlyCollection", collectionContract.UnderlyingType.FullName);
|
|
}
|
|
|
|
return helper.GenerateGetOnlyCollectionReader(collectionContract);
|
|
}
|
|
finally
|
|
{
|
|
if (TD.DCGenReaderStopIsEnabled())
|
|
{
|
|
TD.DCGenReaderStop();
|
|
}
|
|
}
|
|
}
|
|
|
|
[Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Handles all aspects of IL generation including initializing the DynamicMethod."
|
|
+ " Changes to how IL generated could affect how data is deserialized and what gets access to data,"
|
|
+ " therefore we mark it for review so that changes to generation logic are reviewed.")]
|
|
class CriticalHelper
|
|
{
|
|
CodeGenerator ilg;
|
|
LocalBuilder objectLocal;
|
|
Type objectType;
|
|
ArgBuilder xmlReaderArg;
|
|
ArgBuilder contextArg;
|
|
ArgBuilder memberNamesArg;
|
|
ArgBuilder memberNamespacesArg;
|
|
ArgBuilder collectionContractArg;
|
|
|
|
public XmlFormatClassReaderDelegate GenerateClassReader(ClassDataContract classContract)
|
|
{
|
|
ilg = new CodeGenerator();
|
|
bool memberAccessFlag = classContract.RequiresMemberAccessForRead(null);
|
|
try
|
|
{
|
|
ilg.BeginMethod("Read" + classContract.StableName.Name + "FromXml", Globals.TypeOfXmlFormatClassReaderDelegate, memberAccessFlag);
|
|
}
|
|
catch (SecurityException securityException)
|
|
{
|
|
if (memberAccessFlag && securityException.PermissionType.Equals(typeof(ReflectionPermission)))
|
|
{
|
|
classContract.RequiresMemberAccessForRead(securityException);
|
|
}
|
|
else
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
|
|
InitArgs();
|
|
DemandSerializationFormatterPermission(classContract);
|
|
DemandMemberAccessPermission(memberAccessFlag);
|
|
CreateObject(classContract);
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.AddNewObjectMethod, objectLocal);
|
|
InvokeOnDeserializing(classContract);
|
|
LocalBuilder objectId = null;
|
|
if (HasFactoryMethod(classContract))
|
|
{
|
|
objectId = ilg.DeclareLocal(Globals.TypeOfString, "objectIdRead");
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.GetObjectIdMethod);
|
|
ilg.Stloc(objectId);
|
|
}
|
|
if (classContract.IsISerializable)
|
|
ReadISerializable(classContract);
|
|
else
|
|
ReadClass(classContract);
|
|
bool isFactoryType = InvokeFactoryMethod(classContract, objectId);
|
|
if (Globals.TypeOfIDeserializationCallback.IsAssignableFrom(classContract.UnderlyingType))
|
|
ilg.Call(objectLocal, XmlFormatGeneratorStatics.OnDeserializationMethod, null);
|
|
InvokeOnDeserialized(classContract);
|
|
if (objectId == null || !isFactoryType)
|
|
{
|
|
ilg.Load(objectLocal);
|
|
|
|
// Do a conversion back from DateTimeOffsetAdapter to DateTimeOffset after deserialization.
|
|
// DateTimeOffsetAdapter is used here for deserialization purposes to bypass the ISerializable implementation
|
|
// on DateTimeOffset; which does not work in partial trust.
|
|
|
|
if (classContract.UnderlyingType == Globals.TypeOfDateTimeOffsetAdapter)
|
|
{
|
|
ilg.ConvertValue(objectLocal.LocalType, Globals.TypeOfDateTimeOffsetAdapter);
|
|
ilg.Call(XmlFormatGeneratorStatics.GetDateTimeOffsetMethod);
|
|
ilg.ConvertValue(Globals.TypeOfDateTimeOffset, ilg.CurrentMethod.ReturnType);
|
|
}
|
|
else
|
|
{
|
|
ilg.ConvertValue(objectLocal.LocalType, ilg.CurrentMethod.ReturnType);
|
|
}
|
|
}
|
|
return (XmlFormatClassReaderDelegate)ilg.EndMethod();
|
|
}
|
|
|
|
public XmlFormatCollectionReaderDelegate GenerateCollectionReader(CollectionDataContract collectionContract)
|
|
{
|
|
ilg = GenerateCollectionReaderHelper(collectionContract, false /*isGetOnlyCollection*/);
|
|
ReadCollection(collectionContract);
|
|
ilg.Load(objectLocal);
|
|
ilg.ConvertValue(objectLocal.LocalType, ilg.CurrentMethod.ReturnType);
|
|
return (XmlFormatCollectionReaderDelegate)ilg.EndMethod();
|
|
}
|
|
|
|
public XmlFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader(CollectionDataContract collectionContract)
|
|
{
|
|
ilg = GenerateCollectionReaderHelper(collectionContract, true /*isGetOnlyCollection*/);
|
|
ReadGetOnlyCollection(collectionContract);
|
|
return (XmlFormatGetOnlyCollectionReaderDelegate)ilg.EndMethod();
|
|
}
|
|
|
|
CodeGenerator GenerateCollectionReaderHelper(CollectionDataContract collectionContract, bool isGetOnlyCollection)
|
|
{
|
|
ilg = new CodeGenerator();
|
|
bool memberAccessFlag = collectionContract.RequiresMemberAccessForRead(null);
|
|
try
|
|
{
|
|
if (isGetOnlyCollection)
|
|
{
|
|
ilg.BeginMethod("Read" + collectionContract.StableName.Name + "FromXml" + "IsGetOnly", Globals.TypeOfXmlFormatGetOnlyCollectionReaderDelegate, memberAccessFlag);
|
|
}
|
|
else
|
|
{
|
|
ilg.BeginMethod("Read" + collectionContract.StableName.Name + "FromXml" + string.Empty, Globals.TypeOfXmlFormatCollectionReaderDelegate, memberAccessFlag);
|
|
}
|
|
}
|
|
catch (SecurityException securityException)
|
|
{
|
|
if (memberAccessFlag && securityException.PermissionType.Equals(typeof(ReflectionPermission)))
|
|
{
|
|
collectionContract.RequiresMemberAccessForRead(securityException);
|
|
}
|
|
else
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
InitArgs();
|
|
DemandMemberAccessPermission(memberAccessFlag);
|
|
collectionContractArg = ilg.GetArg(4);
|
|
return ilg;
|
|
}
|
|
|
|
void InitArgs()
|
|
{
|
|
xmlReaderArg = ilg.GetArg(0);
|
|
contextArg = ilg.GetArg(1);
|
|
memberNamesArg = ilg.GetArg(2);
|
|
memberNamespacesArg = ilg.GetArg(3);
|
|
}
|
|
|
|
void DemandMemberAccessPermission(bool memberAccessFlag)
|
|
{
|
|
if (memberAccessFlag)
|
|
{
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.DemandMemberAccessPermissionMethod);
|
|
}
|
|
}
|
|
|
|
void DemandSerializationFormatterPermission(ClassDataContract classContract)
|
|
{
|
|
if (!classContract.HasDataContract && !classContract.IsNonAttributedType)
|
|
{
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.DemandSerializationFormatterPermissionMethod);
|
|
}
|
|
}
|
|
|
|
void CreateObject(ClassDataContract classContract)
|
|
{
|
|
Type type = objectType = classContract.UnderlyingType;
|
|
if (type.IsValueType && !classContract.IsNonAttributedType)
|
|
type = Globals.TypeOfValueType;
|
|
|
|
objectLocal = ilg.DeclareLocal(type, "objectDeserialized");
|
|
|
|
if (classContract.UnderlyingType == Globals.TypeOfDBNull)
|
|
{
|
|
ilg.LoadMember(Globals.TypeOfDBNull.GetField("Value"));
|
|
ilg.Stloc(objectLocal);
|
|
}
|
|
else if (classContract.IsNonAttributedType)
|
|
{
|
|
if (type.IsValueType)
|
|
{
|
|
ilg.Ldloca(objectLocal);
|
|
ilg.InitObj(type);
|
|
}
|
|
else
|
|
{
|
|
ilg.New(classContract.GetNonAttributedTypeConstructor());
|
|
ilg.Stloc(objectLocal);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ilg.Call(null, XmlFormatGeneratorStatics.GetUninitializedObjectMethod, DataContract.GetIdForInitialization(classContract));
|
|
ilg.ConvertValue(Globals.TypeOfObject, type);
|
|
ilg.Stloc(objectLocal);
|
|
}
|
|
}
|
|
|
|
void InvokeOnDeserializing(ClassDataContract classContract)
|
|
{
|
|
if (classContract.BaseContract != null)
|
|
InvokeOnDeserializing(classContract.BaseContract);
|
|
if (classContract.OnDeserializing != null)
|
|
{
|
|
ilg.LoadAddress(objectLocal);
|
|
ilg.ConvertAddress(objectLocal.LocalType, objectType);
|
|
ilg.Load(contextArg);
|
|
ilg.LoadMember(XmlFormatGeneratorStatics.GetStreamingContextMethod);
|
|
ilg.Call(classContract.OnDeserializing);
|
|
}
|
|
}
|
|
|
|
void InvokeOnDeserialized(ClassDataContract classContract)
|
|
{
|
|
if (classContract.BaseContract != null)
|
|
InvokeOnDeserialized(classContract.BaseContract);
|
|
if (classContract.OnDeserialized != null)
|
|
{
|
|
ilg.LoadAddress(objectLocal);
|
|
ilg.ConvertAddress(objectLocal.LocalType, objectType);
|
|
ilg.Load(contextArg);
|
|
ilg.LoadMember(XmlFormatGeneratorStatics.GetStreamingContextMethod);
|
|
ilg.Call(classContract.OnDeserialized);
|
|
}
|
|
}
|
|
|
|
bool HasFactoryMethod(ClassDataContract classContract)
|
|
{
|
|
return Globals.TypeOfIObjectReference.IsAssignableFrom(classContract.UnderlyingType);
|
|
}
|
|
|
|
bool InvokeFactoryMethod(ClassDataContract classContract, LocalBuilder objectId)
|
|
{
|
|
if (HasFactoryMethod(classContract))
|
|
{
|
|
ilg.Load(contextArg);
|
|
ilg.LoadAddress(objectLocal);
|
|
ilg.ConvertAddress(objectLocal.LocalType, Globals.TypeOfIObjectReference);
|
|
ilg.Load(objectId);
|
|
ilg.Call(XmlFormatGeneratorStatics.GetRealObjectMethod);
|
|
ilg.ConvertValue(Globals.TypeOfObject, ilg.CurrentMethod.ReturnType);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ReadClass(ClassDataContract classContract)
|
|
{
|
|
if (classContract.HasExtensionData)
|
|
{
|
|
LocalBuilder extensionDataLocal = ilg.DeclareLocal(Globals.TypeOfExtensionDataObject, "extensionData");
|
|
ilg.New(XmlFormatGeneratorStatics.ExtensionDataObjectCtor);
|
|
ilg.Store(extensionDataLocal);
|
|
ReadMembers(classContract, extensionDataLocal);
|
|
|
|
ClassDataContract currentContract = classContract;
|
|
while (currentContract != null)
|
|
{
|
|
MethodInfo extensionDataSetMethod = currentContract.ExtensionDataSetMethod;
|
|
if (extensionDataSetMethod != null)
|
|
ilg.Call(objectLocal, extensionDataSetMethod, extensionDataLocal);
|
|
currentContract = currentContract.BaseContract;
|
|
}
|
|
}
|
|
else
|
|
ReadMembers(classContract, null /*extensionDataLocal*/);
|
|
}
|
|
|
|
void ReadMembers(ClassDataContract classContract, LocalBuilder extensionDataLocal)
|
|
{
|
|
int memberCount = classContract.MemberNames.Length;
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.IncrementItemCountMethod, memberCount);
|
|
|
|
LocalBuilder memberIndexLocal = ilg.DeclareLocal(Globals.TypeOfInt, "memberIndex", -1);
|
|
|
|
int firstRequiredMember;
|
|
bool[] requiredMembers = GetRequiredMembers(classContract, out firstRequiredMember);
|
|
bool hasRequiredMembers = (firstRequiredMember < memberCount);
|
|
LocalBuilder requiredIndexLocal = hasRequiredMembers ? ilg.DeclareLocal(Globals.TypeOfInt, "requiredIndex", firstRequiredMember) : null;
|
|
|
|
object forReadElements = ilg.For(null, null, null);
|
|
ilg.Call(null, XmlFormatGeneratorStatics.MoveToNextElementMethod, xmlReaderArg);
|
|
ilg.IfFalseBreak(forReadElements);
|
|
if (hasRequiredMembers)
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.GetMemberIndexWithRequiredMembersMethod, xmlReaderArg, memberNamesArg, memberNamespacesArg, memberIndexLocal, requiredIndexLocal, extensionDataLocal);
|
|
else
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.GetMemberIndexMethod, xmlReaderArg, memberNamesArg, memberNamespacesArg, memberIndexLocal, extensionDataLocal);
|
|
if (memberCount > 0)
|
|
{
|
|
Label[] memberLabels = ilg.Switch(memberCount);
|
|
ReadMembers(classContract, requiredMembers, memberLabels, memberIndexLocal, requiredIndexLocal);
|
|
ilg.EndSwitch();
|
|
}
|
|
else
|
|
{
|
|
ilg.Pop();
|
|
}
|
|
ilg.EndFor();
|
|
if (hasRequiredMembers)
|
|
{
|
|
ilg.If(requiredIndexLocal, Cmp.LessThan, memberCount);
|
|
ilg.Call(null, XmlFormatGeneratorStatics.ThrowRequiredMemberMissingExceptionMethod, xmlReaderArg, memberIndexLocal, requiredIndexLocal, memberNamesArg);
|
|
ilg.EndIf();
|
|
}
|
|
}
|
|
|
|
int ReadMembers(ClassDataContract classContract, bool[] requiredMembers, Label[] memberLabels, LocalBuilder memberIndexLocal, LocalBuilder requiredIndexLocal)
|
|
{
|
|
int memberCount = (classContract.BaseContract == null) ? 0 : ReadMembers(classContract.BaseContract, requiredMembers,
|
|
memberLabels, memberIndexLocal, requiredIndexLocal);
|
|
|
|
for (int i = 0; i < classContract.Members.Count; i++, memberCount++)
|
|
{
|
|
DataMember dataMember = classContract.Members[i];
|
|
Type memberType = dataMember.MemberType;
|
|
ilg.Case(memberLabels[memberCount], dataMember.Name);
|
|
if (dataMember.IsRequired)
|
|
{
|
|
int nextRequiredIndex = memberCount + 1;
|
|
for (; nextRequiredIndex < requiredMembers.Length; nextRequiredIndex++)
|
|
if (requiredMembers[nextRequiredIndex])
|
|
break;
|
|
ilg.Set(requiredIndexLocal, nextRequiredIndex);
|
|
}
|
|
|
|
LocalBuilder value = null;
|
|
|
|
if (dataMember.IsGetOnlyCollection)
|
|
{
|
|
ilg.LoadAddress(objectLocal);
|
|
ilg.LoadMember(dataMember.MemberInfo);
|
|
value = ilg.DeclareLocal(memberType, dataMember.Name + "Value");
|
|
ilg.Stloc(value);
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.StoreCollectionMemberInfoMethod, value);
|
|
ReadValue(memberType, dataMember.Name, classContract.StableName.Namespace);
|
|
}
|
|
else
|
|
{
|
|
value = ReadValue(memberType, dataMember.Name, classContract.StableName.Namespace);
|
|
ilg.LoadAddress(objectLocal);
|
|
ilg.ConvertAddress(objectLocal.LocalType, objectType);
|
|
ilg.Ldloc(value);
|
|
ilg.StoreMember(dataMember.MemberInfo);
|
|
}
|
|
ilg.Set(memberIndexLocal, memberCount);
|
|
ilg.EndCase();
|
|
}
|
|
return memberCount;
|
|
}
|
|
|
|
bool[] GetRequiredMembers(ClassDataContract contract, out int firstRequiredMember)
|
|
{
|
|
int memberCount = contract.MemberNames.Length;
|
|
bool[] requiredMembers = new bool[memberCount];
|
|
GetRequiredMembers(contract, requiredMembers);
|
|
for (firstRequiredMember = 0; firstRequiredMember < memberCount; firstRequiredMember++)
|
|
if (requiredMembers[firstRequiredMember])
|
|
break;
|
|
return requiredMembers;
|
|
}
|
|
|
|
int GetRequiredMembers(ClassDataContract contract, bool[] requiredMembers)
|
|
{
|
|
int memberCount = (contract.BaseContract == null) ? 0 : GetRequiredMembers(contract.BaseContract, requiredMembers);
|
|
List<DataMember> members = contract.Members;
|
|
for (int i = 0; i < members.Count; i++, memberCount++)
|
|
{
|
|
requiredMembers[memberCount] = members[i].IsRequired;
|
|
}
|
|
return memberCount;
|
|
}
|
|
|
|
void ReadISerializable(ClassDataContract classContract)
|
|
{
|
|
ConstructorInfo ctor = classContract.GetISerializableConstructor();
|
|
ilg.LoadAddress(objectLocal);
|
|
ilg.ConvertAddress(objectLocal.LocalType, objectType);
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.ReadSerializationInfoMethod, xmlReaderArg, classContract.UnderlyingType);
|
|
ilg.Load(contextArg);
|
|
ilg.LoadMember(XmlFormatGeneratorStatics.GetStreamingContextMethod);
|
|
ilg.Call(ctor);
|
|
}
|
|
|
|
LocalBuilder ReadValue(Type type, string name, string ns)
|
|
{
|
|
LocalBuilder value = ilg.DeclareLocal(type, "valueRead");
|
|
LocalBuilder nullableValue = null;
|
|
int nullables = 0;
|
|
while (type.IsGenericType && type.GetGenericTypeDefinition() == Globals.TypeOfNullable)
|
|
{
|
|
nullables++;
|
|
type = type.GetGenericArguments()[0];
|
|
}
|
|
|
|
PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(type);
|
|
if ((primitiveContract != null && primitiveContract.UnderlyingType != Globals.TypeOfObject) || nullables != 0 || type.IsValueType)
|
|
{
|
|
LocalBuilder objectId = ilg.DeclareLocal(Globals.TypeOfString, "objectIdRead");
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.ReadAttributesMethod, xmlReaderArg);
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.ReadIfNullOrRefMethod, xmlReaderArg, type, DataContract.IsTypeSerializable(type));
|
|
ilg.Stloc(objectId);
|
|
// Deserialize null
|
|
ilg.If(objectId, Cmp.EqualTo, Globals.NullObjectId);
|
|
if (nullables != 0)
|
|
{
|
|
ilg.LoadAddress(value);
|
|
ilg.InitObj(value.LocalType);
|
|
}
|
|
else if (type.IsValueType)
|
|
ThrowValidationException(SR.GetString(SR.ValueTypeCannotBeNull, DataContract.GetClrTypeFullName(type)));
|
|
else
|
|
{
|
|
ilg.Load(null);
|
|
ilg.Stloc(value);
|
|
}
|
|
|
|
// Deserialize value
|
|
|
|
// Compare against Globals.NewObjectId, which is set to string.Empty
|
|
ilg.ElseIfIsEmptyString(objectId);
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.GetObjectIdMethod);
|
|
ilg.Stloc(objectId);
|
|
if (type.IsValueType)
|
|
{
|
|
ilg.IfNotIsEmptyString(objectId);
|
|
ThrowValidationException(SR.GetString(SR.ValueTypeCannotHaveId, DataContract.GetClrTypeFullName(type)));
|
|
ilg.EndIf();
|
|
}
|
|
if (nullables != 0)
|
|
{
|
|
nullableValue = value;
|
|
value = ilg.DeclareLocal(type, "innerValueRead");
|
|
}
|
|
|
|
if (primitiveContract != null && primitiveContract.UnderlyingType != Globals.TypeOfObject)
|
|
{
|
|
ilg.Call(xmlReaderArg, primitiveContract.XmlFormatReaderMethod);
|
|
ilg.Stloc(value);
|
|
if (!type.IsValueType)
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.AddNewObjectMethod, value);
|
|
}
|
|
else
|
|
{
|
|
InternalDeserialize(value, type, name, ns);
|
|
}
|
|
// Deserialize ref
|
|
ilg.Else();
|
|
if (type.IsValueType)
|
|
ThrowValidationException(SR.GetString(SR.ValueTypeCannotHaveRef, DataContract.GetClrTypeFullName(type)));
|
|
else
|
|
{
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.GetExistingObjectMethod, objectId, type, name, ns);
|
|
ilg.ConvertValue(Globals.TypeOfObject, type);
|
|
ilg.Stloc(value);
|
|
}
|
|
ilg.EndIf();
|
|
|
|
if (nullableValue != null)
|
|
{
|
|
ilg.If(objectId, Cmp.NotEqualTo, Globals.NullObjectId);
|
|
WrapNullableObject(value, nullableValue, nullables);
|
|
ilg.EndIf();
|
|
value = nullableValue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
InternalDeserialize(value, type, name, ns);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
void InternalDeserialize(LocalBuilder value, Type type, string name, string ns)
|
|
{
|
|
ilg.Load(contextArg);
|
|
ilg.Load(xmlReaderArg);
|
|
Type declaredType = type.IsPointer ? Globals.TypeOfReflectionPointer : type;
|
|
ilg.Load(DataContract.GetId(declaredType.TypeHandle));
|
|
ilg.Ldtoken(declaredType);
|
|
ilg.Load(name);
|
|
ilg.Load(ns);
|
|
ilg.Call(XmlFormatGeneratorStatics.InternalDeserializeMethod);
|
|
|
|
if (type.IsPointer)
|
|
ilg.Call(XmlFormatGeneratorStatics.UnboxPointer);
|
|
else
|
|
ilg.ConvertValue(Globals.TypeOfObject, type);
|
|
ilg.Stloc(value);
|
|
}
|
|
|
|
void WrapNullableObject(LocalBuilder innerValue, LocalBuilder outerValue, int nullables)
|
|
{
|
|
Type innerType = innerValue.LocalType, outerType = outerValue.LocalType;
|
|
ilg.LoadAddress(outerValue);
|
|
ilg.Load(innerValue);
|
|
for (int i = 1; i < nullables; i++)
|
|
{
|
|
Type type = Globals.TypeOfNullable.MakeGenericType(innerType);
|
|
ilg.New(type.GetConstructor(new Type[] { innerType }));
|
|
innerType = type;
|
|
}
|
|
ilg.Call(outerType.GetConstructor(new Type[] { innerType }));
|
|
}
|
|
|
|
void ReadCollection(CollectionDataContract collectionContract)
|
|
{
|
|
Type type = collectionContract.UnderlyingType;
|
|
Type itemType = collectionContract.ItemType;
|
|
bool isArray = (collectionContract.Kind == CollectionKind.Array);
|
|
|
|
ConstructorInfo constructor = collectionContract.Constructor;
|
|
|
|
if (type.IsInterface)
|
|
{
|
|
switch (collectionContract.Kind)
|
|
{
|
|
case CollectionKind.GenericDictionary:
|
|
type = Globals.TypeOfDictionaryGeneric.MakeGenericType(itemType.GetGenericArguments());
|
|
constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, Globals.EmptyTypeArray, null);
|
|
break;
|
|
case CollectionKind.Dictionary:
|
|
type = Globals.TypeOfHashtable;
|
|
constructor = XmlFormatGeneratorStatics.HashtableCtor;
|
|
break;
|
|
case CollectionKind.Collection:
|
|
case CollectionKind.GenericCollection:
|
|
case CollectionKind.Enumerable:
|
|
case CollectionKind.GenericEnumerable:
|
|
case CollectionKind.List:
|
|
case CollectionKind.GenericList:
|
|
type = itemType.MakeArrayType();
|
|
isArray = true;
|
|
break;
|
|
}
|
|
}
|
|
string itemName = collectionContract.ItemName;
|
|
string itemNs = collectionContract.StableName.Namespace;
|
|
|
|
objectLocal = ilg.DeclareLocal(type, "objectDeserialized");
|
|
if (!isArray)
|
|
{
|
|
if (type.IsValueType)
|
|
{
|
|
ilg.Ldloca(objectLocal);
|
|
ilg.InitObj(type);
|
|
}
|
|
else
|
|
{
|
|
ilg.New(constructor);
|
|
ilg.Stloc(objectLocal);
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.AddNewObjectMethod, objectLocal);
|
|
}
|
|
}
|
|
|
|
LocalBuilder size = ilg.DeclareLocal(Globals.TypeOfInt, "arraySize");
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.GetArraySizeMethod);
|
|
ilg.Stloc(size);
|
|
|
|
LocalBuilder objectId = ilg.DeclareLocal(Globals.TypeOfString, "objectIdRead");
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.GetObjectIdMethod);
|
|
ilg.Stloc(objectId);
|
|
|
|
bool canReadPrimitiveArray = false;
|
|
if (isArray && TryReadPrimitiveArray(type, itemType, size))
|
|
{
|
|
canReadPrimitiveArray = true;
|
|
ilg.IfNot();
|
|
}
|
|
|
|
ilg.If(size, Cmp.EqualTo, -1);
|
|
|
|
LocalBuilder growingCollection = null;
|
|
if (isArray)
|
|
{
|
|
growingCollection = ilg.DeclareLocal(type, "growingCollection");
|
|
ilg.NewArray(itemType, 32);
|
|
ilg.Stloc(growingCollection);
|
|
}
|
|
LocalBuilder i = ilg.DeclareLocal(Globals.TypeOfInt, "i");
|
|
object forLoop = ilg.For(i, 0, Int32.MaxValue);
|
|
IsStartElement(memberNamesArg, memberNamespacesArg);
|
|
ilg.If();
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.IncrementItemCountMethod, 1);
|
|
LocalBuilder value = ReadCollectionItem(collectionContract, itemType, itemName, itemNs);
|
|
if (isArray)
|
|
{
|
|
MethodInfo ensureArraySizeMethod = XmlFormatGeneratorStatics.EnsureArraySizeMethod.MakeGenericMethod(itemType);
|
|
ilg.Call(null, ensureArraySizeMethod, growingCollection, i);
|
|
ilg.Stloc(growingCollection);
|
|
ilg.StoreArrayElement(growingCollection, i, value);
|
|
}
|
|
else
|
|
StoreCollectionValue(objectLocal, value, collectionContract);
|
|
ilg.Else();
|
|
IsEndElement();
|
|
ilg.If();
|
|
ilg.Break(forLoop);
|
|
ilg.Else();
|
|
HandleUnexpectedItemInCollection(i);
|
|
ilg.EndIf();
|
|
ilg.EndIf();
|
|
|
|
ilg.EndFor();
|
|
if (isArray)
|
|
{
|
|
MethodInfo trimArraySizeMethod = XmlFormatGeneratorStatics.TrimArraySizeMethod.MakeGenericMethod(itemType);
|
|
ilg.Call(null, trimArraySizeMethod, growingCollection, i);
|
|
ilg.Stloc(objectLocal);
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.AddNewObjectWithIdMethod, objectId, objectLocal);
|
|
}
|
|
ilg.Else();
|
|
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.IncrementItemCountMethod, size);
|
|
if (isArray)
|
|
{
|
|
ilg.NewArray(itemType, size);
|
|
ilg.Stloc(objectLocal);
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.AddNewObjectMethod, objectLocal);
|
|
}
|
|
LocalBuilder j = ilg.DeclareLocal(Globals.TypeOfInt, "j");
|
|
ilg.For(j, 0, size);
|
|
IsStartElement(memberNamesArg, memberNamespacesArg);
|
|
ilg.If();
|
|
LocalBuilder itemValue = ReadCollectionItem(collectionContract, itemType, itemName, itemNs);
|
|
if (isArray)
|
|
ilg.StoreArrayElement(objectLocal, j, itemValue);
|
|
else
|
|
StoreCollectionValue(objectLocal, itemValue, collectionContract);
|
|
ilg.Else();
|
|
HandleUnexpectedItemInCollection(j);
|
|
ilg.EndIf();
|
|
ilg.EndFor();
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.CheckEndOfArrayMethod, xmlReaderArg, size, memberNamesArg, memberNamespacesArg);
|
|
ilg.EndIf();
|
|
|
|
if (canReadPrimitiveArray)
|
|
{
|
|
ilg.Else();
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.AddNewObjectWithIdMethod, objectId, objectLocal);
|
|
ilg.EndIf();
|
|
}
|
|
}
|
|
|
|
void ReadGetOnlyCollection(CollectionDataContract collectionContract)
|
|
{
|
|
Type type = collectionContract.UnderlyingType;
|
|
Type itemType = collectionContract.ItemType;
|
|
bool isArray = (collectionContract.Kind == CollectionKind.Array);
|
|
string itemName = collectionContract.ItemName;
|
|
string itemNs = collectionContract.StableName.Namespace;
|
|
|
|
objectLocal = ilg.DeclareLocal(type, "objectDeserialized");
|
|
ilg.Load(contextArg);
|
|
ilg.LoadMember(XmlFormatGeneratorStatics.GetCollectionMemberMethod);
|
|
ilg.ConvertValue(Globals.TypeOfObject, type);
|
|
ilg.Stloc(objectLocal);
|
|
|
|
//check that items are actually going to be deserialized into the collection
|
|
IsStartElement(memberNamesArg, memberNamespacesArg);
|
|
ilg.If();
|
|
ilg.If(objectLocal, Cmp.EqualTo, null);
|
|
ilg.Call(null, XmlFormatGeneratorStatics.ThrowNullValueReturnedForGetOnlyCollectionExceptionMethod, type);
|
|
|
|
ilg.Else();
|
|
LocalBuilder size = ilg.DeclareLocal(Globals.TypeOfInt, "arraySize");
|
|
if (isArray)
|
|
{
|
|
ilg.Load(objectLocal);
|
|
ilg.Call(XmlFormatGeneratorStatics.GetArrayLengthMethod);
|
|
ilg.Stloc(size);
|
|
}
|
|
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.AddNewObjectMethod, objectLocal);
|
|
|
|
LocalBuilder i = ilg.DeclareLocal(Globals.TypeOfInt, "i");
|
|
object forLoop = ilg.For(i, 0, Int32.MaxValue);
|
|
IsStartElement(memberNamesArg, memberNamespacesArg);
|
|
ilg.If();
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.IncrementItemCountMethod, 1);
|
|
LocalBuilder value = ReadCollectionItem(collectionContract, itemType, itemName, itemNs);
|
|
if (isArray)
|
|
{
|
|
ilg.If(size, Cmp.EqualTo, i);
|
|
ilg.Call(null, XmlFormatGeneratorStatics.ThrowArrayExceededSizeExceptionMethod, size, type);
|
|
ilg.Else();
|
|
ilg.StoreArrayElement(objectLocal, i, value);
|
|
ilg.EndIf();
|
|
}
|
|
else
|
|
StoreCollectionValue(objectLocal, value, collectionContract);
|
|
ilg.Else();
|
|
IsEndElement();
|
|
ilg.If();
|
|
ilg.Break(forLoop);
|
|
ilg.Else();
|
|
HandleUnexpectedItemInCollection(i);
|
|
ilg.EndIf();
|
|
ilg.EndIf();
|
|
ilg.EndFor();
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.CheckEndOfArrayMethod, xmlReaderArg, size, memberNamesArg, memberNamespacesArg);
|
|
|
|
ilg.EndIf();
|
|
ilg.EndIf();
|
|
}
|
|
|
|
bool TryReadPrimitiveArray(Type type, Type itemType, LocalBuilder size)
|
|
{
|
|
PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(itemType);
|
|
if (primitiveContract == null)
|
|
return false;
|
|
|
|
string readArrayMethod = null;
|
|
switch (Type.GetTypeCode(itemType))
|
|
{
|
|
case TypeCode.Boolean:
|
|
readArrayMethod = "TryReadBooleanArray";
|
|
break;
|
|
case TypeCode.DateTime:
|
|
readArrayMethod = "TryReadDateTimeArray";
|
|
break;
|
|
case TypeCode.Decimal:
|
|
readArrayMethod = "TryReadDecimalArray";
|
|
break;
|
|
case TypeCode.Int32:
|
|
readArrayMethod = "TryReadInt32Array";
|
|
break;
|
|
case TypeCode.Int64:
|
|
readArrayMethod = "TryReadInt64Array";
|
|
break;
|
|
case TypeCode.Single:
|
|
readArrayMethod = "TryReadSingleArray";
|
|
break;
|
|
case TypeCode.Double:
|
|
readArrayMethod = "TryReadDoubleArray";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (readArrayMethod != null)
|
|
{
|
|
ilg.Load(xmlReaderArg);
|
|
ilg.Load(contextArg);
|
|
ilg.Load(memberNamesArg);
|
|
ilg.Load(memberNamespacesArg);
|
|
ilg.Load(size);
|
|
ilg.Ldloca(objectLocal);
|
|
ilg.Call(typeof(XmlReaderDelegator).GetMethod(readArrayMethod, Globals.ScanAllMembers));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
LocalBuilder ReadCollectionItem(CollectionDataContract collectionContract, Type itemType, string itemName, string itemNs)
|
|
{
|
|
if (collectionContract.Kind == CollectionKind.Dictionary || collectionContract.Kind == CollectionKind.GenericDictionary)
|
|
{
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.ResetAttributesMethod);
|
|
LocalBuilder value = ilg.DeclareLocal(itemType, "valueRead");
|
|
ilg.Load(collectionContractArg);
|
|
ilg.Call(XmlFormatGeneratorStatics.GetItemContractMethod);
|
|
ilg.Load(xmlReaderArg);
|
|
ilg.Load(contextArg);
|
|
ilg.Call(XmlFormatGeneratorStatics.ReadXmlValueMethod);
|
|
ilg.ConvertValue(Globals.TypeOfObject, itemType);
|
|
ilg.Stloc(value);
|
|
return value;
|
|
}
|
|
else
|
|
{
|
|
return ReadValue(itemType, itemName, itemNs);
|
|
}
|
|
}
|
|
|
|
void StoreCollectionValue(LocalBuilder collection, LocalBuilder value, CollectionDataContract collectionContract)
|
|
{
|
|
if (collectionContract.Kind == CollectionKind.GenericDictionary || collectionContract.Kind == CollectionKind.Dictionary)
|
|
{
|
|
ClassDataContract keyValuePairContract = DataContract.GetDataContract(value.LocalType) as ClassDataContract;
|
|
if (keyValuePairContract == null)
|
|
{
|
|
Fx.Assert("Failed to create contract for KeyValuePair type");
|
|
}
|
|
DataMember keyMember = keyValuePairContract.Members[0];
|
|
DataMember valueMember = keyValuePairContract.Members[1];
|
|
LocalBuilder pairKey = ilg.DeclareLocal(keyMember.MemberType, keyMember.Name);
|
|
LocalBuilder pairValue = ilg.DeclareLocal(valueMember.MemberType, valueMember.Name);
|
|
ilg.LoadAddress(value);
|
|
ilg.LoadMember(keyMember.MemberInfo);
|
|
ilg.Stloc(pairKey);
|
|
ilg.LoadAddress(value);
|
|
ilg.LoadMember(valueMember.MemberInfo);
|
|
ilg.Stloc(pairValue);
|
|
|
|
ilg.Call(collection, collectionContract.AddMethod, pairKey, pairValue);
|
|
if (collectionContract.AddMethod.ReturnType != Globals.TypeOfVoid)
|
|
ilg.Pop();
|
|
}
|
|
else
|
|
{
|
|
ilg.Call(collection, collectionContract.AddMethod, value);
|
|
if (collectionContract.AddMethod.ReturnType != Globals.TypeOfVoid)
|
|
ilg.Pop();
|
|
}
|
|
}
|
|
|
|
void HandleUnexpectedItemInCollection(LocalBuilder iterator)
|
|
{
|
|
IsStartElement();
|
|
ilg.If();
|
|
ilg.Call(contextArg, XmlFormatGeneratorStatics.SkipUnknownElementMethod, xmlReaderArg);
|
|
ilg.Dec(iterator);
|
|
ilg.Else();
|
|
ThrowUnexpectedStateException(XmlNodeType.Element);
|
|
ilg.EndIf();
|
|
}
|
|
|
|
void IsStartElement(ArgBuilder nameArg, ArgBuilder nsArg)
|
|
{
|
|
ilg.Call(xmlReaderArg, XmlFormatGeneratorStatics.IsStartElementMethod2, nameArg, nsArg);
|
|
}
|
|
|
|
void IsStartElement()
|
|
{
|
|
ilg.Call(xmlReaderArg, XmlFormatGeneratorStatics.IsStartElementMethod0);
|
|
}
|
|
|
|
void IsEndElement()
|
|
{
|
|
ilg.Load(xmlReaderArg);
|
|
ilg.LoadMember(XmlFormatGeneratorStatics.NodeTypeProperty);
|
|
ilg.Load(XmlNodeType.EndElement);
|
|
ilg.Ceq();
|
|
}
|
|
|
|
void ThrowUnexpectedStateException(XmlNodeType expectedState)
|
|
{
|
|
ilg.Call(null, XmlFormatGeneratorStatics.CreateUnexpectedStateExceptionMethod, expectedState, xmlReaderArg);
|
|
ilg.Throw();
|
|
}
|
|
|
|
void ThrowValidationException(string msg, params object[] values)
|
|
{
|
|
if (values != null && values.Length > 0)
|
|
ilg.CallStringFormat(msg, values);
|
|
else
|
|
ilg.Load(msg);
|
|
ThrowValidationException();
|
|
}
|
|
|
|
void ThrowValidationException()
|
|
{
|
|
ilg.New(XmlFormatGeneratorStatics.SerializationExceptionCtor);
|
|
ilg.Throw();
|
|
}
|
|
|
|
}
|
|
|
|
[Fx.Tag.SecurityNote(Critical = "Elevates by calling GetUninitializedObject which has a LinkDemand.",
|
|
Safe = "Marked as such so that it's callable from transparent generated IL. Takes id as parameter which "
|
|
+ " is guaranteed to be in internal serialization cache.")]
|
|
[SecuritySafeCritical]
|
|
#if USE_REFEMIT
|
|
public static object UnsafeGetUninitializedObject(int id)
|
|
#else
|
|
static internal object UnsafeGetUninitializedObject(int id)
|
|
#endif
|
|
{
|
|
return FormatterServices.GetUninitializedObject(DataContract.GetDataContractForInitialization(id).TypeForInitialization);
|
|
}
|
|
}
|
|
}
|
|
|