2010-06-21 Marek Habersack <mhabersack@novell.com>
[mcs.git] / class / corlib / System.Runtime.Serialization.Formatters.Binary / CodeGenerator.cs
blobfeb8ffa6d4e48eab5239b2c317fe9b1e20d87844
1 // CodeGenerator.cs
2 //
3 // Author:
4 // Lluis Sanchez Gual (lluis@ximian.com)
5 //
6 // (C) 2004 Novell, Inc
8 //
9 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
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.
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;
39 namespace System.Runtime.Serialization.Formatters.Binary
41 internal class CodeGenerator
43 // Code generation
45 static object monitor = new object ();
47 static ModuleBuilder _module;
49 static CodeGenerator ()
51 AppDomain myDomain = System.Threading.Thread.GetDomain();
52 AssemblyName myAsmName = new AssemblyName();
53 myAsmName.Name = "__MetadataTypes";
55 AssemblyBuilder myAsmBuilder = myDomain.DefineInternalDynamicAssembly (myAsmName, AssemblyBuilderAccess.Run);
56 _module = myAsmBuilder.DefineDynamicModule("__MetadataTypesModule", false);
59 static public Type GenerateMetadataType (Type type, StreamingContext context) {
60 /* SRE is not thread safe */
61 lock (monitor) {
62 return GenerateMetadataTypeInternal (type, context);
66 static public Type GenerateMetadataTypeInternal (Type type, StreamingContext context)
68 string name = type.Name + "__TypeMetadata";
69 string sufix = "";
70 int n = 0;
71 while (_module.GetType (name + sufix) != null)
72 sufix = (++n).ToString();
74 name += sufix;
76 MemberInfo[] members = FormatterServices.GetSerializableMembers (type, context);
78 TypeBuilder typeBuilder = _module.DefineType (name, TypeAttributes.Public, typeof(ClrTypeMetadata));
80 Type[] parameters;
81 MethodBuilder method;
82 ILGenerator gen;
84 // *********************
85 // METHOD public constructor (Type t): base (t);
87 parameters = Type.EmptyTypes;
89 ConstructorBuilder ctor = typeBuilder.DefineConstructor (MethodAttributes.Public, CallingConventions.Standard, parameters);
90 ConstructorInfo baseCtor = typeof(ClrTypeMetadata).GetConstructor (new Type[] { typeof(Type) });
91 gen = ctor.GetILGenerator();
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);
99 // *********************
100 // METHOD public override void WriteAssemblies (ObjectWriter ow, BinaryWriter writer);
102 parameters = new Type[] { typeof(ObjectWriter), typeof(BinaryWriter) };
103 method = typeBuilder.DefineMethod ("WriteAssemblies", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), parameters);
104 gen = method.GetILGenerator();
106 foreach (FieldInfo field in members)
108 Type memberType = field.FieldType;
109 while (memberType.IsArray)
110 memberType = memberType.GetElementType();
112 if (memberType.Assembly != ObjectWriter.CorlibAssembly)
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);
122 gen.Emit(OpCodes.Ret);
123 typeBuilder.DefineMethodOverride (method, typeof(TypeMetadata).GetMethod ("WriteAssemblies"));
125 // *********************
126 // METHOD public override void WriteTypeData (ObjectWriter ow, BinaryWriter writer, bool writeTypes);
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();
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));
137 // Names of fields
138 foreach (FieldInfo field in members)
140 // EMIT writer.Write (name);
141 gen.Emit (OpCodes.Ldarg_2);
142 gen.Emit (OpCodes.Ldstr, field.Name);
143 EmitWrite (gen, typeof(string));
146 Label falseLabel = gen.DefineLabel ();
147 gen.Emit (OpCodes.Ldarg_3);
148 gen.Emit (OpCodes.Brfalse, falseLabel);
150 // Types of fields
151 foreach (FieldInfo field in members)
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));
159 // Type specs of fields
160 foreach (FieldInfo field in members)
162 // EMIT ow.WriteTypeSpec (writer, field.FieldType);
163 EmitWriteTypeSpec (gen, field.FieldType, field.Name);
165 gen.MarkLabel(falseLabel);
167 gen.Emit(OpCodes.Ret);
168 typeBuilder.DefineMethodOverride (method, typeof(TypeMetadata).GetMethod ("WriteTypeData"));
170 // *********************
171 // METHOD public override void WriteObjectData (ObjectWriter ow, BinaryWriter writer, object data)
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();
177 LocalBuilder localBuilder = gen.DeclareLocal (type);
178 OpCode lload = OpCodes.Ldloc;
180 gen.Emit (OpCodes.Ldarg_3);
181 if (type.IsValueType)
183 gen.Emit (OpCodes.Unbox, type);
184 LoadFromPtr (gen, type);
185 lload = OpCodes.Ldloca_S;
187 else
188 gen.Emit (OpCodes.Castclass, type);
190 gen.Emit (OpCodes.Stloc, localBuilder);
192 foreach (FieldInfo field in members)
194 // EMIT ow.WriteValue (writer, ((FieldInfo)members[n]).FieldType, values[n]);
195 Type ftype = field.FieldType;
196 if (BinaryCommon.IsPrimitive (ftype))
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);
206 else
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);
220 gen.Emit(OpCodes.Ret);
221 typeBuilder.DefineMethodOverride (method, typeof(TypeMetadata).GetMethod ("WriteObjectData"));
223 return typeBuilder.CreateType();
226 public static void LoadFromPtr (ILGenerator ig, Type t)
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);
265 static void EmitWriteTypeSpec (ILGenerator gen, Type type, string member)
267 // WARNING Keep in sync with WriteTypeSpec
269 switch (ObjectWriter.GetTypeTag (type))
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;
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;
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));
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;
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;
307 default:
308 // Type spec not needed
309 break;
313 static void EmitLoadTypeAssembly (ILGenerator gen, Type type, string member)
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);
320 static void EmitWrite (ILGenerator gen, Type type)
322 gen.EmitCall (OpCodes.Callvirt, typeof(BinaryWriter).GetMethod("Write", new Type[] { type }), null);
325 public static void EmitWritePrimitiveValue (ILGenerator gen, Type type)
327 switch (Type.GetTypeCode (type))
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;
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;
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;
357 default:
358 if (type == typeof (TimeSpan)) {
359 gen.EmitCall (OpCodes.Call, typeof(TimeSpan).GetProperty("Ticks").GetGetMethod(), null);
360 EmitWrite (gen, typeof(long));
362 else
363 throw new NotSupportedException ("Unsupported primitive type: " + type.FullName);
364 break;
369 // This is needed, because enumerations from assemblies
370 // do not report their underlyingtype, but they report
371 // themselves
373 public static Type EnumToUnderlying (Type t)
375 TypeCode tc = Type.GetTypeCode (t);
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);
399 throw new Exception ("Unhandled typecode in enum " + tc + " from " + t.AssemblyQualifiedName);