4 // Lluis Sanchez Gual (lluis@ximian.com)
6 // (C) 2004 Novell, Inc
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:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
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.
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
45 static ModuleBuilder _module;
47 static CodeGenerator ()
49 AppDomain myDomain = System.Threading.Thread.GetDomain();
50 AssemblyName myAsmName = new AssemblyName();
51 myAsmName.Name = "__MetadataTypes";
53 AssemblyBuilder myAsmBuilder = myDomain.DefineInternalDynamicAssembly (myAsmName, AssemblyBuilderAccess.Run);
54 _module = myAsmBuilder.DefineDynamicModule("__MetadataTypesModule", false);
57 static public Type GenerateMetadataType (Type type, StreamingContext context)
59 string name = type.Name + "__TypeMetadata";
62 while (_module.GetType (name + sufix) != null)
63 sufix = (++n).ToString();
67 MemberInfo[] members = FormatterServices.GetSerializableMembers (type, context);
69 TypeBuilder typeBuilder = _module.DefineType (name, TypeAttributes.Public, typeof(ClrTypeMetadata));
75 // *********************
76 // METHOD public constructor (Type t): base (t);
78 parameters = new Type[0];
80 ConstructorBuilder ctor = typeBuilder.DefineConstructor (MethodAttributes.Public, CallingConventions.Standard, parameters);
81 ConstructorInfo baseCtor = typeof(ClrTypeMetadata).GetConstructor (new Type[] { typeof(Type) });
82 gen = ctor.GetILGenerator();
84 gen.Emit (OpCodes.Ldarg_0);
85 gen.Emit (OpCodes.Ldtoken, type);
86 gen.EmitCall (OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);
87 gen.Emit (OpCodes.Call, baseCtor);
88 gen.Emit (OpCodes.Ret);
90 // *********************
91 // METHOD public override void WriteAssemblies (ObjectWriter ow, BinaryWriter writer);
93 parameters = new Type[] { typeof(ObjectWriter), typeof(BinaryWriter) };
94 method = typeBuilder.DefineMethod ("WriteAssemblies", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), parameters);
95 gen = method.GetILGenerator();
97 foreach (FieldInfo field in members)
99 Type memberType = field.FieldType;
100 while (memberType.IsArray)
101 memberType = memberType.GetElementType();
103 if (memberType.Assembly != ObjectWriter.CorlibAssembly)
105 // EMIT ow.WriteAssembly (writer, memberType.Assembly);
106 gen.Emit (OpCodes.Ldarg_1);
107 gen.Emit (OpCodes.Ldarg_2);
108 EmitLoadTypeAssembly (gen, memberType, field.Name);
109 gen.EmitCall (OpCodes.Callvirt, typeof(ObjectWriter).GetMethod("WriteAssembly"), null);
110 gen.Emit (OpCodes.Pop);
113 gen.Emit(OpCodes.Ret);
114 typeBuilder.DefineMethodOverride (method, typeof(TypeMetadata).GetMethod ("WriteAssemblies"));
116 // *********************
117 // METHOD public override void WriteTypeData (ObjectWriter ow, BinaryWriter writer, bool writeTypes);
119 parameters = new Type[] { typeof(ObjectWriter), typeof(BinaryWriter), typeof(bool) };
120 method = typeBuilder.DefineMethod ("WriteTypeData", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), parameters);
121 gen = method.GetILGenerator();
123 // EMIT writer.Write (members.Length);
124 gen.Emit (OpCodes.Ldarg_2);
125 gen.Emit (OpCodes.Ldc_I4, members.Length);
126 EmitWrite (gen, typeof(int));
129 foreach (FieldInfo field in members)
131 // EMIT writer.Write (name);
132 gen.Emit (OpCodes.Ldarg_2);
133 gen.Emit (OpCodes.Ldstr, field.Name);
134 EmitWrite (gen, typeof(string));
137 Label falseLabel = gen.DefineLabel ();
138 gen.Emit (OpCodes.Ldarg_3);
139 gen.Emit (OpCodes.Brfalse, falseLabel);
142 foreach (FieldInfo field in members)
144 // EMIT writer.Write ((byte) ObjectWriter.GetTypeTag (type));
145 gen.Emit (OpCodes.Ldarg_2);
146 gen.Emit (OpCodes.Ldc_I4_S, (byte) ObjectWriter.GetTypeTag (field.FieldType));
147 EmitWrite (gen, typeof(byte));
150 // Type specs of fields
151 foreach (FieldInfo field in members)
153 // EMIT ow.WriteTypeSpec (writer, field.FieldType);
154 EmitWriteTypeSpec (gen, field.FieldType, field.Name);
156 gen.MarkLabel(falseLabel);
158 gen.Emit(OpCodes.Ret);
159 typeBuilder.DefineMethodOverride (method, typeof(TypeMetadata).GetMethod ("WriteTypeData"));
161 // *********************
162 // METHOD public override void WriteObjectData (ObjectWriter ow, BinaryWriter writer, object data)
164 parameters = new Type[] { typeof(ObjectWriter), typeof(BinaryWriter), typeof(object) };
165 method = typeBuilder.DefineMethod ("WriteObjectData", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), parameters);
166 gen = method.GetILGenerator();
168 LocalBuilder localBuilder = gen.DeclareLocal (type);
169 OpCode lload = OpCodes.Ldloc;
171 gen.Emit (OpCodes.Ldarg_3);
172 if (type.IsValueType)
174 gen.Emit (OpCodes.Unbox, type);
175 LoadFromPtr (gen, type);
176 lload = OpCodes.Ldloca_S;
179 gen.Emit (OpCodes.Castclass, type);
181 gen.Emit (OpCodes.Stloc, localBuilder);
183 foreach (FieldInfo field in members)
185 // EMIT ow.WriteValue (writer, ((FieldInfo)members[n]).FieldType, values[n]);
186 Type ftype = field.FieldType;
187 if (BinaryCommon.IsPrimitive (ftype))
189 gen.Emit (OpCodes.Ldarg_2);
190 gen.Emit (lload, localBuilder);
191 if (ftype == typeof(DateTime) || ftype == typeof(TimeSpan) || ftype == typeof(decimal))
192 gen.Emit (OpCodes.Ldflda, field);
194 gen.Emit (OpCodes.Ldfld, field);
195 EmitWritePrimitiveValue (gen, ftype);
199 gen.Emit (OpCodes.Ldarg_1);
200 gen.Emit (OpCodes.Ldarg_2);
201 gen.Emit (OpCodes.Ldtoken, ftype);
202 gen.EmitCall (OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);
203 gen.Emit (lload, localBuilder);
204 gen.Emit (OpCodes.Ldfld, field);
205 if (ftype.IsValueType)
206 gen.Emit (OpCodes.Box, ftype);
207 gen.EmitCall (OpCodes.Call, typeof(ObjectWriter).GetMethod("WriteValue"), null);
211 gen.Emit(OpCodes.Ret);
212 typeBuilder.DefineMethodOverride (method, typeof(TypeMetadata).GetMethod ("WriteObjectData"));
214 return typeBuilder.CreateType();
217 public static void LoadFromPtr (ILGenerator ig, Type t)
219 if (t == typeof(int))
220 ig.Emit (OpCodes.Ldind_I4);
221 else if (t == typeof(uint))
222 ig.Emit (OpCodes.Ldind_U4);
223 else if (t == typeof(short))
224 ig.Emit (OpCodes.Ldind_I2);
225 else if (t == typeof(ushort))
226 ig.Emit (OpCodes.Ldind_U2);
227 else if (t == typeof(char))
228 ig.Emit (OpCodes.Ldind_U2);
229 else if (t == typeof(byte))
230 ig.Emit (OpCodes.Ldind_U1);
231 else if (t == typeof(sbyte))
232 ig.Emit (OpCodes.Ldind_I1);
233 else if (t == typeof(ulong))
234 ig.Emit (OpCodes.Ldind_I8);
235 else if (t == typeof(long))
236 ig.Emit (OpCodes.Ldind_I8);
237 else if (t == typeof(float))
238 ig.Emit (OpCodes.Ldind_R4);
239 else if (t == typeof(double))
240 ig.Emit (OpCodes.Ldind_R8);
241 else if (t == typeof(bool))
242 ig.Emit (OpCodes.Ldind_I1);
243 else if (t == typeof(IntPtr))
244 ig.Emit (OpCodes.Ldind_I);
246 if (t == typeof(Enum))
247 ig.Emit (OpCodes.Ldind_Ref);
249 LoadFromPtr (ig, EnumToUnderlying (t));
250 } else if (t.IsValueType)
251 ig.Emit (OpCodes.Ldobj, t);
253 ig.Emit (OpCodes.Ldind_Ref);
256 public static void EmitWriteTypeSpec (ILGenerator gen, Type type, string member)
258 // WARNING Keep in sync with WriteTypeSpec
260 switch (ObjectWriter.GetTypeTag (type))
262 case TypeTag.PrimitiveType:
263 // EMIT writer.Write (BinaryCommon.GetTypeCode (type));
264 gen.Emit (OpCodes.Ldarg_2);
265 gen.Emit (OpCodes.Ldc_I4_S, (byte) BinaryCommon.GetTypeCode (type));
266 EmitWrite (gen, typeof(byte));
269 case TypeTag.RuntimeType:
270 // EMIT writer.Write (type.FullName);
271 gen.Emit (OpCodes.Ldarg_2);
272 gen.Emit (OpCodes.Ldstr, type.FullName);
273 EmitWrite (gen, typeof(string));
276 case TypeTag.GenericType:
277 // EMIT writer.Write (type.FullName);
278 gen.Emit (OpCodes.Ldarg_2);
279 gen.Emit (OpCodes.Ldstr, type.FullName);
280 EmitWrite (gen, typeof(string));
282 // EMIT writer.Write ((int)ow.GetAssemblyId (type.Assembly));
283 gen.Emit (OpCodes.Ldarg_2);
284 gen.Emit (OpCodes.Ldarg_1);
285 EmitLoadTypeAssembly (gen, type, member);
286 gen.EmitCall (OpCodes.Callvirt, typeof(ObjectWriter).GetMethod("GetAssemblyId"), null);
287 gen.Emit (OpCodes.Conv_I4);
288 EmitWrite (gen, typeof(int));
291 case TypeTag.ArrayOfPrimitiveType:
292 // EMIT writer.Write (BinaryCommon.GetTypeCode (type.GetElementType()));
293 gen.Emit (OpCodes.Ldarg_2);
294 gen.Emit (OpCodes.Ldc_I4_S, (byte) BinaryCommon.GetTypeCode (type.GetElementType()));
295 EmitWrite (gen, typeof(byte));
299 // Type spec not needed
304 static void EmitLoadTypeAssembly (ILGenerator gen, Type type, string member)
306 gen.Emit (OpCodes.Ldtoken, type);
307 gen.EmitCall (OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);
308 gen.EmitCall (OpCodes.Callvirt, typeof(Type).GetProperty("Assembly").GetGetMethod(), null);
311 static void EmitWrite (ILGenerator gen, Type type)
313 gen.EmitCall (OpCodes.Callvirt, typeof(BinaryWriter).GetMethod("Write", new Type[] { type }), null);
316 public static void EmitWritePrimitiveValue (ILGenerator gen, Type type)
318 switch (Type.GetTypeCode (type))
320 case TypeCode.Boolean:
323 case TypeCode.Double:
328 case TypeCode.Single:
329 case TypeCode.UInt16:
330 case TypeCode.UInt32:
331 case TypeCode.UInt64:
332 case TypeCode.String:
333 EmitWrite (gen, type);
336 case TypeCode.Decimal:
337 // writer.Write (val.ToString (CultureInfo.InvariantCulture));
338 gen.EmitCall (OpCodes.Call, typeof(CultureInfo).GetProperty("InvariantCulture").GetGetMethod(), null);
339 gen.EmitCall (OpCodes.Call, typeof(Decimal).GetMethod("ToString", new Type[] {typeof(IFormatProvider)}), null);
340 EmitWrite (gen, typeof(string));
343 case TypeCode.DateTime:
344 gen.EmitCall (OpCodes.Call, typeof(DateTime).GetProperty("Ticks").GetGetMethod(), null);
345 EmitWrite (gen, typeof(long));
349 if (type == typeof (TimeSpan)) {
350 gen.EmitCall (OpCodes.Call, typeof(TimeSpan).GetProperty("Ticks").GetGetMethod(), null);
351 EmitWrite (gen, typeof(long));
354 throw new NotSupportedException ("Unsupported primitive type: " + type.FullName);
360 // This is needed, because enumerations from assemblies
361 // do not report their underlyingtype, but they report
364 public static Type EnumToUnderlying (Type t)
366 TypeCode tc = Type.GetTypeCode (t);
369 case TypeCode.Boolean:
370 return typeof (bool);
372 return typeof (byte);
374 return typeof (sbyte);
376 return typeof (char);
378 return typeof (short);
379 case TypeCode.UInt16:
380 return typeof (ushort);
383 case TypeCode.UInt32:
384 return typeof (uint);
386 return typeof (long);
387 case TypeCode.UInt64:
388 return typeof (ulong);
390 throw new Exception ("Unhandled typecode in enum " + tc + " from " + t.AssemblyQualifiedName);