Fix incompatibility with .NET's serialization of ObservableCollection - part of bug...
[mono.git] / mcs / class / corlib / System.Runtime.Serialization.Formatters.Binary / CodeGenerator.cs
1 // CodeGenerator.cs
2 //
3 // Author:
4 //   Lluis Sanchez Gual (lluis@ximian.com)
5 //
6 // (C) 2004 Novell, Inc
7
8 //
9 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 #if !FULL_AOT_RUNTIME
32 using System;
33 using System.IO;
34 using System.Collections;
35 using System.Runtime.Serialization;
36 using System.Reflection;
37 using System.Reflection.Emit;
38 using System.Globalization;
39
40 namespace System.Runtime.Serialization.Formatters.Binary
41 {
42         internal class CodeGenerator
43         {
44                 // Code generation
45
46                 static object monitor = new object ();
47                 
48                 static ModuleBuilder _module;
49                 
50                 static CodeGenerator ()
51                 {
52                         AppDomain myDomain = System.Threading.Thread.GetDomain();
53                         AssemblyName myAsmName = new AssemblyName();
54                         myAsmName.Name = "__MetadataTypes";
55                    
56                         AssemblyBuilder myAsmBuilder = myDomain.DefineInternalDynamicAssembly (myAsmName, AssemblyBuilderAccess.Run);
57                         _module = myAsmBuilder.DefineDynamicModule("__MetadataTypesModule", false);
58                 }
59                 
60                 static public Type GenerateMetadataType (Type type, StreamingContext context) {
61                         /* SRE is not thread safe */
62                         lock (monitor) {
63                                 return GenerateMetadataTypeInternal (type, context);
64                         }
65                 }
66
67                 static public Type GenerateMetadataTypeInternal (Type type, StreamingContext context)
68                 {               
69                         string name = type.Name + "__TypeMetadata";
70                         string sufix = "";
71                         int n = 0;
72                         while (_module.GetType (name + sufix) != null)
73                                 sufix = (++n).ToString();
74                                 
75                         name += sufix;
76                                 
77                         MemberInfo[] members = FormatterServices.GetSerializableMembers (type, context);
78                         
79                         TypeBuilder typeBuilder = _module.DefineType (name, TypeAttributes.Public, typeof(ClrTypeMetadata));
80
81                         Type[] parameters;
82                         MethodBuilder method;
83                         ILGenerator gen;
84                         
85                         // *********************
86                         //      METHOD public constructor (Type t): base (t);
87                         
88                         parameters = Type.EmptyTypes;
89
90                         ConstructorBuilder ctor = typeBuilder.DefineConstructor (MethodAttributes.Public, CallingConventions.Standard, parameters);
91                         ConstructorInfo baseCtor = typeof(ClrTypeMetadata).GetConstructor (new Type[] { typeof(Type) });
92                         gen = ctor.GetILGenerator();
93
94                         gen.Emit (OpCodes.Ldarg_0);
95                         gen.Emit (OpCodes.Ldtoken, type);
96                         gen.EmitCall (OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);
97                         gen.Emit (OpCodes.Call, baseCtor);
98                         gen.Emit (OpCodes.Ret);
99
100                         // *********************
101                         //      METHOD public override void WriteAssemblies (ObjectWriter ow, BinaryWriter writer);
102
103                         parameters = new Type[] { typeof(ObjectWriter), typeof(BinaryWriter) };
104                         method = typeBuilder.DefineMethod ("WriteAssemblies", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), parameters);                    
105                         gen = method.GetILGenerator();
106                         
107                         foreach (FieldInfo field in members)
108                         {
109                                 Type memberType = field.FieldType;
110                                 while (memberType.IsArray) 
111                                         memberType = memberType.GetElementType();
112
113                                 if (memberType.Assembly != ObjectWriter.CorlibAssembly)
114                                 {
115                                         // EMIT ow.WriteAssembly (writer, memberType.Assembly);
116                                         gen.Emit (OpCodes.Ldarg_1);
117                                         gen.Emit (OpCodes.Ldarg_2);
118 #if NET_4_0
119                                         EmitLoadType (gen, memberType);
120                                         gen.EmitCall (OpCodes.Callvirt, typeof(ObjectWriter).GetMethod("WriteTypeAssembly"), null);
121 #else
122                                         EmitLoadTypeAssembly (gen, memberType, field.Name);
123                                         gen.EmitCall (OpCodes.Callvirt, typeof(ObjectWriter).GetMethod("WriteAssembly"), null);
124 #endif
125                                         gen.Emit (OpCodes.Pop);
126                                 }
127                         }
128                         gen.Emit(OpCodes.Ret);
129                         typeBuilder.DefineMethodOverride (method, typeof(TypeMetadata).GetMethod ("WriteAssemblies"));
130                         
131                         // *********************
132                         // METHOD public override void WriteTypeData (ObjectWriter ow, BinaryWriter writer, bool writeTypes);
133                         
134                         parameters = new Type[] { typeof(ObjectWriter), typeof(BinaryWriter), typeof(bool) };
135                         method = typeBuilder.DefineMethod ("WriteTypeData", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), parameters);
136                         gen = method.GetILGenerator();
137                         
138                         // EMIT writer.Write (members.Length);
139                         gen.Emit (OpCodes.Ldarg_2);
140                         gen.Emit (OpCodes.Ldc_I4, members.Length);
141                         EmitWrite (gen, typeof(int));
142                         
143                         // Names of fields
144                         foreach (FieldInfo field in members)
145                         {
146                                 // EMIT writer.Write (name);
147                                 gen.Emit (OpCodes.Ldarg_2);
148                                 gen.Emit (OpCodes.Ldstr, field.Name);
149                                 EmitWrite (gen, typeof(string));
150                         }
151
152                         Label falseLabel = gen.DefineLabel ();
153                         gen.Emit (OpCodes.Ldarg_3);
154                         gen.Emit (OpCodes.Brfalse, falseLabel);
155                                         
156                         // Types of fields
157                         foreach (FieldInfo field in members)
158                         {
159                                 // EMIT writer.Write ((byte) ObjectWriter.GetTypeTag (type));
160                                 gen.Emit (OpCodes.Ldarg_2);
161                                 gen.Emit (OpCodes.Ldc_I4_S, (byte) ObjectWriter.GetTypeTag (field.FieldType));
162                                 EmitWrite (gen, typeof(byte));
163                         }
164
165                         // Type specs of fields
166                         foreach (FieldInfo field in members)
167                         {
168                                 // EMIT ow.WriteTypeSpec (writer, field.FieldType);
169                                 EmitWriteTypeSpec (gen, field.FieldType, field.Name);
170                         }
171                         gen.MarkLabel(falseLabel);
172                         
173                         gen.Emit(OpCodes.Ret);
174                         typeBuilder.DefineMethodOverride (method, typeof(TypeMetadata).GetMethod ("WriteTypeData"));
175                         
176                         // *********************
177                         // METHOD public override void WriteObjectData (ObjectWriter ow, BinaryWriter writer, object data)
178                         
179                         parameters = new Type[] { typeof(ObjectWriter), typeof(BinaryWriter), typeof(object) };
180                         method = typeBuilder.DefineMethod ("WriteObjectData", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), parameters);                    
181                         gen = method.GetILGenerator();
182                         
183                         LocalBuilder localBuilder = gen.DeclareLocal (type);
184                         OpCode lload = OpCodes.Ldloc;
185                         
186                         gen.Emit (OpCodes.Ldarg_3);
187                         if (type.IsValueType)
188                         {
189                                 gen.Emit (OpCodes.Unbox, type);
190                                 LoadFromPtr (gen, type);
191                                 lload = OpCodes.Ldloca_S;
192                         }
193                         else
194                                 gen.Emit (OpCodes.Castclass, type);
195                                 
196                         gen.Emit (OpCodes.Stloc, localBuilder);
197                         
198                         foreach (FieldInfo field in members)
199                         {
200                                 // EMIT ow.WriteValue (writer, ((FieldInfo)members[n]).FieldType, values[n]);
201                                 Type ftype = field.FieldType;
202                                 if (BinaryCommon.IsPrimitive (ftype))
203                                 {
204                                         gen.Emit (OpCodes.Ldarg_2);
205                                         gen.Emit (lload, localBuilder);
206                                         if (ftype == typeof(DateTime) || ftype == typeof(TimeSpan) || ftype == typeof(decimal))
207                                                 gen.Emit (OpCodes.Ldflda, field);
208                                         else
209                                                 gen.Emit (OpCodes.Ldfld, field);
210                                         EmitWritePrimitiveValue (gen, ftype);
211                                 }
212                                 else
213                                 {
214                                         gen.Emit (OpCodes.Ldarg_1);
215                                         gen.Emit (OpCodes.Ldarg_2);
216                                         gen.Emit (OpCodes.Ldtoken, ftype);
217                                         gen.EmitCall (OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);
218                                         gen.Emit (lload, localBuilder);
219                                         gen.Emit (OpCodes.Ldfld, field);
220                                         if (ftype.IsValueType)
221                                                 gen.Emit (OpCodes.Box, ftype);
222                                         gen.EmitCall (OpCodes.Call, typeof(ObjectWriter).GetMethod("WriteValue"), null);
223                                 }
224                         }
225                         
226                         gen.Emit(OpCodes.Ret);
227                         typeBuilder.DefineMethodOverride (method, typeof(TypeMetadata).GetMethod ("WriteObjectData"));
228                         
229                 return typeBuilder.CreateType();
230                 }
231                 
232                 public static void LoadFromPtr (ILGenerator ig, Type t)
233                 {
234                         if (t == typeof(int))
235                                 ig.Emit (OpCodes.Ldind_I4);
236                         else if (t == typeof(uint))
237                                 ig.Emit (OpCodes.Ldind_U4);
238                         else if (t == typeof(short))
239                                 ig.Emit (OpCodes.Ldind_I2);
240                         else if (t == typeof(ushort))
241                                 ig.Emit (OpCodes.Ldind_U2);
242                         else if (t == typeof(char))
243                                 ig.Emit (OpCodes.Ldind_U2);
244                         else if (t == typeof(byte))
245                                 ig.Emit (OpCodes.Ldind_U1);
246                         else if (t == typeof(sbyte))
247                                 ig.Emit (OpCodes.Ldind_I1);
248                         else if (t == typeof(ulong))
249                                 ig.Emit (OpCodes.Ldind_I8);
250                         else if (t == typeof(long))
251                                 ig.Emit (OpCodes.Ldind_I8);
252                         else if (t == typeof(float))
253                                 ig.Emit (OpCodes.Ldind_R4);
254                         else if (t == typeof(double))
255                                 ig.Emit (OpCodes.Ldind_R8);
256                         else if (t == typeof(bool))
257                                 ig.Emit (OpCodes.Ldind_I1);
258                         else if (t == typeof(IntPtr))
259                                 ig.Emit (OpCodes.Ldind_I);
260                         else if (t.IsEnum) {
261                                 if (t == typeof(Enum))
262                                         ig.Emit (OpCodes.Ldind_Ref);
263                                 else
264                                         LoadFromPtr (ig, EnumToUnderlying (t));
265                         } else if (t.IsValueType)
266                                 ig.Emit (OpCodes.Ldobj, t);
267                         else
268                                 ig.Emit (OpCodes.Ldind_Ref);
269                 }
270
271                 static void EmitWriteTypeSpec (ILGenerator gen, Type type, string member)
272                 {
273                         // WARNING Keep in sync with WriteTypeSpec
274                         
275                         switch (ObjectWriter.GetTypeTag (type))
276                         {
277                                 case TypeTag.PrimitiveType:
278                                         // EMIT writer.Write (BinaryCommon.GetTypeCode (type));
279                                         gen.Emit (OpCodes.Ldarg_2);
280                                         gen.Emit (OpCodes.Ldc_I4_S, (byte) BinaryCommon.GetTypeCode (type));
281                                         EmitWrite (gen, typeof(byte));
282                                         break;
283
284                                 case TypeTag.RuntimeType:
285                                         // EMIT writer.Write (type.FullName);
286                                         gen.Emit (OpCodes.Ldarg_2);
287                                         gen.Emit (OpCodes.Ldstr, type.FullName);
288                                         EmitWrite (gen, typeof(string));
289                                         break;
290
291                                 case TypeTag.GenericType:
292                                         // EMIT writer.Write (type.FullName);
293                                         gen.Emit (OpCodes.Ldarg_2);
294                                         gen.Emit (OpCodes.Ldstr, type.FullName);
295                                         EmitWrite (gen, typeof(string));
296                                         
297                                         // EMIT writer.Write ((int)ow.GetAssemblyId (type.Assembly));
298                                         gen.Emit (OpCodes.Ldarg_2);
299                                         gen.Emit (OpCodes.Ldarg_1);
300 #if NET_4_0
301                                         EmitLoadType (gen, type);
302                                         gen.EmitCall (OpCodes.Callvirt, typeof(GetForwardedAttribute).GetMethod("GetAssemblyName"), null);
303                                         gen.EmitCall (OpCodes.Callvirt, typeof(ObjectWriter).GetMethod("GetAssemblyNameId"), null);
304 #else
305                                         EmitLoadTypeAssembly (gen, type, member);
306                                         gen.EmitCall (OpCodes.Callvirt, typeof(ObjectWriter).GetMethod("GetAssemblyId"), null);
307 #endif
308                                         gen.Emit (OpCodes.Conv_I4);
309                                         EmitWrite (gen, typeof(int));
310                                         break;
311
312                                 case TypeTag.ArrayOfPrimitiveType:
313                                         // EMIT writer.Write (BinaryCommon.GetTypeCode (type.GetElementType()));
314                                         gen.Emit (OpCodes.Ldarg_2);
315                                         gen.Emit (OpCodes.Ldc_I4_S, (byte) BinaryCommon.GetTypeCode (type.GetElementType()));
316                                         EmitWrite (gen, typeof(byte));
317                                         break;
318
319                                 default:
320                                         // Type spec not needed
321                                         break;
322                         }
323                 }
324                 
325                 static void EmitLoadTypeAssembly (ILGenerator gen, Type type, string member)
326                 {
327                         gen.Emit (OpCodes.Ldtoken, type);
328                         gen.EmitCall (OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);
329                         gen.EmitCall (OpCodes.Callvirt, typeof(Type).GetProperty("Assembly").GetGetMethod(), null);
330                 }
331                 
332                 static void EmitLoadType (ILGenerator gen, Type type)
333                 {
334                         gen.Emit (OpCodes.Ldtoken, type);
335                         gen.EmitCall (OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);
336                 }
337                 
338                 static void EmitWrite (ILGenerator gen, Type type)
339                 {
340                         gen.EmitCall (OpCodes.Callvirt, typeof(BinaryWriter).GetMethod("Write", new Type[] { type }), null);
341                 }
342                 
343                 public static void EmitWritePrimitiveValue (ILGenerator gen, Type type)
344                 {
345                         switch (Type.GetTypeCode (type))
346                         {
347                                 case TypeCode.Boolean:
348                                 case TypeCode.Byte:
349                                 case TypeCode.Char:
350                                 case TypeCode.Double:
351                                 case TypeCode.Int16:
352                                 case TypeCode.Int32:
353                                 case TypeCode.Int64:
354                                 case TypeCode.SByte:
355                                 case TypeCode.Single:
356                                 case TypeCode.UInt16:
357                                 case TypeCode.UInt32:
358                                 case TypeCode.UInt64:
359                                 case TypeCode.String:
360                                         EmitWrite (gen, type);
361                                         break;
362
363                                 case TypeCode.Decimal:
364                                         // writer.Write (val.ToString (CultureInfo.InvariantCulture));
365                                         gen.EmitCall (OpCodes.Call, typeof(CultureInfo).GetProperty("InvariantCulture").GetGetMethod(), null);
366                                         gen.EmitCall (OpCodes.Call, typeof(Decimal).GetMethod("ToString", new Type[] {typeof(IFormatProvider)}), null);
367                                         EmitWrite (gen, typeof(string));
368                                         break;
369                                         
370                                 case TypeCode.DateTime: 
371                                         gen.EmitCall (OpCodes.Call, typeof(DateTime).GetMethod("ToBinary", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance), null);
372                                         EmitWrite (gen, typeof(long));
373                                         break;
374                                         
375                                 default:
376                                         if (type == typeof (TimeSpan)) {
377                                                 gen.EmitCall (OpCodes.Call, typeof(TimeSpan).GetProperty("Ticks").GetGetMethod(), null);
378                                                 EmitWrite (gen, typeof(long));
379                                         }
380                                         else
381                                                 throw new NotSupportedException ("Unsupported primitive type: " + type.FullName);
382                                         break;
383                         }
384                 }
385                 
386                 //
387                 // This is needed, because enumerations from assemblies
388                 // do not report their underlyingtype, but they report
389                 // themselves
390                 //
391                 public static Type EnumToUnderlying (Type t)
392                 {
393                         TypeCode tc = Type.GetTypeCode (t);
394         
395                         switch (tc){
396                         case TypeCode.Boolean:
397                                 return typeof (bool);
398                         case TypeCode.Byte:
399                                 return typeof (byte);
400                         case TypeCode.SByte:
401                                 return typeof (sbyte);
402                         case TypeCode.Char:
403                                 return typeof (char);
404                         case TypeCode.Int16:
405                                 return typeof (short);
406                         case TypeCode.UInt16:
407                                 return typeof (ushort);
408                         case TypeCode.Int32:
409                                 return typeof (int);
410                         case TypeCode.UInt32:
411                                 return typeof (uint);
412                         case TypeCode.Int64:
413                                 return typeof (long);
414                         case TypeCode.UInt64:
415                                 return typeof (ulong);
416                         }
417                         throw new Exception ("Unhandled typecode in enum " + tc + " from " + t.AssemblyQualifiedName);
418                 }
419         }
420  }
421  
422 #endif