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