File: FrameworkFork\System.Runtime.Serialization\System\Runtime\Serialization\XmlDataContract.cs
Web Access
Project: src\src\dotnet-svcutil\lib\src\dotnet-svcutil-lib.csproj (dotnet-svcutil-lib)
// 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.
 
namespace System.Runtime.Serialization
{
    using System;
    using System.Collections.Generic;
    using System.Reflection;
    using System.Threading;
    using Microsoft.Xml;
    using DataContractDictionary = System.Collections.Generic.Dictionary<Microsoft.Xml.XmlQualifiedName, DataContract>;
    using Microsoft.Xml.Serialization;
    using Microsoft.Xml.Schema;
    using System.Security;
    using System.Linq;
 
#if NET_NATIVE
    public delegate IXmlSerializable CreateXmlSerializableDelegate();
    public sealed class XmlDataContract : DataContract
#else
    internal delegate IXmlSerializable CreateXmlSerializableDelegate();
    internal sealed class XmlDataContract : DataContract
#endif
    {
        [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 XmlDataContractCriticalHelper _helper;
 
        /// <SecurityNote>
        /// Critical - initializes SecurityCritical field 'helper'
        /// Safe - doesn't leak anything
        /// </SecurityNote>
        [SecuritySafeCritical]
        public XmlDataContract() : base(new XmlDataContractCriticalHelper())
        {
            _helper = base.Helper as XmlDataContractCriticalHelper;
        }
 
        /// <SecurityNote>
        /// Critical - initializes SecurityCritical field 'helper'
        /// Safe - doesn't leak anything
        /// </SecurityNote>
        [SecuritySafeCritical]
        internal XmlDataContract(Type type) : base(new XmlDataContractCriticalHelper(type))
        {
            _helper = base.Helper as XmlDataContractCriticalHelper;
        }
 
        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 bool IsAnonymous
        {
            /// <SecurityNote>
            /// Critical - fetches the critical IsAnonymous property
            /// Safe - IsAnonymous only needs to be protected for write
            /// </SecurityNote>
            [SecuritySafeCritical]
            get
            { return _helper.IsAnonymous; }
        }
 
        public override bool HasRoot
        {
            /// <SecurityNote>
            /// Critical - fetches the critical HasRoot property
            /// Safe - HasRoot only needs to be protected for write
            /// </SecurityNote>
            [SecuritySafeCritical]
            get
            { return _helper.HasRoot; }
            /// <SecurityNote>
            /// Critical - sets the critical HasRoot property
            /// </SecurityNote>
            [SecurityCritical]
            set
            { _helper.HasRoot = value; }
        }
 
        public override XmlDictionaryString TopLevelElementName
        {
            /// <SecurityNote>
            /// Critical - fetches the critical TopLevelElementName property
            /// Safe - TopLevelElementName only needs to be protected for write
            /// </SecurityNote>
            [SecuritySafeCritical]
            get
            { return _helper.TopLevelElementName; }
            /// <SecurityNote>
            /// Critical - sets the critical TopLevelElementName property
            /// </SecurityNote>
            [SecurityCritical]
            set
            { _helper.TopLevelElementName = value; }
        }
 
        internal XmlSchemaType XsdType
        {
            // TODO: [Fx.Tag.SecurityNote(Critical = "Fetches the critical XsdType property.",
            //    Safe = "XsdType only needs to be protected for write.")]
            [SecuritySafeCritical]
            get { return _helper.XsdType; }
 
            // TODO: [Fx.Tag.SecurityNote(Critical = "Sets the critical XsdType property.")]
            [SecurityCritical]
            set { _helper.XsdType = value; }
        }
 
        public override XmlDictionaryString TopLevelElementNamespace
        {
            /// <SecurityNote>
            /// Critical - fetches the critical TopLevelElementNamespace property
            /// Safe - TopLevelElementNamespace only needs to be protected for write
            /// </SecurityNote>
            [SecuritySafeCritical]
            get
            { return _helper.TopLevelElementNamespace; }
            /// <SecurityNote>
            /// Critical - sets the critical TopLevelElementNamespace property
            /// </SecurityNote>
            [SecurityCritical]
            set
            { _helper.TopLevelElementNamespace = value; }
        }
 
        internal bool IsTopLevelElementNullable
        {
            // TODO: [Fx.Tag.SecurityNote(Critical = "Fetches the critical IsTopLevelElementNullable property.",
            //    Safe = "IsTopLevelElementNullable only needs to be protected for write.")]
            [SecuritySafeCritical]
            get { return _helper.IsTopLevelElementNullable; }
 
            // TODO: [Fx.Tag.SecurityNote(Critical = "Sets the critical IsTopLevelElementNullable property.")]
            [SecurityCritical]
            set { _helper.IsTopLevelElementNullable = value; }
        }
 
        internal bool IsTypeDefinedOnImport
        {
            // TODO: [Fx.Tag.SecurityNote(Critical = "Fetches the critical IsTypeDefinedOnImport property.",
            //    Safe = "IsTypeDefinedOnImport only needs to be protected for write.")]
            [SecuritySafeCritical]
            get { return _helper.IsTypeDefinedOnImport; }
 
            // TODO: [Fx.Tag.SecurityNote(Critical = "Sets the critical IsTypeDefinedOnImport property.")]
            [SecurityCritical]
            set { _helper.IsTypeDefinedOnImport = value; }
        }
 
#if !NET_NATIVE
        internal CreateXmlSerializableDelegate CreateXmlSerializableDelegate
        {
            /// <SecurityNote>
            /// Critical - fetches the critical CreateXmlSerializableDelegate property
            /// Safe - CreateXmlSerializableDelegate only needs to be protected for write; initialized in getter if null
            /// </SecurityNote>
            [SecuritySafeCritical]
            get
            {
                if (_helper.CreateXmlSerializableDelegate == null)
                {
                    lock (this)
                    {
                        if (_helper.CreateXmlSerializableDelegate == null)
                        {
                            CreateXmlSerializableDelegate tempCreateXmlSerializable = GenerateCreateXmlSerializableDelegate();
                            Interlocked.MemoryBarrier();
                            _helper.CreateXmlSerializableDelegate = tempCreateXmlSerializable;
                        }
                    }
                }
                return _helper.CreateXmlSerializableDelegate;
            }
        }
#else
        public CreateXmlSerializableDelegate CreateXmlSerializableDelegate { get; set; }
#endif
 
        internal override bool CanContainReferences
        {
            get { return false; }
        }
 
        public override bool IsBuiltInDataContract
        {
            get
            {
                return UnderlyingType == Globals.TypeOfXmlElement || UnderlyingType == Globals.TypeOfXmlNodeArray;
            }
        }
 
        /// <SecurityNote>
        /// Critical - holds all state used for for (de)serializing XML types.
        ///            since the data is cached statically, we lock down access to it.
        /// </SecurityNote>
        [SecurityCritical]
        private class XmlDataContractCriticalHelper : DataContract.DataContractCriticalHelper
        {
            private DataContractDictionary _knownDataContracts;
            private bool _isKnownTypeAttributeChecked;
            private XmlDictionaryString _topLevelElementName;
            private XmlDictionaryString _topLevelElementNamespace;
            private bool _isTopLevelElementNullable;
            private bool _isTypeDefinedOnImport;
            private XmlSchemaType _xsdType;
            private bool _hasRoot;
            private CreateXmlSerializableDelegate _createXmlSerializable;
 
            internal XmlDataContractCriticalHelper()
            {
            }
 
            internal XmlDataContractCriticalHelper(Type type) : base(type)
            {
                if (type.GetTypeInfo().IsAttributeDefined(Globals.TypeOfDataContractAttribute))
                    throw /*System.Runtime.Serialization.*/DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(string.Format(SRSerialization.IXmlSerializableCannotHaveDataContract, DataContract.GetClrTypeFullName(type))));
                if (type.GetTypeInfo().IsAttributeDefined(Globals.TypeOfCollectionDataContractAttribute))
                    throw /*System.Runtime.Serialization.*/DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(string.Format(SRSerialization.IXmlSerializableCannotHaveCollectionDataContract, DataContract.GetClrTypeFullName(type))));
                XmlSchemaType xsdType;
                bool hasRoot;
                XmlQualifiedName stableName;
                SchemaExporter.GetXmlTypeInfo(type, out stableName, out xsdType, out hasRoot);
                this.StableName = stableName;
                this.XsdType = xsdType;
                this.HasRoot = hasRoot;
                XmlDictionary dictionary = new XmlDictionary();
                this.Name = dictionary.Add(StableName.Name);
                this.Namespace = dictionary.Add(StableName.Namespace);
                object[] xmlRootAttributes = (UnderlyingType == null) ? null : UnderlyingType.GetTypeInfo().GetCustomAttributes(Globals.TypeOfXmlRootAttribute, false).ToArray();
                if (xmlRootAttributes == null || xmlRootAttributes.Length == 0)
                {
                    if (hasRoot)
                    {
                        _topLevelElementName = Name;
                        _topLevelElementNamespace = (this.StableName.Namespace == Globals.SchemaNamespace) ? DictionaryGlobals.EmptyString : Namespace;
                    }
                }
                else
                {
                    if (hasRoot)
                    {
                        XmlRootAttribute xmlRootAttribute = (XmlRootAttribute)xmlRootAttributes[0];
                        _isTopLevelElementNullable = xmlRootAttribute.IsNullable;
                        string elementName = xmlRootAttribute.ElementName;
                        _topLevelElementName = (elementName == null || elementName.Length == 0) ? Name : dictionary.Add(DataContract.EncodeLocalName(elementName));
                        string elementNs = xmlRootAttribute.Namespace;
                        _topLevelElementNamespace = (elementNs == null || elementNs.Length == 0) ? DictionaryGlobals.EmptyString : dictionary.Add(elementNs);
                    }
                    else
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(string.Format(SRSerialization.IsAnyCannotHaveXmlRoot, DataContract.GetClrTypeFullName(UnderlyingType))));
                    }
                }
            }
 
            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 XmlSchemaType XsdType
            {
                get { return _xsdType; }
                set { _xsdType = value; }
            }
 
            internal bool IsAnonymous
            {
                get { return false; }
            }
 
            internal override bool HasRoot
            {
                get { return _hasRoot; }
                set { _hasRoot = value; }
            }
 
            internal override XmlDictionaryString TopLevelElementName
            {
                [SecurityCritical]
                get
                { return _topLevelElementName; }
                [SecurityCritical]
                set
                { _topLevelElementName = value; }
            }
 
            internal override XmlDictionaryString TopLevelElementNamespace
            {
                [SecurityCritical]
                get
                { return _topLevelElementNamespace; }
                [SecurityCritical]
                set
                { _topLevelElementNamespace = value; }
            }
 
            internal bool IsTopLevelElementNullable
            {
                get { return _isTopLevelElementNullable; }
                set { _isTopLevelElementNullable = value; }
            }
 
            internal bool IsTypeDefinedOnImport
            {
                get { return _isTypeDefinedOnImport; }
                set { _isTypeDefinedOnImport = value; }
            }
 
            internal CreateXmlSerializableDelegate CreateXmlSerializableDelegate
            {
                get { return _createXmlSerializable; }
                set { _createXmlSerializable = value; }
            }
        }
 
        private ConstructorInfo GetConstructor()
        {
            Type type = UnderlyingType;
 
            if (type.GetTypeInfo().IsValueType)
                return null;
 
            ConstructorInfo ctor = type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, Array.Empty<Type>());
            if (ctor == null)
                throw /*System.Runtime.Serialization.*/DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(string.Format(SRSerialization.IXmlSerializableMustHaveDefaultConstructor, DataContract.GetClrTypeFullName(type))));
 
            return ctor;
        }
 
        //TODO: [Fx.Tag.SecurityNote(Critical = "Sets critical properties on XmlDataContract.")]
        [SecurityCritical]
        internal void SetTopLevelElementName(XmlQualifiedName elementName)
        {
            if (elementName != null)
            {
                XmlDictionary dictionary = new XmlDictionary();
                this.TopLevelElementName = dictionary.Add(elementName.Name);
                this.TopLevelElementNamespace = dictionary.Add(elementName.Namespace);
            }
        }
 
 
