XmlFormatReaderGenerator.cs 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //-----------------------------------------------------------------------------
  4. namespace System.Runtime.Serialization
  5. {
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Reflection;
  9. #if !NO_DYNAMIC_CODEGEN
  10. using System.Reflection.Emit;
  11. #endif
  12. using System.Runtime.Serialization.Diagnostics.Application;
  13. using System.Security;
  14. using System.Security.Permissions;
  15. using System.Xml;
  16. #if USE_REFEMIT
  17. public delegate object XmlFormatClassReaderDelegate(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString[] memberNames, XmlDictionaryString[] memberNamespaces);
  18. public delegate object XmlFormatCollectionReaderDelegate(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString itemName, XmlDictionaryString itemNamespace, CollectionDataContract collectionContract);
  19. public delegate void XmlFormatGetOnlyCollectionReaderDelegate(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString itemName, XmlDictionaryString itemNamespace, CollectionDataContract collectionContract);
  20. public sealed class XmlFormatReaderGenerator
  21. #else
  22. internal delegate object XmlFormatClassReaderDelegate(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString[] memberNames, XmlDictionaryString[] memberNamespaces);
  23. internal delegate object XmlFormatCollectionReaderDelegate(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString itemName, XmlDictionaryString itemNamespace, CollectionDataContract collectionContract);
  24. internal delegate void XmlFormatGetOnlyCollectionReaderDelegate(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString itemName, XmlDictionaryString itemNamespace, CollectionDataContract collectionContract);
  25. internal sealed partial class XmlFormatReaderGenerator
  26. #endif
  27. {
  28. [Fx.Tag.SecurityNote(Critical = "Holds instance of CriticalHelper which keeps state that was produced within an assert.")]
  29. [SecurityCritical]
  30. CriticalHelper helper;
  31. [Fx.Tag.SecurityNote(Critical = "Initializes SecurityCritical field 'helper'.")]
  32. [SecurityCritical]
  33. public XmlFormatReaderGenerator()
  34. {
  35. helper = new CriticalHelper();
  36. }
  37. [Fx.Tag.SecurityNote(Critical = "Accesses SecurityCritical helper class 'CriticalHelper'.")]
  38. [SecurityCritical]
  39. public XmlFormatClassReaderDelegate GenerateClassReader(ClassDataContract classContract)
  40. {
  41. try
  42. {
  43. if (TD.DCGenReaderStartIsEnabled())
  44. {
  45. TD.DCGenReaderStart("Class", classContract.UnderlyingType.FullName);
  46. }
  47. return helper.GenerateClassReader(classContract);
  48. }
  49. finally
  50. {
  51. if (TD.DCGenReaderStopIsEnabled())
  52. {
  53. TD.DCGenReaderStop();
  54. }
  55. }
  56. }
  57. [Fx.Tag.SecurityNote(Critical = "Accesses SecurityCritical helper class 'CriticalHelper'.")]
  58. [SecurityCritical]
  59. public XmlFormatCollectionReaderDelegate GenerateCollectionReader(CollectionDataContract collectionContract)
  60. {
  61. try
  62. {
  63. if (TD.DCGenReaderStartIsEnabled())
  64. {
  65. TD.DCGenReaderStart("Collection", collectionContract.UnderlyingType.FullName);
  66. }
  67. return helper.GenerateCollectionReader(collectionContract);
  68. }
  69. finally
  70. {
  71. if (TD.DCGenReaderStopIsEnabled())
  72. {
  73. TD.DCGenReaderStop();
  74. }
  75. }
  76. }
  77. [Fx.Tag.SecurityNote(Critical = "Accesses SecurityCritical helper class 'CriticalHelper'.")]
  78. [SecurityCritical]
  79. public XmlFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader(CollectionDataContract collectionContract)
  80. {
  81. try
  82. {
  83. if (TD.DCGenReaderStartIsEnabled())
  84. {
  85. TD.DCGenReaderStart("GetOnlyCollection", collectionContract.UnderlyingType.FullName);
  86. }
  87. return helper.GenerateGetOnlyCollectionReader(collectionContract);
  88. }
  89. finally
  90. {
  91. if (TD.DCGenReaderStopIsEnabled())
  92. {
  93. TD.DCGenReaderStop();
  94. }
  95. }
  96. }
  97. #if !NO_DYNAMIC_CODEGEN
  98. [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Handles all aspects of IL generation including initializing the DynamicMethod."
  99. + " Changes to how IL generated could affect how data is deserialized and what gets access to data,"
  100. + " therefore we mark it for review so that changes to generation logic are reviewed.")]
  101. partial class CriticalHelper
  102. {
  103. CodeGenerator ilg;
  104. LocalBuilder objectLocal;
  105. Type objectType;
  106. ArgBuilder xmlReaderArg;
  107. ArgBuilder contextArg;
  108. ArgBuilder memberNamesArg;
  109. ArgBuilder memberNamespacesArg;
  110. ArgBuilder collectionContractArg;
  111. public XmlFormatClassReaderDelegate GenerateClassReader(ClassDataContract classContract)
  112. {
  113. ilg = new CodeGenerator();
  114. bool memberAccessFlag = classContract.RequiresMemberAccessForRead(null);
  115. try
  116. {
  117. ilg.BeginMethod("Read" + classContract.StableName.Name + "FromXml", Globals.TypeOfXmlFormatClassReaderDelegate, memberAccessFlag);
  118. }
  119. catch (SecurityException securityException)
  120. {
  121. if (memberAccessFlag && securityException.PermissionType.Equals(typeof(ReflectionPermission)))
  122. {
  123. classContract.RequiresMemberAccessForRead(securityException);
  124. }
  125. else
  126. {
  127. throw;
  128. }
  129. }
  130. InitArgs();
  131. DemandSerializationFormatterPermission(classContract);
  132. DemandMemberAccessPermission(memberAccessFlag);
  133. CreateObject(classContract);
  134. ilg.Call(contextArg, XmlFormatGeneratorStatics.AddNewObjectMethod, objectLocal);
  135. InvokeOnDeserializing(classContract);
  136. LocalBuilder objectId = null;
  137. if (HasFactoryMethod(classContract))
  138. {
  139. objectId = ilg.DeclareLocal(Globals.TypeOfString, "objectIdRead");
  140. ilg.Call(contextArg, XmlFormatGeneratorStatics.GetObjectIdMethod);
  141. ilg.Stloc(objectId);
  142. }
  143. if (classContract.IsISerializable)
  144. ReadISerializable(classContract);
  145. else
  146. ReadClass(classContract);
  147. bool isFactoryType = InvokeFactoryMethod(classContract, objectId);
  148. if (Globals.TypeOfIDeserializationCallback.IsAssignableFrom(classContract.UnderlyingType))
  149. ilg.Call(objectLocal, XmlFormatGeneratorStatics.OnDeserializationMethod, null);
  150. InvokeOnDeserialized(classContract);
  151. if (objectId == null || !isFactoryType)
  152. {
  153. ilg.Load(objectLocal);
  154. // Do a conversion back from DateTimeOffsetAdapter to DateTimeOffset after deserialization.
  155. // DateTimeOffsetAdapter is used here for deserialization purposes to bypass the ISerializable implementation
  156. // on DateTimeOffset; which does not work in partial trust.
  157. if (classContract.UnderlyingType == Globals.TypeOfDateTimeOffsetAdapter)
  158. {
  159. ilg.ConvertValue(objectLocal.LocalType, Globals.TypeOfDateTimeOffsetAdapter);
  160. ilg.Call(XmlFormatGeneratorStatics.GetDateTimeOffsetMethod);
  161. ilg.ConvertValue(Globals.TypeOfDateTimeOffset, ilg.CurrentMethod.ReturnType);
  162. }
  163. else
  164. {
  165. ilg.ConvertValue(objectLocal.LocalType, ilg.CurrentMethod.ReturnType);
  166. }
  167. }
  168. return (XmlFormatClassReaderDelegate)ilg.EndMethod();
  169. }
  170. public XmlFormatCollectionReaderDelegate GenerateCollectionReader(CollectionDataContract collectionContract)
  171. {
  172. ilg = GenerateCollectionReaderHelper(collectionContract, false /*isGetOnlyCollection*/);
  173. ReadCollection(collectionContract);
  174. ilg.Load(objectLocal);
  175. ilg.ConvertValue(objectLocal.LocalType, ilg.CurrentMethod.ReturnType);
  176. return (XmlFormatCollectionReaderDelegate)ilg.EndMethod();
  177. }
  178. public XmlFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader(CollectionDataContract collectionContract)
  179. {
  180. ilg = GenerateCollectionReaderHelper(collectionContract, true /*isGetOnlyCollection*/);
  181. ReadGetOnlyCollection(collectionContract);
  182. return (XmlFormatGetOnlyCollectionReaderDelegate)ilg.EndMethod();
  183. }
  184. CodeGenerator GenerateCollectionReaderHelper(CollectionDataContract collectionContract, bool isGetOnlyCollection)
  185. {
  186. ilg = new CodeGenerator();
  187. bool memberAccessFlag = collectionContract.RequiresMemberAccessForRead(null);
  188. try
  189. {
  190. if (isGetOnlyCollection)
  191. {
  192. ilg.BeginMethod("Read" + collectionContract.StableName.Name + "FromXml" + "IsGetOnly", Globals.TypeOfXmlFormatGetOnlyCollectionReaderDelegate, memberAccessFlag);
  193. }
  194. else
  195. {
  196. ilg.BeginMethod("Read" + collectionContract.StableName.Name + "FromXml" + string.Empty, Globals.TypeOfXmlFormatCollectionReaderDelegate, memberAccessFlag);
  197. }
  198. }
  199. catch (SecurityException securityException)
  200. {
  201. if (memberAccessFlag && securityException.PermissionType.Equals(typeof(ReflectionPermission)))
  202. {
  203. collectionContract.RequiresMemberAccessForRead(securityException);
  204. }
  205. else
  206. {
  207. throw;
  208. }
  209. }
  210. InitArgs();
  211. DemandMemberAccessPermission(memberAccessFlag);
  212. collectionContractArg = ilg.GetArg(4);
  213. return ilg;
  214. }
  215. void InitArgs()
  216. {
  217. xmlReaderArg = ilg.GetArg(0);
  218. contextArg = ilg.GetArg(1);
  219. memberNamesArg = ilg.GetArg(2);
  220. memberNamespacesArg = ilg.GetArg(3);
  221. }
  222. void DemandMemberAccessPermission(bool memberAccessFlag)
  223. {
  224. if (memberAccessFlag)
  225. {
  226. ilg.Call(contextArg, XmlFormatGeneratorStatics.DemandMemberAccessPermissionMethod);
  227. }
  228. }
  229. void DemandSerializationFormatterPermission(ClassDataContract classContract)
  230. {
  231. if (!classContract.HasDataContract && !classContract.IsNonAttributedType)
  232. {
  233. ilg.Call(contextArg, XmlFormatGeneratorStatics.DemandSerializationFormatterPermissionMethod);
  234. }
  235. }
  236. void CreateObject(ClassDataContract classContract)
  237. {
  238. Type type = objectType = classContract.UnderlyingType;
  239. if (type.IsValueType && !classContract.IsNonAttributedType)
  240. type = Globals.TypeOfValueType;
  241. objectLocal = ilg.DeclareLocal(type, "objectDeserialized");
  242. if (classContract.UnderlyingType == Globals.TypeOfDBNull)
  243. {
  244. ilg.LoadMember(Globals.TypeOfDBNull.GetField("Value"));
  245. ilg.Stloc(objectLocal);
  246. }
  247. else if (classContract.IsNonAttributedType)
  248. {
  249. if (type.IsValueType)
  250. {
  251. ilg.Ldloca(objectLocal);
  252. ilg.InitObj(type);
  253. }
  254. else
  255. {
  256. ilg.New(classContract.GetNonAttributedTypeConstructor());
  257. ilg.Stloc(objectLocal);
  258. }
  259. }
  260. else
  261. {
  262. ilg.Call(null, XmlFormatGeneratorStatics.GetUninitializedObjectMethod, DataContract.GetIdForInitialization(classContract));
  263. ilg.ConvertValue(Globals.TypeOfObject, type);
  264. ilg.Stloc(objectLocal);
  265. }
  266. }
  267. void InvokeOnDeserializing(ClassDataContract classContract)
  268. {
  269. if (classContract.BaseContract != null)
  270. InvokeOnDeserializing(classContract.BaseContract);
  271. if (classContract.OnDeserializing != null)
  272. {
  273. ilg.LoadAddress(objectLocal);
  274. ilg.ConvertAddress(objectLocal.LocalType, objectType);
  275. ilg.Load(contextArg);
  276. ilg.LoadMember(XmlFormatGeneratorStatics.GetStreamingContextMethod);
  277. ilg.Call(classContract.OnDeserializing);
  278. }
  279. }
  280. void InvokeOnDeserialized(ClassDataContract classContract)
  281. {
  282. if (classContract.BaseContract != null)
  283. InvokeOnDeserialized(classContract.BaseContract);
  284. if (classContract.OnDeserialized != null)
  285. {
  286. ilg.LoadAddress(objectLocal);
  287. ilg.ConvertAddress(objectLocal.LocalType, objectType);
  288. ilg.Load(contextArg);
  289. ilg.LoadMember(XmlFormatGeneratorStatics.GetStreamingContextMethod);
  290. ilg.Call(classContract.OnDeserialized);
  291. }
  292. }
  293. bool HasFactoryMethod(ClassDataContract classContract)
  294. {
  295. return Globals.TypeOfIObjectReference.IsAssignableFrom(classContract.UnderlyingType);
  296. }
  297. bool InvokeFactoryMethod(ClassDataContract classContract, LocalBuilder objectId)
  298. {
  299. if (HasFactoryMethod(classContract))
  300. {
  301. ilg.Load(contextArg);
  302. ilg.LoadAddress(objectLocal);
  303. ilg.ConvertAddress(objectLocal.LocalType, Globals.TypeOfIObjectReference);
  304. ilg.Load(objectId);
  305. ilg.Call(XmlFormatGeneratorStatics.GetRealObjectMethod);
  306. ilg.ConvertValue(Globals.TypeOfObject, ilg.CurrentMethod.ReturnType);
  307. return true;
  308. }
  309. return false;
  310. }
  311. void ReadClass(ClassDataContract classContract)
  312. {
  313. if (classContract.HasExtensionData)
  314. {
  315. LocalBuilder extensionDataLocal = ilg.DeclareLocal(Globals.TypeOfExtensionDataObject, "extensionData");
  316. ilg.New(XmlFormatGeneratorStatics.ExtensionDataObjectCtor);
  317. ilg.Store(extensionDataLocal);
  318. ReadMembers(classContract, extensionDataLocal);
  319. ClassDataContract currentContract = classContract;
  320. while (currentContract != null)
  321. {
  322. MethodInfo extensionDataSetMethod = currentContract.ExtensionDataSetMethod;
  323. if (extensionDataSetMethod != null)
  324. ilg.Call(objectLocal, extensionDataSetMethod, extensionDataLocal);
  325. currentContract = currentContract.BaseContract;
  326. }
  327. }
  328. else
  329. ReadMembers(classContract, null /*extensionDataLocal*/);
  330. }
  331. void ReadMembers(ClassDataContract classContract, LocalBuilder extensionDataLocal)
  332. {
  333. int memberCount = classContract.MemberNames.Length;
  334. ilg.Call(contextArg, XmlFormatGeneratorStatics.IncrementItemCountMethod, memberCount);
  335. LocalBuilder memberIndexLocal = ilg.DeclareLocal(Globals.TypeOfInt, "memberIndex", -1);
  336. int firstRequiredMember;
  337. bool[] requiredMembers = GetRequiredMembers(classContract, out firstRequiredMember);
  338. bool hasRequiredMembers = (firstRequiredMember < memberCount);
  339. LocalBuilder requiredIndexLocal = hasRequiredMembers ? ilg.DeclareLocal(Globals.TypeOfInt, "requiredIndex", firstRequiredMember) : null;
  340. object forReadElements = ilg.For(null, null, null);
  341. ilg.Call(null, XmlFormatGeneratorStatics.MoveToNextElementMethod, xmlReaderArg);
  342. ilg.IfFalseBreak(forReadElements);
  343. if (hasRequiredMembers)
  344. ilg.Call(contextArg, XmlFormatGeneratorStatics.GetMemberIndexWithRequiredMembersMethod, xmlReaderArg, memberNamesArg, memberNamespacesArg, memberIndexLocal, requiredIndexLocal, extensionDataLocal);
  345. else
  346. ilg.Call(contextArg, XmlFormatGeneratorStatics.GetMemberIndexMethod, xmlReaderArg, memberNamesArg, memberNamespacesArg, memberIndexLocal, extensionDataLocal);
  347. if (memberCount > 0)
  348. {
  349. Label[] memberLabels = ilg.Switch(memberCount);
  350. ReadMembers(classContract, requiredMembers, memberLabels, memberIndexLocal, requiredIndexLocal);
  351. ilg.EndSwitch();
  352. }
  353. else
  354. {
  355. ilg.Pop();
  356. }
  357. ilg.EndFor();
  358. if (hasRequiredMembers)
  359. {
  360. ilg.If(requiredIndexLocal, Cmp.LessThan, memberCount);
  361. ilg.Call(null, XmlFormatGeneratorStatics.ThrowRequiredMemberMissingExceptionMethod, xmlReaderArg, memberIndexLocal, requiredIndexLocal, memberNamesArg);
  362. ilg.EndIf();
  363. }
  364. }
  365. int ReadMembers(ClassDataContract classContract, bool[] requiredMembers, Label[] memberLabels, LocalBuilder memberIndexLocal, LocalBuilder requiredIndexLocal)
  366. {
  367. int memberCount = (classContract.BaseContract == null) ? 0 : ReadMembers(classContract.BaseContract, requiredMembers,
  368. memberLabels, memberIndexLocal, requiredIndexLocal);
  369. for (int i = 0; i < classContract.Members.Count; i++, memberCount++)
  370. {
  371. DataMember dataMember = classContract.Members[i];
  372. Type memberType = dataMember.MemberType;
  373. ilg.Case(memberLabels[memberCount], dataMember.Name);
  374. if (dataMember.IsRequired)
  375. {
  376. int nextRequiredIndex = memberCount + 1;
  377. for (; nextRequiredIndex < requiredMembers.Length; nextRequiredIndex++)
  378. if (requiredMembers[nextRequiredIndex])
  379. break;
  380. ilg.Set(requiredIndexLocal, nextRequiredIndex);
  381. }
  382. LocalBuilder value = null;
  383. if (dataMember.IsGetOnlyCollection)
  384. {
  385. ilg.LoadAddress(objectLocal);
  386. ilg.LoadMember(dataMember.MemberInfo);
  387. value = ilg.DeclareLocal(memberType, dataMember.Name + "Value");
  388. ilg.Stloc(value);
  389. ilg.Call(contextArg, XmlFormatGeneratorStatics.StoreCollectionMemberInfoMethod, value);
  390. ReadValue(memberType, dataMember.Name, classContract.StableName.Namespace);
  391. }
  392. else
  393. {
  394. value = ReadValue(memberType, dataMember.Name, classContract.StableName.Namespace);
  395. ilg.LoadAddress(objectLocal);
  396. ilg.ConvertAddress(objectLocal.LocalType, objectType);
  397. ilg.Ldloc(value);
  398. ilg.StoreMember(dataMember.MemberInfo);
  399. }
  400. ilg.Set(memberIndexLocal, memberCount);
  401. ilg.EndCase();
  402. }
  403. return memberCount;
  404. }
  405. bool[] GetRequiredMembers(ClassDataContract contract, out int firstRequiredMember)
  406. {
  407. int memberCount = contract.MemberNames.Length;
  408. bool[] requiredMembers = new bool[memberCount];
  409. GetRequiredMembers(contract, requiredMembers);
  410. for (firstRequiredMember = 0; firstRequiredMember < memberCount; firstRequiredMember++)
  411. if (requiredMembers[firstRequiredMember])
  412. break;
  413. return requiredMembers;
  414. }
  415. int GetRequiredMembers(ClassDataContract contract, bool[] requiredMembers)
  416. {
  417. int memberCount = (contract.BaseContract == null) ? 0 : GetRequiredMembers(contract.BaseContract, requiredMembers);
  418. List<DataMember> members = contract.Members;
  419. for (int i = 0; i < members.Count; i++, memberCount++)
  420. {
  421. requiredMembers[memberCount] = members[i].IsRequired;
  422. }
  423. return memberCount;
  424. }
  425. void ReadISerializable(ClassDataContract classContract)
  426. {
  427. ConstructorInfo ctor = classContract.GetISerializableConstructor();
  428. ilg.LoadAddress(objectLocal);
  429. ilg.ConvertAddress(objectLocal.LocalType, objectType);
  430. ilg.Call(contextArg, XmlFormatGeneratorStatics.ReadSerializationInfoMethod, xmlReaderArg, classContract.UnderlyingType);
  431. ilg.Load(contextArg);
  432. ilg.LoadMember(XmlFormatGeneratorStatics.GetStreamingContextMethod);
  433. ilg.Call(ctor);
  434. }
  435. LocalBuilder ReadValue(Type type, string name, string ns)
  436. {
  437. LocalBuilder value = ilg.DeclareLocal(type, "valueRead");
  438. LocalBuilder nullableValue = null;
  439. int nullables = 0;
  440. while (type.IsGenericType && type.GetGenericTypeDefinition() == Globals.TypeOfNullable)
  441. {
  442. nullables++;
  443. type = type.GetGenericArguments()[0];
  444. }
  445. PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(type);
  446. if ((primitiveContract != null && primitiveContract.UnderlyingType != Globals.TypeOfObject) || nullables != 0 || type.IsValueType)
  447. {
  448. LocalBuilder objectId = ilg.DeclareLocal(Globals.TypeOfString, "objectIdRead");
  449. ilg.Call(contextArg, XmlFormatGeneratorStatics.ReadAttributesMethod, xmlReaderArg);
  450. ilg.Call(contextArg, XmlFormatGeneratorStatics.ReadIfNullOrRefMethod, xmlReaderArg, type, DataContract.IsTypeSerializable(type));
  451. ilg.Stloc(objectId);
  452. // Deserialize null
  453. ilg.If(objectId, Cmp.EqualTo, Globals.NullObjectId);
  454. if (nullables != 0)
  455. {
  456. ilg.LoadAddress(value);
  457. ilg.InitObj(value.LocalType);
  458. }
  459. else if (type.IsValueType)
  460. ThrowValidationException(SR.GetString(SR.ValueTypeCannotBeNull, DataContract.GetClrTypeFullName(type)));
  461. else
  462. {
  463. ilg.Load(null);
  464. ilg.Stloc(value);
  465. }
  466. // Deserialize value
  467. // Compare against Globals.NewObjectId, which is set to string.Empty
  468. ilg.ElseIfIsEmptyString(objectId);
  469. ilg.Call(contextArg, XmlFormatGeneratorStatics.GetObjectIdMethod);
  470. ilg.Stloc(objectId);
  471. if (type.IsValueType)
  472. {
  473. ilg.IfNotIsEmptyString(objectId);
  474. ThrowValidationException(SR.GetString(SR.ValueTypeCannotHaveId, DataContract.GetClrTypeFullName(type)));
  475. ilg.EndIf();
  476. }
  477. if (nullables != 0)
  478. {
  479. nullableValue = value;
  480. value = ilg.DeclareLocal(type, "innerValueRead");
  481. }
  482. if (primitiveContract != null && primitiveContract.UnderlyingType != Globals.TypeOfObject)
  483. {
  484. ilg.Call(xmlReaderArg, primitiveContract.XmlFormatReaderMethod);
  485. ilg.Stloc(value);
  486. if (!type.IsValueType)
  487. ilg.Call(contextArg, XmlFormatGeneratorStatics.AddNewObjectMethod, value);
  488. }
  489. else
  490. {
  491. InternalDeserialize(value, type, name, ns);
  492. }
  493. // Deserialize ref
  494. ilg.Else();
  495. if (type.IsValueType)
  496. ThrowValidationException(SR.GetString(SR.ValueTypeCannotHaveRef, DataContract.GetClrTypeFullName(type)));
  497. else
  498. {
  499. ilg.Call(contextArg, XmlFormatGeneratorStatics.GetExistingObjectMethod, objectId, type, name, ns);
  500. ilg.ConvertValue(Globals.TypeOfObject, type);
  501. ilg.Stloc(value);
  502. }
  503. ilg.EndIf();
  504. if (nullableValue != null)
  505. {
  506. ilg.If(objectId, Cmp.NotEqualTo, Globals.NullObjectId);
  507. WrapNullableObject(value, nullableValue, nullables);
  508. ilg.EndIf();
  509. value = nullableValue;
  510. }
  511. }
  512. else
  513. {
  514. InternalDeserialize(value, type, name, ns);
  515. }
  516. return value;
  517. }
  518. void InternalDeserialize(LocalBuilder value, Type type, string name, string ns)
  519. {
  520. ilg.Load(contextArg);
  521. ilg.Load(xmlReaderArg);
  522. Type declaredType = type.IsPointer ? Globals.TypeOfReflectionPointer : type;
  523. ilg.Load(DataContract.GetId(declaredType.TypeHandle));
  524. ilg.Ldtoken(declaredType);
  525. ilg.Load(name);
  526. ilg.Load(ns);
  527. ilg.Call(XmlFormatGeneratorStatics.InternalDeserializeMethod);
  528. if (type.IsPointer)
  529. ilg.Call(XmlFormatGeneratorStatics.UnboxPointer);
  530. else
  531. ilg.ConvertValue(Globals.TypeOfObject, type);
  532. ilg.Stloc(value);
  533. }
  534. void WrapNullableObject(LocalBuilder innerValue, LocalBuilder outerValue, int nullables)
  535. {
  536. Type innerType = innerValue.LocalType, outerType = outerValue.LocalType;
  537. ilg.LoadAddress(outerValue);
  538. ilg.Load(innerValue);
  539. for (int i = 1; i < nullables; i++)
  540. {
  541. Type type = Globals.TypeOfNullable.MakeGenericType(innerType);
  542. ilg.New(type.GetConstructor(new Type[] { innerType }));
  543. innerType = type;
  544. }
  545. ilg.Call(outerType.GetConstructor(new Type[] { innerType }));
  546. }
  547. void ReadCollection(CollectionDataContract collectionContract)
  548. {
  549. Type type = collectionContract.UnderlyingType;
  550. Type itemType = collectionContract.ItemType;
  551. bool isArray = (collectionContract.Kind == CollectionKind.Array);
  552. ConstructorInfo constructor = collectionContract.Constructor;
  553. if (type.IsInterface)
  554. {
  555. switch (collectionContract.Kind)
  556. {
  557. case CollectionKind.GenericDictionary:
  558. type = Globals.TypeOfDictionaryGeneric.MakeGenericType(itemType.GetGenericArguments());
  559. constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, Globals.EmptyTypeArray, null);
  560. break;
  561. case CollectionKind.Dictionary:
  562. type = Globals.TypeOfHashtable;
  563. constructor = XmlFormatGeneratorStatics.HashtableCtor;
  564. break;
  565. case CollectionKind.Collection:
  566. case CollectionKind.GenericCollection:
  567. case CollectionKind.Enumerable:
  568. case CollectionKind.GenericEnumerable:
  569. case CollectionKind.List:
  570. case CollectionKind.GenericList:
  571. type = itemType.MakeArrayType();
  572. isArray = true;
  573. break;
  574. }
  575. }
  576. string itemName = collectionContract.ItemName;
  577. string itemNs = collectionContract.StableName.Namespace;
  578. objectLocal = ilg.DeclareLocal(type, "objectDeserialized");
  579. if (!isArray)
  580. {
  581. if (type.IsValueType)
  582. {
  583. ilg.Ldloca(objectLocal);
  584. ilg.InitObj(type);
  585. }
  586. else
  587. {
  588. ilg.New(constructor);
  589. ilg.Stloc(objectLocal);
  590. ilg.Call(contextArg, XmlFormatGeneratorStatics.AddNewObjectMethod, objectLocal);
  591. }
  592. }
  593. LocalBuilder size = ilg.DeclareLocal(Globals.TypeOfInt, "arraySize");
  594. ilg.Call(contextArg, XmlFormatGeneratorStatics.GetArraySizeMethod);
  595. ilg.Stloc(size);
  596. LocalBuilder objectId = ilg.DeclareLocal(Globals.TypeOfString, "objectIdRead");
  597. ilg.Call(contextArg, XmlFormatGeneratorStatics.GetObjectIdMethod);
  598. ilg.Stloc(objectId);
  599. bool canReadPrimitiveArray = false;
  600. if (isArray && TryReadPrimitiveArray(type, itemType, size))
  601. {
  602. canReadPrimitiveArray = true;
  603. ilg.IfNot();
  604. }
  605. ilg.If(size, Cmp.EqualTo, -1);
  606. LocalBuilder growingCollection = null;
  607. if (isArray)
  608. {
  609. growingCollection = ilg.DeclareLocal(type, "growingCollection");
  610. ilg.NewArray(itemType, 32);
  611. ilg.Stloc(growingCollection);
  612. }
  613. LocalBuilder i = ilg.DeclareLocal(Globals.TypeOfInt, "i");
  614. object forLoop = ilg.For(i, 0, Int32.MaxValue);
  615. IsStartElement(memberNamesArg, memberNamespacesArg);
  616. ilg.If();
  617. ilg.Call(contextArg, XmlFormatGeneratorStatics.IncrementItemCountMethod, 1);
  618. LocalBuilder value = ReadCollectionItem(collectionContract, itemType, itemName, itemNs);
  619. if (isArray)
  620. {
  621. MethodInfo ensureArraySizeMethod = XmlFormatGeneratorStatics.EnsureArraySizeMethod.MakeGenericMethod(itemType);
  622. ilg.Call(null, ensureArraySizeMethod, growingCollection, i);
  623. ilg.Stloc(growingCollection);
  624. ilg.StoreArrayElement(growingCollection, i, value);
  625. }
  626. else
  627. StoreCollectionValue(objectLocal, value, collectionContract);
  628. ilg.Else();
  629. IsEndElement();
  630. ilg.If();
  631. ilg.Break(forLoop);
  632. ilg.Else();
  633. HandleUnexpectedItemInCollection(i);
  634. ilg.EndIf();
  635. ilg.EndIf();
  636. ilg.EndFor();
  637. if (isArray)
  638. {
  639. MethodInfo trimArraySizeMethod = XmlFormatGeneratorStatics.TrimArraySizeMethod.MakeGenericMethod(itemType);
  640. ilg.Call(null, trimArraySizeMethod, growingCollection, i);
  641. ilg.Stloc(objectLocal);
  642. ilg.Call(contextArg, XmlFormatGeneratorStatics.AddNewObjectWithIdMethod, objectId, objectLocal);
  643. }
  644. ilg.Else();
  645. ilg.Call(contextArg, XmlFormatGeneratorStatics.IncrementItemCountMethod, size);
  646. if (isArray)
  647. {
  648. ilg.NewArray(itemType, size);
  649. ilg.Stloc(objectLocal);
  650. ilg.Call(contextArg, XmlFormatGeneratorStatics.AddNewObjectMethod, objectLocal);
  651. }
  652. LocalBuilder j = ilg.DeclareLocal(Globals.TypeOfInt, "j");
  653. ilg.For(j, 0, size);
  654. IsStartElement(memberNamesArg, memberNamespacesArg);
  655. ilg.If();
  656. LocalBuilder itemValue = ReadCollectionItem(collectionContract, itemType, itemName, itemNs);
  657. if (isArray)
  658. ilg.StoreArrayElement(objectLocal, j, itemValue);
  659. else
  660. StoreCollectionValue(objectLocal, itemValue, collectionContract);
  661. ilg.Else();
  662. HandleUnexpectedItemInCollection(j);
  663. ilg.EndIf();
  664. ilg.EndFor();
  665. ilg.Call(contextArg, XmlFormatGeneratorStatics.CheckEndOfArrayMethod, xmlReaderArg, size, memberNamesArg, memberNamespacesArg);
  666. ilg.EndIf();
  667. if (canReadPrimitiveArray)
  668. {
  669. ilg.Else();
  670. ilg.Call(contextArg, XmlFormatGeneratorStatics.AddNewObjectWithIdMethod, objectId, objectLocal);
  671. ilg.EndIf();
  672. }
  673. }
  674. void ReadGetOnlyCollection(CollectionDataContract collectionContract)
  675. {
  676. Type type = collectionContract.UnderlyingType;
  677. Type itemType = collectionContract.ItemType;
  678. bool isArray = (collectionContract.Kind == CollectionKind.Array);
  679. string itemName = collectionContract.ItemName;
  680. string itemNs = collectionContract.StableName.Namespace;
  681. objectLocal = ilg.DeclareLocal(type, "objectDeserialized");
  682. ilg.Load(contextArg);
  683. ilg.LoadMember(XmlFormatGeneratorStatics.GetCollectionMemberMethod);
  684. ilg.ConvertValue(Globals.TypeOfObject, type);
  685. ilg.Stloc(objectLocal);
  686. //check that items are actually going to be deserialized into the collection
  687. IsStartElement(memberNamesArg, memberNamespacesArg);
  688. ilg.If();
  689. ilg.If(objectLocal, Cmp.EqualTo, null);
  690. ilg.Call(null, XmlFormatGeneratorStatics.ThrowNullValueReturnedForGetOnlyCollectionExceptionMethod, type);
  691. ilg.Else();
  692. LocalBuilder size = ilg.DeclareLocal(Globals.TypeOfInt, "arraySize");
  693. if (isArray)
  694. {
  695. ilg.Load(objectLocal);
  696. ilg.Call(XmlFormatGeneratorStatics.GetArrayLengthMethod);
  697. ilg.Stloc(size);
  698. }
  699. ilg.Call(contextArg, XmlFormatGeneratorStatics.AddNewObjectMethod, objectLocal);
  700. LocalBuilder i = ilg.DeclareLocal(Globals.TypeOfInt, "i");
  701. object forLoop = ilg.For(i, 0, Int32.MaxValue);
  702. IsStartElement(memberNamesArg, memberNamespacesArg);
  703. ilg.If();
  704. ilg.Call(contextArg, XmlFormatGeneratorStatics.IncrementItemCountMethod, 1);
  705. LocalBuilder value = ReadCollectionItem(collectionContract, itemType, itemName, itemNs);
  706. if (isArray)
  707. {
  708. ilg.If(size, Cmp.EqualTo, i);
  709. ilg.Call(null, XmlFormatGeneratorStatics.ThrowArrayExceededSizeExceptionMethod, size, type);
  710. ilg.Else();
  711. ilg.StoreArrayElement(objectLocal, i, value);
  712. ilg.EndIf();
  713. }
  714. else
  715. StoreCollectionValue(objectLocal, value, collectionContract);
  716. ilg.Else();
  717. IsEndElement();
  718. ilg.If();
  719. ilg.Break(forLoop);
  720. ilg.Else();
  721. HandleUnexpectedItemInCollection(i);
  722. ilg.EndIf();
  723. ilg.EndIf();
  724. ilg.EndFor();
  725. ilg.Call(contextArg, XmlFormatGeneratorStatics.CheckEndOfArrayMethod, xmlReaderArg, size, memberNamesArg, memberNamespacesArg);
  726. ilg.EndIf();
  727. ilg.EndIf();
  728. }
  729. bool TryReadPrimitiveArray(Type type, Type itemType, LocalBuilder size)
  730. {
  731. PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(itemType);
  732. if (primitiveContract == null)
  733. return false;
  734. string readArrayMethod = null;
  735. switch (Type.GetTypeCode(itemType))
  736. {
  737. case TypeCode.Boolean:
  738. readArrayMethod = "TryReadBooleanArray";
  739. break;
  740. case TypeCode.DateTime:
  741. readArrayMethod = "TryReadDateTimeArray";
  742. break;
  743. case TypeCode.Decimal:
  744. readArrayMethod = "TryReadDecimalArray";
  745. break;
  746. case TypeCode.Int32:
  747. readArrayMethod = "TryReadInt32Array";
  748. break;
  749. case TypeCode.Int64:
  750. readArrayMethod = "TryReadInt64Array";
  751. break;
  752. case TypeCode.Single:
  753. readArrayMethod = "TryReadSingleArray";
  754. break;
  755. case TypeCode.Double:
  756. readArrayMethod = "TryReadDoubleArray";
  757. break;
  758. default:
  759. break;
  760. }
  761. if (readArrayMethod != null)
  762. {
  763. ilg.Load(xmlReaderArg);
  764. ilg.Load(contextArg);
  765. ilg.Load(memberNamesArg);
  766. ilg.Load(memberNamespacesArg);
  767. ilg.Load(size);
  768. ilg.Ldloca(objectLocal);
  769. ilg.Call(typeof(XmlReaderDelegator).GetMethod(readArrayMethod, Globals.ScanAllMembers));
  770. return true;
  771. }
  772. return false;
  773. }
  774. LocalBuilder ReadCollectionItem(CollectionDataContract collectionContract, Type itemType, string itemName, string itemNs)
  775. {
  776. if (collectionContract.Kind == CollectionKind.Dictionary || collectionContract.Kind == CollectionKind.GenericDictionary)
  777. {
  778. ilg.Call(contextArg, XmlFormatGeneratorStatics.ResetAttributesMethod);
  779. LocalBuilder value = ilg.DeclareLocal(itemType, "valueRead");
  780. ilg.Load(collectionContractArg);
  781. ilg.Call(XmlFormatGeneratorStatics.GetItemContractMethod);
  782. ilg.Load(xmlReaderArg);
  783. ilg.Load(contextArg);
  784. ilg.Call(XmlFormatGeneratorStatics.ReadXmlValueMethod);
  785. ilg.ConvertValue(Globals.TypeOfObject, itemType);
  786. ilg.Stloc(value);
  787. return value;
  788. }
  789. else
  790. {
  791. return ReadValue(itemType, itemName, itemNs);
  792. }
  793. }
  794. void StoreCollectionValue(LocalBuilder collection, LocalBuilder value, CollectionDataContract collectionContract)
  795. {
  796. if (collectionContract.Kind == CollectionKind.GenericDictionary || collectionContract.Kind == CollectionKind.Dictionary)
  797. {
  798. ClassDataContract keyValuePairContract = DataContract.GetDataContract(value.LocalType) as ClassDataContract;
  799. if (keyValuePairContract == null)
  800. {
  801. Fx.Assert("Failed to create contract for KeyValuePair type");
  802. }
  803. DataMember keyMember = keyValuePairContract.Members[0];
  804. DataMember valueMember = keyValuePairContract.Members[1];
  805. LocalBuilder pairKey = ilg.DeclareLocal(keyMember.MemberType, keyMember.Name);
  806. LocalBuilder pairValue = ilg.DeclareLocal(valueMember.MemberType, valueMember.Name);
  807. ilg.LoadAddress(value);
  808. ilg.LoadMember(keyMember.MemberInfo);
  809. ilg.Stloc(pairKey);
  810. ilg.LoadAddress(value);
  811. ilg.LoadMember(valueMember.MemberInfo);
  812. ilg.Stloc(pairValue);
  813. ilg.Call(collection, collectionContract.AddMethod, pairKey, pairValue);
  814. if (collectionContract.AddMethod.ReturnType != Globals.TypeOfVoid)
  815. ilg.Pop();
  816. }
  817. else
  818. {
  819. ilg.Call(collection, collectionContract.AddMethod, value);
  820. if (collectionContract.AddMethod.ReturnType != Globals.TypeOfVoid)
  821. ilg.Pop();
  822. }
  823. }
  824. void HandleUnexpectedItemInCollection(LocalBuilder iterator)
  825. {
  826. IsStartElement();
  827. ilg.If();
  828. ilg.Call(contextArg, XmlFormatGeneratorStatics.SkipUnknownElementMethod, xmlReaderArg);
  829. ilg.Dec(iterator);
  830. ilg.Else();
  831. ThrowUnexpectedStateException(XmlNodeType.Element);
  832. ilg.EndIf();
  833. }
  834. void IsStartElement(ArgBuilder nameArg, ArgBuilder nsArg)
  835. {
  836. ilg.Call(xmlReaderArg, XmlFormatGeneratorStatics.IsStartElementMethod2, nameArg, nsArg);
  837. }
  838. void IsStartElement()
  839. {
  840. ilg.Call(xmlReaderArg, XmlFormatGeneratorStatics.IsStartElementMethod0);
  841. }
  842. void IsEndElement()
  843. {
  844. ilg.Load(xmlReaderArg);
  845. ilg.LoadMember(XmlFormatGeneratorStatics.NodeTypeProperty);
  846. ilg.Load(XmlNodeType.EndElement);
  847. ilg.Ceq();
  848. }
  849. void ThrowUnexpectedStateException(XmlNodeType expectedState)
  850. {
  851. ilg.Call(null, XmlFormatGeneratorStatics.CreateUnexpectedStateExceptionMethod, expectedState, xmlReaderArg);
  852. ilg.Throw();
  853. }
  854. void ThrowValidationException(string msg, params object[] values)
  855. {
  856. if (values != null && values.Length > 0)
  857. ilg.CallStringFormat(msg, values);
  858. else
  859. ilg.Load(msg);
  860. ThrowValidationException();
  861. }
  862. void ThrowValidationException()
  863. {
  864. ilg.New(XmlFormatGeneratorStatics.SerializationExceptionCtor);
  865. ilg.Throw();
  866. }
  867. }
  868. #endif
  869. [Fx.Tag.SecurityNote(Critical = "Elevates by calling GetUninitializedObject which has a LinkDemand.",
  870. Safe = "Marked as such so that it's callable from transparent generated IL. Takes id as parameter which "
  871. + " is guaranteed to be in internal serialization cache.")]
  872. [SecuritySafeCritical]
  873. #if USE_REFEMIT
  874. public static object UnsafeGetUninitializedObject(int id)
  875. #else
  876. static internal object UnsafeGetUninitializedObject(int id)
  877. #endif
  878. {
  879. return FormatterServices.GetUninitializedObject(DataContract.GetDataContractForInitialization(id).TypeForInitialization);
  880. }
  881. }
  882. }