2 namespace System.Runtime.Serialization.Json
5 using System.Collections;
6 using System.Reflection;
7 using System.Reflection.Emit;
8 using System.Runtime.Serialization.Diagnostics.Application;
10 using System.Security.Permissions;
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);
16 class JsonFormatWriterGenerator
18 [Fx.Tag.SecurityNote(Critical = "Holds instance of CriticalHelper which keeps state that was produced within an assert.")]
20 CriticalHelper helper;
22 [Fx.Tag.SecurityNote(Critical = "Initializes SecurityCritical field 'helper'.")]
24 public JsonFormatWriterGenerator()
26 helper = new CriticalHelper();
29 [Fx.Tag.SecurityNote(Critical = "Accesses SecurityCritical helper class 'CriticalHelper'.")]
31 internal JsonFormatClassWriterDelegate GenerateClassWriter(ClassDataContract classContract)
35 if (TD.DCJsonGenWriterStartIsEnabled())
37 TD.DCJsonGenWriterStart("Class", classContract.UnderlyingType.FullName);
40 return helper.GenerateClassWriter(classContract);
44 if (TD.DCJsonGenWriterStopIsEnabled())
46 TD.DCJsonGenWriterStop();
51 [Fx.Tag.SecurityNote(Critical = "Accesses SecurityCritical helper class 'CriticalHelper'.")]
53 internal JsonFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionDataContract collectionContract)
57 if (TD.DCJsonGenWriterStartIsEnabled())
59 TD.DCJsonGenWriterStart("Collection", collectionContract.UnderlyingType.FullName);
62 return helper.GenerateCollectionWriter(collectionContract);
66 if (TD.DCJsonGenWriterStopIsEnabled())
68 TD.DCJsonGenWriterStop();
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.")]
79 ArgBuilder xmlWriterArg;
80 ArgBuilder contextArg;
81 ArgBuilder dataContractArg;
82 LocalBuilder objectLocal;
85 ArgBuilder memberNamesArg;
87 int childElementIndex = 0;
89 internal JsonFormatClassWriterDelegate GenerateClassWriter(ClassDataContract classContract)
91 ilg = new CodeGenerator();
92 bool memberAccessFlag = classContract.RequiresMemberAccessForWrite(null);
95 BeginMethod(ilg, "Write" + classContract.StableName.Name + "ToJson", typeof(JsonFormatClassWriterDelegate), memberAccessFlag);
97 catch (SecurityException securityException)
99 if (memberAccessFlag && securityException.PermissionType.Equals(typeof(ReflectionPermission)))
101 classContract.RequiresMemberAccessForWrite(securityException);
108 InitArgs(classContract.UnderlyingType);
109 memberNamesArg = ilg.GetArg(4);
110 DemandSerializationFormatterPermission(classContract);
111 DemandMemberAccessPermission(memberAccessFlag);
112 if (classContract.IsReadOnlyContract)
114 ThrowIfCannotSerializeReadOnlyTypes(classContract);
116 WriteClass(classContract);
117 return (JsonFormatClassWriterDelegate)ilg.EndMethod();
120 internal JsonFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionDataContract collectionContract)
122 ilg = new CodeGenerator();
123 bool memberAccessFlag = collectionContract.RequiresMemberAccessForWrite(null);
126 BeginMethod(ilg, "Write" + collectionContract.StableName.Name + "ToJson", typeof(JsonFormatCollectionWriterDelegate), memberAccessFlag);
128 catch (SecurityException securityException)
130 if (memberAccessFlag && securityException.PermissionType.Equals(typeof(ReflectionPermission)))
132 collectionContract.RequiresMemberAccessForWrite(securityException);
139 InitArgs(collectionContract.UnderlyingType);
140 DemandMemberAccessPermission(memberAccessFlag);
141 if (collectionContract.IsReadOnlyContract)
143 ThrowIfCannotSerializeReadOnlyTypes(collectionContract);
145 WriteCollection(collectionContract);
146 return (JsonFormatCollectionWriterDelegate)ilg.EndMethod();
149 void BeginMethod(CodeGenerator ilg, string methodName, Type delegateType, bool allowPrivateMemberAccess)
152 ilg.BeginMethod(methodName, delegateType, allowPrivateMemberAccess);
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;
160 DynamicMethod dynamicMethod = new DynamicMethod(methodName, signature.ReturnType, paramTypes, typeof(JsonFormatWriterGenerator).Module, allowPrivateMemberAccess);
161 ilg.BeginMethod(dynamicMethod, delegateType, methodName, paramTypes, allowPrivateMemberAccess);
165 void InitArgs(Type objType)
167 xmlWriterArg = ilg.GetArg(0);
168 contextArg = ilg.GetArg(2);
169 dataContractArg = ilg.GetArg(3);
171 objectLocal = ilg.DeclareLocal(objType, "objSerialized");
172 ArgBuilder objectArg = ilg.GetArg(1);
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.
179 if (objType == Globals.TypeOfDateTimeOffsetAdapter)
181 ilg.ConvertValue(objectArg.ArgType, Globals.TypeOfDateTimeOffset);
182 ilg.Call(XmlFormatGeneratorStatics.GetDateTimeOffsetAdapterMethod);
186 ilg.ConvertValue(objectArg.ArgType, objType);
188 ilg.Stloc(objectLocal);
192 void DemandMemberAccessPermission(bool memberAccessFlag)
194 if (memberAccessFlag)
196 ilg.Call(contextArg, XmlFormatGeneratorStatics.DemandMemberAccessPermissionMethod);
200 void DemandSerializationFormatterPermission(ClassDataContract classContract)
202 if (!classContract.HasDataContract && !classContract.IsNonAttributedType)
204 ilg.Call(contextArg, XmlFormatGeneratorStatics.DemandSerializationFormatterPermissionMethod);
208 void ThrowIfCannotSerializeReadOnlyTypes(ClassDataContract classContract)
210 ThrowIfCannotSerializeReadOnlyTypes(XmlFormatGeneratorStatics.ClassSerializationExceptionMessageProperty);
213 void ThrowIfCannotSerializeReadOnlyTypes(CollectionDataContract classContract)
215 ThrowIfCannotSerializeReadOnlyTypes(XmlFormatGeneratorStatics.CollectionSerializationExceptionMessageProperty);
218 void ThrowIfCannotSerializeReadOnlyTypes(PropertyInfo serializationExceptionMessageProperty)
220 ilg.Load(contextArg);
221 ilg.LoadMember(XmlFormatGeneratorStatics.SerializeReadOnlyTypesProperty);
223 ilg.Load(dataContractArg);
224 ilg.LoadMember(serializationExceptionMessageProperty);
226 ilg.Call(XmlFormatGeneratorStatics.ThrowInvalidDataContractExceptionMethod);
230 void InvokeOnSerializing(ClassDataContract classContract)
232 if (classContract.BaseContract != null)
233 InvokeOnSerializing(classContract.BaseContract);
234 if (classContract.OnSerializing != null)
236 ilg.LoadAddress(objectLocal);
237 ilg.Load(contextArg);
238 ilg.Call(XmlFormatGeneratorStatics.GetStreamingContextMethod);
239 ilg.Call(classContract.OnSerializing);
243 void InvokeOnSerialized(ClassDataContract classContract)
245 if (classContract.BaseContract != null)
246 InvokeOnSerialized(classContract.BaseContract);
247 if (classContract.OnSerialized != null)
249 ilg.LoadAddress(objectLocal);
250 ilg.Load(contextArg);
251 ilg.Call(XmlFormatGeneratorStatics.GetStreamingContextMethod);
252 ilg.Call(classContract.OnSerialized);
256 void WriteClass(ClassDataContract classContract)
258 InvokeOnSerializing(classContract);
260 if (classContract.IsISerializable)
262 ilg.Call(contextArg, JsonFormatGeneratorStatics.WriteJsonISerializableMethod, xmlWriterArg, objectLocal);
266 if (classContract.HasExtensionData)
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);
277 WriteMembers(classContract, null, classContract);
279 InvokeOnSerialized(classContract);
282 int WriteMembers(ClassDataContract classContract, LocalBuilder extensionDataLocal, ClassDataContract derivedMostClassContract)
284 int memberCount = (classContract.BaseContract == null) ? 0 :
285 WriteMembers(classContract.BaseContract, extensionDataLocal, derivedMostClassContract);
287 ilg.Call(contextArg, XmlFormatGeneratorStatics.IncrementItemCountMethod, classContract.Members.Count);
289 for (int i = 0; i < classContract.Members.Count; i++, memberCount++)
291 DataMember member = classContract.Members[i];
292 Type memberType = member.MemberType;
293 LocalBuilder memberValue = null;
294 if (member.IsGetOnlyCollection)
296 ilg.Load(contextArg);
297 ilg.Call(XmlFormatGeneratorStatics.StoreIsGetOnlyCollectionMethod);
299 if (!member.EmitDefaultValue)
301 memberValue = LoadMemberValue(member);
302 ilg.IfNotDefaultValue(memberValue);
305 bool requiresNameAttribute = DataContractJsonSerializer.CheckIfXmlNameRequiresMapping(classContract.MemberNames[i]);
306 if (requiresNameAttribute || !TryWritePrimitive(memberType, memberValue, member.MemberInfo, null /*arrayItemIndex*/, null /*nameLocal*/, i + childElementIndex))
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)
312 ilg.Call(null, JsonFormatGeneratorStatics.WriteJsonNameWithMappingMethod, xmlWriterArg, memberNamesArg, i + childElementIndex);
316 WriteStartElement(null /*nameLocal*/, i + childElementIndex);
318 if (memberValue == null)
319 memberValue = LoadMemberValue(member);
320 WriteValue(memberValue);
323 if (classContract.HasExtensionData)
324 ilg.Call(contextArg, XmlFormatGeneratorStatics.WriteExtensionDataMethod, xmlWriterArg, extensionDataLocal, memberCount);
325 if (!member.EmitDefaultValue)
327 if (member.IsRequired)
330 ilg.Call(null, XmlFormatGeneratorStatics.ThrowRequiredMemberMustBeEmittedMethod, member.Name, classContract.UnderlyingType);
337 childElementIndex += classContract.Members.Count;
341 private LocalBuilder LoadMemberValue(DataMember member)
343 ilg.LoadAddress(objectLocal);
344 ilg.LoadMember(member.MemberInfo);
345 LocalBuilder memberValue = ilg.DeclareLocal(member.MemberType, member.Name + "Value");
346 ilg.Stloc(memberValue);
350 void WriteCollection(CollectionDataContract collectionContract)
352 LocalBuilder itemName = ilg.DeclareLocal(typeof(XmlDictionaryString), "itemName");
353 ilg.Load(contextArg);
354 ilg.LoadMember(JsonFormatGeneratorStatics.CollectionItemNameProperty);
357 if (collectionContract.Kind == CollectionKind.Array)
359 Type itemType = collectionContract.ItemType;
360 LocalBuilder i = ilg.DeclareLocal(Globals.TypeOfInt, "i");
362 ilg.Call(contextArg, XmlFormatGeneratorStatics.IncrementArrayCountMethod, xmlWriterArg, objectLocal);
364 if (!TryWritePrimitiveArray(collectionContract.UnderlyingType, itemType, objectLocal, itemName))
366 WriteArrayAttribute();
367 ilg.For(i, 0, objectLocal);
368 if (!TryWritePrimitive(itemType, null /*value*/, null /*memberInfo*/, i /*arrayItemIndex*/, itemName, 0 /*nameIndex*/))
370 WriteStartElement(itemName, 0 /*nameIndex*/);
371 ilg.LoadArrayElement(objectLocal, i);
372 LocalBuilder memberValue = ilg.DeclareLocal(itemType, "memberValue");
373 ilg.Stloc(memberValue);
374 WriteValue(memberValue);
382 MethodInfo incrementCollectionCountMethod = null;
383 switch (collectionContract.Kind)
385 case CollectionKind.Collection:
386 case CollectionKind.List:
387 case CollectionKind.Dictionary:
388 incrementCollectionCountMethod = XmlFormatGeneratorStatics.IncrementCollectionCountMethod;
390 case CollectionKind.GenericCollection:
391 case CollectionKind.GenericList:
392 incrementCollectionCountMethod = XmlFormatGeneratorStatics.IncrementCollectionCountGenericMethod.MakeGenericMethod(collectionContract.ItemType);
394 case CollectionKind.GenericDictionary:
395 incrementCollectionCountMethod = XmlFormatGeneratorStatics.IncrementCollectionCountGenericMethod.MakeGenericMethod(Globals.TypeOfKeyValuePair.MakeGenericType(collectionContract.ItemType.GetGenericArguments()));
398 if (incrementCollectionCountMethod != null)
400 ilg.Call(contextArg, incrementCollectionCountMethod, xmlWriterArg, objectLocal);
403 bool isDictionary = false, isGenericDictionary = false;
404 Type enumeratorType = null;
405 Type[] keyValueTypes = null;
406 if (collectionContract.Kind == CollectionKind.GenericDictionary)
408 isGenericDictionary = true;
409 keyValueTypes = collectionContract.ItemType.GetGenericArguments();
410 enumeratorType = Globals.TypeOfGenericDictionaryEnumerator.MakeGenericType(keyValueTypes);
412 else if (collectionContract.Kind == CollectionKind.Dictionary)
415 keyValueTypes = new Type[] { Globals.TypeOfObject, Globals.TypeOfObject };
416 enumeratorType = Globals.TypeOfDictionaryEnumerator;
420 enumeratorType = collectionContract.GetEnumeratorMethod.ReturnType;
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)
426 if (enumeratorType.IsInterface)
428 if (moveNextMethod == null)
429 moveNextMethod = JsonFormatGeneratorStatics.MoveNextMethod;
430 if (getCurrentMethod == null)
431 getCurrentMethod = JsonFormatGeneratorStatics.GetCurrentMethod;
435 Type ienumeratorInterface = Globals.TypeOfIEnumerator;
436 CollectionKind kind = collectionContract.Kind;
437 if (kind == CollectionKind.GenericDictionary || kind == CollectionKind.GenericCollection || kind == CollectionKind.GenericEnumerable)
439 Type[] interfaceTypes = enumeratorType.GetInterfaces();
440 foreach (Type interfaceType in interfaceTypes)
442 if (interfaceType.IsGenericType
443 && interfaceType.GetGenericTypeDefinition() == Globals.TypeOfIEnumeratorGeneric
444 && interfaceType.GetGenericArguments()[0] == collectionContract.ItemType)
446 ienumeratorInterface = interfaceType;
451 if (moveNextMethod == null)
452 moveNextMethod = CollectionDataContract.GetTargetMethodWithName(Globals.MoveNextMethodName, enumeratorType, ienumeratorInterface);
453 if (getCurrentMethod == null)
454 getCurrentMethod = CollectionDataContract.GetTargetMethodWithName(Globals.GetCurrentMethodName, enumeratorType, ienumeratorInterface);
457 Type elementType = getCurrentMethod.ReturnType;
458 LocalBuilder currentValue = ilg.DeclareLocal(elementType, "currentValue");
460 LocalBuilder enumerator = ilg.DeclareLocal(enumeratorType, "enumerator");
461 ilg.Call(objectLocal, collectionContract.GetEnumeratorMethod);
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);
468 else if (isGenericDictionary)
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);
475 ilg.Stloc(enumerator);
477 bool canWriteSimpleDictionary = isDictionary || isGenericDictionary;
478 if (canWriteSimpleDictionary)
480 Type genericDictionaryKeyValueType = Globals.TypeOfKeyValue.MakeGenericType(keyValueTypes);
481 PropertyInfo genericDictionaryKeyProperty = genericDictionaryKeyValueType.GetProperty(JsonGlobals.KeyString);
482 PropertyInfo genericDictionaryValueProperty = genericDictionaryKeyValueType.GetProperty(JsonGlobals.ValueString);
484 ilg.Load(contextArg);
485 ilg.LoadMember(JsonFormatGeneratorStatics.UseSimpleDictionaryFormatWriteProperty);
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);
492 ilg.LoadAddress(currentValue);
493 ilg.LoadMember(genericDictionaryKeyProperty);
494 ilg.ToString(keyValueTypes[0]);
497 ilg.LoadAddress(currentValue);
498 ilg.LoadMember(genericDictionaryValueProperty);
499 ilg.Stloc(pairValue);
501 WriteStartElement(pairKey, 0 /*nameIndex*/);
502 WriteValue(pairValue);
505 ilg.EndForEach(moveNextMethod);
509 WriteArrayAttribute();
511 ilg.ForEach(currentValue, elementType, enumeratorType, enumerator, getCurrentMethod);
512 if (incrementCollectionCountMethod == null)
514 ilg.Call(contextArg, XmlFormatGeneratorStatics.IncrementItemCountMethod, 1);
516 if (!TryWritePrimitive(elementType, currentValue, null /*memberInfo*/, null /*arrayItemIndex*/, itemName, 0 /*nameIndex*/))
518 WriteStartElement(itemName, 0 /*nameIndex*/);
520 if (isGenericDictionary || isDictionary)
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);
535 WriteValue(currentValue);
539 ilg.EndForEach(moveNextMethod);
541 if (canWriteSimpleDictionary)
548 bool TryWritePrimitive(Type type, LocalBuilder value, MemberInfo memberInfo, LocalBuilder arrayItemIndex, LocalBuilder name, int nameIndex)
550 PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(type);
551 if (primitiveContract == null || primitiveContract.UnderlyingType == Globals.TypeOfObject)
555 if (type.IsValueType)
557 ilg.Load(xmlWriterArg);
561 ilg.Load(contextArg);
562 ilg.Load(xmlWriterArg);
564 // load primitive value
569 else if (memberInfo != null)
571 ilg.LoadAddress(objectLocal);
572 ilg.LoadMember(memberInfo);
576 ilg.LoadArrayElement(objectLocal, arrayItemIndex);
585 ilg.LoadArrayElement(memberNamesArg, nameIndex);
589 // call method to write primitive
590 ilg.Call(primitiveContract.XmlFormatWriterMethod);
594 bool TryWritePrimitiveArray(Type type, Type itemType, LocalBuilder value, LocalBuilder itemName)
596 PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(itemType);
597 if (primitiveContract == null)
600 string writeArrayMethod = null;
601 switch (Type.GetTypeCode(itemType))
603 case TypeCode.Boolean:
604 writeArrayMethod = "WriteJsonBooleanArray";
606 case TypeCode.DateTime:
607 writeArrayMethod = "WriteJsonDateTimeArray";
609 case TypeCode.Decimal:
610 writeArrayMethod = "WriteJsonDecimalArray";
613 writeArrayMethod = "WriteJsonInt32Array";
616 writeArrayMethod = "WriteJsonInt64Array";
618 case TypeCode.Single:
619 writeArrayMethod = "WriteJsonSingleArray";
621 case TypeCode.Double:
622 writeArrayMethod = "WriteJsonDoubleArray";
627 if (writeArrayMethod != null)
629 WriteArrayAttribute();
630 ilg.Call(xmlWriterArg, typeof(JsonWriterDelegator).GetMethod(writeArrayMethod, Globals.ScanAllMembers, null, new Type[] { type, typeof(XmlDictionaryString), typeof(XmlDictionaryString) }, null), value, itemName, null);
636 void WriteArrayAttribute()
638 ilg.Call(xmlWriterArg, JsonFormatGeneratorStatics.WriteAttributeStringMethod,
640 JsonGlobals.typeString /* local name */,
641 string.Empty /* namespace */,
642 JsonGlobals.arrayString /* value */);
645 void WriteObjectAttribute()
647 ilg.Call(xmlWriterArg, JsonFormatGeneratorStatics.WriteAttributeStringMethod,
649 JsonGlobals.typeString /* local name */,
650 null /* namespace */,
651 JsonGlobals.objectString /* value */);
654 void WriteValue(LocalBuilder memberValue)
656 Type memberType = memberValue.LocalType;
657 if (memberType.IsPointer)
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);
666 bool isNullableOfT = (memberType.IsGenericType &&
667 memberType.GetGenericTypeDefinition() == Globals.TypeOfNullable);
668 if (memberType.IsValueType && !isNullableOfT)
670 PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(memberType);
671 if (primitiveContract != null)
672 ilg.Call(xmlWriterArg, primitiveContract.XmlFormatContentWriterMethod, memberValue);
674 InternalSerialize(XmlFormatGeneratorStatics.InternalSerializeMethod, memberValue, memberType, false /* writeXsiType */);
680 memberValue = UnwrapNullableObject(memberValue); //Leaves !HasValue on stack
681 memberType = memberValue.LocalType;
685 ilg.Load(memberValue);
690 ilg.Call(contextArg, XmlFormatGeneratorStatics.WriteNullMethod, xmlWriterArg, memberType, DataContract.IsTypeSerializable(memberType));
692 PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(memberType);
693 if (primitiveContract != null && primitiveContract.UnderlyingType != Globals.TypeOfObject)
697 ilg.Call(xmlWriterArg, primitiveContract.XmlFormatContentWriterMethod, memberValue);
701 ilg.Call(contextArg, primitiveContract.XmlFormatContentWriterMethod, xmlWriterArg, memberValue);
706 if (memberType == Globals.TypeOfObject || //boxed Nullable<T>
707 memberType == Globals.TypeOfValueType ||
708 ((IList)Globals.TypeOfNullable.GetInterfaces()).Contains(memberType))
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));
719 InternalSerialize((isNullableOfT ? XmlFormatGeneratorStatics.InternalSerializeMethod : XmlFormatGeneratorStatics.InternalSerializeReferenceMethod),
720 memberValue, memberType, false /* writeXsiType */);
722 if (memberType == Globals.TypeOfObject) //boxed Nullable<T>
729 void InternalSerialize(MethodInfo methodInfo, LocalBuilder memberValue, Type memberType, bool writeXsiType)
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);
747 LocalBuilder UnwrapNullableObject(LocalBuilder memberValue)// Leaves !HasValue on stack
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)
755 Type innerType = memberType.GetGenericArguments()[0];
757 ilg.Call(XmlFormatGeneratorStatics.GetHasValueMethod.MakeGenericMethod(innerType));
759 ilg.Call(XmlFormatGeneratorStatics.GetNullableValueMethod.MakeGenericMethod(innerType));
760 memberType = innerType;
762 memberValue = ilg.DeclareLocal(memberType, "nullableUnwrappedMemberValue");
763 ilg.Stloc(memberValue);
764 ilg.Load(false); //isNull
766 ilg.MarkLabel(onNull);
768 ilg.Call(XmlFormatGeneratorStatics.GetDefaultValueMethod.MakeGenericMethod(memberType));
769 ilg.Stloc(memberValue);
770 ilg.Load(true); //isNull
775 void WriteStartElement(LocalBuilder nameLocal, int nameIndex)
777 ilg.Load(xmlWriterArg);
780 if (nameLocal == null)
781 ilg.LoadArrayElement(memberNamesArg, nameIndex);
788 if (nameLocal != null && nameLocal.LocalType == typeof(string))
790 ilg.Call(JsonFormatGeneratorStatics.WriteStartElementStringMethod);
794 ilg.Call(JsonFormatGeneratorStatics.WriteStartElementMethod);
798 void WriteEndElement()
800 ilg.Call(xmlWriterArg, JsonFormatGeneratorStatics.WriteEndElementMethod);