Merge pull request #409 from Alkarex/patch-1
[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                                         EmitLoadTypeAssembly (gen, memberType, field.Name);
119                                         gen.EmitCall (OpCodes.Callvirt, typeof(ObjectWriter).GetMethod("WriteAssembly"), null);
120                                         gen.Emit (OpCodes.Pop);
121                                 }
122                         }
123                         gen.Emit(OpCodes.Ret);
124                         typeBuilder.DefineMethodOverride (method, typeof(TypeMetadata).GetMethod ("WriteAssemblies"));
125                         
126                         // *********************
127                         // METHOD public override void WriteTypeData (ObjectWriter ow, BinaryWriter writer, bool writeTypes);
128                         
129                         parameters = new Type[] { typeof(ObjectWriter), typeof(BinaryWriter), typeof(bool) };
130                         method = typeBuilder.DefineMethod ("WriteTypeData", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), parameters);
131                         gen = method.GetILGenerator();
132                         
133                         // EMIT writer.Write (members.Length);
134                         gen.Emit (OpCodes.Ldarg_2);
135                         gen.Emit (OpCodes.Ldc_I4, members.Length);
136                         EmitWrite (gen, typeof(int));
137                         
138                         // Names of fields
139                         foreach (FieldInfo field in members)
140                         {
141                                 // EMIT writer.Write (name);
142                                 gen.Emit (OpCodes.Ldarg_2);
143                                 gen.Emit (OpCodes.Ldstr, field.Name);
144                                 EmitWrite (gen, typeof(string));
145                         }
146
147                         Label falseLabel = gen.DefineLabel ();
148                         gen.Emit (OpCodes.Ldarg_3);
149                         gen.Emit (OpCodes.Brfalse, falseLabel);
150                                         
151                         // Types of fields
152                         foreach (FieldInfo field in members)
153                         {
154                                 // EMIT writer.Write ((byte) ObjectWriter.GetTypeTag (type));
155                                 gen.Emit (OpCodes.Ldarg_2);
156                                 gen.Emit (OpCodes.Ldc_I4_S, (byte) ObjectWriter.GetTypeTag (field.FieldType));
157                                 EmitWrite (gen, typeof(byte));
158                         }
159
160                         // Type specs of fields
161                         foreach (FieldInfo field in members)
162                         {
163                                 // EMIT ow.WriteTypeSpec (writer, field.FieldType);
164                                 EmitWriteTypeSpec (gen, field.FieldType, field.Name);
165                         }
166                         gen.MarkLabel(falseLabel);
167                         
168                         gen.Emit(OpCodes.Ret);
169                         typeBuilder.DefineMethodOverride (method, typeof(TypeMetadata).GetMethod ("WriteTypeData"));
170                         
171                         // *********************
172                         // METHOD public override void WriteObjectData (ObjectWriter ow, BinaryWriter writer, object data)
173                         
174                         parameters = new Type[] { typeof(ObjectWriter), typeof(BinaryWriter), typeof(object) };
175                         method = typeBuilder.DefineMethod ("WriteObjectData", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), parameters);                    
176                         gen = method.GetILGenerator();
177                         
178                         LocalBuilder localBuilder = gen.DeclareLocal (type);
179                         OpCode lload = OpCodes.Ldloc;
180                         
181                         gen.Emit (OpCodes.Ldarg_3);
182                         if (type.IsValueType)
183                         {
184                                 gen.Emit (OpCodes.Unbox, type);
185                                 LoadFromPtr (gen, type);
186                                 lload = OpCodes.Ldloca_S;
187                         }
188                         else
189                                 gen.Emit (OpCodes.Castclass, type);
190                                 
191                         gen.Emit (OpCodes.Stloc, localBuilder);
192                         
193                         foreach (FieldInfo field in members)
194                         {
195                                 // EMIT ow.WriteValue (writer, ((FieldInfo)members[n]).FieldType, values[n]);
196                                 Type ftype = field.FieldType;
197                                 if (BinaryCommon.IsPrimitive (ftype))
198                                 {
199                                         gen.Emit (OpCodes.Ldarg_2);
200                                         gen.Emit (lload, localBuilder);
201                                         if (ftype == typeof(DateTime) || ftype == typeof(TimeSpan) || ftype == typeof(decimal))
202                                                 gen.Emit (OpCodes.Ldflda, field);
203                                         else
204                                                 gen.Emit (OpCodes.Ldfld, field);
205                                         EmitWritePrimitiveValue (gen, ftype);
206                                 }
207                                 else
208                                 {
209                                         gen.Emit (OpCodes.Ldarg_1);
210                                         gen.Emit (OpCodes.Ldarg_2);
211                                         gen.Emit (OpCodes.Ldtoken, ftype);
212                                         gen.EmitCall (OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);
213                                         gen.Emit (lload, localBuilder);
214                                         gen.Emit (OpCodes.Ldfld, field);
215                                         if (ftype.IsValueType)
216                                                 gen.Emit (OpCodes.Box, ftype);
217                                         gen.EmitCall (OpCodes.Call, typeof(ObjectWriter).GetMethod("WriteValue"), null);
218                                 }
219                         }
220                         
221                         gen.Emit(OpCodes.Ret);
222                         typeBuilder.DefineMethodOverride (method, typeof(TypeMetadata).GetMethod ("WriteObjectData"));
223                         
224                 return typeBuilder.CreateType();
225                 }
226                 
227                 public static void LoadFromPtr (ILGenerator ig, Type t)
228                 {
229                         if (t == typeof(int))
230                                 ig.Emit (OpCodes.Ldind_I4);
231                         else if (t == typeof(uint))
232                                 ig.Emit (OpCodes.Ldind_U4);
233                         else if (t == typeof(short))
234                                 ig.Emit (OpCodes.Ldind_I2);
235                         else if (t == typeof(ushort))
236                                 ig.Emit (OpCodes.Ldind_U2);
237                         else if (t == typeof(char))
238                                 ig.Emit (OpCodes.Ldind_U2);
239                         else if (t == typeof(byte))
240                                 ig.Emit (OpCodes.Ldind_U1);
241                         else if (t == typeof(sbyte))
242                                 ig.Emit (OpCodes.Ldind_I1);
243                         else if (t == typeof(ulong))
244                                 ig.Emit (OpCodes.Ldind_I8);
245                         else if (t == typeof(long))
246                                 ig.Emit (OpCodes.Ldind_I8);
247                         else if (t == typeof(float))
248                                 ig.Emit (OpCodes.Ldind_R4);
249                         else if (t == typeof(double))
250                                 ig.Emit (OpCodes.Ldind_R8);
251                         else if (t == typeof(bool))
252                                 ig.Emit (OpCodes.Ldind_I1);
253                         else if (t == typeof(IntPtr))
254                                 ig.Emit (OpCodes.Ldind_I);
255                         else if (t.IsEnum) {
256                                 if (t == typeof(Enum))
257                                         ig.Emit (OpCodes.Ldind_Ref);
258                                 else
259                                         LoadFromPtr (ig, EnumToUnderlying (t));
260                         } else if (t.IsValueType)
261                                 ig.Emit (OpCodes.Ldobj, t);
262                         else
263                                 ig.Emit (OpCodes.Ldind_Ref);
264                 }
265
266                 static void EmitWriteTypeSpec (ILGenerator gen, Type type, string member)
267                 {
268                         // WARNING Keep in sync with WriteTypeSpec
269                         
270                         switch (ObjectWriter.GetTypeTag (type))
271                         {
272                                 case TypeTag.PrimitiveType:
273                                         // EMIT writer.Write (BinaryCommon.GetTypeCode (type));
274                                         gen.Emit (OpCodes.Ldarg_2);
275                                         gen.Emit (OpCodes.Ldc_I4_S, (byte) BinaryCommon.GetTypeCode (type));
276                                         EmitWrite (gen, typeof(byte));
277                                         break;
278
279                                 case TypeTag.RuntimeType:
280                                         // EMIT writer.Write (type.FullName);
281                                         gen.Emit (OpCodes.Ldarg_2);
282                                         gen.Emit (OpCodes.Ldstr, type.FullName);
283                                         EmitWrite (gen, typeof(string));
284                                         break;
285
286                                 case TypeTag.GenericType:
287                                         // EMIT writer.Write (type.FullName);
288                                         gen.Emit (OpCodes.Ldarg_2);
289                                         gen.Emit (OpCodes.Ldstr, type.FullName);
290                                         EmitWrite (gen, typeof(string));
291                                         
292                                         // EMIT writer.Write ((int)ow.GetAssemblyId (type.Assembly));
293                                         gen.Emit (OpCodes.Ldarg_2);
294                                         gen.Emit (OpCodes.Ldarg_1);
295                                         EmitLoadTypeAssembly (gen, type, member);
296                                         gen.EmitCall (OpCodes.Callvirt, typeof(ObjectWriter).GetMethod("GetAssemblyId"), null);
297                                         gen.Emit (OpCodes.Conv_I4);
298                                         EmitWrite (gen, typeof(int));
299                                         break;
300
301                                 case TypeTag.ArrayOfPrimitiveType:
302                                         // EMIT writer.Write (BinaryCommon.GetTypeCode (type.GetElementType()));
303                                         gen.Emit (OpCodes.Ldarg_2);
304                                         gen.Emit (OpCodes.Ldc_I4_S, (byte) BinaryCommon.GetTypeCode (type.GetElementType()));
305                                         EmitWrite (gen, typeof(byte));
306                                         break;
307
308                                 default:
309                                         // Type spec not needed
310                                         break;
311                         }
312                 }
313                 
314                 static void EmitLoadTypeAssembly (ILGenerator gen, Type type, string member)
315                 {
316                         gen.Emit (OpCodes.Ldtoken, type);
317                         gen.EmitCall (OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);
318                         gen.EmitCall (OpCodes.Callvirt, typeof(Type).GetProperty("Assembly").GetGetMethod(), null);
319                 }
320                 
321                 static void EmitWrite (ILGenerator gen, Type type)
322                 {
323                         gen.EmitCall (OpCodes.Callvirt, typeof(BinaryWriter).GetMethod("Write", new Type[] { type }), null);
324                 }
325                 
326                 public static void EmitWritePrimitiveValue (ILGenerator gen, Type type)
327                 {
328                         switch (Type.GetTypeCode (type))
329                         {
330                                 case TypeCode.Boolean:
331                                 case TypeCode.Byte:
332                                 case TypeCode.Char:
333                                 case TypeCode.Double:
334                                 case TypeCode.Int16:
335                                 case TypeCode.Int32:
336                                 case TypeCode.Int64:
337                                 case TypeCode.SByte:
338                                 case TypeCode.Single:
339                                 case TypeCode.UInt16:
340                                 case TypeCode.UInt32:
341                                 case TypeCode.UInt64:
342                                 case TypeCode.String:
343                                         EmitWrite (gen, type);
344                                         break;
345
346                                 case TypeCode.Decimal:
347                                         // writer.Write (val.ToString (CultureInfo.InvariantCulture));
348                                         gen.EmitCall (OpCodes.Call, typeof(CultureInfo).GetProperty("InvariantCulture").GetGetMethod(), null);
349                                         gen.EmitCall (OpCodes.Call, typeof(Decimal).GetMethod("ToString", new Type[] {typeof(IFormatProvider)}), null);
350                                         EmitWrite (gen, typeof(string));
351                                         break;
352                                         
353                                 case TypeCode.DateTime: 
354                                         gen.EmitCall (OpCodes.Call, typeof(DateTime).GetMethod("ToBinary", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance), null);
355                                         EmitWrite (gen, typeof(long));
356                                         break;
357                                         
358                                 default:
359                                         if (type == typeof (TimeSpan)) {
360                                                 gen.EmitCall (OpCodes.Call, typeof(TimeSpan).GetProperty("Ticks").GetGetMethod(), null);
361                                                 EmitWrite (gen, typeof(long));
362                                         }
363                                         else
364                                                 throw new NotSupportedException ("Unsupported primitive type: " + type.FullName);
365                                         break;
366                         }
367                 }
368                 
369                 //
370                 // This is needed, because enumerations from assemblies
371                 // do not report their underlyingtype, but they report
372                 // themselves
373                 //
374                 public static Type EnumToUnderlying (Type t)
375                 {
376                         TypeCode tc = Type.GetTypeCode (t);
377         
378                         switch (tc){
379                         case TypeCode.Boolean:
380                                 return typeof (bool);
381                         case TypeCode.Byte:
382                                 return typeof (byte);
383                         case TypeCode.SByte:
384                                 return typeof (sbyte);
385                         case TypeCode.Char:
386                                 return typeof (char);
387                         case TypeCode.Int16:
388                                 return typeof (short);
389                         case TypeCode.UInt16:
390                                 return typeof (ushort);
391                         case TypeCode.Int32:
392                                 return typeof (int);
393                         case TypeCode.UInt32:
394                                 return typeof (uint);
395                         case TypeCode.Int64:
396                                 return typeof (long);
397                         case TypeCode.UInt64:
398                                 return typeof (ulong);
399                         }
400                         throw new Exception ("Unhandled typecode in enum " + tc + " from " + t.AssemblyQualifiedName);
401                 }
402         }
403  }
404  
405 #endif