// 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.Diagnostics; namespace System.Runtime.Serialization { using System; using System.Collections; using System.Collections.Generic; using System.Reflection; using System.Threading; using Microsoft.Xml; using System.Linq; using DataContractDictionary = System.Collections.Generic.Dictionary<Microsoft.Xml.XmlQualifiedName, DataContract>; using System.Security; //Special Adapter class to serialize KeyValuePair as Dictionary needs it when polymorphism is involved [DataContract(Namespace = "http://schemas.datacontract.org/2004/07/System.Collections.Generic")] internal class KeyValuePairAdapter<K, T> { private K _kvpKey; private T _kvpValue; public KeyValuePairAdapter(KeyValuePair<K, T> kvPair) { _kvpKey = kvPair.Key; _kvpValue = kvPair.Value; } [DataMember(Name = "key")] public K Key { get { return _kvpKey; } set { _kvpKey = value; } } [DataMember(Name = "value")] public T Value { get { return _kvpValue; } set { _kvpValue = value; } } internal KeyValuePair<K, T> GetKeyValuePair() { return new KeyValuePair<K, T>(_kvpKey, _kvpValue); } internal static KeyValuePairAdapter<K, T> GetKeyValuePairAdapter(KeyValuePair<K, T> kvPair) { return new KeyValuePairAdapter<K, T>(kvPair); } } [DataContract(Namespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays")] #if USE_REFEMIT public struct KeyValue<K, V> #else internal struct KeyValue<K, V> #endif { private K _key; private V _value; internal KeyValue(K key, V value) { _key = key; _value = value; } [DataMember(IsRequired = true)] public K Key { get { return _key; } set { _key = value; } } [DataMember(IsRequired = true)] public V Value { get { return _value; } set { _value = value; } } } #if NET_NATIVE public enum CollectionKind : byte #else internal enum CollectionKind : byte #endif { None, GenericDictionary, Dictionary, GenericList, GenericCollection, List, GenericEnumerable, Collection, Enumerable, Array, } #if USE_REFEMIT || NET_NATIVE public sealed class CollectionDataContract : DataContract #else internal sealed class CollectionDataContract : DataContract #endif { [SecurityCritical] /// <SecurityNote> /// Critical - XmlDictionaryString representing the XML element name for collection items. /// statically cached and used from IL generated code. /// </SecurityNote> private XmlDictionaryString _collectionItemName; [SecurityCritical] /// <SecurityNote> /// Critical - XmlDictionaryString representing the XML namespace for collection items. /// statically cached and used from IL generated code. /// </SecurityNote> private XmlDictionaryString _childElementNamespace; [SecurityCritical] /// <SecurityNote> /// Critical - internal DataContract representing the contract for collection items. /// statically cached and used from IL generated code. /// </SecurityNote> private DataContract _itemContract; [SecurityCritical] /// <SecurityNote> /// Critical - holds instance of CriticalHelper which keeps state that is cached statically for serialization. /// Static fields are marked SecurityCritical or readonly to prevent /// data from being modified or leaked to other components in appdomain. /// </SecurityNote> private CollectionDataContractCriticalHelper _helper; [SecuritySafeCritical] public CollectionDataContract(CollectionKind kind) : base(new CollectionDataContractCriticalHelper(kind)) { InitCollectionDataContract(this); } /// <SecurityNote> /// Critical - initializes SecurityCritical field 'helper' /// Safe - doesn't leak anything /// </SecurityNote> [SecuritySafeCritical] internal CollectionDataContract(Type type) : base(new CollectionDataContractCriticalHelper(type)) { InitCollectionDataContract(this); } [SecuritySafeCritical] /// <SecurityNote> /// Critical - initializes SecurityCritical field 'helper' /// Safe - doesn't leak anything /// </SecurityNote> private CollectionDataContract(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, MethodInfo addMethod, ConstructorInfo constructor) : base(new CollectionDataContractCriticalHelper(type, kind, itemType, getEnumeratorMethod, addMethod, constructor)) { InitCollectionDataContract(GetSharedTypeContract(type)); } [SecuritySafeCritical] /// <SecurityNote> /// Critical - initializes SecurityCritical field 'helper' /// Safe - doesn't leak anything /// </SecurityNote> private CollectionDataContract(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, MethodInfo addMethod, ConstructorInfo constructor, bool isConstructorCheckRequired) : base(new CollectionDataContractCriticalHelper(type, kind, itemType, getEnumeratorMethod, addMethod, constructor, isConstructorCheckRequired)) { InitCollectionDataContract(GetSharedTypeContract(type)); } [SecuritySafeCritical] /// <SecurityNote> /// Critical - initializes SecurityCritical field 'helper' /// Safe - doesn't leak anything /// </SecurityNote> private CollectionDataContract(Type type, string invalidCollectionInSharedContractMessage) : base(new CollectionDataContractCriticalHelper(type, invalidCollectionInSharedContractMessage)) { InitCollectionDataContract(GetSharedTypeContract(type)); } [SecurityCritical] /// <SecurityNote> /// Critical - initializes SecurityCritical fields; called from all constructors /// </SecurityNote> private void InitCollectionDataContract(DataContract sharedTypeContract) { _helper = base.Helper as CollectionDataContractCriticalHelper; _collectionItemName = _helper.CollectionItemName; if (_helper.Kind == CollectionKind.Dictionary || _helper.Kind == CollectionKind.GenericDictionary) { _itemContract = _helper.ItemContract; } _helper.SharedTypeContract = sharedTypeContract; } private static Type[] KnownInterfaces { /// <SecurityNote> /// Critical - fetches the critical knownInterfaces property /// Safe - knownInterfaces only needs to be protected for write /// </SecurityNote> [SecuritySafeCritical] get { return CollectionDataContractCriticalHelper.KnownInterfaces; } } internal CollectionKind Kind { /// <SecurityNote> /// Critical - fetches the critical kind property /// Safe - kind only needs to be protected for write /// </SecurityNote> [SecuritySafeCritical] get { return _helper.Kind; } } public Type ItemType { /// <SecurityNote> /// Critical - fetches the critical itemType property /// Safe - itemType only needs to be protected for write /// </SecurityNote> [SecuritySafeCritical] get { return _helper.ItemType; } set { _helper.ItemType = value; } } public DataContract ItemContract { /// <SecurityNote> /// Critical - fetches the critical itemContract property /// Safe - itemContract only needs to be protected for write /// </SecurityNote> [SecuritySafeCritical] get { return _itemContract ?? _helper.ItemContract; } /// <SecurityNote> /// Critical - sets the critical itemContract property /// </SecurityNote> [SecurityCritical] set { _itemContract = value; _helper.ItemContract = value; } } internal DataContract SharedTypeContract { /// <SecurityNote> /// Critical - fetches the critical sharedTypeContract property /// Safe - sharedTypeContract only needs to be protected for write /// </SecurityNote> [SecuritySafeCritical] get { return _helper.SharedTypeContract; } } public string ItemName { /// <SecurityNote> /// Critical - fetches the critical itemName property /// Safe - itemName only needs to be protected for write /// </SecurityNote> [SecuritySafeCritical] get { return _helper.ItemName; } /// <SecurityNote> /// Critical - sets the critical itemName property /// </SecurityNote> [SecurityCritical] set { _helper.ItemName = value; } } public XmlDictionaryString CollectionItemName { /// <SecurityNote> /// Critical - fetches the critical collectionItemName property /// Safe - collectionItemName only needs to be protected for write /// </SecurityNote> [SecuritySafeCritical] get { return _collectionItemName; } set { _collectionItemName = value; } } public string KeyName { /// <SecurityNote> /// Critical - fetches the critical keyName property /// Safe - keyName only needs to be protected for write /// </SecurityNote> [SecuritySafeCritical] get { return _helper.KeyName; } /// <SecurityNote> /// Critical - sets the critical keyName property /// </SecurityNote> [SecurityCritical] set { _helper.KeyName = value; } } public string ValueName { /// <SecurityNote> /// Critical - fetches the critical valueName property /// Safe - valueName only needs to be protected for write /// </SecurityNote> [SecuritySafeCritical] get { return _helper.ValueName; } /// <SecurityNote> /// Critical - sets the critical valueName property /// </SecurityNote> [SecurityCritical] set { _helper.ValueName = value; } } internal bool IsDictionary { get { return KeyName != null; } } public XmlDictionaryString ChildElementNamespace { /// <SecurityNote> /// Critical - fetches the critical childElementNamespace property /// Safe - childElementNamespace only needs to be protected for write; initialized in getter if null /// </SecurityNote> [SecuritySafeCritical] get { if (_childElementNamespace == null) { lock (this) { if (_childElementNamespace == null) { if (_helper.ChildElementNamespace == null && !IsDictionary) { XmlDictionaryString tempChildElementNamespace = ClassDataContract.GetChildNamespaceToDeclare(this, ItemType, new XmlDictionary()); Interlocked.MemoryBarrier(); _helper.ChildElementNamespace = tempChildElementNamespace; } _childElementNamespace = _helper.ChildElementNamespace; } } } return _childElementNamespace; } } internal bool IsItemTypeNullable { /// <SecurityNote> /// Critical - fetches the critical isItemTypeNullable property /// Safe - isItemTypeNullable only needs to be protected for write /// </SecurityNote> [SecuritySafeCritical] get { return _helper.IsItemTypeNullable; } /// <SecurityNote> /// Critical - Sets the critical isItemTypeNullable property /// </SecurityNote> [SecurityCritical] set { _helper.IsItemTypeNullable = value; } } internal bool IsConstructorCheckRequired { /// <SecurityNote> /// Critical - fetches the critical isConstructorCheckRequired property /// Safe - isConstructorCheckRequired only needs to be protected for write /// </SecurityNote> [SecuritySafeCritical] get { return _helper.IsConstructorCheckRequired; } /// <SecurityNote> /// Critical - sets the critical isConstructorCheckRequired property /// </SecurityNote> [SecurityCritical] set { _helper.IsConstructorCheckRequired = value; } } internal MethodInfo GetEnumeratorMethod { /// <SecurityNote> /// Critical - fetches the critical getEnumeratorMethod property /// Safe - getEnumeratorMethod only needs to be protected for write /// </SecurityNote> [SecuritySafeCritical] get { return _helper.GetEnumeratorMethod; } } internal MethodInfo AddMethod { /// <SecurityNote> /// Critical - fetches the critical addMethod property /// Safe - addMethod only needs to be protected for write /// </SecurityNote> [SecuritySafeCritical] get { return _helper.AddMethod; } } internal ConstructorInfo Constructor { /// <SecurityNote> /// Critical - fetches the critical constructor property /// Safe - constructor only needs to be protected for write /// </SecurityNote> [SecuritySafeCritical] get { return _helper.Constructor; } } public override DataContractDictionary KnownDataContracts { /// <SecurityNote> /// Critical - fetches the critical knownDataContracts property /// Safe - knownDataContracts only needs to be protected for write /// </SecurityNote> [SecuritySafeCritical] get { return _helper.KnownDataContracts; } /// <SecurityNote> /// Critical - sets the critical knownDataContracts property /// </SecurityNote> [SecurityCritical] set { _helper.KnownDataContracts = value; } } internal string InvalidCollectionInSharedContractMessage { /// <SecurityNote> /// Critical - fetches the critical invalidCollectionInSharedContractMessage property /// Safe - invalidCollectionInSharedContractMessage only needs to be protected for write /// </SecurityNote> [SecuritySafeCritical] get { return _helper.InvalidCollectionInSharedContractMessage; } } #if !NET_NATIVE internal XmlFormatCollectionWriterDelegate XmlFormatWriterDelegate { /// <SecurityNote> /// Critical - fetches the critical xmlFormatWriterDelegate property /// Safe - xmlFormatWriterDelegate only needs to be protected for write; initialized in getter if null /// </SecurityNote> [SecuritySafeCritical] get { if (_helper.XmlFormatWriterDelegate == null) { lock (this) { if (_helper.XmlFormatWriterDelegate == null) { XmlFormatCollectionWriterDelegate tempDelegate = new XmlFormatWriterGenerator().GenerateCollectionWriter(this); Interlocked.MemoryBarrier(); _helper.XmlFormatWriterDelegate = tempDelegate; } } } return _helper.XmlFormatWriterDelegate; } } #else public XmlFormatCollectionWriterDelegate XmlFormatWriterDelegate { get; set; } #endif #if !NET_NATIVE internal XmlFormatCollectionReaderDelegate XmlFormatReaderDelegate { /// <SecurityNote> /// Critical - fetches the critical xmlFormatReaderDelegate property /// Safe - xmlFormatReaderDelegate only needs to be protected for write; initialized in getter if null /// </SecurityNote> [SecuritySafeCritical] get { if (_helper.XmlFormatReaderDelegate == null) { lock (this) { if (_helper.XmlFormatReaderDelegate == null) { XmlFormatCollectionReaderDelegate tempDelegate = new XmlFormatReaderGenerator().GenerateCollectionReader(this); Interlocked.MemoryBarrier(); _helper.XmlFormatReaderDelegate = tempDelegate; } } } return _helper.XmlFormatReaderDelegate; } } #else public XmlFormatCollectionReaderDelegate XmlFormatReaderDelegate { get; set; } #endif #if !NET_NATIVE internal XmlFormatGetOnlyCollectionReaderDelegate XmlFormatGetOnlyCollectionReaderDelegate { /// <SecurityNote> /// Critical - fetches the critical xmlFormatReaderDelegate property /// Safe - xmlFormatReaderDelegate only needs to be protected for write; initialized in getter if null /// </SecurityNote> [SecuritySafeCritical] get { if (_helper.XmlFormatGetOnlyCollectionReaderDelegate == null) { lock (this) { if (_helper.XmlFormatGetOnlyCollectionReaderDelegate == null) { if (UnderlyingType.GetTypeInfo().IsInterface && (Kind == CollectionKind.Enumerable || Kind == CollectionKind.Collection || Kind == CollectionKind.GenericEnumerable)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(string.Format(SRSerialization.GetOnlyCollectionMustHaveAddMethod, GetClrTypeFullName(UnderlyingType)))); } Debug.Assert(AddMethod != null || Kind == CollectionKind.Array, "Add method cannot be null if the collection is being used as a get-only property"); XmlFormatGetOnlyCollectionReaderDelegate tempDelegate = new XmlFormatReaderGenerator().GenerateGetOnlyCollectionReader(this); Interlocked.MemoryBarrier(); _helper.XmlFormatGetOnlyCollectionReaderDelegate = tempDelegate; } } } return _helper.XmlFormatGetOnlyCollectionReaderDelegate; } } #else public XmlFormatGetOnlyCollectionReaderDelegate XmlFormatGetOnlyCollectionReaderDelegate { get; set; } #endif [SecurityCritical] /// <SecurityNote> /// Critical - holds all state used for (de)serializing collections. /// since the data is cached statically, we lock down access to it. /// </SecurityNote> private class CollectionDataContractCriticalHelper : DataContract.DataContractCriticalHelper { private static Type[] s_knownInterfaces; private Type _itemType; private bool _isItemTypeNullable; private CollectionKind _kind; private readonly MethodInfo _getEnumeratorMethod, _addMethod; private readonly ConstructorInfo _constructor; private DataContract _itemContract; private DataContract _sharedTypeContract; private DataContractDictionary _knownDataContracts; private bool _isKnownTypeAttributeChecked; private string _itemName; private bool _itemNameSetExplicit; private XmlDictionaryString _collectionItemName; private string _keyName; private string _valueName; private XmlDictionaryString _childElementNamespace; private string _invalidCollectionInSharedContractMessage; private XmlFormatCollectionReaderDelegate _xmlFormatReaderDelegate; private XmlFormatGetOnlyCollectionReaderDelegate _xmlFormatGetOnlyCollectionReaderDelegate; private XmlFormatCollectionWriterDelegate _xmlFormatWriterDelegate; private bool _isConstructorCheckRequired = false; internal static Type[] KnownInterfaces { get { if (s_knownInterfaces == null) { // Listed in priority order s_knownInterfaces = new Type[] { Globals.TypeOfIDictionaryGeneric, Globals.TypeOfIDictionary, Globals.TypeOfIListGeneric, Globals.TypeOfICollectionGeneric, Globals.TypeOfIList, Globals.TypeOfIEnumerableGeneric, Globals.TypeOfICollection, Globals.TypeOfIEnumerable }; } return s_knownInterfaces; } } private void Init(CollectionKind kind, Type itemType, CollectionDataContractAttribute collectionContractAttribute) { _kind = kind; if (itemType != null) { _itemType = itemType; bool isDictionary = (kind == CollectionKind.Dictionary || kind == CollectionKind.GenericDictionary); string itemName = null, keyName = null, valueName = null; if (collectionContractAttribute != null) { if (collectionContractAttribute.IsItemNameSetExplicitly) { if (collectionContractAttribute.ItemName == null || collectionContractAttribute.ItemName.Length == 0) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(string.Format(SRSerialization.InvalidCollectionContractItemName, DataContract.GetClrTypeFullName(UnderlyingType)))); itemName = DataContract.EncodeLocalName(collectionContractAttribute.ItemName); _itemNameSetExplicit = true; } if (collectionContractAttribute.IsKeyNameSetExplicitly) { if (collectionContractAttribute.KeyName == null || collectionContractAttribute.KeyName.Length == 0) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(string.Format(SRSerialization.InvalidCollectionContractKeyName, DataContract.GetClrTypeFullName(UnderlyingType)))); if (!isDictionary) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(string.Format(SRSerialization.InvalidCollectionContractKeyNoDictionary, DataContract.GetClrTypeFullName(UnderlyingType), collectionContractAttribute.KeyName))); keyName = DataContract.EncodeLocalName(collectionContractAttribute.KeyName); } if (collectionContractAttribute.IsValueNameSetExplicitly) { if (collectionContractAttribute.ValueName == null || collectionContractAttribute.ValueName.Length == 0) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(string.Format(SRSerialization.InvalidCollectionContractValueName, DataContract.GetClrTypeFullName(UnderlyingType)))); if (!isDictionary) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(string.Format(SRSerialization.InvalidCollectionContractValueNoDictionary, DataContract.GetClrTypeFullName(UnderlyingType), collectionContractAttribute.ValueName))); valueName = DataContract.EncodeLocalName(collectionContractAttribute.ValueName); } } XmlDictionary dictionary = isDictionary ? new XmlDictionary(5) : new XmlDictionary(3); this.Name = dictionary.Add(this.StableName.Name); this.Namespace = dictionary.Add(this.StableName.Namespace); _itemName = itemName ?? DataContract.GetStableName(DataContract.UnwrapNullableType(itemType)).Name; _collectionItemName = dictionary.Add(_itemName); if (isDictionary) { _keyName = keyName ?? Globals.KeyLocalName; _valueName = valueName ?? Globals.ValueLocalName; } } if (collectionContractAttribute != null) { this.IsReference = collectionContractAttribute.IsReference; } } internal CollectionDataContractCriticalHelper(CollectionKind kind) : base() { Init(kind, null, null); } // array internal CollectionDataContractCriticalHelper(Type type) : base(type) { if (type == Globals.TypeOfArray) type = Globals.TypeOfObjectArray; if (type.GetArrayRank() > 1) throw /*System.Runtime.Serialization.*/DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(string.Format(SRSerialization.SupportForMultidimensionalArraysNotPresent))); this.StableName = DataContract.GetStableName(type); Init(CollectionKind.Array, type.GetElementType(), null); } // collection internal CollectionDataContractCriticalHelper(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, MethodInfo addMethod, ConstructorInfo constructor) : base(type) { if (getEnumeratorMethod == null) throw /*System.Runtime.Serialization.*/DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(string.Format(SRSerialization.CollectionMustHaveGetEnumeratorMethod, DataContract.GetClrTypeFullName(type)))); if (addMethod == null && !type.GetTypeInfo().IsInterface) throw /*System.Runtime.Serialization.*/DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(string.Format(SRSerialization.CollectionMustHaveAddMethod, DataContract.GetClrTypeFullName(type)))); if (itemType == null) throw /*System.Runtime.Serialization.*/DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(string.Format(SRSerialization.CollectionMustHaveItemType, DataContract.GetClrTypeFullName(type)))); CollectionDataContractAttribute collectionContractAttribute; this.StableName = DataContract.GetCollectionStableName(type, itemType, out collectionContractAttribute); Init(kind, itemType, collectionContractAttribute); _getEnumeratorMethod = getEnumeratorMethod; _addMethod = addMethod; _constructor = constructor; } // collection internal CollectionDataContractCriticalHelper(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, MethodInfo addMethod, ConstructorInfo constructor, bool isConstructorCheckRequired) : this(type, kind, itemType, getEnumeratorMethod, addMethod, constructor) { _isConstructorCheckRequired = isConstructorCheckRequired; } internal CollectionDataContractCriticalHelper(Type type, string invalidCollectionInSharedContractMessage) : base(type) { Init(CollectionKind.Collection, null /*itemType*/, null); _invalidCollectionInSharedContractMessage = invalidCollectionInSharedContractMessage; } internal CollectionKind Kind { get { return _kind; } } internal Type ItemType { get { return _itemType; } set { _itemType = value; } } internal DataContract ItemContract { get { if (_itemContract == null && UnderlyingType != null) { if (IsDictionary) { if (String.CompareOrdinal(KeyName, ValueName) == 0) { DataContract.ThrowInvalidDataContractException( string.Format(SRSerialization.DupKeyValueName, DataContract.GetClrTypeFullName(UnderlyingType), KeyName), UnderlyingType); } _itemContract = ClassDataContract.CreateClassDataContractForKeyValue(ItemType, Namespace, new string[] { KeyName, ValueName }); // Ensure that DataContract gets added to the static DataContract cache for dictionary items DataContract.GetDataContract(ItemType); } else { #if NET_NATIVE _itemContract = DataContract.GetDataContractFromGeneratedAssembly(ItemType); #else _itemContract = DataContract.GetDataContract(ItemType); #endif } } return _itemContract; } set { _itemContract = value; } } internal DataContract SharedTypeContract { get { return _sharedTypeContract; } set { _sharedTypeContract = value; } } internal string ItemName { get { return _itemName; } set { _itemName = value; } } internal bool IsConstructorCheckRequired { get { return _isConstructorCheckRequired; } set { _isConstructorCheckRequired = value; } } public XmlDictionaryString CollectionItemName { get { return _collectionItemName; } } internal string KeyName { get { return _keyName; } set { _keyName = value; } } internal string ValueName { get { return _valueName; } set { _valueName = value; } } internal bool IsDictionary { get { return KeyName != null; } } public XmlDictionaryString ChildElementNamespace { get { return _childElementNamespace; } set { _childElementNamespace = value; } } internal bool IsItemTypeNullable { get { return _isItemTypeNullable; } set { _isItemTypeNullable = value; } } internal MethodInfo GetEnumeratorMethod { get { return _getEnumeratorMethod; } } internal MethodInfo AddMethod { get { return _addMethod; } } internal ConstructorInfo Constructor { get { return _constructor; } } internal override DataContractDictionary KnownDataContracts { [SecurityCritical] get { if (!_isKnownTypeAttributeChecked && UnderlyingType != null) { lock (this) { if (!_isKnownTypeAttributeChecked) { _knownDataContracts = DataContract.ImportKnownTypeAttributes(this.UnderlyingType); Interlocked.MemoryBarrier(); _isKnownTypeAttributeChecked = true; } } } return _knownDataContracts; } [SecurityCritical] set { _knownDataContracts = value; } } internal string InvalidCollectionInSharedContractMessage { get { return _invalidCollectionInSharedContractMessage; } } internal bool ItemNameSetExplicit { get { return _itemNameSetExplicit; } } internal XmlFormatCollectionWriterDelegate XmlFormatWriterDelegate { get { return _xmlFormatWriterDelegate; } set { _xmlFormatWriterDelegate = value; } } internal XmlFormatCollectionReaderDelegate XmlFormatReaderDelegate { get { return _xmlFormatReaderDelegate; } set { _xmlFormatReaderDelegate = value; } } internal XmlFormatGetOnlyCollectionReaderDelegate XmlFormatGetOnlyCollectionReaderDelegate { get { return _xmlFormatGetOnlyCollectionReaderDelegate; } set { _xmlFormatGetOnlyCollectionReaderDelegate = value; } } } private DataContract GetSharedTypeContract(Type type) { if (type.GetTypeInfo().IsAttributeDefined(Globals.TypeOfCollectionDataContractAttribute)) { return this; } if (type.GetTypeInfo().IsAttributeDefined(Globals.TypeOfDataContractAttribute)) { return new ClassDataContract(type); } return null; } internal static bool IsCollectionInterface(Type type) { if (type.GetTypeInfo().IsGenericType) type = type.GetGenericTypeDefinition(); return ((IList<Type>)KnownInterfaces).Contains(type); } internal static bool IsCollection(Type type) { Type itemType; return IsCollection(type, out itemType); } internal static bool IsCollection(Type type, out Type itemType) { return IsCollectionHelper(type, out itemType, true /*constructorRequired*/); } internal static bool IsCollection(Type type, bool constructorRequired) { Type itemType; return IsCollectionHelper(type, out itemType, constructorRequired); } private static bool IsCollectionHelper(Type type, out Type itemType, bool constructorRequired) { if (type.IsArray && DataContract.GetBuiltInDataContract(type) == null) { itemType = type.GetElementType(); return true; } DataContract dataContract; return IsCollectionOrTryCreate(type, false /*tryCreate*/, out dataContract, out itemType, constructorRequired); } internal static bool TryCreate(Type type, out DataContract dataContract) { Type itemType; return IsCollectionOrTryCreate(type, true /*tryCreate*/, out dataContract, out itemType, true /*constructorRequired*/); } internal static bool CreateGetOnlyCollectionDataContract(Type type, out DataContract dataContract) { Type itemType; if (type.IsArray) { dataContract = new CollectionDataContract(type); return true; } else { return IsCollectionOrTryCreate(type, true /*tryCreate*/, out dataContract, out itemType, false /*constructorRequired*/); } } internal static bool TryCreateGetOnlyCollectionDataContract(Type type, out DataContract dataContract) { #if !NET_NATIVE Type itemType; if (type.IsArray) { dataContract = new CollectionDataContract(type); return true; } else { return IsCollectionOrTryCreate(type, true /*tryCreate*/, out dataContract, out itemType, false /*constructorRequired*/); } #else dataContract = DataContract.GetDataContractFromGeneratedAssembly(type); if (dataContract is CollectionDataContract) { return true; } else { dataContract = null; return false; } #endif } internal static MethodInfo GetTargetMethodWithName(string name, Type type, Type interfaceType) { Type t = type.GetInterfaces().Where(it => it.Equals(interfaceType)).FirstOrDefault(); if (t == null) return null; return t.GetMethod(name); } private static bool IsArraySegment(Type t) { return t.GetTypeInfo().IsGenericType && (t.GetGenericTypeDefinition() == typeof(ArraySegment<>)); } private static bool IsCollectionOrTryCreate(Type type, bool tryCreate, out DataContract dataContract, out Type itemType, bool constructorRequired) { dataContract = null; itemType = Globals.TypeOfObject; if (DataContract.GetBuiltInDataContract(type) != null) { return HandleIfInvalidCollection(type, tryCreate, false/*hasCollectionDataContract*/, false/*isBaseTypeCollection*/, SRSerialization.CollectionTypeCannotBeBuiltIn, null, ref dataContract); } MethodInfo addMethod, getEnumeratorMethod; bool hasCollectionDataContract = IsCollectionDataContract(type); Type baseType = type.GetTypeInfo().BaseType; bool isBaseTypeCollection = (baseType != null && baseType != Globals.TypeOfObject && baseType != Globals.TypeOfValueType && baseType != Globals.TypeOfUri) ? IsCollection(baseType) : false; if (type.GetTypeInfo().IsAttributeDefined(Globals.TypeOfDataContractAttribute)) { return HandleIfInvalidCollection(type, tryCreate, hasCollectionDataContract, isBaseTypeCollection, SRSerialization.CollectionTypeCannotHaveDataContract, null, ref dataContract); } if (Globals.TypeOfIXmlSerializable.IsAssignableFrom(type) || IsArraySegment(type)) { return false; } if (!Globals.TypeOfIEnumerable.IsAssignableFrom(type)) { return HandleIfInvalidCollection(type, tryCreate, hasCollectionDataContract, isBaseTypeCollection, SRSerialization.CollectionTypeIsNotIEnumerable, null, ref dataContract); } if (type.GetTypeInfo().IsInterface) { Type interfaceTypeToCheck = type.GetTypeInfo().IsGenericType ? type.GetGenericTypeDefinition() : type; Type[] knownInterfaces = KnownInterfaces; for (int i = 0; i < knownInterfaces.Length; i++) { if (knownInterfaces[i] == interfaceTypeToCheck) { addMethod = null; if (type.GetTypeInfo().IsGenericType) { Type[] genericArgs = type.GetGenericArguments(); if (interfaceTypeToCheck == Globals.TypeOfIDictionaryGeneric) { itemType = Globals.TypeOfKeyValue.MakeGenericType(genericArgs); addMethod = type.GetMethod(Globals.AddMethodName); getEnumeratorMethod = Globals.TypeOfIEnumerableGeneric.MakeGenericType(Globals.TypeOfKeyValuePair.MakeGenericType(genericArgs)).GetMethod(Globals.GetEnumeratorMethodName); } else { itemType = genericArgs[0]; // ICollection<T> has AddMethod var collectionType = Globals.TypeOfICollectionGeneric.MakeGenericType(itemType); if (collectionType.IsAssignableFrom(type)) { addMethod = collectionType.GetMethod(Globals.AddMethodName); } getEnumeratorMethod = Globals.TypeOfIEnumerableGeneric.MakeGenericType(itemType).GetMethod(Globals.GetEnumeratorMethodName); } } else { if (interfaceTypeToCheck == Globals.TypeOfIDictionary) { itemType = typeof(KeyValue<object, object>); addMethod = type.GetMethod(Globals.AddMethodName); } else { itemType = Globals.TypeOfObject; // IList has AddMethod if (interfaceTypeToCheck == Globals.TypeOfIList) { addMethod = type.GetMethod(Globals.AddMethodName); } } getEnumeratorMethod = Globals.TypeOfIEnumerable.GetMethod(Globals.GetEnumeratorMethodName); } if (tryCreate) dataContract = new CollectionDataContract(type, (CollectionKind)(i + 1), itemType, getEnumeratorMethod, addMethod, null/*defaultCtor*/); return true; } } } ConstructorInfo defaultCtor = null; if (!type.GetTypeInfo().IsValueType) { defaultCtor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, Array.Empty<Type>()); if (defaultCtor == null && constructorRequired) { return HandleIfInvalidCollection(type, tryCreate, hasCollectionDataContract, isBaseTypeCollection/*createContractWithException*/, SRSerialization.CollectionTypeDoesNotHaveDefaultCtor, null, ref dataContract); } } Type knownInterfaceType = null; CollectionKind kind = CollectionKind.None; bool multipleDefinitions = false; Type[] interfaceTypes = type.GetInterfaces(); foreach (Type interfaceType in interfaceTypes) { Type interfaceTypeToCheck = interfaceType.GetTypeInfo().IsGenericType ? interfaceType.GetGenericTypeDefinition() : interfaceType; Type[] knownInterfaces = KnownInterfaces; for (int i = 0; i < knownInterfaces.Length; i++) { if (knownInterfaces[i] == interfaceTypeToCheck) { CollectionKind currentKind = (CollectionKind)(i + 1); if (kind == CollectionKind.None || currentKind < kind) { kind = currentKind; knownInterfaceType = interfaceType; multipleDefinitions = false; } else if ((kind & currentKind) == currentKind) multipleDefinitions = true; break; } } } if (kind == CollectionKind.None) { return HandleIfInvalidCollection(type, tryCreate, hasCollectionDataContract, isBaseTypeCollection, SRSerialization.CollectionTypeIsNotIEnumerable, null, ref dataContract); } if (kind == CollectionKind.Enumerable || kind == CollectionKind.Collection || kind == CollectionKind.GenericEnumerable) { if (multipleDefinitions) knownInterfaceType = Globals.TypeOfIEnumerable; itemType = knownInterfaceType.GetTypeInfo().IsGenericType ? knownInterfaceType.GetGenericArguments()[0] : Globals.TypeOfObject; GetCollectionMethods(type, knownInterfaceType, new Type[] { itemType }, false /*addMethodOnInterface*/, out getEnumeratorMethod, out addMethod); if (addMethod == null) { return HandleIfInvalidCollection(type, tryCreate, hasCollectionDataContract, isBaseTypeCollection/*createContractWithException*/, SRSerialization.CollectionTypeDoesNotHaveAddMethod, DataContract.GetClrTypeFullName(itemType), ref dataContract); } if (tryCreate) dataContract = new CollectionDataContract(type, kind, itemType, getEnumeratorMethod, addMethod, defaultCtor, !constructorRequired); } else { if (multipleDefinitions) { return HandleIfInvalidCollection(type, tryCreate, hasCollectionDataContract, isBaseTypeCollection/*createContractWithException*/, SRSerialization.CollectionTypeHasMultipleDefinitionsOfInterface, KnownInterfaces[(int)kind - 1].Name, ref dataContract); } Type[] addMethodTypeArray = null; switch (kind) { case CollectionKind.GenericDictionary: addMethodTypeArray = knownInterfaceType.GetGenericArguments(); bool isOpenGeneric = knownInterfaceType.GetTypeInfo().IsGenericTypeDefinition || (addMethodTypeArray[0].IsGenericParameter && addMethodTypeArray[1].IsGenericParameter); itemType = isOpenGeneric ? Globals.TypeOfKeyValue : Globals.TypeOfKeyValue.MakeGenericType(addMethodTypeArray); break; case CollectionKind.Dictionary: addMethodTypeArray = new Type[] { Globals.TypeOfObject, Globals.TypeOfObject }; itemType = Globals.TypeOfKeyValue.MakeGenericType(addMethodTypeArray); break; case CollectionKind.GenericList: case CollectionKind.GenericCollection: addMethodTypeArray = knownInterfaceType.GetGenericArguments(); itemType = addMethodTypeArray[0]; break; case CollectionKind.List: itemType = Globals.TypeOfObject; addMethodTypeArray = new Type[] { itemType }; break; } if (tryCreate) { GetCollectionMethods(type, knownInterfaceType, addMethodTypeArray, true /*addMethodOnInterface*/, out getEnumeratorMethod, out addMethod); #if !NET_NATIVE dataContract = new CollectionDataContract(type, kind, itemType, getEnumeratorMethod, addMethod, defaultCtor, !constructorRequired); #else dataContract = DataContract.GetDataContractFromGeneratedAssembly(type); #endif } } return true; } internal static bool IsCollectionDataContract(Type type) { return type.GetTypeInfo().IsAttributeDefined(Globals.TypeOfCollectionDataContractAttribute); } private static bool HandleIfInvalidCollection(Type type, bool tryCreate, bool hasCollectionDataContract, bool createContractWithException, string message, string param, ref DataContract dataContract) { if (hasCollectionDataContract) { if (tryCreate) throw /*System.Runtime.Serialization.*/DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(GetInvalidCollectionMessage(message, string.Format(SRSerialization.InvalidCollectionDataContract, DataContract.GetClrTypeFullName(type)), param))); return true; } if (createContractWithException) { if (tryCreate) dataContract = new CollectionDataContract(type, GetInvalidCollectionMessage(message, string.Format(SRSerialization.InvalidCollectionType, DataContract.GetClrTypeFullName(type)), param)); return true; } return false; } private static string GetInvalidCollectionMessage(string message, string nestedMessage, string param) { return (param == null) ? string.Format(message, nestedMessage) : string.Format(message, nestedMessage, param); } private static void FindCollectionMethodsOnInterface(Type type, Type interfaceType, ref MethodInfo addMethod, ref MethodInfo getEnumeratorMethod) { Type t = type.GetInterfaces().Where(it => it.Equals(interfaceType)).FirstOrDefault(); if (t != null) { addMethod = t.GetMethod(Globals.AddMethodName) ?? addMethod; getEnumeratorMethod = t.GetMethod(Globals.GetEnumeratorMethodName) ?? getEnumeratorMethod; } } private static void GetCollectionMethods(Type type, Type interfaceType, Type[] addMethodTypeArray, bool addMethodOnInterface, out MethodInfo getEnumeratorMethod, out MethodInfo addMethod) { addMethod = getEnumeratorMethod = null; if (addMethodOnInterface) { addMethod = type.GetMethod(Globals.AddMethodName, BindingFlags.Instance | BindingFlags.Public, addMethodTypeArray); if (addMethod == null || addMethod.GetParameters()[0].ParameterType != addMethodTypeArray[0]) { FindCollectionMethodsOnInterface(type, interfaceType, ref addMethod, ref getEnumeratorMethod); if (addMethod == null) { Type[] parentInterfaceTypes = interfaceType.GetInterfaces(); foreach (Type parentInterfaceType in parentInterfaceTypes) { if (IsKnownInterface(parentInterfaceType)) { FindCollectionMethodsOnInterface(type, parentInterfaceType, ref addMethod, ref getEnumeratorMethod); if (addMethod == null) { break; } } } } } } else { // GetMethod returns Add() method with parameter closest matching T in assignability/inheritance chain addMethod = type.GetMethod(Globals.AddMethodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, addMethodTypeArray); if (addMethod == null) return; } if (getEnumeratorMethod == null) { getEnumeratorMethod = type.GetMethod(Globals.GetEnumeratorMethodName, BindingFlags.Instance | BindingFlags.Public, Array.Empty<Type>()); if (getEnumeratorMethod == null || !Globals.TypeOfIEnumerator.IsAssignableFrom(getEnumeratorMethod.ReturnType)) { Type ienumerableInterface = interfaceType.GetInterfaces().Where(t => t.FullName.StartsWith("System.Collections.Generic.IEnumerable")).FirstOrDefault(); if (ienumerableInterface == null) ienumerableInterface = Globals.TypeOfIEnumerable; getEnumeratorMethod = GetTargetMethodWithName(Globals.GetEnumeratorMethodName, type, ienumerableInterface); } } } private static bool IsKnownInterface(Type type) { Type typeToCheck = type.GetTypeInfo().IsGenericType ? type.GetGenericTypeDefinition() : type; foreach (Type knownInterfaceType in KnownInterfaces) { if (typeToCheck == knownInterfaceType) { return true; } } return false; } internal override DataContract GetValidContract(SerializationMode mode) { if (InvalidCollectionInSharedContractMessage != null) throw /*System.Runtime.Serialization.*/DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(InvalidCollectionInSharedContractMessage)); return this; } internal override DataContract GetValidContract() { if (this.IsConstructorCheckRequired) { CheckConstructor(); } return this; } /// <SecurityNote> /// Critical - sets the critical IsConstructorCheckRequired property on CollectionDataContract /// </SecurityNote> [SecuritySafeCritical] private void CheckConstructor() { if (this.Constructor == null) { throw /*System.Runtime.Serialization.*/DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(string.Format(SRSerialization.CollectionTypeDoesNotHaveDefaultCtor, DataContract.GetClrTypeFullName(this.UnderlyingType)))); } else { this.IsConstructorCheckRequired = false; } } internal override bool IsValidContract(SerializationMode mode) { return (InvalidCollectionInSharedContractMessage == null); } /// <SecurityNote> /// Review - calculates whether this collection requires MemberAccessPermission for deserialization. /// since this information is used to determine whether to give the generated code access /// permissions to private members, any changes to the logic should be reviewed. /// </SecurityNote> internal bool RequiresMemberAccessForRead(SecurityException securityException) { if (!IsTypeVisible(UnderlyingType)) { if (securityException != null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityException(string.Format( SRSerialization.PartialTrustCollectionContractTypeNotPublic, DataContract.GetClrTypeFullName(UnderlyingType)), securityException)); } return true; } if (ItemType != null && !IsTypeVisible(ItemType)) { if (securityException != null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityException(string.Format( SRSerialization.PartialTrustCollectionContractTypeNotPublic, DataContract.GetClrTypeFullName(ItemType)), securityException)); } return true; } if (ConstructorRequiresMemberAccess(Constructor)) { if (securityException != null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityException(string.Format( SRSerialization.PartialTrustCollectionContractNoPublicConstructor, DataContract.GetClrTypeFullName(UnderlyingType)), securityException)); } return true; } if (MethodRequiresMemberAccess(this.AddMethod)) { if (securityException != null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityException(string.Format( SRSerialization.PartialTrustCollectionContractAddMethodNotPublic, DataContract.GetClrTypeFullName(UnderlyingType), this.AddMethod.Name), securityException)); } return true; } return false; } /// <SecurityNote> /// Review - calculates whether this collection requires MemberAccessPermission for serialization. /// since this information is used to determine whether to give the generated code access /// permissions to private members, any changes to the logic should be reviewed. /// </SecurityNote> internal bool RequiresMemberAccessForWrite(SecurityException securityException) { if (!IsTypeVisible(UnderlyingType)) { if (securityException != null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityException(string.Format( SRSerialization.PartialTrustCollectionContractTypeNotPublic, DataContract.GetClrTypeFullName(UnderlyingType)), securityException)); } return true; } if (ItemType != null && !IsTypeVisible(ItemType)) { if (securityException != null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityException(string.Format( SRSerialization.PartialTrustCollectionContractTypeNotPublic, DataContract.GetClrTypeFullName(ItemType)), securityException)); } return true; } return false; } public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context) { // IsGetOnlyCollection value has already been used to create current collectiondatacontract, value can now be reset. context.IsGetOnlyCollection = false; XmlFormatWriterDelegate(xmlWriter, obj, context, this); } public override object ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context) { xmlReader.Read(); object o = null; if (context.IsGetOnlyCollection) { // IsGetOnlyCollection value has already been used to create current collectiondatacontract, value can now be reset. context.IsGetOnlyCollection = false; #if NET_NATIVE if (XmlFormatGetOnlyCollectionReaderDelegate == null) { throw new InvalidDataContractException(SR.Format(SR.SerializationCodeIsMissingForType, UnderlyingType.ToString())); } #endif XmlFormatGetOnlyCollectionReaderDelegate(xmlReader, context, CollectionItemName, Namespace, this); } else { o = XmlFormatReaderDelegate(xmlReader, context, CollectionItemName, Namespace, this); } xmlReader.ReadEndElement(); return o; } internal class DictionaryEnumerator : IEnumerator<KeyValue<object, object>> { private IDictionaryEnumerator _enumerator; public DictionaryEnumerator(IDictionaryEnumerator enumerator) { _enumerator = enumerator; } public void Dispose() { GC.SuppressFinalize(this); } public bool MoveNext() { return _enumerator.MoveNext(); } public KeyValue<object, object> Current { get { return new KeyValue<object, object>(_enumerator.Key, _enumerator.Value); } } object System.Collections.IEnumerator.Current { get { return Current; } } public void Reset() { _enumerator.Reset(); } } internal class GenericDictionaryEnumerator<K, V> : IEnumerator<KeyValue<K, V>> { private IEnumerator<KeyValuePair<K, V>> _enumerator; public GenericDictionaryEnumerator(IEnumerator<KeyValuePair<K, V>> enumerator) { _enumerator = enumerator; } public void Dispose() { GC.SuppressFinalize(this); } public bool MoveNext() { return _enumerator.MoveNext(); } public KeyValue<K, V> Current { get { KeyValuePair<K, V> current = _enumerator.Current; return new KeyValue<K, V>(current.Key, current.Value); } } object System.Collections.IEnumerator.Current { get { return Current; } } public void Reset() { _enumerator.Reset(); } } } } |