4 // Lluis Sanchez Gual (lluis@ximian.com)
6 // (C) 2004 Novell, Inc
10 using System.Collections;
11 using System.Runtime.Serialization;
12 using System.Reflection;
13 using System.Reflection.Emit;
14 using System.Globalization;
16 namespace System.Runtime.Serialization.Formatters.Binary
18 internal class CodeGenerator
22 static ModuleBuilder _module;
24 static public Type GenerateMetadataType (Type type, StreamingContext context)
28 lock (typeof (ObjectWriter))
30 if (_module == null) {
31 AppDomain myDomain = System.Threading.Thread.GetDomain();
32 AssemblyName myAsmName = new AssemblyName();
33 myAsmName.Name = "__MetadataTypes";
35 AssemblyBuilder myAsmBuilder = myDomain.DefineDynamicAssembly (myAsmName, AssemblyBuilderAccess.Run);
36 _module = myAsmBuilder.DefineDynamicModule("__MetadataTypesModule", true);
41 string name = type.Name + "__TypeMetadata";
44 while (_module.GetType (name + sufix) != null)
45 sufix = (++n).ToString();
49 MemberInfo[] members = FormatterServices.GetSerializableMembers (type, context);
51 TypeBuilder typeBuilder = _module.DefineType (name, TypeAttributes.Public, typeof(TypeMetadata));
57 // *********************
58 // METHOD public constructor (Type t): base (t);
60 parameters = new Type[0];
62 ConstructorBuilder ctor = typeBuilder.DefineConstructor (MethodAttributes.Public, CallingConventions.Standard, parameters);
63 ConstructorInfo baseCtor = typeof(TypeMetadata).GetConstructor (new Type[] { typeof(Type) });
64 gen = ctor.GetILGenerator();
66 gen.Emit (OpCodes.Ldarg_0);
67 gen.Emit (OpCodes.Ldtoken, type);
68 gen.EmitCall (OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);
69 gen.Emit (OpCodes.Call, baseCtor);
70 gen.Emit (OpCodes.Ret);
72 // *********************
73 // METHOD public override void WriteAssemblies (ObjectWriter ow, BinaryWriter writer);
75 parameters = new Type[] { typeof(ObjectWriter), typeof(BinaryWriter) };
76 method = typeBuilder.DefineMethod ("WriteAssemblies", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), parameters);
77 gen = method.GetILGenerator();
79 foreach (FieldInfo field in members)
81 Type memberType = field.FieldType;
82 while (memberType.IsArray)
83 memberType = memberType.GetElementType();
85 if (memberType.Assembly != ObjectWriter.CorlibAssembly)
87 // EMIT ow.WriteAssembly (writer, memberType.Assembly);
88 gen.Emit (OpCodes.Ldarg_1);
89 gen.Emit (OpCodes.Ldarg_2);
90 EmitLoadTypeAssembly (gen, memberType, field.Name);
91 gen.EmitCall (OpCodes.Callvirt, typeof(ObjectWriter).GetMethod("WriteAssembly"), null);
92 gen.Emit (OpCodes.Pop);
95 gen.Emit(OpCodes.Ret);
96 typeBuilder.DefineMethodOverride (method, typeof(TypeMetadata).GetMethod ("WriteAssemblies"));
98 // *********************
99 // METHOD public override void WriteTypeData (ObjectWriter ow, BinaryWriter writer);
101 parameters = new Type[] { typeof(ObjectWriter), typeof(BinaryWriter) };
102 method = typeBuilder.DefineMethod ("WriteTypeData", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), parameters);
103 gen = method.GetILGenerator();
105 // EMIT writer.Write (members.Length);
106 gen.Emit (OpCodes.Ldarg_2);
107 gen.Emit (OpCodes.Ldc_I4, members.Length);
108 EmitWrite (gen, typeof(int));
111 foreach (FieldInfo field in members)
113 // EMIT writer.Write (name);
114 gen.Emit (OpCodes.Ldarg_2);
116 if (field.DeclaringType == type)
117 gen.Emit (OpCodes.Ldstr, field.Name);
119 gen.Emit (OpCodes.Ldstr, field.DeclaringType.Name + "+" + field.Name);
120 EmitWrite (gen, typeof(string));
124 foreach (FieldInfo field in members)
126 // EMIT writer.Write ((byte) ObjectWriter.GetTypeTag (type));
127 gen.Emit (OpCodes.Ldarg_2);
128 gen.Emit (OpCodes.Ldc_I4_S, (byte) ObjectWriter.GetTypeTag (field.FieldType));
129 EmitWrite (gen, typeof(byte));
132 // Type specs of fields
133 foreach (FieldInfo field in members)
135 // EMIT ow.WriteTypeSpec (writer, field.FieldType);
136 EmitWriteTypeSpec (gen, field.FieldType, field.Name);
139 gen.Emit(OpCodes.Ret);
140 typeBuilder.DefineMethodOverride (method, typeof(TypeMetadata).GetMethod ("WriteTypeData"));
142 // *********************
143 // METHOD public override void WriteObjectData (ObjectWriter ow, BinaryWriter writer, object data)
145 parameters = new Type[] { typeof(ObjectWriter), typeof(BinaryWriter), typeof(object) };
146 method = typeBuilder.DefineMethod ("WriteObjectData", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), parameters);
147 gen = method.GetILGenerator();
149 LocalBuilder localBuilder = gen.DeclareLocal (type);
150 OpCode lload = OpCodes.Ldloc;
152 gen.Emit (OpCodes.Ldarg_3);
153 if (type.IsValueType)
155 gen.Emit (OpCodes.Unbox, type);
156 LoadFromPtr (gen, type);
157 lload = OpCodes.Ldloca_S;
160 gen.Emit (OpCodes.Castclass, type);
162 gen.Emit (OpCodes.Stloc, localBuilder);
164 foreach (FieldInfo field in members)
166 // EMIT ow.WriteValue (writer, ((FieldInfo)members[n]).FieldType, values[n]);
167 Type ftype = field.FieldType;
168 if (BinaryCommon.IsPrimitive (ftype))
170 gen.Emit (OpCodes.Ldarg_2);
171 gen.Emit (lload, localBuilder);
172 if (ftype == typeof(DateTime) || ftype == typeof(TimeSpan) || ftype == typeof(decimal))
173 gen.Emit (OpCodes.Ldflda, field);
175 gen.Emit (OpCodes.Ldfld, field);
176 EmitWritePrimitiveValue (gen, ftype);
180 gen.Emit (OpCodes.Ldarg_1);
181 gen.Emit (OpCodes.Ldarg_2);
182 gen.Emit (OpCodes.Ldtoken, ftype);
183 gen.EmitCall (OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);
184 gen.Emit (lload, localBuilder);
185 gen.Emit (OpCodes.Ldfld, field);
186 if (ftype.IsValueType)
187 gen.Emit (OpCodes.Box, ftype);
188 gen.EmitCall (OpCodes.Call, typeof(ObjectWriter).GetMethod("WriteValue"), null);
192 gen.Emit(OpCodes.Ret);
193 typeBuilder.DefineMethodOverride (method, typeof(TypeMetadata).GetMethod ("WriteObjectData"));
195 return typeBuilder.CreateType();
198 public static void LoadFromPtr (ILGenerator ig, Type t)
200 if (t == typeof(int))
201 ig.Emit (OpCodes.Ldind_I4);
202 else if (t == typeof(uint))
203 ig.Emit (OpCodes.Ldind_U4);
204 else if (t == typeof(short))
205 ig.Emit (OpCodes.Ldind_I2);
206 else if (t == typeof(ushort))
207 ig.Emit (OpCodes.Ldind_U2);
208 else if (t == typeof(char))
209 ig.Emit (OpCodes.Ldind_U2);
210 else if (t == typeof(byte))
211 ig.Emit (OpCodes.Ldind_U1);
212 else if (t == typeof(sbyte))
213 ig.Emit (OpCodes.Ldind_I1);
214 else if (t == typeof(ulong))
215 ig.Emit (OpCodes.Ldind_I8);
216 else if (t == typeof(long))
217 ig.Emit (OpCodes.Ldind_I8);
218 else if (t == typeof(float))
219 ig.Emit (OpCodes.Ldind_R4);
220 else if (t == typeof(double))
221 ig.Emit (OpCodes.Ldind_R8);
222 else if (t == typeof(bool))
223 ig.Emit (OpCodes.Ldind_I1);
224 else if (t == typeof(IntPtr))
225 ig.Emit (OpCodes.Ldind_I);
227 if (t == typeof(Enum))
228 ig.Emit (OpCodes.Ldind_Ref);
230 LoadFromPtr (ig, t.UnderlyingSystemType);
231 } else if (t.IsValueType)
232 ig.Emit (OpCodes.Ldobj, t);
234 ig.Emit (OpCodes.Ldind_Ref);
237 public static void EmitWriteTypeSpec (ILGenerator gen, Type type, string member)
239 // WARNING Keep in sync with WriteTypeSpec
241 switch (ObjectWriter.GetTypeTag (type))
243 case TypeTag.PrimitiveType:
244 // EMIT writer.Write (BinaryCommon.GetTypeCode (type));
245 gen.Emit (OpCodes.Ldarg_2);
246 gen.Emit (OpCodes.Ldc_I4_S, (byte) BinaryCommon.GetTypeCode (type));
247 EmitWrite (gen, typeof(byte));
250 case TypeTag.RuntimeType:
251 // EMIT writer.Write (type.FullName);
252 gen.Emit (OpCodes.Ldarg_2);
253 gen.Emit (OpCodes.Ldstr, type.FullName);
254 EmitWrite (gen, typeof(string));
257 case TypeTag.GenericType:
258 // EMIT writer.Write (type.FullName);
259 gen.Emit (OpCodes.Ldarg_2);
260 gen.Emit (OpCodes.Ldstr, type.FullName);
261 EmitWrite (gen, typeof(string));
263 // EMIT writer.Write ((int)ow.GetAssemblyId (type.Assembly));
264 gen.Emit (OpCodes.Ldarg_2);
265 gen.Emit (OpCodes.Ldarg_1);
266 EmitLoadTypeAssembly (gen, type, member);
267 gen.EmitCall (OpCodes.Callvirt, typeof(ObjectWriter).GetMethod("GetAssemblyId"), null);
268 gen.Emit (OpCodes.Conv_I4);
269 EmitWrite (gen, typeof(int));
272 case TypeTag.ArrayOfPrimitiveType:
273 // EMIT writer.Write (BinaryCommon.GetTypeCode (type.GetElementType()));
274 gen.Emit (OpCodes.Ldarg_2);
275 gen.Emit (OpCodes.Ldc_I4_S, (byte) BinaryCommon.GetTypeCode (type.GetElementType()));
276 EmitWrite (gen, typeof(byte));
280 // Type spec not needed
285 static void EmitLoadTypeAssembly (ILGenerator gen, Type type, string member)
287 gen.Emit (OpCodes.Ldtoken, type);
288 gen.EmitCall (OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);
289 gen.EmitCall (OpCodes.Callvirt, typeof(Type).GetProperty("Assembly").GetGetMethod(), null);
292 static void EmitWrite (ILGenerator gen, Type type)
294 gen.EmitCall (OpCodes.Callvirt, typeof(BinaryWriter).GetMethod("Write", new Type[] { type }), null);
297 public static void EmitWritePrimitiveValue (ILGenerator gen, Type type)
299 switch (Type.GetTypeCode (type))
301 case TypeCode.Boolean:
304 case TypeCode.Double:
309 case TypeCode.Single:
310 case TypeCode.UInt16:
311 case TypeCode.UInt32:
312 case TypeCode.UInt64:
313 case TypeCode.String:
314 EmitWrite (gen, type);
317 case TypeCode.Decimal:
318 // writer.Write (val.ToString (CultureInfo.InvariantCulture));
319 gen.EmitCall (OpCodes.Call, typeof(CultureInfo).GetProperty("InvariantCulture").GetGetMethod(), null);
320 gen.EmitCall (OpCodes.Call, typeof(Decimal).GetMethod("ToString", new Type[] {typeof(IFormatProvider)}), null);
321 EmitWrite (gen, typeof(string));
324 case TypeCode.DateTime:
325 gen.EmitCall (OpCodes.Call, typeof(DateTime).GetProperty("Ticks").GetGetMethod(), null);
326 EmitWrite (gen, typeof(long));
330 if (type == typeof (TimeSpan)) {
331 gen.EmitCall (OpCodes.Call, typeof(TimeSpan).GetProperty("Ticks").GetGetMethod(), null);
332 EmitWrite (gen, typeof(long));
335 throw new NotSupportedException ("Unsupported primitive type: " + type.FullName);