0fe5d5ecd1c717659a4da2c9c52dcd8fbf3ffcc0
[mono.git] / mcs / class / referencesource / System.Runtime.Serialization / System / Runtime / Serialization / XmlFormatWriterGenerator.cs
1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //-----------------------------------------------------------------------------
4 namespace System.Runtime.Serialization
5 {
6     using System;
7     using System.Collections;
8     using System.Collections.Generic;
9     using System.Reflection;
10     using System.Reflection.Emit;
11     using System.Security;
12     using System.Security.Permissions;
13     using System.Runtime.Serialization.Diagnostics.Application;
14     using System.Xml;
15
16 #if USE_REFEMIT
17     public delegate void XmlFormatClassWriterDelegate(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context,ClassDataContract dataContract);
18     public delegate void XmlFormatCollectionWriterDelegate(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context, CollectionDataContract dataContract);
19
20     public sealed class XmlFormatWriterGenerator
21 #else
22     internal delegate void XmlFormatClassWriterDelegate(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context, ClassDataContract dataContract);
23     internal delegate void XmlFormatCollectionWriterDelegate(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context, CollectionDataContract dataContract);
24
25     internal sealed class XmlFormatWriterGenerator
26 #endif
27     {
28         [Fx.Tag.SecurityNote(Critical = "Holds instance of CriticalHelper which keeps state that was produced within an assert.")]
29         [SecurityCritical]
30         CriticalHelper helper;
31
32         [Fx.Tag.SecurityNote(Critical = "Initializes SecurityCritical field 'helper'.")]
33         [SecurityCritical]
34         public XmlFormatWriterGenerator()
35         {
36             helper = new CriticalHelper();
37         }
38
39         [Fx.Tag.SecurityNote(Critical = "Accesses SecurityCritical helper class 'CriticalHelper'.")]
40         [SecurityCritical]
41         internal XmlFormatClassWriterDelegate GenerateClassWriter(ClassDataContract classContract)
42         {
43             try
44             {
45                 if (TD.DCGenWriterStartIsEnabled())
46                 {
47                     TD.DCGenWriterStart("Class", classContract.UnderlyingType.FullName);
48                 }
49
50                 return helper.GenerateClassWriter(classContract);
51             }
52             finally
53             {
54                 if (TD.DCGenWriterStopIsEnabled())
55                 {
56                     TD.DCGenWriterStop();
57                 }
58             }
59         }
60
61         [Fx.Tag.SecurityNote(Critical = "Accesses SecurityCritical helper class 'CriticalHelper'.")]
62         [SecurityCritical]
63         internal XmlFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionDataContract collectionContract)
64         {
65             try
66             {
67                 if (TD.DCGenWriterStartIsEnabled())
68                 {
69                     TD.DCGenWriterStart("Collection", collectionContract.UnderlyingType.FullName);
70                 }
71
72                 return helper.GenerateCollectionWriter(collectionContract);
73
74             }
75             finally
76             {
77                 if (TD.DCGenWriterStopIsEnabled())
78                 {
79                     TD.DCGenWriterStop();
80                 }
81             }
82         }
83
84         [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Handles all aspects of IL generation including initializing the DynamicMethod."
85             + " Changes to how IL generated could affect how data is serialized and what gets access to data,"
86             + " therefore we mark it for review so that changes to generation logic are reviewed.")]
87         class CriticalHelper
88         {
89             CodeGenerator ilg;
90             ArgBuilder xmlWriterArg;
91             ArgBuilder contextArg;
92             ArgBuilder dataContractArg;
93             LocalBuilder objectLocal;
94
95             // Used for classes
96             LocalBuilder contractNamespacesLocal;
97             LocalBuilder memberNamesLocal;
98             LocalBuilder childElementNamespacesLocal;
99             int typeIndex = 1;
100             int childElementIndex = 0;
101
102             internal XmlFormatClassWriterDelegate GenerateClassWriter(ClassDataContract classContract)
103             {
104                 ilg = new CodeGenerator();
105                 bool memberAccessFlag = classContract.RequiresMemberAccessForWrite(null);
106                 try
107                 {
108                     ilg.BeginMethod("Write" + classContract.StableName.Name + "ToXml", Globals.TypeOfXmlFormatClassWriterDelegate, memberAccessFlag);
109                 }
110                 catch (SecurityException securityException)
111                 {
112                     if (memberAccessFlag && securityException.PermissionType.Equals(typeof(ReflectionPermission)))
113                     {
114                         classContract.RequiresMemberAccessForWrite(securityException);
115                     }
116                     else
117                     {
118                         throw;
119                     }
120                 }
121                 InitArgs(classContract.UnderlyingType);
122                 DemandSerializationFormatterPermission(classContract);
123                 DemandMemberAccessPermission(memberAccessFlag);
124                 if (classContract.IsReadOnlyContract)
125                 {
126                     ThrowIfCannotSerializeReadOnlyTypes(classContract);
127                 }
128                 WriteClass(classContract);
129                 return (XmlFormatClassWriterDelegate)ilg.EndMethod();
130             }
131
132             internal XmlFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionDataContract collectionContract)
133             {
134                 ilg = new CodeGenerator();
135                 bool memberAccessFlag = collectionContract.RequiresMemberAccessForWrite(null);
136                 try
137                 {
138                     ilg.BeginMethod("Write" + collectionContract.StableName.Name + "ToXml", Globals.TypeOfXmlFormatCollectionWriterDelegate, memberAccessFlag);
139                 }
140                 catch (SecurityException securityException)
141                 {
142                     if (memberAccessFlag && securityException.PermissionType.Equals(typeof(ReflectionPermission)))
143                     {
144                         collectionContract.RequiresMemberAccessForWrite(securityException);
145                     }
146                     else
147                     {
148                         throw;
149                     }
150                 }
151                 InitArgs(collectionContract.UnderlyingType);
152                 DemandMemberAccessPermission(memberAccessFlag);
153                 if (collectionContract.IsReadOnlyContract)
154                 {
155                     ThrowIfCannotSerializeReadOnlyTypes(collectionContract);
156                 }
157                 WriteCollection(collectionContract);
158                 return (XmlFormatCollectionWriterDelegate)ilg.EndMethod();
159             }
160
161             void InitArgs(Type objType)
162             {
163                 xmlWriterArg = ilg.GetArg(0);
164                 contextArg = ilg.GetArg(2);
165                 dataContractArg = ilg.GetArg(3);
166
167                 objectLocal = ilg.DeclareLocal(objType, "objSerialized");
168                 ArgBuilder objectArg = ilg.GetArg(1);
169                 ilg.Load(objectArg);
170
171                 // Copy the data from the DataTimeOffset object passed in to the DateTimeOffsetAdapter.
172                 // DateTimeOffsetAdapter is used here for serialization purposes to bypass the ISerializable implementation
173                 // on DateTimeOffset; which does not work in partial trust.
174
175                 if (objType == Globals.TypeOfDateTimeOffsetAdapter)
176                 {
177                     ilg.ConvertValue(objectArg.ArgType, Globals.TypeOfDateTimeOffset);
178                     ilg.Call(XmlFormatGeneratorStatics.GetDateTimeOffsetAdapterMethod);
179                 }
180                 else
181                 {
182                     ilg.ConvertValue(objectArg.ArgType, objType);
183                 }
184                 ilg.Stloc(objectLocal);
185             }
186
187             void DemandMemberAccessPermission(bool memberAccessFlag)
188             {
189                 if (memberAccessFlag)
190                 {
191                     ilg.Call(contextArg, XmlFormatGeneratorStatics.DemandMemberAccessPermissionMethod);
192                 }
193             }
194
195             void DemandSerializationFormatterPermission(ClassDataContract classContract)
196             {
197                 if (!classContract.HasDataContract && !classContract.IsNonAttributedType)
198                 {
199                     ilg.Call(contextArg, XmlFormatGeneratorStatics.DemandSerializationFormatterPermissionMethod);
200                 }
201             }
202
203             void ThrowIfCannotSerializeReadOnlyTypes(ClassDataContract classContract)
204             {
205                 ThrowIfCannotSerializeReadOnlyTypes(XmlFormatGeneratorStatics.ClassSerializationExceptionMessageProperty);
206             }
207
208             void ThrowIfCannotSerializeReadOnlyTypes(CollectionDataContract classContract)
209             {
210                 ThrowIfCannotSerializeReadOnlyTypes(XmlFormatGeneratorStatics.CollectionSerializationExceptionMessageProperty);
211             }
212
213             void ThrowIfCannotSerializeReadOnlyTypes(PropertyInfo serializationExceptionMessageProperty)
214             {
215                 ilg.Load(contextArg);
216                 ilg.LoadMember(XmlFormatGeneratorStatics.SerializeReadOnlyTypesProperty);
217                 ilg.IfNot();
218                 ilg.Load(dataContractArg);
219                 ilg.LoadMember(serializationExceptionMessageProperty);
220                 ilg.Load(null);
221                 ilg.Call(XmlFormatGeneratorStatics.ThrowInvalidDataContractExceptionMethod);
222                 ilg.EndIf();
223             }
224
225             void InvokeOnSerializing(ClassDataContract classContract)
226             {
227                 if (classContract.BaseContract != null)
228                     InvokeOnSerializing(classContract.BaseContract);
229                 if (classContract.OnSerializing != null)
230                 {
231                     ilg.LoadAddress(objectLocal);
232                     ilg.Load(contextArg);
233                     ilg.Call(XmlFormatGeneratorStatics.GetStreamingContextMethod);
234                     ilg.Call(classContract.OnSerializing);
235                 }
236             }
237
238             void InvokeOnSerialized(ClassDataContract classContract)
239             {
240                 if (classContract.BaseContract != null)
241                     InvokeOnSerialized(classContract.BaseContract);
242                 if (classContract.OnSerialized != null)
243                 {
244                     ilg.LoadAddress(objectLocal);
245                     ilg.Load(contextArg);
246                     ilg.Call(XmlFormatGeneratorStatics.GetStreamingContextMethod);
247                     ilg.Call(classContract.OnSerialized);
248                 }
249             }
250
251             void WriteClass(ClassDataContract classContract)
252             {
253                 InvokeOnSerializing(classContract);
254
255                 if (classContract.IsISerializable)
256                     ilg.Call(contextArg, XmlFormatGeneratorStatics.WriteISerializableMethod, xmlWriterArg, objectLocal);
257                 else
258                 {
259                     if (classContract.ContractNamespaces.Length > 1)
260                     {
261                         contractNamespacesLocal = ilg.DeclareLocal(typeof(XmlDictionaryString[]), "contractNamespaces");
262                         ilg.Load(dataContractArg);
263                         ilg.LoadMember(XmlFormatGeneratorStatics.ContractNamespacesField);
264                         ilg.Store(contractNamespacesLocal);
265                     }
266
267                     memberNamesLocal = ilg.DeclareLocal(typeof(XmlDictionaryString[]), "memberNames");
268                     ilg.Load(dataContractArg);
269                     ilg.LoadMember(XmlFormatGeneratorStatics.MemberNamesField);
270                     ilg.Store(memberNamesLocal);
271
272                     for (int i = 0; i < classContract.ChildElementNamespaces.Length; i++)
273                     {
274                         if (classContract.ChildElementNamespaces[i] != null)
275                         {
276                             childElementNamespacesLocal = ilg.DeclareLocal(typeof(XmlDictionaryString[]), "childElementNamespaces");
277                             ilg.Load(dataContractArg);
278                             ilg.LoadMember(XmlFormatGeneratorStatics.ChildElementNamespacesProperty);
279                             ilg.Store(childElementNamespacesLocal);
280                         }
281                     }
282
283                     if (classContract.HasExtensionData)
284                     {
285                         LocalBuilder extensionDataLocal = ilg.DeclareLocal(Globals.TypeOfExtensionDataObject, "extensionData");
286                         ilg.Load(objectLocal);
287                         ilg.ConvertValue(objectLocal.LocalType, Globals.TypeOfIExtensibleDataObject);
288                         ilg.LoadMember(XmlFormatGeneratorStatics.ExtensionDataProperty);
289                         ilg.Store(extensionDataLocal);
290                         ilg.Call(contextArg, XmlFormatGeneratorStatics.WriteExtensionDataMethod, xmlWriterArg, extensionDataLocal, -1);
291                         WriteMembers(classContract, extensionDataLocal, classContract);
292                     }
293                     else
294                         WriteMembers(classContract, null, classContract);
295                 }
296                 InvokeOnSerialized(classContract);
297             }
298
299             int WriteMembers(ClassDataContract classContract, LocalBuilder extensionDataLocal, ClassDataContract derivedMostClassContract)
300             {
301                 int memberCount = (classContract.BaseContract == null) ? 0 :
302                     WriteMembers(classContract.BaseContract, extensionDataLocal, derivedMostClassContract);
303
304                 LocalBuilder namespaceLocal = ilg.DeclareLocal(typeof(XmlDictionaryString), "ns");
305                 if (contractNamespacesLocal == null)
306                 {
307                     ilg.Load(dataContractArg);
308                     ilg.LoadMember(XmlFormatGeneratorStatics.NamespaceProperty);
309                 }
310                 else
311                     ilg.LoadArrayElement(contractNamespacesLocal, typeIndex - 1);
312                 ilg.Store(namespaceLocal);
313
314                 ilg.Call(contextArg, XmlFormatGeneratorStatics.IncrementItemCountMethod, classContract.Members.Count);
315
316                 for (int i = 0; i < classContract.Members.Count; i++, memberCount++)
317                 {
318                     DataMember member = classContract.Members[i];
319                     Type memberType = member.MemberType;
320                     LocalBuilder memberValue = null;
321                     if (member.IsGetOnlyCollection)
322                     {
323                         ilg.Load(contextArg);
324                         ilg.Call(XmlFormatGeneratorStatics.StoreIsGetOnlyCollectionMethod);
325                     }
326                     if (!member.EmitDefaultValue)
327                     {
328                         memberValue = LoadMemberValue(member);
329                         ilg.IfNotDefaultValue(memberValue);
330                     }
331                     bool writeXsiType = CheckIfMemberHasConflict(member, classContract, derivedMostClassContract);
332                     if (writeXsiType || !TryWritePrimitive(memberType, memberValue, member.MemberInfo, null /*arrayItemIndex*/, namespaceLocal, null /*nameLocal*/, i + childElementIndex))
333                     {
334                         WriteStartElement(memberType, classContract.Namespace, namespaceLocal, null /*nameLocal*/, i + childElementIndex);
335                         if (classContract.ChildElementNamespaces[i + childElementIndex] != null)
336                         {
337                             ilg.Load(xmlWriterArg);
338                             ilg.LoadArrayElement(childElementNamespacesLocal, i + childElementIndex);
339                             ilg.Call(XmlFormatGeneratorStatics.WriteNamespaceDeclMethod);
340                         }
341                         if (memberValue == null)
342                             memberValue = LoadMemberValue(member);
343                         WriteValue(memberValue, writeXsiType);
344                         WriteEndElement();
345                     }
346                     if (classContract.HasExtensionData)
347                         ilg.Call(contextArg, XmlFormatGeneratorStatics.WriteExtensionDataMethod, xmlWriterArg, extensionDataLocal, memberCount);
348                     if (!member.EmitDefaultValue)
349                     {
350                         if (member.IsRequired)
351                         {
352                             ilg.Else();
353                             ilg.Call(null, XmlFormatGeneratorStatics.ThrowRequiredMemberMustBeEmittedMethod, member.Name, classContract.UnderlyingType);
354                         }
355                         ilg.EndIf();
356                     }
357
358                 }
359
360                 typeIndex++;
361                 childElementIndex += classContract.Members.Count;
362                 return memberCount;
363             }
364
365             private LocalBuilder LoadMemberValue(DataMember member)
366             {
367                 ilg.LoadAddress(objectLocal);
368                 ilg.LoadMember(member.MemberInfo);
369                 LocalBuilder memberValue = ilg.DeclareLocal(member.MemberType, member.Name + "Value");
370                 ilg.Stloc(memberValue);
371                 return memberValue;
372             }
373
374             void WriteCollection(CollectionDataContract collectionContract)
375             {
376                 LocalBuilder itemNamespace = ilg.DeclareLocal(typeof(XmlDictionaryString), "itemNamespace");
377                 ilg.Load(dataContractArg);
378                 ilg.LoadMember(XmlFormatGeneratorStatics.NamespaceProperty);
379                 ilg.Store(itemNamespace);
380
381                 LocalBuilder itemName = ilg.DeclareLocal(typeof(XmlDictionaryString), "itemName");
382                 ilg.Load(dataContractArg);
383                 ilg.LoadMember(XmlFormatGeneratorStatics.CollectionItemNameProperty);
384                 ilg.Store(itemName);
385
386                 if (collectionContract.ChildElementNamespace != null)
387                 {
388                     ilg.Load(xmlWriterArg);
389                     ilg.Load(dataContractArg);
390                     ilg.LoadMember(XmlFormatGeneratorStatics.ChildElementNamespaceProperty);
391                     ilg.Call(XmlFormatGeneratorStatics.WriteNamespaceDeclMethod);
392                 }
393
394                 if (collectionContract.Kind == CollectionKind.Array)
395                 {
396                     Type itemType = collectionContract.ItemType;
397                     LocalBuilder i = ilg.DeclareLocal(Globals.TypeOfInt, "i");
398
399                     ilg.Call(contextArg, XmlFormatGeneratorStatics.IncrementArrayCountMethod, xmlWriterArg, objectLocal);
400
401                     if (!TryWritePrimitiveArray(collectionContract.UnderlyingType, itemType, objectLocal, itemName, itemNamespace))
402                     {
403                         ilg.For(i, 0, objectLocal);
404                         if (!TryWritePrimitive(itemType, null /*value*/, null /*memberInfo*/, i /*arrayItemIndex*/, itemNamespace, itemName, 0 /*nameIndex*/))
405                         {
406                             WriteStartElement(itemType, collectionContract.Namespace, itemNamespace, itemName, 0 /*nameIndex*/);
407                             ilg.LoadArrayElement(objectLocal, i);
408                             LocalBuilder memberValue = ilg.DeclareLocal(itemType, "memberValue");
409                             ilg.Stloc(memberValue);
410                             WriteValue(memberValue, false /*writeXsiType*/);
411                             WriteEndElement();
412                         }
413                         ilg.EndFor();
414                     }
415                 }
416                 else
417                 {
418                     MethodInfo incrementCollectionCountMethod = null;
419                     switch (collectionContract.Kind)
420                     {
421                         case CollectionKind.Collection:
422                         case CollectionKind.List:
423                         case CollectionKind.Dictionary:
424                             incrementCollectionCountMethod = XmlFormatGeneratorStatics.IncrementCollectionCountMethod;
425                             break;
426                         case CollectionKind.GenericCollection:
427                         case CollectionKind.GenericList:
428                             incrementCollectionCountMethod = XmlFormatGeneratorStatics.IncrementCollectionCountGenericMethod.MakeGenericMethod(collectionContract.ItemType);
429                             break;
430                         case CollectionKind.GenericDictionary:
431                             incrementCollectionCountMethod = XmlFormatGeneratorStatics.IncrementCollectionCountGenericMethod.MakeGenericMethod(Globals.TypeOfKeyValuePair.MakeGenericType(collectionContract.ItemType.GetGenericArguments()));
432                             break;
433                     }
434                     if (incrementCollectionCountMethod != null)
435                     {
436                         ilg.Call(contextArg, incrementCollectionCountMethod, xmlWriterArg, objectLocal);
437                     }
438
439                     bool isDictionary = false, isGenericDictionary = false;
440                     Type enumeratorType = null;
441                     Type[] keyValueTypes = null;
442                     if (collectionContract.Kind == CollectionKind.GenericDictionary)
443                     {
444                         isGenericDictionary = true;
445                         keyValueTypes = collectionContract.ItemType.GetGenericArguments();
446                         enumeratorType = Globals.TypeOfGenericDictionaryEnumerator.MakeGenericType(keyValueTypes);
447                     }
448                     else if (collectionContract.Kind == CollectionKind.Dictionary)
449                     {
450                         isDictionary = true;
451                         keyValueTypes = new Type[] { Globals.TypeOfObject, Globals.TypeOfObject };
452                         enumeratorType = Globals.TypeOfDictionaryEnumerator;
453                     }
454                     else
455                     {
456                         enumeratorType = collectionContract.GetEnumeratorMethod.ReturnType;
457                     }
458                     MethodInfo moveNextMethod = enumeratorType.GetMethod(Globals.MoveNextMethodName, BindingFlags.Instance | BindingFlags.Public, null, Globals.EmptyTypeArray, null);
459                     MethodInfo getCurrentMethod = enumeratorType.GetMethod(Globals.GetCurrentMethodName, BindingFlags.Instance | BindingFlags.Public, null, Globals.EmptyTypeArray, null);
460                     if (moveNextMethod == null || getCurrentMethod == null)
461                     {
462                         if (enumeratorType.IsInterface)
463                         {
464                             if (moveNextMethod == null)
465                                 moveNextMethod = XmlFormatGeneratorStatics.MoveNextMethod;
466                             if (getCurrentMethod == null)
467                                 getCurrentMethod = XmlFormatGeneratorStatics.GetCurrentMethod;
468                         }
469                         else
470                         {
471                             Type ienumeratorInterface = Globals.TypeOfIEnumerator;
472                             CollectionKind kind = collectionContract.Kind;
473                             if (kind == CollectionKind.GenericDictionary || kind == CollectionKind.GenericCollection || kind == CollectionKind.GenericEnumerable)
474                             {
475                                 Type[] interfaceTypes = enumeratorType.GetInterfaces();
476                                 foreach (Type interfaceType in interfaceTypes)
477                                 {
478                                     if (interfaceType.IsGenericType
479                                         && interfaceType.GetGenericTypeDefinition() == Globals.TypeOfIEnumeratorGeneric
480                                         && interfaceType.GetGenericArguments()[0] == collectionContract.ItemType)
481                                     {
482                                         ienumeratorInterface = interfaceType;
483                                         break;
484                                     }
485                                 }
486                             }
487                             if (moveNextMethod == null)
488                                 moveNextMethod = CollectionDataContract.GetTargetMethodWithName(Globals.MoveNextMethodName, enumeratorType, ienumeratorInterface);
489                             if (getCurrentMethod == null)
490                                 getCurrentMethod = CollectionDataContract.GetTargetMethodWithName(Globals.GetCurrentMethodName, enumeratorType, ienumeratorInterface);
491                         }
492                     }
493                     Type elementType = getCurrentMethod.ReturnType;
494                     LocalBuilder currentValue = ilg.DeclareLocal(elementType, "currentValue");
495
496                     LocalBuilder enumerator = ilg.DeclareLocal(enumeratorType, "enumerator");
497                     ilg.Call(objectLocal, collectionContract.GetEnumeratorMethod);
498                     if (isDictionary)
499                     {
500                         ilg.ConvertValue(collectionContract.GetEnumeratorMethod.ReturnType, Globals.TypeOfIDictionaryEnumerator);
501                         ilg.New(XmlFormatGeneratorStatics.DictionaryEnumeratorCtor);
502                     }
503                     else if (isGenericDictionary)
504                     {
505                         Type ctorParam = Globals.TypeOfIEnumeratorGeneric.MakeGenericType(Globals.TypeOfKeyValuePair.MakeGenericType(keyValueTypes));
506                         ConstructorInfo dictEnumCtor = enumeratorType.GetConstructor(Globals.ScanAllMembers, null, new Type[] { ctorParam }, null);
507                         ilg.ConvertValue(collectionContract.GetEnumeratorMethod.ReturnType, ctorParam);
508                         ilg.New(dictEnumCtor);
509                     }
510                     ilg.Stloc(enumerator);
511
512                     ilg.ForEach(currentValue, elementType, enumeratorType, enumerator, getCurrentMethod);
513                     if (incrementCollectionCountMethod == null)
514                     {
515                         ilg.Call(contextArg, XmlFormatGeneratorStatics.IncrementItemCountMethod, 1);
516                     }
517                     if (!TryWritePrimitive(elementType, currentValue, null /*memberInfo*/, null /*arrayItemIndex*/, itemNamespace, itemName, 0 /*nameIndex*/))
518                     {
519                         WriteStartElement(elementType, collectionContract.Namespace, itemNamespace, itemName, 0 /*nameIndex*/);
520
521                         if (isGenericDictionary || isDictionary)
522                         {
523                             ilg.Call(dataContractArg, XmlFormatGeneratorStatics.GetItemContractMethod);
524                             ilg.Load(xmlWriterArg);
525                             ilg.Load(currentValue);
526                             ilg.ConvertValue(currentValue.LocalType, Globals.TypeOfObject);
527                             ilg.Load(contextArg);
528                             ilg.Call(XmlFormatGeneratorStatics.WriteXmlValueMethod);
529                         }
530                         else
531                         {
532                             WriteValue(currentValue, false /*writeXsiType*/);
533                         }
534                         WriteEndElement();
535                     }
536                     ilg.EndForEach(moveNextMethod);
537                 }
538             }
539
540             bool TryWritePrimitive(Type type, LocalBuilder value, MemberInfo memberInfo, LocalBuilder arrayItemIndex, LocalBuilder ns, LocalBuilder name, int nameIndex)
541             {
542                 PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(type);
543                 if (primitiveContract == null || primitiveContract.UnderlyingType == Globals.TypeOfObject)
544                     return false;
545
546                 // load xmlwriter
547                 if (type.IsValueType)
548                 {
549                     ilg.Load(xmlWriterArg);
550                 }
551                 else
552                 {
553                     ilg.Load(contextArg);
554                     ilg.Load(xmlWriterArg);
555                 }
556                 // load primitive value 
557                 if (value != null)
558                 {
559                     ilg.Load(value);
560                 }
561                 else if (memberInfo != null)
562                 {
563                     ilg.LoadAddress(objectLocal);
564                     ilg.LoadMember(memberInfo);
565                 }
566                 else
567                 {
568                     ilg.LoadArrayElement(objectLocal, arrayItemIndex);
569                 }
570                 // load name
571                 if (name != null)
572                 {
573                     ilg.Load(name);
574                 }
575                 else
576                 {
577                     ilg.LoadArrayElement(memberNamesLocal, nameIndex);
578                 }
579                 // load namespace
580                 ilg.Load(ns);
581                 // call method to write primitive
582                 ilg.Call(primitiveContract.XmlFormatWriterMethod);
583                 return true;
584             }
585
586             bool TryWritePrimitiveArray(Type type, Type itemType, LocalBuilder value, LocalBuilder itemName, LocalBuilder itemNamespace)
587             {
588                 PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(itemType);
589                 if (primitiveContract == null)
590                     return false;
591
592                 string writeArrayMethod = null;
593                 switch (Type.GetTypeCode(itemType))
594                 {
595                     case TypeCode.Boolean:
596                         writeArrayMethod = "WriteBooleanArray";
597                         break;
598                     case TypeCode.DateTime:
599                         writeArrayMethod = "WriteDateTimeArray";
600                         break;
601                     case TypeCode.Decimal:
602                         writeArrayMethod = "WriteDecimalArray";
603                         break;
604                     case TypeCode.Int32:
605                         writeArrayMethod = "WriteInt32Array";
606                         break;
607                     case TypeCode.Int64:
608                         writeArrayMethod = "WriteInt64Array";
609                         break;
610                     case TypeCode.Single:
611                         writeArrayMethod = "WriteSingleArray";
612                         break;
613                     case TypeCode.Double:
614                         writeArrayMethod = "WriteDoubleArray";
615                         break;
616                     default:
617                         break;
618                 }
619                 if (writeArrayMethod != null)
620                 {
621                     ilg.Load(xmlWriterArg);
622                     ilg.Load(value);
623                     ilg.Load(itemName);
624                     ilg.Load(itemNamespace);
625                     ilg.Call(typeof(XmlWriterDelegator).GetMethod(writeArrayMethod, Globals.ScanAllMembers, null, new Type[] { type, typeof(XmlDictionaryString), typeof(XmlDictionaryString) }, null));
626                     return true;
627                 }
628                 return false;
629             }
630
631             void WriteValue(LocalBuilder memberValue, bool writeXsiType)
632             {
633                 Type memberType = memberValue.LocalType;
634                 if (memberType.IsPointer)
635                 {
636                     ilg.Load(memberValue);
637                     ilg.Load(memberType);
638                     ilg.Call(XmlFormatGeneratorStatics.BoxPointer);
639                     memberType = Globals.TypeOfReflectionPointer;
640                     memberValue = ilg.DeclareLocal(memberType, "memberValueRefPointer");
641                     ilg.Store(memberValue);
642                 }
643                 bool isNullableOfT = (memberType.IsGenericType &&
644                                       memberType.GetGenericTypeDefinition() == Globals.TypeOfNullable);
645                 if (memberType.IsValueType && !isNullableOfT)
646                 {
647                     PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(memberType);
648                     if (primitiveContract != null && !writeXsiType)
649                         ilg.Call(xmlWriterArg, primitiveContract.XmlFormatContentWriterMethod, memberValue);
650                     else
651                         InternalSerialize(XmlFormatGeneratorStatics.InternalSerializeMethod, memberValue, memberType, writeXsiType);
652                 }
653                 else
654                 {
655                     if (isNullableOfT)
656                     {
657                         memberValue = UnwrapNullableObject(memberValue); //Leaves !HasValue on stack
658                         memberType = memberValue.LocalType;
659                     }
660                     else
661                     {
662                         ilg.Load(memberValue);
663                         ilg.Load(null);
664                         ilg.Ceq();
665                     }
666                     ilg.If();
667                     ilg.Call(contextArg, XmlFormatGeneratorStatics.WriteNullMethod, xmlWriterArg, memberType, DataContract.IsTypeSerializable(memberType));
668                     ilg.Else();
669                     PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(memberType);
670                     if (primitiveContract != null && primitiveContract.UnderlyingType != Globals.TypeOfObject && !writeXsiType)
671                     {
672                         if (isNullableOfT)
673                         {
674                             ilg.Call(xmlWriterArg, primitiveContract.XmlFormatContentWriterMethod, memberValue);
675                         }
676                         else
677                         {
678                             ilg.Call(contextArg, primitiveContract.XmlFormatContentWriterMethod, xmlWriterArg, memberValue);
679                         }
680                     }
681                     else
682                     {
683                         if (memberType == Globals.TypeOfObject || //boxed Nullable<T>
684                             memberType == Globals.TypeOfValueType ||
685                             ((IList)Globals.TypeOfNullable.GetInterfaces()).Contains(memberType))
686                         {
687                             ilg.Load(memberValue);
688                             ilg.ConvertValue(memberValue.LocalType, Globals.TypeOfObject);
689                             memberValue = ilg.DeclareLocal(Globals.TypeOfObject, "unwrappedMemberValue");
690                             memberType = memberValue.LocalType;
691                             ilg.Stloc(memberValue);
692                             ilg.If(memberValue, Cmp.EqualTo, null);
693                             ilg.Call(contextArg, XmlFormatGeneratorStatics.WriteNullMethod, xmlWriterArg, memberType, DataContract.IsTypeSerializable(memberType));
694                             ilg.Else();
695                         }
696                         InternalSerialize((isNullableOfT ? XmlFormatGeneratorStatics.InternalSerializeMethod : XmlFormatGeneratorStatics.InternalSerializeReferenceMethod),
697                             memberValue, memberType, writeXsiType);
698
699                         if (memberType == Globals.TypeOfObject) //boxed Nullable<T>
700                             ilg.EndIf();
701                     }
702                     ilg.EndIf();
703                 }
704             }
705
706             void InternalSerialize(MethodInfo methodInfo, LocalBuilder memberValue, Type memberType, bool writeXsiType)
707             {
708                 ilg.Load(contextArg);
709                 ilg.Load(xmlWriterArg);
710                 ilg.Load(memberValue);
711                 ilg.ConvertValue(memberValue.LocalType, Globals.TypeOfObject);
712                 LocalBuilder typeHandleValue = ilg.DeclareLocal(typeof(RuntimeTypeHandle), "typeHandleValue");
713                 ilg.Call(null, typeof(Type).GetMethod("GetTypeHandle"), memberValue);
714                 ilg.Stloc(typeHandleValue);
715                 ilg.LoadAddress(typeHandleValue);
716                 ilg.Ldtoken(memberType);
717                 ilg.Call(typeof(RuntimeTypeHandle).GetMethod("Equals", new Type[] { typeof(RuntimeTypeHandle) }));
718                 ilg.Load(writeXsiType);
719                 ilg.Load(DataContract.GetId(memberType.TypeHandle));
720                 ilg.Ldtoken(memberType);
721                 ilg.Call(methodInfo);
722             }
723
724
725             LocalBuilder UnwrapNullableObject(LocalBuilder memberValue)// Leaves !HasValue on stack
726             {
727                 Type memberType = memberValue.LocalType;
728                 Label onNull = ilg.DefineLabel();
729                 Label end = ilg.DefineLabel();
730                 ilg.Load(memberValue);
731                 while (memberType.IsGenericType && memberType.GetGenericTypeDefinition() == Globals.TypeOfNullable)
732                 {
733                     Type innerType = memberType.GetGenericArguments()[0];
734                     ilg.Dup();
735                     ilg.Call(XmlFormatGeneratorStatics.GetHasValueMethod.MakeGenericMethod(innerType));
736                     ilg.Brfalse(onNull);
737                     ilg.Call(XmlFormatGeneratorStatics.GetNullableValueMethod.MakeGenericMethod(innerType));
738                     memberType = innerType;
739                 }
740                 memberValue = ilg.DeclareLocal(memberType, "nullableUnwrappedMemberValue");
741                 ilg.Stloc(memberValue);
742                 ilg.Load(false); //isNull
743                 ilg.Br(end);
744                 ilg.MarkLabel(onNull);
745                 ilg.Pop();
746                 ilg.Call(XmlFormatGeneratorStatics.GetDefaultValueMethod.MakeGenericMethod(memberType));
747                 ilg.Stloc(memberValue);
748                 ilg.Load(true); //isNull
749                 ilg.MarkLabel(end);
750                 return memberValue;
751             }
752
753             bool NeedsPrefix(Type type, XmlDictionaryString ns)
754             {
755                 return type == Globals.TypeOfXmlQualifiedName && (ns != null && ns.Value != null && ns.Value.Length > 0);
756             }
757
758             void WriteStartElement(Type type, XmlDictionaryString ns, LocalBuilder namespaceLocal, LocalBuilder nameLocal, int nameIndex)
759             {
760                 bool needsPrefix = NeedsPrefix(type, ns);
761                 ilg.Load(xmlWriterArg);
762                 // prefix
763                 if (needsPrefix)
764                     ilg.Load(Globals.ElementPrefix);
765
766                 // localName
767                 if (nameLocal == null)
768                     ilg.LoadArrayElement(memberNamesLocal, nameIndex);
769                 else
770                     ilg.Load(nameLocal);
771
772                 // namespace
773                 ilg.Load(namespaceLocal);
774
775                 ilg.Call(needsPrefix ? XmlFormatGeneratorStatics.WriteStartElementMethod3 : XmlFormatGeneratorStatics.WriteStartElementMethod2);
776             }
777
778             void WriteEndElement()
779             {
780                 ilg.Call(xmlWriterArg, XmlFormatGeneratorStatics.WriteEndElementMethod);
781             }
782
783             bool CheckIfMemberHasConflict(DataMember member, ClassDataContract classContract, ClassDataContract derivedMostClassContract)
784             {
785                 // Check for conflict with base type members
786                 if (CheckIfConflictingMembersHaveDifferentTypes(member))
787                     return true;
788
789                 // Check for conflict with derived type members
790                 string name = member.Name;
791                 string ns = classContract.StableName.Namespace;
792                 ClassDataContract currentContract = derivedMostClassContract;
793                 while (currentContract != null && currentContract != classContract)
794                 {
795                     if (ns == currentContract.StableName.Namespace)
796                     {
797                         List<DataMember> members = currentContract.Members;
798                         for (int j = 0; j < members.Count; j++)
799                         {
800                             if (name == members[j].Name)
801                                 return CheckIfConflictingMembersHaveDifferentTypes(members[j]);
802                         }
803                     }
804                     currentContract = currentContract.BaseContract;
805                 }
806
807                 return false;
808             }
809
810             bool CheckIfConflictingMembersHaveDifferentTypes(DataMember member)
811             {
812                 while (member.ConflictingMember != null)
813                 {
814                     if (member.MemberType != member.ConflictingMember.MemberType)
815                         return true;
816                     member = member.ConflictingMember;
817                 }
818                 return false;
819             }
820
821 #if NotUsed
822         static Hashtable nsToPrefixTable = new Hashtable(4);
823         internal static string GetPrefix(string ns)
824         {
825             string prefix = (string)nsToPrefixTable[ns];
826             if (prefix == null)
827             {
828                 lock (nsToPrefixTable)
829                 {
830                     if (prefix == null)
831                     {
832                         prefix = "p" + nsToPrefixTable.Count;
833                         nsToPrefixTable.Add(ns, prefix);
834                     }
835                 }
836             }
837             return prefix;
838         }
839 #endif
840
841         }
842     }
843 }
844
845