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