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.
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
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 */
63 return GenerateMetadataTypeInternal (type
, context
);
67 static public Type
GenerateMetadataTypeInternal (Type type
, StreamingContext context
)
69 string name
= type
.Name
+ "__TypeMetadata";
72 while (_module
.GetType (name
+ sufix
) != null)
73 sufix
= (++n
).ToString();
77 MemberInfo
[] members
= FormatterServices
.GetSerializableMembers (type
, context
);
79 TypeBuilder typeBuilder
= _module
.DefineType (name
, TypeAttributes
.Public
, typeof(ClrTypeMetadata
));
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
);
119 EmitLoadType (gen
, memberType
);
120 gen
.EmitCall (OpCodes
.Callvirt
, typeof(ObjectWriter
).GetMethod("WriteTypeAssembly"), null);
122 EmitLoadTypeAssembly (gen
, memberType
, field
.Name
);
123 gen
.EmitCall (OpCodes
.Callvirt
, typeof(ObjectWriter
).GetMethod("WriteAssembly"), null);
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));
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
);
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
;
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
);
209 gen
.Emit (OpCodes
.Ldfld
, field
);
210 EmitWritePrimitiveValue (gen
, ftype
);
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
);
261 if (t
== typeof(Enum
))
262 ig
.Emit (OpCodes
.Ldind_Ref
);
264 LoadFromPtr (ig
, EnumToUnderlying (t
));
265 } else if (t
.IsValueType
)
266 ig
.Emit (OpCodes
.Ldobj
, t
);
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));
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));
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
);
301 EmitLoadType (gen
, type
);
302 gen
.EmitCall (OpCodes
.Callvirt
, typeof(GetForwardedAttribute
).GetMethod("GetAssemblyName"), null);
303 gen
.EmitCall (OpCodes
.Callvirt
, typeof(ObjectWriter
).GetMethod("GetAssemblyNameId"), null);
305 EmitLoadTypeAssembly (gen
, type
, member
);
306 gen
.EmitCall (OpCodes
.Callvirt
, typeof(ObjectWriter
).GetMethod("GetAssemblyId"), null);
308 gen
.Emit (OpCodes
.Conv_I4
);
309 EmitWrite (gen
, typeof(int));
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));
320 // Type spec not needed
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
:
350 case TypeCode
.Double
:
355 case TypeCode
.Single
:
356 case TypeCode
.UInt16
:
357 case TypeCode
.UInt32
:
358 case TypeCode
.UInt64
:
359 case TypeCode
.String
:
360 EmitWrite (gen
, type
);
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));
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));
376 if (type
== typeof (TimeSpan
)) {
377 gen
.EmitCall (OpCodes
.Call
, typeof(TimeSpan
).GetProperty("Ticks").GetGetMethod(), null);
378 EmitWrite (gen
, typeof(long));
381 throw new NotSupportedException ("Unsupported primitive type: " + type
.FullName
);
387 // This is needed, because enumerations from assemblies
388 // do not report their underlyingtype, but they report
391 public static Type
EnumToUnderlying (Type t
)
393 TypeCode tc
= Type
.GetTypeCode (t
);
396 case TypeCode
.Boolean
:
397 return typeof (bool);
399 return typeof (byte);
401 return typeof (sbyte);
403 return typeof (char);
405 return typeof (short);
406 case TypeCode
.UInt16
:
407 return typeof (ushort);
410 case TypeCode
.UInt32
:
411 return typeof (uint);
413 return typeof (long);
414 case TypeCode
.UInt64
:
415 return typeof (ulong);
417 throw new Exception ("Unhandled typecode in enum " + tc
+ " from " + t
.AssemblyQualifiedName
);