**** Merged from MCS ****
[mono-project.git] / mcs / class / corlib / System.Runtime.Serialization.Formatters.Binary / CodeGenerator.cs
blob8544f251122cf4a4942093cacbfa2e6420fe6e2a
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 ModuleBuilder _module;
47 static public Type GenerateMetadataType (Type type, StreamingContext context)
49 if (_module == null)
51 lock (typeof (ObjectWriter))
53 if (_module == null) {
54 AppDomain myDomain = System.Threading.Thread.GetDomain();
55 AssemblyName myAsmName = new AssemblyName();
56 myAsmName.Name = "__MetadataTypes";
58 AssemblyBuilder myAsmBuilder = myDomain.DefineDynamicAssembly (myAsmName, AssemblyBuilderAccess.Run);
59 _module = myAsmBuilder.DefineDynamicModule("__MetadataTypesModule", true);
64 string name = type.Name + "__TypeMetadata";
65 string sufix = "";
66 int n = 0;
67 while (_module.GetType (name + sufix) != null)
68 sufix = (++n).ToString();
70 name += sufix;
72 MemberInfo[] members = FormatterServices.GetSerializableMembers (type, context);
74 TypeBuilder typeBuilder = _module.DefineType (name, TypeAttributes.Public, typeof(TypeMetadata));
76 Type[] parameters;
77 MethodBuilder method;
78 ILGenerator gen;
80 // *********************
81 // METHOD public constructor (Type t): base (t);
83 parameters = new Type[0];
85 ConstructorBuilder ctor = typeBuilder.DefineConstructor (MethodAttributes.Public, CallingConventions.Standard, parameters);
86 ConstructorInfo baseCtor = typeof(TypeMetadata).GetConstructor (new Type[] { typeof(Type) });
87 gen = ctor.GetILGenerator();
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);
95 // *********************
96 // METHOD public override void WriteAssemblies (ObjectWriter ow, BinaryWriter writer);
98 parameters = new Type[] { typeof(ObjectWriter), typeof(BinaryWriter) };
99 method = typeBuilder.DefineMethod ("WriteAssemblies", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), parameters);
100 gen = method.GetILGenerator();
102 foreach (FieldInfo field in members)
104 Type memberType = field.FieldType;
105 while (memberType.IsArray)
106 memberType = memberType.GetElementType();
108 if (memberType.Assembly != ObjectWriter.CorlibAssembly)
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);
118 gen.Emit(OpCodes.Ret);
119 typeBuilder.DefineMethodOverride (method, typeof(TypeMetadata).GetMethod ("WriteAssemblies"));
121 // *********************
122 // METHOD public override void WriteTypeData (ObjectWriter ow, BinaryWriter writer);
124 parameters = new Type[] { typeof(ObjectWriter), typeof(BinaryWriter) };
125 method = typeBuilder.DefineMethod ("WriteTypeData", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), parameters);
126 gen = method.GetILGenerator();
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));
133 // Names of fields
134 foreach (FieldInfo field in members)
136 // EMIT writer.Write (name);
137 gen.Emit (OpCodes.Ldarg_2);
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));
146 // Types of fields
147 foreach (FieldInfo field in members)
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));
155 // Type specs of fields
156 foreach (FieldInfo field in members)
158 // EMIT ow.WriteTypeSpec (writer, field.FieldType);
159 EmitWriteTypeSpec (gen, field.FieldType, field.Name);
162 gen.Emit(OpCodes.Ret);
163 typeBuilder.DefineMethodOverride (method, typeof(TypeMetadata).GetMethod ("WriteTypeData"));
165 // *********************
166 // METHOD public override void WriteObjectData (ObjectWriter ow, BinaryWriter writer, object data)
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();
172 LocalBuilder localBuilder = gen.DeclareLocal (type);
173 OpCode lload = OpCodes.Ldloc;
175 gen.Emit (OpCodes.Ldarg_3);
176 if (type.IsValueType)
178 gen.Emit (OpCodes.Unbox, type);
179 LoadFromPtr (gen, type);
180 lload = OpCodes.Ldloca_S;
182 else
183 gen.Emit (OpCodes.Castclass, type);
185 gen.Emit (OpCodes.Stloc, localBuilder);
187 foreach (FieldInfo field in members)
189 // EMIT ow.WriteValue (writer, ((FieldInfo)members[n]).FieldType, values[n]);
190 Type ftype = field.FieldType;
191 if (BinaryCommon.IsPrimitive (ftype))
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);
201 else
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);
215 gen.Emit(OpCodes.Ret);
216 typeBuilder.DefineMethodOverride (method, typeof(TypeMetadata).GetMethod ("WriteObjectData"));
218 return typeBuilder.CreateType();
221 public static void LoadFromPtr (ILGenerator ig, Type t)
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, t.UnderlyingSystemType);
254 } else if (t.IsValueType)
255 ig.Emit (OpCodes.Ldobj, t);
256 else
257 ig.Emit (OpCodes.Ldind_Ref);
260 public static void EmitWriteTypeSpec (ILGenerator gen, Type type, string member)
262 // WARNING Keep in sync with WriteTypeSpec
264 switch (ObjectWriter.GetTypeTag (type))
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;
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;
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));
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;
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;
302 default:
303 // Type spec not needed
304 break;
308 static void EmitLoadTypeAssembly (ILGenerator gen, Type type, string member)
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);
315 static void EmitWrite (ILGenerator gen, Type type)
317 gen.EmitCall (OpCodes.Callvirt, typeof(BinaryWriter).GetMethod("Write", new Type[] { type }), null);
320 public static void EmitWritePrimitiveValue (ILGenerator gen, Type type)
322 switch (Type.GetTypeCode (type))
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;
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;
347 case TypeCode.DateTime:
348 gen.EmitCall (OpCodes.Call, typeof(DateTime).GetProperty("Ticks").GetGetMethod(), null);
349 EmitWrite (gen, typeof(long));
350 break;
352 default:
353 if (type == typeof (TimeSpan)) {
354 gen.EmitCall (OpCodes.Call, typeof(TimeSpan).GetProperty("Ticks").GetGetMethod(), null);
355 EmitWrite (gen, typeof(long));
357 else
358 throw new NotSupportedException ("Unsupported primitive type: " + type.FullName);
359 break;