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