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