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 public Type
GenerateMetadataType (Type type
, StreamingContext context
)
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";
67 while (_module
.GetType (name
+ sufix
) != null)
68 sufix
= (++n
).ToString();
72 MemberInfo
[] members
= FormatterServices
.GetSerializableMembers (type
, context
);
74 TypeBuilder typeBuilder
= _module
.DefineType (name
, TypeAttributes
.Public
, typeof(TypeMetadata
));
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));
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
);
142 gen
.Emit (OpCodes
.Ldstr
, field
.DeclaringType
.Name
+ "+" + field
.Name
);
143 EmitWrite (gen
, typeof(string));
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
;
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
);
198 gen
.Emit (OpCodes
.Ldfld
, field
);
199 EmitWritePrimitiveValue (gen
, ftype
);
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
);
250 if (t
== typeof(Enum
))
251 ig
.Emit (OpCodes
.Ldind_Ref
);
253 LoadFromPtr (ig
, t
.UnderlyingSystemType
);
254 } else if (t
.IsValueType
)
255 ig
.Emit (OpCodes
.Ldobj
, t
);
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));
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));
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));
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));
303 // Type spec not needed
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
:
327 case TypeCode
.Double
:
332 case TypeCode
.Single
:
333 case TypeCode
.UInt16
:
334 case TypeCode
.UInt32
:
335 case TypeCode
.UInt64
:
336 case TypeCode
.String
:
337 EmitWrite (gen
, type
);
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));
347 case TypeCode
.DateTime
:
348 gen
.EmitCall (OpCodes
.Call
, typeof(DateTime
).GetProperty("Ticks").GetGetMethod(), null);
349 EmitWrite (gen
, typeof(long));
353 if (type
== typeof (TimeSpan
)) {
354 gen
.EmitCall (OpCodes
.Call
, typeof(TimeSpan
).GetProperty("Ticks").GetGetMethod(), null);
355 EmitWrite (gen
, typeof(long));
358 throw new NotSupportedException ("Unsupported primitive type: " + type
.FullName
);