XmlFormatWriterGenerator.cs 40 KB

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