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