#if !NET_NATIVE
        /// <SecurityNote>
        /// Critical - calls CodeGenerator.BeginMethod which is SecurityCritical
        /// Safe - self-contained: returns the delegate to the generated IL but otherwise all IL generation is self-contained here
        /// </SecurityNote>
        [SecuritySafeCritical]
        internal CreateXmlSerializableDelegate GenerateCreateXmlSerializableDelegate()
        {
            Type type = this.UnderlyingType;
            CodeGenerator ilg = new CodeGenerator();
            bool memberAccessFlag = RequiresMemberAccessForCreate(null) && !(type.FullName == "System.Xml.Linq.XElement");
            try
            {
                ilg.BeginMethod("Create" + DataContract.GetClrTypeFullName(type), typeof(CreateXmlSerializableDelegate), memberAccessFlag);
            }
            catch (SecurityException securityException)
            {
                if (memberAccessFlag)
                {
                    RequiresMemberAccessForCreate(securityException);
                }
                else
                {
                    throw;
                }
            }
            if (type.GetTypeInfo().IsValueType)
            {
                System.Reflection.Emit.LocalBuilder local = ilg.DeclareLocal(type, type.Name + "Value");
                ilg.Ldloca(local);
                ilg.InitObj(type);
                ilg.Ldloc(local);
            }
            else
            {
                // Special case XElement
                // codegen the same as 'internal XElement : this("default") { }'
                ConstructorInfo ctor = GetConstructor();
                if (!ctor.IsPublic && type.FullName == "System.Xml.Linq.XElement")
                {
                    Type xName = type.GetTypeInfo().Assembly.GetType("System.Xml.Linq.XName");
                    if (xName != null)
                    {
                        MethodInfo XName_op_Implicit = xName.GetMethod(
                            "op_Implicit",
                            BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public,
                            new Type[] { typeof(String) }
                            );
                        ConstructorInfo XElement_ctor = type.GetConstructor(
                            BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public,
                            new Type[] { xName }
                            );
                        if (XName_op_Implicit != null && XElement_ctor != null)
                        {
                            ilg.Ldstr("default");
                            ilg.Call(XName_op_Implicit);
                            ctor = XElement_ctor;
                        }
                    }
                }
                ilg.New(ctor);
            }
            ilg.ConvertValue(this.UnderlyingType, Globals.TypeOfIXmlSerializable);
            ilg.Ret();
            return (CreateXmlSerializableDelegate)ilg.EndMethod();
        }
 
        /// <SecurityNote>
        /// Review - calculates whether this Xml type 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>
        private bool RequiresMemberAccessForCreate(SecurityException securityException)
        {
            if (!IsTypeVisible(UnderlyingType))
            {
                if (securityException != null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        new SecurityException(string.Format(SRSerialization.PartialTrustIXmlSerializableTypeNotPublic, DataContract.GetClrTypeFullName(UnderlyingType)),
                        securityException));
                }
                return true;
            }
 
            if (ConstructorRequiresMemberAccess(GetConstructor()))
            {
                if (securityException != null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        new SecurityException(string.Format(SRSerialization.PartialTrustIXmlSerialzableNoPublicConstructor, DataContract.GetClrTypeFullName(UnderlyingType)),
                        securityException));
                }
                return true;
            }
 
            return false;
        }
#endif
 
        public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context)
        {
            if (context == null)
                XmlObjectSerializerWriteContext.WriteRootIXmlSerializable(xmlWriter, obj);
            else
                context.WriteIXmlSerializable(xmlWriter, obj);
        }
 
        public override object ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
        {
            object o;
            if (context == null)
            {
                o = XmlObjectSerializerReadContext.ReadRootIXmlSerializable(xmlReader, this, true /*isMemberType*/);
            }
            else
            {
                o = context.ReadIXmlSerializable(xmlReader, this, true /*isMemberType*/);
                context.AddNewObject(o);
            }
            xmlReader.ReadEndElement();
            return o;
        }
    }
}