eb4fef4a1cc4a361b10d2f29133628b2e6d2f2cf
[mono.git] / mcs / class / referencesource / System.Runtime.Serialization / System / Runtime / Serialization / XmlDataContract.cs
1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //-----------------------------------------------------------------------------
4 namespace System.Runtime.Serialization
5 {
6     using System;
7     using System.Collections.Generic;
8     using System.Reflection;
9     using System.Security;
10     using System.Security.Permissions;
11     using System.Threading;
12     using System.Xml;
13     using System.Xml.Schema;
14     using System.Xml.Serialization;
15     using DataContractDictionary = System.Collections.Generic.Dictionary<System.Xml.XmlQualifiedName, DataContract>;
16
17     internal delegate IXmlSerializable CreateXmlSerializableDelegate();
18 #if USE_REFEMIT
19     public sealed class XmlDataContract : DataContract
20 #else
21     internal sealed class XmlDataContract : DataContract
22 #endif
23     {
24         [Fx.Tag.SecurityNote(Critical = "Holds instance of CriticalHelper which keeps state that is cached statically for serialization."
25             + " Static fields are marked SecurityCritical or readonly to prevent data from being modified or leaked to other components in appdomain.")]
26         [SecurityCritical]
27         XmlDataContractCriticalHelper helper;
28
29         [Fx.Tag.SecurityNote(Critical = "Initializes SecurityCritical field 'helper'.",
30             Safe = "Doesn't leak anything.")]
31         [SecuritySafeCritical]
32         internal XmlDataContract()
33             : base(new XmlDataContractCriticalHelper())
34         {
35             helper = base.Helper as XmlDataContractCriticalHelper;
36         }
37
38         [Fx.Tag.SecurityNote(Critical = "Initializes SecurityCritical field 'helper'.",
39             Safe = "Doesn't leak anything.")]
40         [SecuritySafeCritical]
41         internal XmlDataContract(Type type)
42             : base(new XmlDataContractCriticalHelper(type))
43         {
44             helper = base.Helper as XmlDataContractCriticalHelper;
45         }
46
47         internal override DataContractDictionary KnownDataContracts
48         {
49             [Fx.Tag.SecurityNote(Critical = "Fetches the critical KnownDataContracts property.",
50                 Safe = "KnownDataContracts only needs to be protected for write.")]
51             [SecuritySafeCritical]
52             get { return helper.KnownDataContracts; }
53
54             [Fx.Tag.SecurityNote(Critical = "Sets the critical KnownDataContracts property.")]
55             [SecurityCritical]
56             set { helper.KnownDataContracts = value; }
57         }
58
59         internal XmlSchemaType XsdType
60         {
61             [Fx.Tag.SecurityNote(Critical = "Fetches the critical XsdType property.",
62                 Safe = "XsdType only needs to be protected for write.")]
63             [SecuritySafeCritical]
64             get { return helper.XsdType; }
65
66             [Fx.Tag.SecurityNote(Critical = "Sets the critical XsdType property.")]
67             [SecurityCritical]
68             set { helper.XsdType = value; }
69         }
70
71         internal bool IsAnonymous
72         {
73             [Fx.Tag.SecurityNote(Critical = "Fetches the critical IsAnonymous property.",
74                 Safe = "IsAnonymous only needs to be protected for write.")]
75             [SecuritySafeCritical]
76             get { return helper.IsAnonymous; }
77         }
78
79         internal override bool HasRoot
80         {
81             [Fx.Tag.SecurityNote(Critical = "Fetches the critical HasRoot property.",
82                 Safe = "HasRoot only needs to be protected for write.")]
83             [SecuritySafeCritical]
84             get { return helper.HasRoot; }
85
86             [Fx.Tag.SecurityNote(Critical = "Sets the critical HasRoot property.")]
87             [SecurityCritical]
88             set { helper.HasRoot = value; }
89         }
90
91         internal override XmlDictionaryString TopLevelElementName
92         {
93             [Fx.Tag.SecurityNote(Critical = "Fetches the critical TopLevelElementName property.",
94                 Safe = "TopLevelElementName only needs to be protected for write.")]
95             [SecuritySafeCritical]
96             get { return helper.TopLevelElementName; }
97
98             [Fx.Tag.SecurityNote(Critical = "Sets the critical TopLevelElementName property.")]
99             [SecurityCritical]
100             set { helper.TopLevelElementName = value; }
101         }
102
103         internal override XmlDictionaryString TopLevelElementNamespace
104         {
105             [Fx.Tag.SecurityNote(Critical = "Fetches the critical TopLevelElementNamespace property.",
106                 Safe = "TopLevelElementNamespace only needs to be protected for write.")]
107             [SecuritySafeCritical]
108             get { return helper.TopLevelElementNamespace; }
109
110             [Fx.Tag.SecurityNote(Critical = "Sets the critical TopLevelElementNamespace property.")]
111             [SecurityCritical]
112             set { helper.TopLevelElementNamespace = value; }
113         }
114
115         internal bool IsTopLevelElementNullable
116         {
117             [Fx.Tag.SecurityNote(Critical = "Fetches the critical IsTopLevelElementNullable property.",
118                 Safe = "IsTopLevelElementNullable only needs to be protected for write.")]
119             [SecuritySafeCritical]
120             get { return helper.IsTopLevelElementNullable; }
121
122             [Fx.Tag.SecurityNote(Critical = "Sets the critical IsTopLevelElementNullable property.")]
123             [SecurityCritical]
124             set { helper.IsTopLevelElementNullable = value; }
125         }
126
127         internal bool IsTypeDefinedOnImport
128         {
129             [Fx.Tag.SecurityNote(Critical = "Fetches the critical IsTypeDefinedOnImport property.",
130                 Safe = "IsTypeDefinedOnImport only needs to be protected for write.")]
131             [SecuritySafeCritical]
132             get { return helper.IsTypeDefinedOnImport; }
133
134             [Fx.Tag.SecurityNote(Critical = "Sets the critical IsTypeDefinedOnImport property.")]
135             [SecurityCritical]
136             set { helper.IsTypeDefinedOnImport = value; }
137         }
138
139         internal CreateXmlSerializableDelegate CreateXmlSerializableDelegate
140         {
141             [Fx.Tag.SecurityNote(Critical = "Fetches the critical CreateXmlSerializableDelegate property.",
142                 Safe = "CreateXmlSerializableDelegate only needs to be protected for write; initialized in getter if null.")]
143             [SecuritySafeCritical]
144             get
145             {
146                 if (helper.CreateXmlSerializableDelegate == null)
147                 {
148                     lock (this)
149                     {
150                         if (helper.CreateXmlSerializableDelegate == null)
151                         {
152                             CreateXmlSerializableDelegate tempCreateXmlSerializable = GenerateCreateXmlSerializableDelegate();
153                             Thread.MemoryBarrier();
154                             helper.CreateXmlSerializableDelegate = tempCreateXmlSerializable;
155                         }
156                     }
157                 }
158                 return helper.CreateXmlSerializableDelegate;
159             }
160         }
161
162         internal override bool CanContainReferences
163         {
164             get { return false; }
165         }
166
167         internal override bool IsBuiltInDataContract
168         {
169             get
170             {
171                 return UnderlyingType == Globals.TypeOfXmlElement || UnderlyingType == Globals.TypeOfXmlNodeArray;
172             }
173         }
174
175         [Fx.Tag.SecurityNote(Critical = "Holds all state used for for (de)serializing XML types."
176             + " Since the data is cached statically, we lock down access to it.")]
177         [SecurityCritical(SecurityCriticalScope.Everything)]
178         class XmlDataContractCriticalHelper : DataContract.DataContractCriticalHelper
179         {
180             DataContractDictionary knownDataContracts;
181             bool isKnownTypeAttributeChecked;
182             XmlDictionaryString topLevelElementName;
183             XmlDictionaryString topLevelElementNamespace;
184             bool isTopLevelElementNullable;
185             bool isTypeDefinedOnImport;
186             XmlSchemaType xsdType;
187             bool hasRoot;
188             CreateXmlSerializableDelegate createXmlSerializable;
189
190             internal XmlDataContractCriticalHelper()
191             {
192             }
193
194             internal XmlDataContractCriticalHelper(Type type)
195                 : base(type)
196             {
197                 if (type.IsDefined(Globals.TypeOfDataContractAttribute, false))
198                     throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.IXmlSerializableCannotHaveDataContract, DataContract.GetClrTypeFullName(type))));
199                 if (type.IsDefined(Globals.TypeOfCollectionDataContractAttribute, false))
200                     throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.IXmlSerializableCannotHaveCollectionDataContract, DataContract.GetClrTypeFullName(type))));
201                 XmlSchemaType xsdType;
202                 bool hasRoot;
203                 XmlQualifiedName stableName;
204                 SchemaExporter.GetXmlTypeInfo(type, out stableName, out xsdType, out hasRoot);
205                 this.StableName = stableName;
206                 this.XsdType = xsdType;
207                 this.HasRoot = hasRoot;
208                 XmlDictionary dictionary = new XmlDictionary();
209                 this.Name = dictionary.Add(StableName.Name);
210                 this.Namespace = dictionary.Add(StableName.Namespace);
211                 object[] xmlRootAttributes = (UnderlyingType == null) ? null : UnderlyingType.GetCustomAttributes(Globals.TypeOfXmlRootAttribute, false);
212                 if (xmlRootAttributes == null || xmlRootAttributes.Length == 0)
213                 {
214                     if (hasRoot)
215                     {
216                         topLevelElementName = Name;
217                         topLevelElementNamespace = (this.StableName.Namespace == Globals.SchemaNamespace) ? DictionaryGlobals.EmptyString : Namespace;
218                         isTopLevelElementNullable = true;
219                     }
220                 }
221                 else
222                 {
223                     if (hasRoot)
224                     {
225                         XmlRootAttribute xmlRootAttribute = (XmlRootAttribute)xmlRootAttributes[0];
226                         isTopLevelElementNullable = xmlRootAttribute.IsNullable;
227                         string elementName = xmlRootAttribute.ElementName;
228                         topLevelElementName = (elementName == null || elementName.Length == 0) ? Name : dictionary.Add(DataContract.EncodeLocalName(elementName));
229                         string elementNs = xmlRootAttribute.Namespace;
230                         topLevelElementNamespace = (elementNs == null || elementNs.Length == 0) ? DictionaryGlobals.EmptyString : dictionary.Add(elementNs);
231                     }
232                     else
233                     {
234                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.IsAnyCannotHaveXmlRoot, DataContract.GetClrTypeFullName(UnderlyingType))));
235                     }
236                 }
237             }
238
239             internal override DataContractDictionary KnownDataContracts
240             {
241                 get
242                 {
243                     if (!isKnownTypeAttributeChecked && UnderlyingType != null)
244                     {
245                         lock (this)
246                         {
247                             if (!isKnownTypeAttributeChecked)
248                             {
249                                 knownDataContracts = DataContract.ImportKnownTypeAttributes(this.UnderlyingType);
250                                 Thread.MemoryBarrier();
251                                 isKnownTypeAttributeChecked = true;
252                             }
253                         }
254                     }
255                     return knownDataContracts;
256                 }
257                 set { knownDataContracts = value; }
258             }
259
260             internal XmlSchemaType XsdType
261             {
262                 get { return xsdType; }
263                 set { xsdType = value; }
264             }
265
266             internal bool IsAnonymous
267             {
268                 get { return xsdType != null; }
269             }
270
271             internal override bool HasRoot
272             {
273                 get { return hasRoot; }
274                 set { hasRoot = value; }
275             }
276
277             internal override XmlDictionaryString TopLevelElementName
278             {
279                 get { return topLevelElementName; }
280                 set { topLevelElementName = value; }
281             }
282
283             internal override XmlDictionaryString TopLevelElementNamespace
284             {
285                 get { return topLevelElementNamespace; }
286                 set { topLevelElementNamespace = value; }
287             }
288
289             internal bool IsTopLevelElementNullable
290             {
291                 get { return isTopLevelElementNullable; }
292                 set { isTopLevelElementNullable = value; }
293             }
294
295             internal bool IsTypeDefinedOnImport
296             {
297                 get { return isTypeDefinedOnImport; }
298                 set { isTypeDefinedOnImport = value; }
299             }
300
301             internal CreateXmlSerializableDelegate CreateXmlSerializableDelegate
302             {
303                 get { return createXmlSerializable; }
304                 set { createXmlSerializable = value; }
305             }
306
307         }
308
309         ConstructorInfo GetConstructor()
310         {
311             Type type = UnderlyingType;
312
313             if (type.IsValueType)
314                 return null;
315
316             ConstructorInfo ctor = type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, Globals.EmptyTypeArray, null);
317             if (ctor == null)
318                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.IXmlSerializableMustHaveDefaultConstructor, DataContract.GetClrTypeFullName(type))));
319
320             return ctor;
321         }
322
323         [Fx.Tag.SecurityNote(Critical = "Sets critical properties on XmlDataContract.")]
324         [SecurityCritical]
325         internal void SetTopLevelElementName(XmlQualifiedName elementName)
326         {
327             if (elementName != null)
328             {
329                 XmlDictionary dictionary = new XmlDictionary();
330                 this.TopLevelElementName = dictionary.Add(elementName.Name);
331                 this.TopLevelElementNamespace = dictionary.Add(elementName.Namespace);
332             }
333         }
334
335         [Fx.Tag.SecurityNote(Critical = "Calls CodeGenerator.BeginMethod which is SecurityCritical.",
336             Safe = "Self-contained: returns the delegate to the generated IL but otherwise all IL generation is self-contained here.")]
337         [SecuritySafeCritical]
338         internal CreateXmlSerializableDelegate GenerateCreateXmlSerializableDelegate()
339         {
340             Type type = this.UnderlyingType;
341             CodeGenerator ilg = new CodeGenerator();
342             bool memberAccessFlag = RequiresMemberAccessForCreate(null);
343             try
344             {
345                 ilg.BeginMethod("Create" + DataContract.GetClrTypeFullName(type), typeof(CreateXmlSerializableDelegate), memberAccessFlag);
346             }
347             catch (SecurityException securityException)
348             {
349                 if (memberAccessFlag && securityException.PermissionType.Equals(typeof(ReflectionPermission)))
350                 {
351                     RequiresMemberAccessForCreate(securityException);
352                 }
353                 else
354                 {
355                     throw;
356                 }
357             }
358             if (type.IsValueType)
359             {
360                 System.Reflection.Emit.LocalBuilder local = ilg.DeclareLocal(type, type.Name + "Value");
361                 ilg.Ldloca(local);
362                 ilg.InitObj(type);
363                 ilg.Ldloc(local);
364             }
365             else
366             {
367                 ilg.New(GetConstructor());
368             }
369             ilg.ConvertValue(this.UnderlyingType, Globals.TypeOfIXmlSerializable);
370             ilg.Ret();
371             return (CreateXmlSerializableDelegate)ilg.EndMethod();
372         }
373
374         [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Calculates whether this Xml type requires MemberAccessPermission for deserialization."
375             + " Since this information is used to determine whether to give the generated code access"
376             + " permissions to private members, any changes to the logic should be reviewed.")]
377         bool RequiresMemberAccessForCreate(SecurityException securityException)
378         {
379             if (!IsTypeVisible(UnderlyingType))
380             {
381                 if (securityException != null)
382                 {
383                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
384                         new SecurityException(SR.GetString(SR.PartialTrustIXmlSerializableTypeNotPublic, DataContract.GetClrTypeFullName(UnderlyingType)),
385                         securityException));
386                 }
387                 return true;
388             }
389
390             if (ConstructorRequiresMemberAccess(GetConstructor()))
391             {
392                 if (securityException != null)
393                 {
394                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
395                         new SecurityException(SR.GetString(SR.PartialTrustIXmlSerialzableNoPublicConstructor, DataContract.GetClrTypeFullName(UnderlyingType)),
396                         securityException));
397                 }
398                 return true;
399             }
400
401             return false;
402         }
403
404         internal override bool Equals(object other, Dictionary<DataContractPairKey, object> checkedContracts)
405         {
406             if (IsEqualOrChecked(other, checkedContracts))
407                 return true;
408
409             XmlDataContract dataContract = other as XmlDataContract;
410             if (dataContract != null)
411             {
412                 if (this.HasRoot != dataContract.HasRoot)
413                     return false;
414
415                 if (this.IsAnonymous)
416                 {
417                     return dataContract.IsAnonymous;
418                 }
419                 else
420                 {
421                     return (StableName.Name == dataContract.StableName.Name && StableName.Namespace == dataContract.StableName.Namespace);
422                 }
423             }
424             return false;
425         }
426
427         public override int GetHashCode()
428         {
429             return base.GetHashCode();
430         }
431
432         public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context)
433         {
434             if (context == null)
435                 XmlObjectSerializerWriteContext.WriteRootIXmlSerializable(xmlWriter, obj);
436             else
437                 context.WriteIXmlSerializable(xmlWriter, obj);
438         }
439
440         public override object ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
441         {
442             object o;
443             if (context == null)
444             {
445                 o = XmlObjectSerializerReadContext.ReadRootIXmlSerializable(xmlReader, this, true /*isMemberType*/);
446             }
447             else
448             {
449                 o = context.ReadIXmlSerializable(xmlReader, this, true /*isMemberType*/);
450                 context.AddNewObject(o);
451             }
452             xmlReader.ReadEndElement();
453             return o;
454         }
455
456     }
457 }
458