Merged into single file, added assertions
[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 using System;
32 using System.IO;
33 using System.Collections;
34 using System.Runtime.Serialization;
35 using System.Reflection;
36 using System.Reflection.Emit;
37 using System.Globalization;
38
39 namespace System.Runtime.Serialization.Formatters.Binary
40 {
41         internal class CodeGenerator
42         {
43                 // Code generation
44
45                 static object monitor = new object ();
46                 
47                 static ModuleBuilder _module;
48                 
49                 static CodeGenerator ()
50                 {
51                         AppDomain myDomain = System.Threading.Thread.GetDomain();
52                         AssemblyName myAsmName = new AssemblyName();
53                         myAsmName.Name = "__MetadataTypes";
54                    
55                         AssemblyBuilder myAsmBuilder = myDomain.DefineInternalDynamicAssembly (myAsmName, AssemblyBuilderAccess.Run);
56                         _module = myAsmBuilder.DefineDynamicModule("__MetadataTypesModule", false);
57                 }
58                 
59                 static public Type GenerateMetadataType (Type type, StreamingContext context) {
60                         /* SRE is not thread safe */
61                         lock (monitor) {
62                                 return GenerateMetadataTypeInternal (type, context);
63                         }
64                 }
65
66                 static public Type GenerateMetadataTypeInternal (Type type, StreamingContext context)
67                 {               
68                         string name = type.Name + "__TypeMetadata";
69                         string sufix = "";
70                         int n = 0;
71                         while (_module.GetType (name + sufix) != null)
72                                 sufix = (++n).ToString();
73                                 
74                         name += sufix;
75                                 
76                         MemberInfo[] members = FormatterServices.GetSerializableMembers (type, context);
77                         
78                         TypeBuilder typeBuilder = _module.DefineType (name, TypeAttributes.Public, typeof(ClrTypeMetadata));
79
80                         Type[] parameters;
81                         MethodBuilder method;
82                         ILGenerator gen;
83                         
84                         // *********************
85                         //      METHOD public constructor (Type t): base (t);
86                         
87                         parameters = Type.EmptyTypes;
88
89                         ConstructorBuilder ctor = typeBuilder.DefineConstructor (MethodAttributes.Public, CallingConventions.Standard, parameters);
90                         ConstructorInfo baseCtor = typeof(ClrTypeMetadata).GetConstructor (new Type[] { typeof(Type) });
91                         gen = ctor.GetILGenerator();
92
93                         gen.Emit (OpCodes.Ldarg_0);
94                         gen.Emit (OpCodes.Ldtoken, type);
95                         gen.EmitCall (OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);
96                         gen.Emit (OpCodes.Call, baseCtor);
97                         gen.Emit (OpCodes.Ret);
98
99                         // *********************
100                         //      METHOD public override void WriteAssemblies (ObjectWriter ow, BinaryWriter writer);
101
102                         parameters = new Type[] { typeof(ObjectWriter), typeof(BinaryWriter) };
103                         method = typeBuilder.DefineMethod ("WriteAssemblies", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), parameters);                    
104                         gen = method.GetILGenerator();
105                         
106                         foreach (FieldInfo field in members)
107                         {
108                                 Type memberType = field.FieldType;
109                                 while (memberType.IsArray) 
110                                         memberType = memberType.GetElementType();
111
112                                 if (memberType.Assembly != ObjectWriter.CorlibAssembly)
113                                 {
114                                         // EMIT ow.WriteAssembly (writer, memberType.Assembly);
115                                         gen.Emit (OpCodes.Ldarg_1);
116                                         gen.Emit (OpCodes.Ldarg_2);
117                                         EmitLoadTypeAssembly (gen, memberType, field.Name);
118                                         gen.EmitCall (OpCodes.Callvirt, typeof(ObjectWriter).GetMethod("WriteAssembly"), null);
119                                         gen.Emit (OpCodes.Pop);
120                                 }
121                         }
122                         gen.Emit(OpCodes.Ret);
123                         typeBuilder.DefineMethodOverride (method, typeof(TypeMetadata).GetMethod ("WriteAssemblies"));
124                         
125                         // *********************
126                         // METHOD public override void WriteTypeData (ObjectWriter ow, BinaryWriter writer, bool writeTypes);
127                         
128                         parameters = new Type[] { typeof(ObjectWriter), typeof(BinaryWriter), typeof(bool) };
129                         method = typeBuilder.DefineMethod ("WriteTypeData", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), parameters);
130                         gen = method.GetILGenerator();
131                         
132                         // EMIT writer.Write (members.Length);
133                         gen.Emit (OpCodes.Ldarg_2);
134                         gen.Emit (OpCodes.Ldc_I4, members.Length);
135                         EmitWrite (gen, typeof(int));
136                         
137                         // Names of fields
138                         foreach (FieldInfo field in members)
139                         {
140                                 // EMIT writer.Write (name);
141                                 gen.Emit (OpCodes.Ldarg_2);
142                                 gen.Emit (OpCodes.Ldstr, field.Name);
143                                 EmitWrite (gen, typeof(string));
144                         }
145
146                         Label falseLabel = gen.DefineLabel ();
147                         gen.Emit (OpCodes.Ldarg_3);
148                         gen.Emit (OpCodes.Brfalse, falseLabel);
149                                         
150                         // Types of fields
151                         foreach (FieldInfo field in members)
152                         {
153                                 // EMIT writer.Write ((byte) ObjectWriter.GetTypeTag (type));
154                                 gen.Emit (OpCodes.Ldarg_2);
155                                 gen.Emit (OpCodes.Ldc_I4_S, (byte) ObjectWriter.GetTypeTag (field.FieldType));
156                                 EmitWrite (gen, typeof(byte));
157                         }
158
159                         // Type specs of fields
160                         foreach (FieldInfo field in members)
161                         {
162                                 // EMIT ow.WriteTypeSpec (writer, field.FieldType);
163                                 EmitWriteTypeSpec (gen, field.FieldType, field.Name);
164                         }
165                         gen.MarkLabel(falseLabel);
166                         
167                         gen.Emit(OpCodes.Ret);
168                         typeBuilder.DefineMethodOverride (method, typeof(TypeMetadata).GetMethod ("WriteTypeData"));
169                         
170                         // *********************
171                         // METHOD public override void WriteObjectData (ObjectWriter ow, BinaryWriter writer, object data)
172                         
173                         parameters = new Type[] { typeof(ObjectWriter), typeof(BinaryWriter), typeof(object) };
174                         method = typeBuilder.DefineMethod ("WriteObjectData", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), parameters);                    
175                         gen = method.GetILGenerator();
176                         
177                         LocalBuilder localBuilder = gen.DeclareLocal (type);
178                         OpCode lload = OpCodes.Ldloc;
179                         
180                         gen.Emit (OpCodes.Ldarg_3);
181                         if (type.IsValueType)
182                         {
183                                 gen.Emit (OpCodes.Unbox, type);
184                                 LoadFromPtr (gen, type);
185                                 lload = OpCodes.Ldloca_S;
186                         }
187                         else
188                                 gen.Emit (OpCodes.Castclass, type);
189                                 
190                         gen.Emit (OpCodes.Stloc, localBuilder);
191                         
192                         foreach (FieldInfo field in members)
193                         {
194                                 // EMIT ow.WriteValue (writer, ((FieldInfo)members[n]).FieldType, values[n]);
195                                 Type ftype = field.FieldType;
196                                 if (BinaryCommon.IsPrimitive (ftype))
197                                 {
198                                         gen.Emit (OpCodes.Ldarg_2);
199                                         gen.Emit (lload, localBuilder);
200                                         if (ftype == typeof(DateTime) || ftype == typeof(TimeSpan) || ftype == typeof(decimal))
201                                                 gen.Emit (OpCodes.Ldflda, field);
202                                         else
203                                                 gen.Emit (OpCodes.Ldfld, field);
204                                         EmitWritePrimitiveValue (gen, ftype);
205                                 }
206                                 else
207                                 {
208                                         gen.Emit (OpCodes.Ldarg_1);
209                                         gen.Emit (OpCodes.Ldarg_2);
210                                         gen.Emit (OpCodes.Ldtoken, ftype);
211                                         gen.EmitCall (OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);
212                                         gen.Emit (lload, localBuilder);
213                                         gen.Emit (OpCodes.Ldfld, field);
214                                         if (ftype.IsValueType)
215                                                 gen.Emit (OpCodes.Box, ftype);
216                                         gen.EmitCall (OpCodes.Call, typeof(ObjectWriter).GetMethod("WriteValue"), null);
217                                 }
218                         }
219                         
220                         gen.Emit(OpCodes.Ret);
221                         typeBuilder.DefineMethodOverride (method, typeof(TypeMetadata).GetMethod ("WriteObjectData"));
222                         
223                 return typeBuilder.CreateType();
224                 }
225                 
226                 public static void LoadFromPtr (ILGenerator ig, Type t)
227                 {
228                         if (t == typeof(int))
229                                 ig.Emit (OpCodes.Ldind_I4);
230                         else if (t == typeof(uint))
231                                 ig.Emit (OpCodes.Ldind_U4);
232                         else if (t == typeof(short))
233                                 ig.Emit (OpCodes.Ldind_I2);
234                         else if (t == typeof(ushort))
235                                 ig.Emit (OpCodes.Ldind_U2);
236                         else if (t == typeof(char))
237                                 ig.Emit (OpCodes.Ldind_U2);
238                         else if (t == typeof(byte))
239                                 ig.Emit (OpCodes.Ldind_U1);
240                         else if (t == typeof(sbyte))
241                                 ig.Emit (OpCodes.Ldind_I1);
242                         else if (t == typeof(ulong))
243                                 ig.Emit (OpCodes.Ldind_I8);
244                         else if (t == typeof(long))
245                                 ig.Emit (OpCodes.Ldind_I8);
246                         else if (t == typeof(float))
247                                 ig.Emit (OpCodes.Ldind_R4);
248                         else if (t == typeof(double))
249                                 ig.Emit (OpCodes.Ldind_R8);
250                         else if (t == typeof(bool))
251                                 ig.Emit (OpCodes.Ldind_I1);
252                         else if (t == typeof(IntPtr))
253                                 ig.Emit (OpCodes.Ldind_I);
254                         else if (t.IsEnum) {
255                                 if (t == typeof(Enum))
256                                         ig.Emit (OpCodes.Ldind_Ref);
257                                 else
258                                         LoadFromPtr (ig, EnumToUnderlying (t));
259                         } else if (t.IsValueType)
260                                 ig.Emit (OpCodes.Ldobj, t);
261                         else
262                                 ig.Emit (OpCodes.Ldind_Ref);
263                 }
264
265                 static void EmitWriteTypeSpec (ILGenerator gen, Type type, string member)
266                 {
267                         // WARNING Keep in sync with WriteTypeSpec
268                         
269                         switch (ObjectWriter.GetTypeTag (type))
270                         {
271                                 case TypeTag.PrimitiveType:
272                                         // EMIT writer.Write (BinaryCommon.GetTypeCode (type));
273                                         gen.Emit (OpCodes.Ldarg_2);
274                                         gen.Emit (OpCodes.Ldc_I4_S, (byte) BinaryCommon.GetTypeCode (type));
275                                         EmitWrite (gen, typeof(byte));
276                                         break;
277
278                                 case TypeTag.RuntimeType:
279                                         // EMIT writer.Write (type.FullName);
280                                         gen.Emit (OpCodes.Ldarg_2);
281                                         gen.Emit (OpCodes.Ldstr, type.FullName);
282                                         EmitWrite (gen, typeof(string));
283                                         break;
284
285                                 case TypeTag.GenericType:
286                                         // EMIT writer.Write (type.FullName);
287                                         gen.Emit (OpCodes.Ldarg_2);
288                                         gen.Emit (OpCodes.Ldstr, type.FullName);
289                                         EmitWrite (gen, typeof(string));
290                                         
291                                         // EMIT writer.Write ((int)ow.GetAssemblyId (type.Assembly));
292                                         gen.Emit (OpCodes.Ldarg_2);
293                                         gen.Emit (OpCodes.Ldarg_1);
294                                         EmitLoadTypeAssembly (gen, type, member);
295                                         gen.EmitCall (OpCodes.Callvirt, typeof(ObjectWriter).GetMethod("GetAssemblyId"), null);
296                                         gen.Emit (OpCodes.Conv_I4);
297                                         EmitWrite (gen, typeof(int));
298                                         break;
299
300                                 case TypeTag.ArrayOfPrimitiveType:
301                                         // EMIT writer.Write (BinaryCommon.GetTypeCode (type.GetElementType()));
302                                         gen.Emit (OpCodes.Ldarg_2);
303                                         gen.Emit (OpCodes.Ldc_I4_S, (byte) BinaryCommon.GetTypeCode (type.GetElementType()));
304                                         EmitWrite (gen, typeof(byte));
305                                         break;
306
307                                 default:
308                                         // Type spec not needed
309                                         break;
310                         }
311                 }
312                 
313                 static void EmitLoadTypeAssembly (ILGenerator gen, Type type, string member)
314                 {
315                         gen.Emit (OpCodes.Ldtoken, type);
316                         gen.EmitCall (OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);
317                         gen.EmitCall (OpCodes.Callvirt, typeof(Type).GetProperty("Assembly").GetGetMethod(), null);
318                 }
319                 
320                 static void EmitWrite (ILGenerator gen, Type type)
321                 {
322                         gen.EmitCall (OpCodes.Callvirt, typeof(BinaryWriter).GetMethod("Write", new Type[] { type }), null);
323                 }
324                 
325                 public static void EmitWritePrimitiveValue (ILGenerator gen, Type type)
326                 {
327                         switch (Type.GetTypeCode (type))
328                         {
329                                 case TypeCode.Boolean:
330                                 case TypeCode.Byte:
331                                 case TypeCode.Char:
332                                 case TypeCode.Double:
333                                 case TypeCode.Int16:
334                                 case TypeCode.Int32:
335                                 case TypeCode.Int64:
336                                 case TypeCode.SByte:
337                                 case TypeCode.Single:
338                                 case TypeCode.UInt16:
339                                 case TypeCode.UInt32:
340                                 case TypeCode.UInt64:
341                                 case TypeCode.String:
342                                         EmitWrite (gen, type);
343                                         break;
344
345                                 case TypeCode.Decimal:
346                                         // writer.Write (val.ToString (CultureInfo.InvariantCulture));
347                                         gen.EmitCall (OpCodes.Call, typeof(CultureInfo).GetProperty("InvariantCulture").GetGetMethod(), null);
348                                         gen.EmitCall (OpCodes.Call, typeof(Decimal).GetMethod("ToString", new Type[] {typeof(IFormatProvider)}), null);
349                                         EmitWrite (gen, typeof(string));
350                                         break;
351                                         
352                                 case TypeCode.DateTime: 
353                                         gen.EmitCall (OpCodes.Call, typeof(DateTime).GetMethod("ToBinary", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance), null);
354                                         EmitWrite (gen, typeof(long));
355                                         break;
356                                         
357                                 default:
358                                         if (type == typeof (TimeSpan)) {
359                                                 gen.EmitCall (OpCodes.Call, typeof(TimeSpan).GetProperty("Ticks").GetGetMethod(), null);
360                                                 EmitWrite (gen, typeof(long));
361                                         }
362                                         else
363                                                 throw new NotSupportedException ("Unsupported primitive type: " + type.FullName);
364                                         break;
365                         }
366                 }
367                 
368                 //
369                 // This is needed, because enumerations from assemblies
370                 // do not report their underlyingtype, but they report
371                 // themselves
372                 //
373                 public static Type EnumToUnderlying (Type t)
374                 {
375                         TypeCode tc = Type.GetTypeCode (t);
376         
377                         switch (tc){
378                         case TypeCode.Boolean:
379                                 return typeof (bool);
380                         case TypeCode.Byte:
381                                 return typeof (byte);
382                         case TypeCode.SByte:
383                                 return typeof (sbyte);
384                         case TypeCode.Char:
385                                 return typeof (char);
386                         case TypeCode.Int16:
387                                 return typeof (short);
388                         case TypeCode.UInt16:
389                                 return typeof (ushort);
390                         case TypeCode.Int32:
391                                 return typeof (int);
392                         case TypeCode.UInt32:
393                                 return typeof (uint);
394                         case TypeCode.Int64:
395                                 return typeof (long);
396                         case TypeCode.UInt64:
397                                 return typeof (ulong);
398                         }
399                         throw new Exception ("Unhandled typecode in enum " + tc + " from " + t.AssemblyQualifiedName);
400                 }
401         }
402  }
403