When .NET serializes it will use the TypeForwardedFrom information when
[mono-project.git] / mcs / class / corlib / System.Runtime.Serialization.Formatters.Binary / CodeGenerator.cs
blob671c196163e08ad69c39a05bf664164bfdbc25ac
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 #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;
40 namespace System.Runtime.Serialization.Formatters.Binary
42 internal class CodeGenerator
44 // Code generation
46 static object monitor = new object ();
48 static ModuleBuilder _module;
50 static CodeGenerator ()
52 AppDomain myDomain = System.Threading.Thread.GetDomain();
53 AssemblyName myAsmName = new AssemblyName();
54 myAsmName.Name = "__MetadataTypes";
56 AssemblyBuilder myAsmBuilder = myDomain.DefineInternalDynamicAssembly (myAsmName, AssemblyBuilderAccess.Run);
57 _module = myAsmBuilder.DefineDynamicModule("__MetadataTypesModule", false);
60 static public Type GenerateMetadataType (Type type, StreamingContext context) {
61 /* SRE is not thread safe */
62 lock (monitor) {
63 return GenerateMetadataTypeInternal (type, context);
67 static public Type GenerateMetadataTypeInternal (Type type, StreamingContext context)
69 string name = type.Name + "__TypeMetadata";
70 string sufix = "";
71 int n = 0;
72 while (_module.GetType (name + sufix) != null)
73 sufix = (++n).ToString();
75 name += sufix;
77 MemberInfo[] members = FormatterServices.GetSerializableMembers (type, context);
79 TypeBuilder typeBuilder = _module.DefineType (name, TypeAttributes.Public, typeof(ClrTypeMetadata));
81 Type[] parameters;
82 MethodBuilder method;
83 ILGenerator gen;
85 // *********************
86 // METHOD public constructor (Type t): base (t);
88 parameters = Type.EmptyTypes;
90 ConstructorBuilder ctor = typeBuilder.DefineConstructor (MethodAttributes.Public, CallingConventions.Standard, parameters);
91 ConstructorInfo baseCtor = typeof(ClrTypeMetadata).GetConstructor (new Type[] { typeof(Type) });
92 gen = ctor.GetILGenerator();
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);
100 // *********************
101 // METHOD public override void WriteAssemblies (ObjectWriter ow, BinaryWriter writer);
103 parameters = new Type[] { typeof(ObjectWriter), typeof(BinaryWriter) };
104 method = typeBuilder.DefineMethod ("WriteAssemblies", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), parameters);
105 gen = method.GetILGenerator();
107 foreach (FieldInfo field in members)
109 Type memberType = field.FieldType;
110 while (memberType.IsArray)
111 memberType = memberType.GetElementType();
113 if (memberType.Assembly != ObjectWriter.CorlibAssembly)
115 // EMIT ow.WriteAssembly (writer, memberType.Assembly);
116 gen.Emit (OpCodes.Ldarg_1);
117 gen.Emit (OpCodes.Ldarg_2);
118 #if NET_4_0
119 EmitLoadType (gen, memberType);
120 gen.EmitCall (OpCodes.Callvirt, typeof(ObjectWriter).GetMethod("WriteTypeAssembly"), null);
121 #else
122 EmitLoadTypeAssembly (gen, memberType, field.Name);
123 gen.EmitCall (OpCodes.Callvirt, typeof(ObjectWriter).GetMethod("WriteAssembly"), null);
124 #endif
125 gen.Emit (OpCodes.Pop);
128 gen.Emit(OpCodes.Ret);
129 typeBuilder.DefineMethodOverride (method, typeof(TypeMetadata).GetMethod ("WriteAssemblies"));
131 // *********************
132 // METHOD public override void WriteTypeData (ObjectWriter ow, BinaryWriter writer, bool writeTypes);
134 parameters = new Type[] { typeof(ObjectWriter), typeof(BinaryWriter), typeof(bool) };
135 method = typeBuilder.DefineMethod ("WriteTypeData", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), parameters);
136 gen = method.GetILGenerator();
138 // EMIT writer.Write (members.Length);
139 gen.Emit (OpCodes.Ldarg_2);
140 gen.Emit (OpCodes.Ldc_I4, members.Length);
141 EmitWrite (gen, typeof(int));
143 // Names of fields
144 foreach (FieldInfo field in members)
146 // EMIT writer.Write (name);
147 gen.Emit (OpCodes.Ldarg_2);
148 gen.Emit (OpCodes.Ldstr, field.Name);
149 EmitWrite (gen, typeof(string));
152 Label falseLabel = gen.DefineLabel ();
153 gen.Emit (OpCodes.Ldarg_3);
154 gen.Emit (OpCodes.Brfalse, falseLabel);
156 // Types of fields
157 foreach (FieldInfo field in members)
159 // EMIT writer.Write ((byte) ObjectWriter.GetTypeTag (type));
160 gen.Emit (OpCodes.Ldarg_2);
161 gen.Emit (OpCodes.Ldc_I4_S, (byte) ObjectWriter.GetTypeTag (field.FieldType));
162 EmitWrite (gen, typeof(byte));
165 // Type specs of fields
166 foreach (FieldInfo field in members)
168 // EMIT ow.WriteTypeSpec (writer, field.FieldType);
169 EmitWriteTypeSpec (gen, field.FieldType, field.Name);
171 gen.MarkLabel(falseLabel);
173 gen.Emit(OpCodes.Ret);
174 typeBuilder.DefineMethodOverride (method, typeof(TypeMetadata).GetMethod ("WriteTypeData"));
176 // *********************
177 // METHOD public override void WriteObjectData (ObjectWriter ow, BinaryWriter writer, object data)
179 parameters = new Type[] { typeof(ObjectWriter), typeof(BinaryWriter), typeof(object) };
180 method = typeBuilder.DefineMethod ("WriteObjectData", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), parameters);
181 gen = method.GetILGenerator();
183 LocalBuilder localBuilder = gen.DeclareLocal (type);
184 OpCode lload = OpCodes.Ldloc;
186 gen.Emit (OpCodes.Ldarg_3);
187 if (type.IsValueType)
189 gen.Emit (OpCodes.Unbox, type);
190 LoadFromPtr (gen, type);
191 lload = OpCodes.Ldloca_S;
193 else
194 gen.Emit (OpCodes.Castclass, type);
196 gen.Emit (OpCodes.Stloc, localBuilder);
198 foreach (FieldInfo field in members)
200 // EMIT ow.WriteValue (writer, ((FieldInfo)members[n]).FieldType, values[n]);
201 Type ftype = field.FieldType;
202 if (BinaryCommon.IsPrimitive (ftype))
204 gen.Emit (OpCodes.Ldarg_2);
205 gen.Emit (lload, localBuilder);
206 if (ftype == typeof(DateTime) || ftype == typeof(TimeSpan) || ftype == typeof(decimal))
207 gen.Emit (OpCodes.Ldflda, field);
208 else
209 gen.Emit (OpCodes.Ldfld, field);
210 EmitWritePrimitiveValue (gen, ftype);
212 else
214 gen.Emit (OpCodes.Ldarg_1);
215 gen.Emit (OpCodes.Ldarg_2);
216 gen.Emit (OpCodes.Ldtoken, ftype);
217 gen.EmitCall (OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);
218 gen.Emit (lload, localBuilder);
219 gen.Emit (OpCodes.Ldfld, field);
220 if (ftype.IsValueType)
221 gen.Emit (OpCodes.Box, ftype);
222 gen.EmitCall (OpCodes.Call, typeof(ObjectWriter).GetMethod("WriteValue"), null);
226 gen.Emit(OpCodes.Ret);
227 typeBuilder.DefineMethodOverride (method, typeof(TypeMetadata).GetMethod ("WriteObjectData"));
229 return typeBuilder.CreateType();
232 public static void LoadFromPtr (ILGenerator ig, Type t)
234 if (t == typeof(int))
235 ig.Emit (OpCodes.Ldind_I4);
236 else if (t == typeof(uint))
237 ig.Emit (OpCodes.Ldind_U4);
238 else if (t == typeof(short))
239 ig.Emit (OpCodes.Ldind_I2);
240 else if (t == typeof(ushort))
241 ig.Emit (OpCodes.Ldind_U2);
242 else if (t == typeof(char))
243 ig.Emit (OpCodes.Ldind_U2);
244 else if (t == typeof(byte))
245 ig.Emit (OpCodes.Ldind_U1);
246 else if (t == typeof(sbyte))
247 ig.Emit (OpCodes.Ldind_I1);
248 else if (t == typeof(ulong))
249 ig.Emit (OpCodes.Ldind_I8);
250 else if (t == typeof(long))
251 ig.Emit (OpCodes.Ldind_I8);
252 else if (t == typeof(float))
253 ig.Emit (OpCodes.Ldind_R4);
254 else if (t == typeof(double))
255 ig.Emit (OpCodes.Ldind_R8);
256 else if (t == typeof(bool))
257 ig.Emit (OpCodes.Ldind_I1);
258 else if (t == typeof(IntPtr))
259 ig.Emit (OpCodes.Ldind_I);
260 else if (t.IsEnum) {
261 if (t == typeof(Enum))
262 ig.Emit (OpCodes.Ldind_Ref);
263 else
264 LoadFromPtr (ig, EnumToUnderlying (t));
265 } else if (t.IsValueType)
266 ig.Emit (OpCodes.Ldobj, t);
267 else
268 ig.Emit (OpCodes.Ldind_Ref);
271 static void EmitWriteTypeSpec (ILGenerator gen, Type type, string member)
273 // WARNING Keep in sync with WriteTypeSpec
275 switch (ObjectWriter.GetTypeTag (type))
277 case TypeTag.PrimitiveType:
278 // EMIT writer.Write (BinaryCommon.GetTypeCode (type));
279 gen.Emit (OpCodes.Ldarg_2);
280 gen.Emit (OpCodes.Ldc_I4_S, (byte) BinaryCommon.GetTypeCode (type));
281 EmitWrite (gen, typeof(byte));
282 break;
284 case TypeTag.RuntimeType:
285 // EMIT writer.Write (type.FullName);
286 gen.Emit (OpCodes.Ldarg_2);
287 gen.Emit (OpCodes.Ldstr, type.FullName);
288 EmitWrite (gen, typeof(string));
289 break;
291 case TypeTag.GenericType:
292 // EMIT writer.Write (type.FullName);
293 gen.Emit (OpCodes.Ldarg_2);
294 gen.Emit (OpCodes.Ldstr, type.FullName);
295 EmitWrite (gen, typeof(string));
297 // EMIT writer.Write ((int)ow.GetAssemblyId (type.Assembly));
298 gen.Emit (OpCodes.Ldarg_2);
299 gen.Emit (OpCodes.Ldarg_1);
300 #if NET_4_0
301 EmitLoadType (gen, type);
302 gen.EmitCall (OpCodes.Callvirt, typeof(GetForwardedAttribute).GetMethod("GetAssemblyName"), null);
303 gen.EmitCall (OpCodes.Callvirt, typeof(ObjectWriter).GetMethod("GetAssemblyNameId"), null);
304 #else
305 EmitLoadTypeAssembly (gen, type, member);
306 gen.EmitCall (OpCodes.Callvirt, typeof(ObjectWriter).GetMethod("GetAssemblyId"), null);
307 #endif
308 gen.Emit (OpCodes.Conv_I4);
309 EmitWrite (gen, typeof(int));
310 break;
312 case TypeTag.ArrayOfPrimitiveType:
313 // EMIT writer.Write (BinaryCommon.GetTypeCode (type.GetElementType()));
314 gen.Emit (OpCodes.Ldarg_2);
315 gen.Emit (OpCodes.Ldc_I4_S, (byte) BinaryCommon.GetTypeCode (type.GetElementType()));
316 EmitWrite (gen, typeof(byte));
317 break;
319 default:
320 // Type spec not needed
321 break;
325 static void EmitLoadTypeAssembly (ILGenerator gen, Type type, string member)
327 gen.Emit (OpCodes.Ldtoken, type);
328 gen.EmitCall (OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);
329 gen.EmitCall (OpCodes.Callvirt, typeof(Type).GetProperty("Assembly").GetGetMethod(), null);
332 static void EmitLoadType (ILGenerator gen, Type type)
334 gen.Emit (OpCodes.Ldtoken, type);
335 gen.EmitCall (OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);
338 static void EmitWrite (ILGenerator gen, Type type)
340 gen.EmitCall (OpCodes.Callvirt, typeof(BinaryWriter).GetMethod("Write", new Type[] { type }), null);
343 public static void EmitWritePrimitiveValue (ILGenerator gen, Type type)
345 switch (Type.GetTypeCode (type))
347 case TypeCode.Boolean:
348 case TypeCode.Byte:
349 case TypeCode.Char:
350 case TypeCode.Double:
351 case TypeCode.Int16:
352 case TypeCode.Int32:
353 case TypeCode.Int64:
354 case TypeCode.SByte:
355 case TypeCode.Single:
356 case TypeCode.UInt16:
357 case TypeCode.UInt32:
358 case TypeCode.UInt64:
359 case TypeCode.String:
360 EmitWrite (gen, type);
361 break;
363 case TypeCode.Decimal:
364 // writer.Write (val.ToString (CultureInfo.InvariantCulture));
365 gen.EmitCall (OpCodes.Call, typeof(CultureInfo).GetProperty("InvariantCulture").GetGetMethod(), null);
366 gen.EmitCall (OpCodes.Call, typeof(Decimal).GetMethod("ToString", new Type[] {typeof(IFormatProvider)}), null);
367 EmitWrite (gen, typeof(string));
368 break;
370 case TypeCode.DateTime:
371 gen.EmitCall (OpCodes.Call, typeof(DateTime).GetMethod("ToBinary", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance), null);
372 EmitWrite (gen, typeof(long));
373 break;
375 default:
376 if (type == typeof (TimeSpan)) {
377 gen.EmitCall (OpCodes.Call, typeof(TimeSpan).GetProperty("Ticks").GetGetMethod(), null);
378 EmitWrite (gen, typeof(long));
380 else
381 throw new NotSupportedException ("Unsupported primitive type: " + type.FullName);
382 break;
387 // This is needed, because enumerations from assemblies
388 // do not report their underlyingtype, but they report
389 // themselves
391 public static Type EnumToUnderlying (Type t)
393 TypeCode tc = Type.GetTypeCode (t);
395 switch (tc){
396 case TypeCode.Boolean:
397 return typeof (bool);
398 case TypeCode.Byte:
399 return typeof (byte);
400 case TypeCode.SByte:
401 return typeof (sbyte);
402 case TypeCode.Char:
403 return typeof (char);
404 case TypeCode.Int16:
405 return typeof (short);
406 case TypeCode.UInt16:
407 return typeof (ushort);
408 case TypeCode.Int32:
409 return typeof (int);
410 case TypeCode.UInt32:
411 return typeof (uint);
412 case TypeCode.Int64:
413 return typeof (long);
414 case TypeCode.UInt64:
415 return typeof (ulong);
417 throw new Exception ("Unhandled typecode in enum " + tc + " from " + t.AssemblyQualifiedName);
422 #endif