// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using Microsoft.Xml; using Microsoft.Xml.Schema; using System.Reflection; using System.Reflection.Emit; using System.Collections; using System.Collections.Generic; using System.Security; namespace System.Runtime.Serialization { #if USE_REFEMIT || NET_NATIVE 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 { #if !NET_NATIVE [SecurityCritical] /// <SecurityNote> /// Critical - holds instance of CriticalHelper which keeps state that was produced within an assert /// </SecurityNote> private CriticalHelper _helper; /// <SecurityNote> /// Critical - initializes SecurityCritical field 'helper' /// </SecurityNote> [SecurityCritical] public XmlFormatReaderGenerator() { _helper = new CriticalHelper(); } /// <SecurityNote> /// Critical - accesses SecurityCritical helper class 'CriticalHelper' /// </SecurityNote> [SecurityCritical] public XmlFormatClassReaderDelegate GenerateClassReader(ClassDataContract classContract) { return _helper.GenerateClassReader(classContract); } /// <SecurityNote> /// Critical - accesses SecurityCritical helper class 'CriticalHelper' /// </SecurityNote> [SecurityCritical] public XmlFormatCollectionReaderDelegate GenerateCollectionReader(CollectionDataContract collectionContract) { return _helper.GenerateCollectionReader(collectionContract); } /// <SecurityNote> /// Critical - accesses SecurityCritical helper class 'CriticalHelper' /// </SecurityNote> [SecurityCritical] public XmlFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader(CollectionDataContract collectionContract) { return _helper.GenerateGetOnlyCollectionReader(collectionContract); } /// <SecurityNote> /// Review - 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. /// </SecurityNote> private class CriticalHelper { private CodeGenerator _ilg; private LocalBuilder _objectLocal; private Type _objectType; private ArgBuilder _xmlReaderArg; private ArgBuilder _contextArg; private ArgBuilder _memberNamesArg; private ArgBuilder _memberNamespacesArg; private 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) { classContract.RequiresMemberAccessForRead(securityException); } else { throw; } } InitArgs(); CreateObject(classContract); _ilg.Call(_contextArg, XmlFormatGeneratorStatics.AddNewObjectMethod, _objectLocal); InvokeOnDeserializing(classContract); LocalBuilder objectId = null; ReadClass(classContract); InvokeOnDeserialized(classContract); if (objectId == null) { _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); } //Copy the KeyValuePairAdapter<K,T> to a KeyValuePair<K,T>. else if (classContract.IsKeyValuePairAdapter) { _ilg.Call(classContract.GetKeyValuePairMethodInfo); _ilg.ConvertValue(Globals.TypeOfKeyValuePair.MakeGenericType(classContract.KeyValuePairGenericArguments), _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(); } private 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) { collectionContract.RequiresMemberAccessForRead(securityException); } else { throw; } } InitArgs(); _collectionContractArg = _ilg.GetArg(4); return _ilg; } private void InitArgs() { _xmlReaderArg = _ilg.GetArg(0); _contextArg = _ilg.GetArg(1); _memberNamesArg = _ilg.GetArg(2); _memberNamespacesArg = _ilg.GetArg(3); } private void CreateObject(ClassDataContract classContract) { Type type = _objectType = classContract.UnderlyingType; if (type.GetTypeInfo().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.GetTypeInfo().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); } } private 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); } } private 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); } } private void ReadClass(ClassDataContract classContract) { ReadMembers(classContract, null /*extensionDataLocal*/); } private 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); Label[] memberLabels = _ilg.Switch(memberCount); ReadMembers(classContract, requiredMembers, memberLabels, memberIndexLocal, requiredIndexLocal); _ilg.EndSwitch(); _ilg.EndFor(); if (hasRequiredMembers) { _ilg.If(requiredIndexLocal, Cmp.LessThan, memberCount); _ilg.Call(null, XmlFormatGeneratorStatics.ThrowRequiredMemberMissingExceptionMethod, _xmlReaderArg, memberIndexLocal, requiredIndexLocal, _memberNamesArg); _ilg.EndIf(); } } private 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); } #if FEATURE_LEGACYNETCF // The DataContractSerializer in the full framework doesn't support unordered elements: // deserialization will fail if the data members in the XML are not sorted alphabetically. // But the NetCF DataContractSerializer does support unordered element. To maintain compatibility // with Mango we always search for the member from the beginning of the member list. // We set memberIndexLocal to -1 because GetMemberIndex always starts from memberIndex+1. if (CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) ilg.Set(memberIndexLocal, (int)-1); else #endif // FEATURE_LEGACYNETCF _ilg.Set(memberIndexLocal, memberCount); _ilg.EndCase(); } return memberCount; } private 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; } private 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; } private LocalBuilder ReadValue(Type type, string name, string ns) { LocalBuilder value = _ilg.DeclareLocal(type, "valueRead"); LocalBuilder nullableValue = null; int nullables = 0; while (type.GetTypeInfo().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.GetTypeInfo().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.GetTypeInfo().IsValueType) ThrowValidationException(string.Format(SRSerialization.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.GetTypeInfo().IsValueType) { _ilg.IfNotIsEmptyString(objectId); ThrowValidationException(string.Format(SRSerialization.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.GetTypeInfo().IsValueType) _ilg.Call(_contextArg, XmlFormatGeneratorStatics.AddNewObjectMethod, value); } else { InternalDeserialize(value, type, name, ns); } // Deserialize ref _ilg.Else(); if (type.GetTypeInfo().IsValueType) ThrowValidationException(string.Format(SRSerialization.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; } private void InternalDeserialize(LocalBuilder value, Type type, string name, string ns) { _ilg.Load(_contextArg); _ilg.Load(_xmlReaderArg); Type declaredType = type; _ilg.Load(DataContract.GetId(declaredType.TypeHandle)); _ilg.Ldtoken(declaredType); _ilg.Load(name); _ilg.Load(ns); _ilg.Call(XmlFormatGeneratorStatics.InternalDeserializeMethod); _ilg.ConvertValue(Globals.TypeOfObject, type); _ilg.Stloc(value); } private 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 })); } private void ReadCollection(CollectionDataContract collectionContract) { Type type = collectionContract.UnderlyingType; Type itemType = collectionContract.ItemType; bool isArray = (collectionContract.Kind == CollectionKind.Array); ConstructorInfo constructor = collectionContract.Constructor; if (type.GetTypeInfo().IsInterface) { switch (collectionContract.Kind) { case CollectionKind.GenericDictionary: type = Globals.TypeOfDictionaryGeneric.MakeGenericType(itemType.GetGenericArguments()); constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public, Array.Empty<Type>()); 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.GetTypeInfo().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(); } } private 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(); } private bool TryReadPrimitiveArray(Type type, Type itemType, LocalBuilder size) { PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(itemType); if (primitiveContract == null) return false; string readArrayMethod = null; switch (itemType.GetTypeCode()) { 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; } private 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); } } private 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) { DiagnosticUtility.DebugAssert("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(); } } private void HandleUnexpectedItemInCollection(LocalBuilder iterator) { IsStartElement(); _ilg.If(); _ilg.Call(_contextArg, XmlFormatGeneratorStatics.SkipUnknownElementMethod, _xmlReaderArg); _ilg.Dec(iterator); _ilg.Else(); ThrowUnexpectedStateException(XmlNodeType.Element); _ilg.EndIf(); } private void IsStartElement(ArgBuilder nameArg, ArgBuilder nsArg) { _ilg.Call(_xmlReaderArg, XmlFormatGeneratorStatics.IsStartElementMethod2, nameArg, nsArg); } private void IsStartElement() { _ilg.Call(_xmlReaderArg, XmlFormatGeneratorStatics.IsStartElementMethod0); } private void IsEndElement() { _ilg.Load(_xmlReaderArg); _ilg.LoadMember(XmlFormatGeneratorStatics.NodeTypeProperty); _ilg.Load(XmlNodeType.EndElement); _ilg.Ceq(); } private void ThrowUnexpectedStateException(XmlNodeType expectedState) { _ilg.Call(null, XmlFormatGeneratorStatics.CreateUnexpectedStateExceptionMethod, expectedState, _xmlReaderArg); _ilg.Throw(); } private void ThrowValidationException(string msg, params object[] values) { { _ilg.Load(msg); } ThrowValidationException(); } private void ThrowValidationException() { //SerializationException is internal in SL and so cannot be directly invoked from DynamicMethod //So use helper function to create SerializationException _ilg.Call(XmlFormatGeneratorStatics.CreateSerializationExceptionMethod); _ilg.Throw(); } } #endif [SecuritySafeCritical] static internal object UnsafeGetUninitializedObject(Type type) { throw new NotImplementedException(); } /// <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. /// </SecurityNote> [SecuritySafeCritical] #if USE_REFEMIT public static object UnsafeGetUninitializedObject(int id) #else static internal object UnsafeGetUninitializedObject(int id) #endif { var type = DataContract.GetDataContractForInitialization(id).TypeForInitialization; return UnsafeGetUninitializedObject(type); } static internal object TryGetUninitializedObjectWithFormatterServices(Type type) { object obj = null; var formatterServiceType = typeof(string).GetTypeInfo().Assembly.GetType("System.Runtime.Serialization.FormatterServices"); if (formatterServiceType != null) { var methodInfo = formatterServiceType.GetMethod("GetUninitializedObject", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); if (methodInfo != null) { obj = methodInfo.Invoke(null, new object[] { type }); } } return obj; } } } |