Add NO_DYNAMIC_CODEGEN condition. We will be based on (non-emit) reflection.
[mono.git] / mcs / class / referencesource / System.Runtime.Serialization / System / Runtime / Serialization / Json / JsonFormatReaderGenerator.cs
1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //-----------------------------------------------------------------------------
4 namespace System.Runtime.Serialization.Json
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;
13     using System.Runtime.Serialization.Diagnostics.Application;
14     using System.Security;
15     using System.Security.Permissions;
16     using System.Xml;
17
18     delegate object JsonFormatClassReaderDelegate(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContextComplexJson context, XmlDictionaryString emptyDictionaryString, XmlDictionaryString[] memberNames);
19     delegate object JsonFormatCollectionReaderDelegate(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContextComplexJson context, XmlDictionaryString emptyDictionaryString, XmlDictionaryString itemName, CollectionDataContract collectionContract);
20     delegate void JsonFormatGetOnlyCollectionReaderDelegate(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContextComplexJson context, XmlDictionaryString emptyDictionaryString, XmlDictionaryString itemName, CollectionDataContract collectionContract);
21
22     sealed partial class JsonFormatReaderGenerator
23     {
24         [Fx.Tag.SecurityNote(Critical = "Holds instance of CriticalHelper which keeps state that was produced within an assert.")]
25         [SecurityCritical]
26         CriticalHelper helper;
27
28         [Fx.Tag.SecurityNote(Critical = "Initializes SecurityCritical field 'helper'.")]
29         [SecurityCritical]
30         public JsonFormatReaderGenerator()
31         {
32             helper = new CriticalHelper();
33         }
34
35         [Fx.Tag.SecurityNote(Critical = "Accesses SecurityCritical helper class 'CriticalHelper'.")]
36         [SecurityCritical]
37         public JsonFormatClassReaderDelegate GenerateClassReader(ClassDataContract classContract)
38         {
39
40
41             try
42             {
43                 if (TD.DCJsonGenReaderStartIsEnabled())
44                 {
45                     TD.DCJsonGenReaderStart("Class", classContract.UnderlyingType.FullName);
46                 }
47
48                 return helper.GenerateClassReader(classContract);
49             }
50             finally
51             {
52                 if (TD.DCJsonGenReaderStopIsEnabled())
53                 {
54                     TD.DCJsonGenReaderStop();
55                 }
56             }
57         }
58
59         [Fx.Tag.SecurityNote(Critical = "Accesses SecurityCritical helper class 'CriticalHelper'.")]
60         [SecurityCritical]
61         public JsonFormatCollectionReaderDelegate GenerateCollectionReader(CollectionDataContract collectionContract)
62         {
63             try
64             {
65                 if (TD.DCJsonGenReaderStartIsEnabled())
66                 {
67                     TD.DCJsonGenReaderStart("Collection", collectionContract.StableName.Name);
68                 }
69
70                 return helper.GenerateCollectionReader(collectionContract);
71
72             }
73             finally
74             {
75                 if (TD.DCJsonGenReaderStopIsEnabled())
76                 {
77                     TD.DCJsonGenReaderStop();
78                 }
79             }
80
81         }
82
83         [Fx.Tag.SecurityNote(Critical = "Accesses SecurityCritical helper class 'CriticalHelper'.")]
84         [SecurityCritical]
85         public JsonFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader(CollectionDataContract collectionContract)
86         {
87
88             try
89             {
90                 if (TD.DCJsonGenReaderStartIsEnabled())
91                 {
92                     TD.DCJsonGenReaderStart("GetOnlyCollection", collectionContract.UnderlyingType.FullName);
93                 }
94
95                 return helper.GenerateGetOnlyCollectionReader(collectionContract);
96
97             }
98             finally
99             {
100                 if (TD.DCJsonGenReaderStopIsEnabled())
101                 {
102                     TD.DCJsonGenReaderStop();
103                 }
104             }
105         }
106
107 #if !NO_DYNAMIC_CODEGEN
108         [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - handles all aspects of IL generation including initializing the DynamicMethod."
109             + "Changes to how IL generated could affect how data is deserialized and what gets access to data, "
110             + "therefore we mark it for review so that changes to generation logic are reviewed.")]
111         partial class CriticalHelper
112         {
113             CodeGenerator ilg;
114             LocalBuilder objectLocal;
115             Type objectType;
116             ArgBuilder xmlReaderArg;
117             ArgBuilder contextArg;
118             ArgBuilder memberNamesArg;
119             ArgBuilder collectionContractArg;
120             ArgBuilder emptyDictionaryStringArg;
121
122             public JsonFormatClassReaderDelegate GenerateClassReader(ClassDataContract classContract)
123             {
124                 ilg = new CodeGenerator();
125                 bool memberAccessFlag = classContract.RequiresMemberAccessForRead(null);
126                 try
127                 {
128                     BeginMethod(ilg, "Read" + classContract.StableName.Name + "FromJson", typeof(JsonFormatClassReaderDelegate), memberAccessFlag);
129                 }
130                 catch (SecurityException securityException)
131                 {
132                     if (memberAccessFlag && securityException.PermissionType.Equals(typeof(ReflectionPermission)))
133                     {
134                         classContract.RequiresMemberAccessForRead(securityException);
135                     }
136                     else
137                     {
138                         throw;
139                     }
140                 }
141                 InitArgs();
142                 DemandSerializationFormatterPermission(classContract);
143                 DemandMemberAccessPermission(memberAccessFlag);
144                 CreateObject(classContract);
145                 ilg.Call(contextArg, XmlFormatGeneratorStatics.AddNewObjectMethod, objectLocal);
146                 InvokeOnDeserializing(classContract);
147                 if (classContract.IsISerializable)
148                     ReadISerializable(classContract);
149                 else
150                     ReadClass(classContract);
151                 if (Globals.TypeOfIDeserializationCallback.IsAssignableFrom(classContract.UnderlyingType))
152                     ilg.Call(objectLocal, JsonFormatGeneratorStatics.OnDeserializationMethod, null);
153                 InvokeOnDeserialized(classContract);
154                 if (!InvokeFactoryMethod(classContract))
155                 {
156                     ilg.Load(objectLocal);
157                     // Do a conversion back from DateTimeOffsetAdapter to DateTimeOffset after deserialization.
158                     // DateTimeOffsetAdapter is used here for deserialization purposes to bypass the ISerializable implementation
159                     // on DateTimeOffset; which does not work in partial trust.
160
161                     if (classContract.UnderlyingType == Globals.TypeOfDateTimeOffsetAdapter)
162                     {
163                         ilg.ConvertValue(objectLocal.LocalType, Globals.TypeOfDateTimeOffsetAdapter);
164                         ilg.Call(XmlFormatGeneratorStatics.GetDateTimeOffsetMethod);
165                         ilg.ConvertValue(Globals.TypeOfDateTimeOffset, ilg.CurrentMethod.ReturnType);
166                     }
167                     else
168                     {
169                         ilg.ConvertValue(objectLocal.LocalType, ilg.CurrentMethod.ReturnType);
170                     }
171                 }
172                 return (JsonFormatClassReaderDelegate)ilg.EndMethod();
173             }
174
175             public JsonFormatCollectionReaderDelegate GenerateCollectionReader(CollectionDataContract collectionContract)
176             {
177                 ilg = GenerateCollectionReaderHelper(collectionContract, false /*isGetOnlyCollection*/);
178                 ReadCollection(collectionContract);
179                 ilg.Load(objectLocal);
180                 ilg.ConvertValue(objectLocal.LocalType, ilg.CurrentMethod.ReturnType);
181                 return (JsonFormatCollectionReaderDelegate)ilg.EndMethod();
182             }
183
184             public JsonFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader(CollectionDataContract collectionContract)
185             {
186                 ilg = GenerateCollectionReaderHelper(collectionContract, true /*isGetOnlyCollection*/);
187                 ReadGetOnlyCollection(collectionContract);
188                 return (JsonFormatGetOnlyCollectionReaderDelegate)ilg.EndMethod();
189             }
190
191             CodeGenerator GenerateCollectionReaderHelper(CollectionDataContract collectionContract, bool isGetOnlyCollection)
192             {
193                 ilg = new CodeGenerator();
194                 bool memberAccessFlag = collectionContract.RequiresMemberAccessForRead(null);
195                 try
196                 {
197                     if (isGetOnlyCollection)
198                     {
199                         BeginMethod(ilg, "Read" + collectionContract.StableName.Name + "FromJson" + "IsGetOnly", typeof(JsonFormatGetOnlyCollectionReaderDelegate), memberAccessFlag);
200                     }
201                     else
202                     {
203                         BeginMethod(ilg, "Read" + collectionContract.StableName.Name + "FromJson", typeof(JsonFormatCollectionReaderDelegate), memberAccessFlag);
204                     }
205                 }
206                 catch (SecurityException securityException)
207                 {
208                     if (memberAccessFlag && securityException.PermissionType.Equals(typeof(ReflectionPermission)))
209                     {
210                         collectionContract.RequiresMemberAccessForRead(securityException);
211                     }
212                     else
213                     {
214                         throw;
215                     }
216                 }
217                 InitArgs();
218                 DemandMemberAccessPermission(memberAccessFlag);
219                 collectionContractArg = ilg.GetArg(4);
220                 return ilg;
221             }
222
223             void BeginMethod(CodeGenerator ilg, string methodName, Type delegateType, bool allowPrivateMemberAccess)
224             {
225 #if USE_REFEMIT
226                 ilg.BeginMethod(methodName, delegateType, allowPrivateMemberAccess);
227 #else
228
229                 MethodInfo signature = delegateType.GetMethod("Invoke");
230                 ParameterInfo[] parameters = signature.GetParameters();
231                 Type[] paramTypes = new Type[parameters.Length];
232                 for (int i = 0; i < parameters.Length; i++)
233                     paramTypes[i] = parameters[i].ParameterType;
234
235                 DynamicMethod dynamicMethod = new DynamicMethod(methodName, signature.ReturnType, paramTypes, typeof(JsonFormatReaderGenerator).Module, allowPrivateMemberAccess);
236                 ilg.BeginMethod(dynamicMethod, delegateType, methodName, paramTypes, allowPrivateMemberAccess);
237 #endif
238             }
239
240             void InitArgs()
241             {
242                 xmlReaderArg = ilg.GetArg(0);
243                 contextArg = ilg.GetArg(1);
244                 emptyDictionaryStringArg = ilg.GetArg(2);
245                 memberNamesArg = ilg.GetArg(3);
246             }
247
248             void DemandMemberAccessPermission(bool memberAccessFlag)
249             {
250                 if (memberAccessFlag)
251                 {
252                     ilg.Call(contextArg, XmlFormatGeneratorStatics.DemandMemberAccessPermissionMethod);
253                 }
254             }
255
256             void DemandSerializationFormatterPermission(ClassDataContract classContract)
257             {
258                 if (!classContract.HasDataContract && !classContract.IsNonAttributedType)
259                 {
260                     ilg.Call(contextArg, XmlFormatGeneratorStatics.DemandSerializationFormatterPermissionMethod);
261                 }
262             }
263
264
265             void CreateObject(ClassDataContract classContract)
266             {
267                 Type type = objectType = classContract.UnderlyingType;
268                 if (type.IsValueType && !classContract.IsNonAttributedType)
269                     type = Globals.TypeOfValueType;
270
271                 objectLocal = ilg.DeclareLocal(type, "objectDeserialized");
272
273                 if (classContract.UnderlyingType == Globals.TypeOfDBNull)
274                 {
275                     ilg.LoadMember(Globals.TypeOfDBNull.GetField("Value"));
276                     ilg.Stloc(objectLocal);
277                 }
278                 else if (classContract.IsNonAttributedType)
279                 {
280                     if (type.IsValueType)
281                     {
282                         ilg.Ldloca(objectLocal);
283                         ilg.InitObj(type);
284                     }
285                     else
286                     {
287                         ilg.New(classContract.GetNonAttributedTypeConstructor());
288                         ilg.Stloc(objectLocal);
289                     }
290                 }
291                 else
292                 {
293                     ilg.Call(null, JsonFormatGeneratorStatics.GetUninitializedObjectMethod, DataContract.GetIdForInitialization(classContract));
294                     ilg.ConvertValue(Globals.TypeOfObject, type);
295                     ilg.Stloc(objectLocal);
296                 }
297             }
298
299             void InvokeOnDeserializing(ClassDataContract classContract)
300             {
301                 if (classContract.BaseContract != null)
302                     InvokeOnDeserializing(classContract.BaseContract);
303                 if (classContract.OnDeserializing != null)
304                 {
305                     ilg.LoadAddress(objectLocal);
306                     ilg.ConvertAddress(objectLocal.LocalType, objectType);
307                     ilg.Load(contextArg);
308                     ilg.LoadMember(XmlFormatGeneratorStatics.GetStreamingContextMethod);
309                     ilg.Call(classContract.OnDeserializing);
310                 }
311             }
312
313             void InvokeOnDeserialized(ClassDataContract classContract)
314             {
315                 if (classContract.BaseContract != null)
316                     InvokeOnDeserialized(classContract.BaseContract);
317                 if (classContract.OnDeserialized != null)
318                 {
319                     ilg.LoadAddress(objectLocal);
320                     ilg.ConvertAddress(objectLocal.LocalType, objectType);
321                     ilg.Load(contextArg);
322                     ilg.LoadMember(XmlFormatGeneratorStatics.GetStreamingContextMethod);
323                     ilg.Call(classContract.OnDeserialized);
324                 }
325             }
326
327             bool HasFactoryMethod(ClassDataContract classContract)
328             {
329                 return Globals.TypeOfIObjectReference.IsAssignableFrom(classContract.UnderlyingType);
330             }
331
332             bool InvokeFactoryMethod(ClassDataContract classContract)
333             {
334                 if (HasFactoryMethod(classContract))
335                 {
336                     ilg.Load(contextArg);
337                     ilg.LoadAddress(objectLocal);
338                     ilg.ConvertAddress(objectLocal.LocalType, Globals.TypeOfIObjectReference);
339                     ilg.Load(Globals.NewObjectId);
340                     ilg.Call(XmlFormatGeneratorStatics.GetRealObjectMethod);
341                     ilg.ConvertValue(Globals.TypeOfObject, ilg.CurrentMethod.ReturnType);
342                     return true;
343                 }
344                 return false;
345             }
346
347             void ReadClass(ClassDataContract classContract)
348             {
349                 if (classContract.HasExtensionData)
350                 {
351                     LocalBuilder extensionDataLocal = ilg.DeclareLocal(Globals.TypeOfExtensionDataObject, "extensionData");
352                     ilg.New(JsonFormatGeneratorStatics.ExtensionDataObjectCtor);
353                     ilg.Store(extensionDataLocal);
354                     ReadMembers(classContract, extensionDataLocal);
355
356                     ClassDataContract currentContract = classContract;
357                     while (currentContract != null)
358                     {
359                         MethodInfo extensionDataSetMethod = currentContract.ExtensionDataSetMethod;
360                         if (extensionDataSetMethod != null)
361                             ilg.Call(objectLocal, extensionDataSetMethod, extensionDataLocal);
362                         currentContract = currentContract.BaseContract;
363                     }
364                 }
365                 else
366                     ReadMembers(classContract, null /*extensionDataLocal*/);
367             }
368
369             void ReadMembers(ClassDataContract classContract, LocalBuilder extensionDataLocal)
370             {
371                 int memberCount = classContract.MemberNames.Length;
372                 ilg.Call(contextArg, XmlFormatGeneratorStatics.IncrementItemCountMethod, memberCount);
373
374                 BitFlagsGenerator expectedElements = new BitFlagsGenerator(memberCount, ilg, classContract.UnderlyingType.Name + "_ExpectedElements");
375                 byte[] requiredElements = new byte[expectedElements.GetLocalCount()];
376                 SetRequiredElements(classContract, requiredElements);
377                 SetExpectedElements(expectedElements, 0 /*startIndex*/);
378
379                 LocalBuilder memberIndexLocal = ilg.DeclareLocal(Globals.TypeOfInt, "memberIndex", -1);
380                 Label throwDuplicateMemberLabel = ilg.DefineLabel();
381                 Label throwMissingRequiredMembersLabel = ilg.DefineLabel();
382
383                 object forReadElements = ilg.For(null, null, null);
384                 ilg.Call(null, XmlFormatGeneratorStatics.MoveToNextElementMethod, xmlReaderArg);
385                 ilg.IfFalseBreak(forReadElements);
386                 ilg.Call(contextArg, JsonFormatGeneratorStatics.GetJsonMemberIndexMethod, xmlReaderArg, memberNamesArg, memberIndexLocal, extensionDataLocal);
387                 if (memberCount > 0)
388                 {
389                     Label[] memberLabels = ilg.Switch(memberCount);
390                     ReadMembers(classContract, expectedElements, memberLabels, throwDuplicateMemberLabel, memberIndexLocal);
391                     ilg.EndSwitch();
392                 }
393                 else
394                 {
395                     ilg.Pop();
396                 }
397                 ilg.EndFor();
398                 CheckRequiredElements(expectedElements, requiredElements, throwMissingRequiredMembersLabel);
399                 Label endOfTypeLabel = ilg.DefineLabel();
400                 ilg.Br(endOfTypeLabel);
401
402                 ilg.MarkLabel(throwDuplicateMemberLabel);
403                 ilg.Call(null, JsonFormatGeneratorStatics.ThrowDuplicateMemberExceptionMethod, objectLocal, memberNamesArg, memberIndexLocal);
404
405                 ilg.MarkLabel(throwMissingRequiredMembersLabel);
406                 ilg.Load(objectLocal);
407                 ilg.ConvertValue(objectLocal.LocalType, Globals.TypeOfObject);
408                 ilg.Load(memberNamesArg);
409                 expectedElements.LoadArray();
410                 LoadArray(requiredElements, "requiredElements");
411                 ilg.Call(JsonFormatGeneratorStatics.ThrowMissingRequiredMembersMethod);
412
413                 ilg.MarkLabel(endOfTypeLabel);
414             }
415
416             int ReadMembers(ClassDataContract classContract, BitFlagsGenerator expectedElements,
417                 Label[] memberLabels, Label throwDuplicateMemberLabel, LocalBuilder memberIndexLocal)
418             {
419                 int memberCount = (classContract.BaseContract == null) ? 0 :
420                     ReadMembers(classContract.BaseContract, expectedElements, memberLabels, throwDuplicateMemberLabel, memberIndexLocal);
421
422                 for (int i = 0; i < classContract.Members.Count; i++, memberCount++)
423                 {
424                     DataMember dataMember = classContract.Members[i];
425                     Type memberType = dataMember.MemberType;
426                     ilg.Case(memberLabels[memberCount], dataMember.Name);
427                     ilg.Set(memberIndexLocal, memberCount);
428                     expectedElements.Load(memberCount);
429                     ilg.Brfalse(throwDuplicateMemberLabel);
430                     LocalBuilder value = null;
431                     if (dataMember.IsGetOnlyCollection)
432                     {
433                         ilg.LoadAddress(objectLocal);
434                         ilg.LoadMember(dataMember.MemberInfo);
435                         value = ilg.DeclareLocal(memberType, dataMember.Name + "Value");
436                         ilg.Stloc(value);
437                         ilg.Call(contextArg, XmlFormatGeneratorStatics.StoreCollectionMemberInfoMethod, value);
438                         ReadValue(memberType, dataMember.Name);
439                     }
440                     else
441                     {
442                         value = ReadValue(memberType, dataMember.Name);
443                         ilg.LoadAddress(objectLocal);
444                         ilg.ConvertAddress(objectLocal.LocalType, objectType);
445                         ilg.Ldloc(value);
446                         ilg.StoreMember(dataMember.MemberInfo);
447                     }
448                     ResetExpectedElements(expectedElements, memberCount);
449                     ilg.EndCase();
450                 }
451                 return memberCount;
452             }
453
454             void CheckRequiredElements(BitFlagsGenerator expectedElements, byte[] requiredElements, Label throwMissingRequiredMembersLabel)
455             {
456                 for (int i = 0; i < requiredElements.Length; i++)
457                 {
458                     ilg.Load(expectedElements.GetLocal(i));
459                     ilg.Load(requiredElements[i]);
460                     ilg.And();
461                     ilg.Load(0);
462                     ilg.Ceq();
463                     ilg.Brfalse(throwMissingRequiredMembersLabel);
464                 }
465             }
466
467             void LoadArray(byte[] array, string name)
468             {
469                 LocalBuilder localArray = ilg.DeclareLocal(Globals.TypeOfByteArray, name);
470                 ilg.NewArray(typeof(byte), array.Length);
471                 ilg.Store(localArray);
472                 for (int i = 0; i < array.Length; i++)
473                 {
474                     ilg.StoreArrayElement(localArray, i, array[i]);
475                 }
476                 ilg.Load(localArray);
477             }
478
479             int SetRequiredElements(ClassDataContract contract, byte[] requiredElements)
480             {
481                 int memberCount = (contract.BaseContract == null) ? 0 :
482                     SetRequiredElements(contract.BaseContract, requiredElements);
483                 List<DataMember> members = contract.Members;
484                 for (int i = 0; i < members.Count; i++, memberCount++)
485                 {
486                     if (members[i].IsRequired)
487                     {
488                         BitFlagsGenerator.SetBit(requiredElements, memberCount);
489                     }
490                 }
491                 return memberCount;
492             }
493
494             void SetExpectedElements(BitFlagsGenerator expectedElements, int startIndex)
495             {
496                 int memberCount = expectedElements.GetBitCount();
497                 for (int i = startIndex; i < memberCount; i++)
498                 {
499                     expectedElements.Store(i, true);
500                 }
501             }
502
503             void ResetExpectedElements(BitFlagsGenerator expectedElements, int index)
504             {
505                 expectedElements.Store(index, false);
506             }
507
508             void ReadISerializable(ClassDataContract classContract)
509             {
510                 ConstructorInfo ctor = classContract.UnderlyingType.GetConstructor(Globals.ScanAllMembers, null, JsonFormatGeneratorStatics.SerInfoCtorArgs, null);
511                 if (ctor == null)
512                     throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.SerializationInfo_ConstructorNotFound, DataContract.GetClrTypeFullName(classContract.UnderlyingType))));
513                 ilg.LoadAddress(objectLocal);
514                 ilg.ConvertAddress(objectLocal.LocalType, objectType);
515                 ilg.Call(contextArg, XmlFormatGeneratorStatics.ReadSerializationInfoMethod, xmlReaderArg, classContract.UnderlyingType);
516                 ilg.Load(contextArg);
517                 ilg.LoadMember(XmlFormatGeneratorStatics.GetStreamingContextMethod);
518                 ilg.Call(ctor);
519             }
520
521             LocalBuilder ReadValue(Type type, string name)
522             {
523                 LocalBuilder value = ilg.DeclareLocal(type, "valueRead");
524                 LocalBuilder nullableValue = null;
525                 int nullables = 0;
526                 while (type.IsGenericType && type.GetGenericTypeDefinition() == Globals.TypeOfNullable)
527                 {
528                     nullables++;
529                     type = type.GetGenericArguments()[0];
530                 }
531
532                 PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(type);
533                 if ((primitiveContract != null && primitiveContract.UnderlyingType != Globals.TypeOfObject) || nullables != 0 || type.IsValueType)
534                 {
535                     LocalBuilder objectId = ilg.DeclareLocal(Globals.TypeOfString, "objectIdRead");
536                     ilg.Call(contextArg, XmlFormatGeneratorStatics.ReadAttributesMethod, xmlReaderArg);
537                     ilg.Call(contextArg, XmlFormatGeneratorStatics.ReadIfNullOrRefMethod, xmlReaderArg, type, DataContract.IsTypeSerializable(type));
538                     ilg.Stloc(objectId);
539                     // Deserialize null
540                     ilg.If(objectId, Cmp.EqualTo, Globals.NullObjectId);
541                     if (nullables != 0)
542                     {
543                         ilg.LoadAddress(value);
544                         ilg.InitObj(value.LocalType);
545                     }
546                     else if (type.IsValueType)
547                         ThrowSerializationException(SR.GetString(SR.ValueTypeCannotBeNull, DataContract.GetClrTypeFullName(type)));
548                     else
549                     {
550                         ilg.Load(null);
551                         ilg.Stloc(value);
552                     }
553
554                     // Deserialize value
555
556                     // Compare against Globals.NewObjectId, which is set to string.Empty
557                     ilg.ElseIfIsEmptyString(objectId);
558                     ilg.Call(contextArg, XmlFormatGeneratorStatics.GetObjectIdMethod);
559                     ilg.Stloc(objectId);
560                     if (type.IsValueType)
561                     {
562                         ilg.IfNotIsEmptyString(objectId);
563                         ThrowSerializationException(SR.GetString(SR.ValueTypeCannotHaveId, DataContract.GetClrTypeFullName(type)));
564                         ilg.EndIf();
565                     }
566                     if (nullables != 0)
567                     {
568                         nullableValue = value;
569                         value = ilg.DeclareLocal(type, "innerValueRead");
570                     }
571
572                     if (primitiveContract != null && primitiveContract.UnderlyingType != Globals.TypeOfObject)
573                     {
574                         ilg.Call(xmlReaderArg, primitiveContract.XmlFormatReaderMethod);
575                         ilg.Stloc(value);
576                         if (!type.IsValueType)
577                             ilg.Call(contextArg, XmlFormatGeneratorStatics.AddNewObjectMethod, value);
578                     }
579                     else
580                     {
581                         InternalDeserialize(value, type, name);
582                     }
583                     // Deserialize ref
584                     ilg.Else();
585                     if (type.IsValueType)
586                         ThrowSerializationException(SR.GetString(SR.ValueTypeCannotHaveRef, DataContract.GetClrTypeFullName(type)));
587                     else
588                     {
589                         ilg.Call(contextArg, XmlFormatGeneratorStatics.GetExistingObjectMethod, objectId, type, name, string.Empty);
590                         ilg.ConvertValue(Globals.TypeOfObject, type);
591                         ilg.Stloc(value);
592                     }
593                     ilg.EndIf();
594
595                     if (nullableValue != null)
596                     {
597                         ilg.If(objectId, Cmp.NotEqualTo, Globals.NullObjectId);
598                         WrapNullableObject(value, nullableValue, nullables);
599                         ilg.EndIf();
600                         value = nullableValue;
601                     }
602                 }
603                 else
604                 {
605                     InternalDeserialize(value, type, name);
606                 }
607
608                 return value;
609             }
610
611             void InternalDeserialize(LocalBuilder value, Type type, string name)
612             {
613                 ilg.Load(contextArg);
614                 ilg.Load(xmlReaderArg);
615                 Type declaredType = type.IsPointer ? Globals.TypeOfReflectionPointer : type;
616                 ilg.Load(DataContract.GetId(declaredType.TypeHandle));
617                 ilg.Ldtoken(declaredType);
618                 ilg.Load(name);
619                 // Empty namespace
620                 ilg.Load(string.Empty);
621                 ilg.Call(XmlFormatGeneratorStatics.InternalDeserializeMethod);
622
623                 if (type.IsPointer)
624                     ilg.Call(JsonFormatGeneratorStatics.UnboxPointer);
625                 else
626                     ilg.ConvertValue(Globals.TypeOfObject, type);
627                 ilg.Stloc(value);
628             }
629
630             void WrapNullableObject(LocalBuilder innerValue, LocalBuilder outerValue, int nullables)
631             {
632                 Type innerType = innerValue.LocalType, outerType = outerValue.LocalType;
633                 ilg.LoadAddress(outerValue);
634                 ilg.Load(innerValue);
635                 for (int i = 1; i < nullables; i++)
636                 {
637                     Type type = Globals.TypeOfNullable.MakeGenericType(innerType);
638                     ilg.New(type.GetConstructor(new Type[] { innerType }));
639                     innerType = type;
640                 }
641                 ilg.Call(outerType.GetConstructor(new Type[] { innerType }));
642             }
643
644             void ReadCollection(CollectionDataContract collectionContract)
645             {
646                 Type type = collectionContract.UnderlyingType;
647                 Type itemType = collectionContract.ItemType;
648                 bool isArray = (collectionContract.Kind == CollectionKind.Array);
649                 ConstructorInfo constructor = collectionContract.Constructor;
650                 if (type.IsInterface)
651                 {
652                     switch (collectionContract.Kind)
653                     {
654                         case CollectionKind.GenericDictionary:
655                             type = Globals.TypeOfDictionaryGeneric.MakeGenericType(itemType.GetGenericArguments());
656                             constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Globals.EmptyTypeArray, null);
657                             break;
658                         case CollectionKind.Dictionary:
659                             type = Globals.TypeOfHashtable;
660                             constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Globals.EmptyTypeArray, null);
661                             break;
662                         case CollectionKind.Collection:
663                         case CollectionKind.GenericCollection:
664                         case CollectionKind.Enumerable:
665                         case CollectionKind.GenericEnumerable:
666                         case CollectionKind.List:
667                         case CollectionKind.GenericList:
668                             type = itemType.MakeArrayType();
669                             isArray = true;
670                             break;
671                     }
672                 }
673
674                 objectLocal = ilg.DeclareLocal(type, "objectDeserialized");
675                 if (!isArray)
676                 {
677                     if (type.IsValueType)
678                     {
679                         ilg.Ldloca(objectLocal);
680                         ilg.InitObj(type);
681                     }
682                     else
683                     {
684                         ilg.New(constructor);
685                         ilg.Stloc(objectLocal);
686                         ilg.Call(contextArg, XmlFormatGeneratorStatics.AddNewObjectMethod, objectLocal);
687                     }
688                 }
689
690                 bool canReadSimpleDictionary = collectionContract.Kind == CollectionKind.Dictionary ||
691                                                collectionContract.Kind == CollectionKind.GenericDictionary;
692                 if (canReadSimpleDictionary)
693                 {
694                     ilg.Load(contextArg);
695                     ilg.LoadMember(JsonFormatGeneratorStatics.UseSimpleDictionaryFormatReadProperty);
696                     ilg.If();
697
698                     ReadSimpleDictionary(collectionContract, itemType);
699
700                     ilg.Else();
701                 }
702
703                 LocalBuilder objectId = ilg.DeclareLocal(Globals.TypeOfString, "objectIdRead");
704                 ilg.Call(contextArg, XmlFormatGeneratorStatics.GetObjectIdMethod);
705                 ilg.Stloc(objectId);
706
707                 bool canReadPrimitiveArray = false;
708                 if (isArray && TryReadPrimitiveArray(itemType))
709                 {
710                     canReadPrimitiveArray = true;
711                     ilg.IfNot();
712                 }
713
714                 LocalBuilder growingCollection = null;
715                 if (isArray)
716                 {
717                     growingCollection = ilg.DeclareLocal(type, "growingCollection");
718                     ilg.NewArray(itemType, 32);
719                     ilg.Stloc(growingCollection);
720                 }
721                 LocalBuilder i = ilg.DeclareLocal(Globals.TypeOfInt, "i");
722                 object forLoop = ilg.For(i, 0, Int32.MaxValue);
723                 // Empty namespace
724                 IsStartElement(memberNamesArg, emptyDictionaryStringArg);
725                 ilg.If();
726                 ilg.Call(contextArg, XmlFormatGeneratorStatics.IncrementItemCountMethod, 1);
727                 LocalBuilder value = ReadCollectionItem(collectionContract, itemType);
728                 if (isArray)
729                 {
730                     MethodInfo ensureArraySizeMethod = XmlFormatGeneratorStatics.EnsureArraySizeMethod.MakeGenericMethod(itemType);
731                     ilg.Call(null, ensureArraySizeMethod, growingCollection, i);
732                     ilg.Stloc(growingCollection);
733                     ilg.StoreArrayElement(growingCollection, i, value);
734                 }
735                 else
736                     StoreCollectionValue(objectLocal, value, collectionContract);
737                 ilg.Else();
738                 IsEndElement();
739                 ilg.If();
740                 ilg.Break(forLoop);
741                 ilg.Else();
742                 HandleUnexpectedItemInCollection(i);
743                 ilg.EndIf();
744                 ilg.EndIf();
745
746                 ilg.EndFor();
747                 if (isArray)
748                 {
749                     MethodInfo trimArraySizeMethod = XmlFormatGeneratorStatics.TrimArraySizeMethod.MakeGenericMethod(itemType);
750                     ilg.Call(null, trimArraySizeMethod, growingCollection, i);
751                     ilg.Stloc(objectLocal);
752                     ilg.Call(contextArg, XmlFormatGeneratorStatics.AddNewObjectWithIdMethod, objectId, objectLocal);
753                 }
754
755                 if (canReadPrimitiveArray)
756                 {
757                     ilg.Else();
758                     ilg.Call(contextArg, XmlFormatGeneratorStatics.AddNewObjectWithIdMethod, objectId, objectLocal);
759                     ilg.EndIf();
760                 }
761
762                 if (canReadSimpleDictionary)
763                 {
764                     ilg.EndIf();
765                 }
766             }
767
768             void ReadSimpleDictionary(CollectionDataContract collectionContract, Type keyValueType)
769             {
770                 Type[] keyValueTypes = keyValueType.GetGenericArguments();
771                 Type keyType = keyValueTypes[0];
772                 Type valueType = keyValueTypes[1];
773
774                 int keyTypeNullableDepth = 0;
775                 Type keyTypeOriginal = keyType;
776                 while (keyType.IsGenericType && keyType.GetGenericTypeDefinition() == Globals.TypeOfNullable)
777                 {
778                     keyTypeNullableDepth++;
779                     keyType = keyType.GetGenericArguments()[0];
780                 }
781
782                 ClassDataContract keyValueDataContract = (ClassDataContract)collectionContract.ItemContract;
783                 DataContract keyDataContract = keyValueDataContract.Members[0].MemberTypeContract;
784
785                 KeyParseMode keyParseMode = KeyParseMode.Fail;
786
787                 if (keyType == Globals.TypeOfString || keyType == Globals.TypeOfObject)
788                 {
789                     keyParseMode = KeyParseMode.AsString;
790                 }
791                 else if (keyType.IsEnum)
792                 {
793                     keyParseMode = KeyParseMode.UsingParseEnum;
794                 }
795                 else if (keyDataContract.ParseMethod != null)
796                 {
797                     keyParseMode = KeyParseMode.UsingCustomParse;
798                 }
799
800                 if (keyParseMode == KeyParseMode.Fail)
801                 {
802                     ThrowSerializationException(
803                         SR.GetString(
804                             SR.KeyTypeCannotBeParsedInSimpleDictionary,
805                                 DataContract.GetClrTypeFullName(collectionContract.UnderlyingType),
806                                 DataContract.GetClrTypeFullName(keyType)));
807                 }
808                 else
809                 {
810                     LocalBuilder nodeType = ilg.DeclareLocal(typeof(XmlNodeType), "nodeType");
811
812                     ilg.BeginWhileCondition();
813                     ilg.Call(xmlReaderArg, JsonFormatGeneratorStatics.MoveToContentMethod);
814                     ilg.Stloc(nodeType);
815                     ilg.Load(nodeType);
816                     ilg.Load(XmlNodeType.EndElement);
817                     ilg.BeginWhileBody(Cmp.NotEqualTo);
818
819                     ilg.Load(nodeType);
820                     ilg.Load(XmlNodeType.Element);
821                     ilg.If(Cmp.NotEqualTo);
822                     ThrowUnexpectedStateException(XmlNodeType.Element);
823                     ilg.EndIf();
824
825                     ilg.Call(contextArg, XmlFormatGeneratorStatics.IncrementItemCountMethod, 1);
826
827                     if (keyParseMode == KeyParseMode.UsingParseEnum)
828                     {
829                         ilg.Load(keyType);
830                     }
831
832                     ilg.Load(xmlReaderArg);
833                     ilg.Call(JsonFormatGeneratorStatics.GetJsonMemberNameMethod);
834
835                     if (keyParseMode == KeyParseMode.UsingParseEnum)
836                     {
837                         ilg.Call(JsonFormatGeneratorStatics.ParseEnumMethod);
838                         ilg.ConvertValue(Globals.TypeOfObject, keyType);
839                     }
840                     else if (keyParseMode == KeyParseMode.UsingCustomParse)
841                     {
842                         ilg.Call(keyDataContract.ParseMethod);
843                     }
844                     LocalBuilder pairKey = ilg.DeclareLocal(keyType, "key");
845                     ilg.Stloc(pairKey);
846                     if (keyTypeNullableDepth > 0)
847                     {
848                         LocalBuilder pairKeyNullable = ilg.DeclareLocal(keyTypeOriginal, "keyOriginal");
849                         WrapNullableObject(pairKey, pairKeyNullable, keyTypeNullableDepth);
850                         pairKey = pairKeyNullable;
851                     }
852
853                     LocalBuilder pairValue = ReadValue(valueType, String.Empty);
854                     StoreKeyValuePair(objectLocal, collectionContract, pairKey, pairValue);
855
856                     ilg.EndWhile();
857                 }
858             }
859
860             void ReadGetOnlyCollection(CollectionDataContract collectionContract)
861             {
862                 Type type = collectionContract.UnderlyingType;
863                 Type itemType = collectionContract.ItemType;
864                 bool isArray = (collectionContract.Kind == CollectionKind.Array);
865                 LocalBuilder size = ilg.DeclareLocal(Globals.TypeOfInt, "arraySize");
866
867                 objectLocal = ilg.DeclareLocal(type, "objectDeserialized");
868                 ilg.Load(contextArg);
869                 ilg.LoadMember(XmlFormatGeneratorStatics.GetCollectionMemberMethod);
870                 ilg.ConvertValue(Globals.TypeOfObject, type);
871                 ilg.Stloc(objectLocal);
872
873                 bool canReadSimpleDictionary = collectionContract.Kind == CollectionKind.Dictionary ||
874                                                collectionContract.Kind == CollectionKind.GenericDictionary;
875                 if (canReadSimpleDictionary)
876                 {
877                     ilg.Load(contextArg);
878                     ilg.LoadMember(JsonFormatGeneratorStatics.UseSimpleDictionaryFormatReadProperty);
879                     ilg.If();
880
881                     ilg.If(objectLocal, Cmp.EqualTo, null);
882                     ilg.Call(null, XmlFormatGeneratorStatics.ThrowNullValueReturnedForGetOnlyCollectionExceptionMethod, type);
883                     ilg.Else();
884
885                     ReadSimpleDictionary(collectionContract, itemType);
886
887                     ilg.Call(contextArg, XmlFormatGeneratorStatics.CheckEndOfArrayMethod, xmlReaderArg, size, memberNamesArg, emptyDictionaryStringArg);
888
889                     ilg.EndIf();
890
891                     ilg.Else();
892                 }
893
894                 //check that items are actually going to be deserialized into the collection
895                 IsStartElement(memberNamesArg, emptyDictionaryStringArg);
896                 ilg.If();
897                 ilg.If(objectLocal, Cmp.EqualTo, null);
898                 ilg.Call(null, XmlFormatGeneratorStatics.ThrowNullValueReturnedForGetOnlyCollectionExceptionMethod, type);
899
900                 ilg.Else();
901
902                 if (isArray)
903                 {
904                     ilg.Load(objectLocal);
905                     ilg.Call(XmlFormatGeneratorStatics.GetArrayLengthMethod);
906                     ilg.Stloc(size);
907                 }
908
909                 LocalBuilder i = ilg.DeclareLocal(Globals.TypeOfInt, "i");
910                 object forLoop = ilg.For(i, 0, Int32.MaxValue);
911                 IsStartElement(memberNamesArg, emptyDictionaryStringArg);
912                 ilg.If();
913                 ilg.Call(contextArg, XmlFormatGeneratorStatics.IncrementItemCountMethod, 1);
914                 LocalBuilder value = ReadCollectionItem(collectionContract, itemType);
915                 if (isArray)
916                 {
917                     ilg.If(size, Cmp.EqualTo, i);
918                     ilg.Call(null, XmlFormatGeneratorStatics.ThrowArrayExceededSizeExceptionMethod, size, type);
919                     ilg.Else();
920                     ilg.StoreArrayElement(objectLocal, i, value);
921                     ilg.EndIf();
922                 }
923                 else
924                     StoreCollectionValue(objectLocal, value, collectionContract);
925                 ilg.Else();
926                 IsEndElement();
927                 ilg.If();
928                 ilg.Break(forLoop);
929                 ilg.Else();
930                 HandleUnexpectedItemInCollection(i);
931                 ilg.EndIf();
932                 ilg.EndIf();
933                 ilg.EndFor();
934                 ilg.Call(contextArg, XmlFormatGeneratorStatics.CheckEndOfArrayMethod, xmlReaderArg, size, memberNamesArg, emptyDictionaryStringArg);
935
936                 ilg.EndIf();
937                 ilg.EndIf();
938
939                 if (canReadSimpleDictionary)
940                 {
941                     ilg.EndIf();
942                 }
943             }
944
945             bool TryReadPrimitiveArray(Type itemType)
946             {
947                 PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(itemType);
948                 if (primitiveContract == null)
949                     return false;
950
951                 string readArrayMethod = null;
952                 switch (Type.GetTypeCode(itemType))
953                 {
954                     case TypeCode.Boolean:
955                         readArrayMethod = "TryReadBooleanArray";
956                         break;
957                     case TypeCode.Decimal:
958                         readArrayMethod = "TryReadDecimalArray";
959                         break;
960                     case TypeCode.Int32:
961                         readArrayMethod = "TryReadInt32Array";
962                         break;
963                     case TypeCode.Int64:
964                         readArrayMethod = "TryReadInt64Array";
965                         break;
966                     case TypeCode.Single:
967                         readArrayMethod = "TryReadSingleArray";
968                         break;
969                     case TypeCode.Double:
970                         readArrayMethod = "TryReadDoubleArray";
971                         break;
972                     case TypeCode.DateTime:
973                         readArrayMethod = "TryReadJsonDateTimeArray";
974                         break;
975                     default:
976                         break;
977                 }
978                 if (readArrayMethod != null)
979                 {
980                     ilg.Load(xmlReaderArg);
981                     ilg.ConvertValue(typeof(XmlReaderDelegator), typeof(JsonReaderDelegator));
982                     ilg.Load(contextArg);
983                     ilg.Load(memberNamesArg);
984                     // Empty namespace
985                     ilg.Load(emptyDictionaryStringArg);
986                     // -1 Array Size
987                     ilg.Load(-1);
988                     ilg.Ldloca(objectLocal);
989                     ilg.Call(typeof(JsonReaderDelegator).GetMethod(readArrayMethod, Globals.ScanAllMembers));
990                     return true;
991                 }
992                 return false;
993             }
994
995             LocalBuilder ReadCollectionItem(CollectionDataContract collectionContract, Type itemType)
996             {
997                 if (collectionContract.Kind == CollectionKind.Dictionary || collectionContract.Kind == CollectionKind.GenericDictionary)
998                 {
999                     ilg.Call(contextArg, XmlFormatGeneratorStatics.ResetAttributesMethod);
1000                     LocalBuilder value = ilg.DeclareLocal(itemType, "valueRead");
1001                     ilg.Load(collectionContractArg);
1002                     ilg.Call(JsonFormatGeneratorStatics.GetItemContractMethod);
1003                     ilg.Call(JsonFormatGeneratorStatics.GetRevisedItemContractMethod);
1004                     ilg.Load(xmlReaderArg);
1005                     ilg.Load(contextArg);
1006                     ilg.Call(JsonFormatGeneratorStatics.ReadJsonValueMethod);
1007                     ilg.ConvertValue(Globals.TypeOfObject, itemType);
1008                     ilg.Stloc(value);
1009                     return value;
1010                 }
1011                 else
1012                 {
1013                     return ReadValue(itemType, JsonGlobals.itemString);
1014                 }
1015             }
1016
1017             void StoreCollectionValue(LocalBuilder collection, LocalBuilder value, CollectionDataContract collectionContract)
1018             {
1019                 if (collectionContract.Kind == CollectionKind.GenericDictionary || collectionContract.Kind == CollectionKind.Dictionary)
1020                 {
1021                     ClassDataContract keyValuePairContract = DataContract.GetDataContract(value.LocalType) as ClassDataContract;
1022                     if (keyValuePairContract == null)
1023                     {
1024                         Fx.Assert("Failed to create contract for KeyValuePair type");
1025                     }
1026                     DataMember keyMember = keyValuePairContract.Members[0];
1027                     DataMember valueMember = keyValuePairContract.Members[1];
1028                     LocalBuilder pairKey = ilg.DeclareLocal(keyMember.MemberType, keyMember.Name);
1029                     LocalBuilder pairValue = ilg.DeclareLocal(valueMember.MemberType, valueMember.Name);
1030                     ilg.LoadAddress(value);
1031                     ilg.LoadMember(keyMember.MemberInfo);
1032                     ilg.Stloc(pairKey);
1033                     ilg.LoadAddress(value);
1034                     ilg.LoadMember(valueMember.MemberInfo);
1035                     ilg.Stloc(pairValue);
1036
1037                     StoreKeyValuePair(collection, collectionContract, pairKey, pairValue);
1038                 }
1039                 else
1040                 {
1041                     ilg.Call(collection, collectionContract.AddMethod, value);
1042                     if (collectionContract.AddMethod.ReturnType != Globals.TypeOfVoid)
1043                         ilg.Pop();
1044                 }
1045             }
1046
1047             void StoreKeyValuePair(LocalBuilder collection, CollectionDataContract collectionContract, LocalBuilder pairKey, LocalBuilder pairValue)
1048             {
1049                 ilg.Call(collection, collectionContract.AddMethod, pairKey, pairValue);
1050                 if (collectionContract.AddMethod.ReturnType != Globals.TypeOfVoid)
1051                     ilg.Pop();
1052             }
1053
1054             void HandleUnexpectedItemInCollection(LocalBuilder iterator)
1055             {
1056                 IsStartElement();
1057                 ilg.If();
1058                 ilg.Call(contextArg, XmlFormatGeneratorStatics.SkipUnknownElementMethod, xmlReaderArg);
1059                 ilg.Dec(iterator);
1060                 ilg.Else();
1061                 ThrowUnexpectedStateException(XmlNodeType.Element);
1062                 ilg.EndIf();
1063             }
1064
1065             void IsStartElement(ArgBuilder nameArg, ArgBuilder nsArg)
1066             {
1067                 ilg.Call(xmlReaderArg, JsonFormatGeneratorStatics.IsStartElementMethod2, nameArg, nsArg);
1068             }
1069
1070             void IsStartElement()
1071             {
1072                 ilg.Call(xmlReaderArg, JsonFormatGeneratorStatics.IsStartElementMethod0);
1073             }
1074
1075             void IsEndElement()
1076             {
1077                 ilg.Load(xmlReaderArg);
1078                 ilg.LoadMember(JsonFormatGeneratorStatics.NodeTypeProperty);
1079                 ilg.Load(XmlNodeType.EndElement);
1080                 ilg.Ceq();
1081             }
1082
1083             void ThrowUnexpectedStateException(XmlNodeType expectedState)
1084             {
1085                 ilg.Call(null, XmlFormatGeneratorStatics.CreateUnexpectedStateExceptionMethod, expectedState, xmlReaderArg);
1086                 ilg.Throw();
1087             }
1088
1089             void ThrowSerializationException(string msg, params object[] values)
1090             {
1091                 if (values != null && values.Length > 0)
1092                     ilg.CallStringFormat(msg, values);
1093                 else
1094                     ilg.Load(msg);
1095                 ThrowSerializationException();
1096             }
1097
1098             void ThrowSerializationException()
1099             {
1100                 ilg.New(JsonFormatGeneratorStatics.SerializationExceptionCtor);
1101                 ilg.Throw();
1102             }
1103
1104             enum KeyParseMode
1105             {
1106                 Fail,
1107                 AsString,
1108                 UsingParseEnum,
1109                 UsingCustomParse
1110             }
1111         }
1112 #endif
1113     }
1114 }
1115