1 //------------------------------------------------------------------------------
2 // <copyright file="XmlSerializationILGen.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">[....]</owner>
6 //------------------------------------------------------------------------------
8 namespace System.Xml.Serialization {
10 using System.Collections;
11 using System.Collections.Generic;
12 using System.Reflection;
13 using System.Reflection.Emit;
14 using System.Text.RegularExpressions;
16 internal class XmlSerializationILGen {
17 int nextMethodNumber = 0;
18 Hashtable methodNames = new Hashtable();
19 // Lookup name->created Method
20 Dictionary<string, MethodBuilderInfo> methodBuilders = new Dictionary<string, MethodBuilderInfo>();
21 // Lookup name->created Type
22 internal Dictionary<string, Type> CreatedTypes = new Dictionary<string, Type>();
23 // Lookup name->class Member
24 internal Dictionary<string, MemberInfo> memberInfos = new Dictionary<string, MemberInfo>();
25 ReflectionAwareILGen raCodeGen;
27 TypeDesc stringTypeDesc = null;
28 TypeDesc qnameTypeDesc = null;
30 TypeMapping[] referencedMethods;
32 Hashtable generatedMethods = new Hashtable();
33 ModuleBuilder moduleBuilder;
34 TypeAttributes typeAttributes;
35 protected TypeBuilder typeBuilder;
36 protected CodeGenerator ilg;
38 internal XmlSerializationILGen(TypeScope[] scopes, string access, string className) {
40 if (scopes.Length > 0) {
41 stringTypeDesc = scopes[0].GetTypeDesc(typeof(string));
42 qnameTypeDesc = scopes[0].GetTypeDesc(typeof(XmlQualifiedName));
44 this.raCodeGen = new ReflectionAwareILGen();
45 this.className = className;
46 System.Diagnostics.Debug.Assert(access == "public");
47 this.typeAttributes = TypeAttributes.Public;
50 internal int NextMethodNumber { get { return nextMethodNumber; } set { nextMethodNumber = value; } }
51 internal ReflectionAwareILGen RaCodeGen { get { return raCodeGen; } }
52 internal TypeDesc StringTypeDesc { get { return stringTypeDesc; } }
53 internal TypeDesc QnameTypeDesc { get { return qnameTypeDesc; } }
54 internal string ClassName { get { return className; } }
55 internal TypeScope[] Scopes { get { return scopes; } }
56 internal Hashtable MethodNames { get { return methodNames; } }
57 internal Hashtable GeneratedMethods { get { return generatedMethods; } }
59 internal ModuleBuilder ModuleBuilder {
60 get { System.Diagnostics.Debug.Assert(moduleBuilder != null); return moduleBuilder; }
61 set { System.Diagnostics.Debug.Assert(moduleBuilder == null && value != null); moduleBuilder = value; }
63 internal TypeAttributes TypeAttributes { get { return typeAttributes; } }
65 static Dictionary<string, Regex> regexs = new Dictionary<string, Regex>();
66 static internal Regex NewRegex(string pattern) {
69 if (!regexs.TryGetValue(pattern, out regex)) {
70 regex = new Regex(pattern);
71 regexs.Add(pattern, regex);
77 internal MethodBuilder EnsureMethodBuilder(TypeBuilder typeBuilder, string methodName,
78 MethodAttributes attributes, Type returnType, Type[] parameterTypes) {
79 MethodBuilderInfo methodBuilderInfo;
80 if (!methodBuilders.TryGetValue(methodName, out methodBuilderInfo)) {
81 MethodBuilder methodBuilder = typeBuilder.DefineMethod(
86 methodBuilderInfo = new MethodBuilderInfo(methodBuilder, parameterTypes);
87 methodBuilders.Add(methodName, methodBuilderInfo);
91 methodBuilderInfo.Validate(returnType, parameterTypes, attributes);
95 return methodBuilderInfo.MethodBuilder;
98 internal MethodBuilderInfo GetMethodBuilder(string methodName) {
99 System.Diagnostics.Debug.Assert(methodBuilders.ContainsKey(methodName));
100 return methodBuilders[methodName];
102 internal virtual void GenerateMethod(TypeMapping mapping) { }
104 internal void GenerateReferencedMethods() {
105 while (references > 0) {
106 TypeMapping mapping = referencedMethods[--references];
107 GenerateMethod(mapping);
111 internal string ReferenceMapping(TypeMapping mapping) {
112 if (generatedMethods[mapping] == null) {
113 referencedMethods = EnsureArrayIndex(referencedMethods, references);
114 referencedMethods[references++] = mapping;
116 return (string)methodNames[mapping];
119 TypeMapping[] EnsureArrayIndex(TypeMapping[] a, int index) {
120 if (a == null) return new TypeMapping[32];
121 if (index < a.Length) return a;
122 TypeMapping[] b = new TypeMapping[a.Length + 32];
123 Array.Copy(a, b, index);
127 internal FieldBuilder GenerateHashtableGetBegin(string privateName, string publicName, TypeBuilder serializerContractTypeBuilder) {
128 FieldBuilder fieldBuilder = serializerContractTypeBuilder.DefineField(
131 FieldAttributes.Private
133 ilg = new CodeGenerator(serializerContractTypeBuilder);
134 PropertyBuilder propertyBuilder = serializerContractTypeBuilder.DefineProperty(
136 PropertyAttributes.None,
137 CallingConventions.HasThis,
139 null, null, null, null, null);
144 CodeGenerator.EmptyTypeArray,
145 CodeGenerator.EmptyStringArray,
146 CodeGenerator.PublicOverrideMethodAttributes | MethodAttributes.SpecialName);
147 propertyBuilder.SetGetMethod(ilg.MethodBuilder);
150 ilg.LoadMember(fieldBuilder);
152 // this 'if' ends in GenerateHashtableGetEnd
155 ConstructorInfo Hashtable_ctor = typeof(Hashtable).GetConstructor(
156 CodeGenerator.InstanceBindingFlags,
158 CodeGenerator.EmptyTypeArray,
161 LocalBuilder _tmpLoc = ilg.DeclareLocal(typeof(Hashtable), "_tmp");
162 ilg.New(Hashtable_ctor);
168 internal void GenerateHashtableGetEnd(FieldBuilder fieldBuilder) {
170 ilg.LoadMember(fieldBuilder);
175 ilg.Ldloc(typeof(Hashtable), "_tmp");
176 ilg.StoreMember(fieldBuilder);
179 // 'endif' from GenerateHashtableGetBegin
183 ilg.LoadMember(fieldBuilder);
188 internal FieldBuilder GeneratePublicMethods(string privateName, string publicName, string[] methods, XmlMapping[] xmlMappings, TypeBuilder serializerContractTypeBuilder) {
189 FieldBuilder fieldBuilder = GenerateHashtableGetBegin(privateName, publicName, serializerContractTypeBuilder);
190 if (methods != null && methods.Length != 0 && xmlMappings != null && xmlMappings.Length == methods.Length) {
191 MethodInfo Hashtable_set_Item = typeof(Hashtable).GetMethod(
193 CodeGenerator.InstanceBindingFlags,
195 new Type[] { typeof(Object), typeof(Object) },
198 for (int i = 0; i < methods.Length; i++) {
199 if (methods[i] == null)
201 ilg.Ldloc(typeof(Hashtable), "_tmp");
202 ilg.Ldstr(xmlMappings[i].Key);
203 ilg.Ldstr(methods[i]);
204 ilg.Call(Hashtable_set_Item);
207 GenerateHashtableGetEnd(fieldBuilder);
211 internal void GenerateSupportedTypes(Type[] types, TypeBuilder serializerContractTypeBuilder) {
212 ilg = new CodeGenerator(serializerContractTypeBuilder);
216 new Type[] { typeof(Type) },
217 new string[] { "type" },
218 CodeGenerator.PublicOverrideMethodAttributes);
219 Hashtable uniqueTypes = new Hashtable();
220 for (int i = 0; i < types.Length; i++) {
221 Type type = types[i];
225 if (!type.IsPublic && !type.IsNestedPublic)
227 if (uniqueTypes[type] != null)
229 // DDB172141: Wrong generated CS for serializer of List<string> type
230 if (type.IsGenericType || type.ContainsGenericParameters)
232 uniqueTypes[type] = type;
247 internal string GenerateBaseSerializer(string baseSerializer, string readerClass, string writerClass, CodeIdentifiers classes) {
248 baseSerializer = CodeIdentifier.MakeValid(baseSerializer);
249 baseSerializer = classes.AddUnique(baseSerializer, baseSerializer);
251 TypeBuilder baseSerializerTypeBuilder = CodeGenerator.CreateTypeBuilder(
253 CodeIdentifier.GetCSharpName(baseSerializer),
254 TypeAttributes.Public | TypeAttributes.Abstract | TypeAttributes.BeforeFieldInit,
255 typeof(XmlSerializer),
256 CodeGenerator.EmptyTypeArray);
258 ConstructorInfo readerCtor = CreatedTypes[readerClass].GetConstructor(
259 CodeGenerator.InstanceBindingFlags,
261 CodeGenerator.EmptyTypeArray,
264 ilg = new CodeGenerator(baseSerializerTypeBuilder);
265 ilg.BeginMethod(typeof(XmlSerializationReader),
267 CodeGenerator.EmptyTypeArray,
268 CodeGenerator.EmptyStringArray,
269 CodeGenerator.ProtectedOverrideMethodAttributes);
273 ConstructorInfo writerCtor = CreatedTypes[writerClass].GetConstructor(
274 CodeGenerator.InstanceBindingFlags,
276 CodeGenerator.EmptyTypeArray,
279 ilg.BeginMethod(typeof(XmlSerializationWriter),
281 CodeGenerator.EmptyTypeArray,
282 CodeGenerator.EmptyStringArray,
283 CodeGenerator.ProtectedOverrideMethodAttributes);
287 baseSerializerTypeBuilder.DefineDefaultConstructor(
288 MethodAttributes.Family | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
289 Type baseSerializerType = baseSerializerTypeBuilder.CreateType();
290 CreatedTypes.Add(baseSerializerType.Name, baseSerializerType);
292 return baseSerializer;
295 internal string GenerateTypedSerializer(string readMethod, string writeMethod, XmlMapping mapping, CodeIdentifiers classes, string baseSerializer, string readerClass, string writerClass) {
296 string serializerName = CodeIdentifier.MakeValid(Accessor.UnescapeName(mapping.Accessor.Mapping.TypeDesc.Name));
297 serializerName = classes.AddUnique(serializerName + "Serializer", mapping);
299 TypeBuilder typedSerializerTypeBuilder = CodeGenerator.CreateTypeBuilder(
301 CodeIdentifier.GetCSharpName(serializerName),
302 TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit,
303 CreatedTypes[baseSerializer],
304 CodeGenerator.EmptyTypeArray
307 ilg = new CodeGenerator(typedSerializerTypeBuilder);
311 new Type[] { typeof(XmlReader) },
312 new string[] { "xmlReader" },
313 CodeGenerator.PublicOverrideMethodAttributes
316 if (mapping.Accessor.Any) {
318 ilg.Stloc(ilg.ReturnLocal);
319 ilg.Br(ilg.ReturnLabel);
322 MethodInfo XmlReader_IsStartElement = typeof(XmlReader).GetMethod(
324 CodeGenerator.InstanceBindingFlags,
326 new Type[] { typeof(String), typeof(String) },
329 ilg.Ldarg(ilg.GetArg("xmlReader"));
330 ilg.Ldstr(mapping.Accessor.Name);
331 ilg.Ldstr(mapping.Accessor.Namespace);
332 ilg.Call(XmlReader_IsStartElement);
333 ilg.Stloc(ilg.ReturnLocal);
334 ilg.Br(ilg.ReturnLabel);
336 ilg.MarkLabel(ilg.ReturnLabel);
337 ilg.Ldloc(ilg.ReturnLocal);
340 if (writeMethod != null) {
341 ilg = new CodeGenerator(typedSerializerTypeBuilder);
345 new Type[] { typeof(object), typeof(XmlSerializationWriter) },
346 new string[] { "objectToSerialize", "writer" },
347 CodeGenerator.ProtectedOverrideMethodAttributes);
348 MethodInfo writerType_writeMethod = CreatedTypes[writerClass].GetMethod(
350 CodeGenerator.InstanceBindingFlags,
352 new Type[] { (mapping is XmlMembersMapping) ? typeof(object[]) : typeof(object) },
356 ilg.Castclass(CreatedTypes[writerClass]);
357 ilg.Ldarg("objectToSerialize");
358 if (mapping is XmlMembersMapping) {
359 ilg.ConvertValue(typeof(object), typeof(object[]));
361 ilg.Call(writerType_writeMethod);
364 if (readMethod != null) {
365 ilg = new CodeGenerator(typedSerializerTypeBuilder);
369 new Type[] { typeof(XmlSerializationReader) },
370 new string[] { "reader" },
371 CodeGenerator.ProtectedOverrideMethodAttributes);
372 MethodInfo readerType_readMethod = CreatedTypes[readerClass].GetMethod(
374 CodeGenerator.InstanceBindingFlags,
376 CodeGenerator.EmptyTypeArray,
380 ilg.Castclass(CreatedTypes[readerClass]);
381 ilg.Call(readerType_readMethod);
384 typedSerializerTypeBuilder.DefineDefaultConstructor(CodeGenerator.PublicMethodAttributes);
385 Type typedSerializerType = typedSerializerTypeBuilder.CreateType();
386 CreatedTypes.Add(typedSerializerType.Name, typedSerializerType);
388 return typedSerializerType.Name;
391 FieldBuilder GenerateTypedSerializers(Hashtable serializers, TypeBuilder serializerContractTypeBuilder) {
392 string privateName = "typedSerializers";
393 FieldBuilder fieldBuilder = GenerateHashtableGetBegin(privateName, "TypedSerializers", serializerContractTypeBuilder);
394 MethodInfo Hashtable_Add = typeof(Hashtable).GetMethod(
396 CodeGenerator.InstanceBindingFlags,
398 new Type[] { typeof(Object), typeof(Object) },
402 foreach (string key in serializers.Keys) {
403 ConstructorInfo ctor = CreatedTypes[(string)serializers[key]].GetConstructor(
404 CodeGenerator.InstanceBindingFlags,
406 CodeGenerator.EmptyTypeArray,
409 ilg.Ldloc(typeof(Hashtable), "_tmp");
412 ilg.Call(Hashtable_Add);
414 GenerateHashtableGetEnd(fieldBuilder);
418 //GenerateGetSerializer(serializers, xmlMappings);
419 void GenerateGetSerializer(Hashtable serializers, XmlMapping[] xmlMappings, TypeBuilder serializerContractTypeBuilder) {
420 ilg = new CodeGenerator(serializerContractTypeBuilder);
422 typeof(XmlSerializer),
424 new Type[] { typeof(Type) },
425 new string[] { "type" },
426 CodeGenerator.PublicOverrideMethodAttributes);
428 for (int i = 0; i < xmlMappings.Length; i++) {
429 if (xmlMappings[i] is XmlTypeMapping) {
430 Type type = xmlMappings[i].Accessor.Mapping.TypeDesc.Type;
433 if (!type.IsPublic && !type.IsNestedPublic)
435 // DDB172141: Wrong generated CS for serializer of List<string> type
436 if (type.IsGenericType || type.ContainsGenericParameters)
442 ConstructorInfo ctor = CreatedTypes[(string)serializers[xmlMappings[i].Key]].GetConstructor(
443 CodeGenerator.InstanceBindingFlags,
445 CodeGenerator.EmptyTypeArray,
449 ilg.Stloc(ilg.ReturnLocal);
450 ilg.Br(ilg.ReturnLabel);
456 ilg.Stloc(ilg.ReturnLocal);
457 ilg.Br(ilg.ReturnLabel);
458 ilg.MarkLabel(ilg.ReturnLabel);
459 ilg.Ldloc(ilg.ReturnLocal);
463 internal void GenerateSerializerContract(string className, XmlMapping[] xmlMappings, Type[] types, string readerType, string[] readMethods, string writerType, string[] writerMethods, Hashtable serializers) {
464 TypeBuilder serializerContractTypeBuilder = CodeGenerator.CreateTypeBuilder(
466 "XmlSerializerContract",
467 TypeAttributes.Public | TypeAttributes.BeforeFieldInit,
468 typeof(XmlSerializerImplementation),
469 CodeGenerator.EmptyTypeArray
472 ilg = new CodeGenerator(serializerContractTypeBuilder);
473 PropertyBuilder propertyBuilder = serializerContractTypeBuilder.DefineProperty(
475 PropertyAttributes.None,
476 CallingConventions.HasThis,
477 typeof(XmlSerializationReader),
478 null, null, null, null, null);
480 typeof(XmlSerializationReader),
482 CodeGenerator.EmptyTypeArray,
483 CodeGenerator.EmptyStringArray,
484 CodeGenerator.PublicOverrideMethodAttributes | MethodAttributes.SpecialName);
485 propertyBuilder.SetGetMethod(ilg.MethodBuilder);
486 ConstructorInfo ctor = CreatedTypes[readerType].GetConstructor(
487 CodeGenerator.InstanceBindingFlags,
489 CodeGenerator.EmptyTypeArray,
495 ilg = new CodeGenerator(serializerContractTypeBuilder);
496 propertyBuilder = serializerContractTypeBuilder.DefineProperty(
498 PropertyAttributes.None,
499 CallingConventions.HasThis,
500 typeof(XmlSerializationWriter),
501 null, null, null, null, null);
503 typeof(XmlSerializationWriter),
505 CodeGenerator.EmptyTypeArray,
506 CodeGenerator.EmptyStringArray,
507 CodeGenerator.PublicOverrideMethodAttributes | MethodAttributes.SpecialName);
508 propertyBuilder.SetGetMethod(ilg.MethodBuilder);
509 ctor = CreatedTypes[writerType].GetConstructor(
510 CodeGenerator.InstanceBindingFlags,
512 CodeGenerator.EmptyTypeArray,
518 FieldBuilder readMethodsField = GeneratePublicMethods("readMethods", "ReadMethods", readMethods, xmlMappings, serializerContractTypeBuilder);
519 FieldBuilder writeMethodsField = GeneratePublicMethods("writeMethods", "WriteMethods", writerMethods, xmlMappings, serializerContractTypeBuilder);
520 FieldBuilder typedSerializersField = GenerateTypedSerializers(serializers, serializerContractTypeBuilder);
521 GenerateSupportedTypes(types, serializerContractTypeBuilder);
522 GenerateGetSerializer(serializers, xmlMappings, serializerContractTypeBuilder);
525 ConstructorInfo baseCtor = typeof(XmlSerializerImplementation).GetConstructor(
526 CodeGenerator.InstanceBindingFlags,
528 CodeGenerator.EmptyTypeArray,
531 ilg = new CodeGenerator(serializerContractTypeBuilder);
535 CodeGenerator.EmptyTypeArray,
536 CodeGenerator.EmptyStringArray,
537 CodeGenerator.PublicMethodAttributes | MethodAttributes.RTSpecialName | MethodAttributes.SpecialName
541 ilg.StoreMember(readMethodsField);
544 ilg.StoreMember(writeMethodsField);
547 ilg.StoreMember(typedSerializersField);
552 Type serializerContractType = serializerContractTypeBuilder.CreateType();
553 CreatedTypes.Add(serializerContractType.Name, serializerContractType);
557 internal static bool IsWildcard(SpecialMapping mapping) {
558 if (mapping is SerializableMapping)
559 return ((SerializableMapping)mapping).IsAny;
560 return mapping.TypeDesc.CanBeElementValue;
562 internal void ILGenLoad(string source) {
563 ILGenLoad(source, null);
565 internal void ILGenLoad(string source, Type type) {
566 if (source.StartsWith("o.@", StringComparison.Ordinal)) {
567 System.Diagnostics.Debug.Assert(memberInfos.ContainsKey(source.Substring(3)));
568 MemberInfo memInfo = memberInfos[source.Substring(3)];
569 ilg.LoadMember(ilg.GetVariable("o"), memInfo);
571 Type memType = (memInfo.MemberType == MemberTypes.Field) ? ((FieldInfo)memInfo).FieldType : ((PropertyInfo)memInfo).PropertyType;
572 ilg.ConvertValue(memType, type);
576 SourceInfo info = new SourceInfo(source, null, null, null, ilg);