2 // -----------------------------------------------------------------------
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // -----------------------------------------------------------------------
7 using System.Collections;
8 using System.Collections.Generic;
9 using System.ComponentModel.Composition;
10 using System.Globalization;
13 using System.Reflection;
14 using System.Reflection.Emit;
16 namespace Microsoft.Internal
18 internal static class GenerationServices
20 // Type.GetTypeFromHandle
21 private static readonly MethodInfo _typeGetTypeFromHandleMethod = typeof(Type).GetMethod("GetTypeFromHandle");
24 // typeofs are pretty expensive, so we cache them statically
25 private static readonly Type TypeType = typeof(System.Type);
26 private static readonly Type StringType = typeof(System.String);
27 private static readonly Type CharType = typeof(System.Char);
28 private static readonly Type BooleanType = typeof(System.Boolean);
29 private static readonly Type ByteType = typeof(System.Byte);
30 private static readonly Type SByteType = typeof(System.SByte);
31 private static readonly Type Int16Type = typeof(System.Int16);
32 private static readonly Type UInt16Type = typeof(System.UInt16);
33 private static readonly Type Int32Type = typeof(System.Int32);
34 private static readonly Type UInt32Type = typeof(System.UInt32);
35 private static readonly Type Int64Type = typeof(System.Int64);
36 private static readonly Type UInt64Type = typeof(System.UInt64);
37 private static readonly Type DoubleType = typeof(System.Double);
38 private static readonly Type SingleType = typeof(System.Single);
39 private static readonly Type IEnumerableTypeofT = typeof(System.Collections.Generic.IEnumerable<>);
40 private static readonly Type IEnumerableType = typeof(System.Collections.IEnumerable);
42 private static readonly MethodInfo ExceptionGetData = typeof(Exception).GetProperty("Data").GetGetMethod();
43 private static readonly MethodInfo DictionaryAdd = typeof(IDictionary).GetMethod("Add");
44 private static readonly ConstructorInfo ObjectCtor = typeof(object).GetConstructor(Type.EmptyTypes);
46 public static ILGenerator CreateGeneratorForPublicConstructor(this TypeBuilder typeBuilder, Type[] ctrArgumentTypes)
48 ConstructorBuilder ctorBuilder = typeBuilder.DefineConstructor(
49 MethodAttributes.Public,
50 CallingConventions.Standard,
53 ILGenerator ctorIL = ctorBuilder.GetILGenerator();
54 ctorIL.Emit(OpCodes.Ldarg_0);
55 ctorIL.Emit(OpCodes.Call, ObjectCtor);
60 /// Generates the code that loads the supplied value on the stack
61 /// This is not as simple as it seems, as different instructions need to be generated depending
64 /// 1. All primitive types
70 /// 7. Delegates on static functions or any of the above
71 /// Everything else cannot be represented as literals
72 /// <param name="ilGenerator"></param>
73 /// <param name="item"></param>
74 /// <param name="key"></param>
75 /// <param name="value"></param>
76 /// <returns></returns>
77 public static void LoadValue(this ILGenerator ilGenerator, object value)
79 Assumes.NotNull(ilGenerator);
82 // Get nulls out of the way - they are basically typeless, so we just load null
86 ilGenerator.LoadNull();
91 // Prepare for literal loading - decide whether we should box, and handle enums properly
93 Type valueType = value.GetType();
94 object rawValue = value;
97 // enums are special - we need to load the underlying constant on the stack
98 rawValue = Convert.ChangeType(value, Enum.GetUnderlyingType(valueType), null);
99 valueType = rawValue.GetType();
103 // Generate IL depending on the valueType - this is messier than it should ever be, but sadly necessary
105 if (valueType == GenerationServices.StringType)
107 // we need to check for strings before enumerables, because strings are IEnumerable<char>
108 ilGenerator.LoadString((string)rawValue);
110 else if (GenerationServices.TypeType.IsAssignableFrom(valueType))
112 ilGenerator.LoadTypeOf((Type)rawValue);
114 else if (GenerationServices.IEnumerableType.IsAssignableFrom(valueType))
116 // NOTE : strings and dictionaries are also enumerables, but we have already handled those
117 ilGenerator.LoadEnumerable((IEnumerable) rawValue);
120 (valueType == GenerationServices.CharType) ||
121 (valueType == GenerationServices.BooleanType) ||
122 (valueType == GenerationServices.ByteType) ||
123 (valueType == GenerationServices.SByteType) ||
124 (valueType == GenerationServices.Int16Type) ||
125 (valueType == GenerationServices.UInt16Type) ||
126 (valueType == GenerationServices.Int32Type)
129 // NOTE : Everything that is 32 bit or less uses ldc.i4. We need to pass int32, even if the actual types is shorter - this is IL memory model
130 // direct casting to (int) won't work, because the value is boxed, thus we need to use Convert.
131 // Sadly, this will not work for all cases - namely large uint32 - because they can't semantically fit into 32 signed bits
132 // We have a special case for that next
133 ilGenerator.LoadInt((int)Convert.ChangeType(rawValue, typeof(int), CultureInfo.InvariantCulture));
135 else if (valueType == GenerationServices.UInt32Type)
137 // NOTE : This one is a bit tricky. Ldc.I4 takes an Int32 as an argument, although it really treats it as a 32bit number
138 // That said, some UInt32 values are larger that Int32.MaxValue, so the Convert call above will fail, which is why
139 // we need to treat this case individually and cast to uint, and then - unchecked - to int.
140 ilGenerator.LoadInt(unchecked((int)((uint)rawValue)));
142 else if (valueType == GenerationServices.Int64Type)
144 ilGenerator.LoadLong((long)rawValue);
146 else if (valueType == GenerationServices.UInt64Type)
148 // NOTE : This one is a bit tricky. Ldc.I8 takes an Int64 as an argument, although it really treats it as a 64bit number
149 // That said, some UInt64 values are larger that Int64.MaxValue, so the direct case we use above (or Convert, for that matter)will fail, which is why
150 // we need to treat this case individually and cast to ulong, and then - unchecked - to long.
151 ilGenerator.LoadLong(unchecked((long)((ulong)rawValue)));
153 else if (valueType == GenerationServices.SingleType)
155 ilGenerator.LoadFloat((float)rawValue);
157 else if (valueType == GenerationServices.DoubleType)
159 ilGenerator.LoadDouble((double)rawValue);
163 throw new InvalidOperationException(
164 string.Format(CultureInfo.CurrentCulture, Strings.InvalidMetadataValue, value.GetType().FullName));
168 /// Generates the code that adds an object to a dictionary stored in a local variable
169 /// <param name="ilGenerator"></param>
170 /// <param name="dictionary"></param>
171 /// <param name="key"></param>
172 /// <param name="value"></param>
173 /// <returns></returns>
174 public static void AddItemToLocalDictionary(this ILGenerator ilGenerator, LocalBuilder dictionary, object key, object value)
176 Assumes.NotNull(ilGenerator);
177 Assumes.NotNull(dictionary);
178 Assumes.NotNull(key);
179 Assumes.NotNull(value);
181 ilGenerator.Emit(OpCodes.Ldloc, dictionary);
182 ilGenerator.LoadValue(key);
183 ilGenerator.LoadValue(value);
184 ilGenerator.Emit(OpCodes.Callvirt, DictionaryAdd);
187 /// Generates the code that adds an object from a local variable to a dictionary also stored in a local
188 /// <param name="ilGenerator"></param>
189 /// <param name="dictionary"></param>
190 /// <param name="key"></param>
191 /// <param name="value"></param>
192 /// <returns></returns>
193 public static void AddLocalToLocalDictionary(this ILGenerator ilGenerator, LocalBuilder dictionary, object key, LocalBuilder value)
195 Assumes.NotNull(ilGenerator);
196 Assumes.NotNull(dictionary);
197 Assumes.NotNull(key);
198 Assumes.NotNull(value);
200 ilGenerator.Emit(OpCodes.Ldloc, dictionary);
201 ilGenerator.LoadValue(key);
202 ilGenerator.Emit(OpCodes.Ldloc, value);
203 ilGenerator.Emit(OpCodes.Callvirt, DictionaryAdd);
206 /// Generates the code to get the type of an object and store it in a local
207 /// <param name="ilGenerator"></param>
208 /// <param name="dictionary"></param>
209 /// <param name="key"></param>
210 /// <param name="value"></param>
211 /// <returns></returns>
212 public static void GetExceptionDataAndStoreInLocal(this ILGenerator ilGenerator, LocalBuilder exception, LocalBuilder dataStore)
214 Assumes.NotNull(ilGenerator);
215 Assumes.NotNull(exception);
216 Assumes.NotNull(dataStore);
218 ilGenerator.Emit(OpCodes.Ldloc, exception);
219 ilGenerator.Emit(OpCodes.Callvirt, ExceptionGetData);
220 ilGenerator.Emit(OpCodes.Stloc, dataStore);
223 private static void LoadEnumerable(this ILGenerator ilGenerator, IEnumerable enumerable)
225 Assumes.NotNull(ilGenerator);
226 Assumes.NotNull(enumerable);
228 // We load enumerable as an array - this is the most compact and efficient way of representing it
229 Type elementType = null;
230 Type closedType = null;
231 if (ReflectionServices.TryGetGenericInterfaceType(enumerable.GetType(), GenerationServices.IEnumerableTypeofT, out closedType))
233 elementType = closedType.GetGenericArguments()[0];
237 elementType = typeof(object);
241 // elem[] array = new elem[<enumerable.Count()>]
243 Type generatedArrayType = elementType.MakeArrayType();
244 LocalBuilder generatedArrayLocal = ilGenerator.DeclareLocal(generatedArrayType);
246 ilGenerator.LoadInt(enumerable.Cast<object>().Count());
247 ilGenerator.Emit(OpCodes.Newarr, elementType);
248 ilGenerator.Emit(OpCodes.Stloc, generatedArrayLocal);
251 foreach (object value in enumerable)
254 //array[<index>] = value;
256 ilGenerator.Emit(OpCodes.Ldloc, generatedArrayLocal);
257 ilGenerator.LoadInt(index);
258 ilGenerator.LoadValue(value);
259 if (GenerationServices.IsBoxingRequiredForValue(value) && !elementType.IsValueType)
261 ilGenerator.Emit(OpCodes.Box, value.GetType());
263 ilGenerator.Emit(OpCodes.Stelem, elementType);
267 ilGenerator.Emit(OpCodes.Ldloc, generatedArrayLocal);
270 private static bool IsBoxingRequiredForValue(object value)
278 return value.GetType().IsValueType;
283 private static void LoadNull(this ILGenerator ilGenerator)
285 ilGenerator.Emit(OpCodes.Ldnull);
288 private static void LoadString(this ILGenerator ilGenerator, string s)
290 Assumes.NotNull(ilGenerator);
293 ilGenerator.LoadNull();
297 ilGenerator.Emit(OpCodes.Ldstr, s);
302 private static void LoadInt(this ILGenerator ilGenerator, int value)
304 Assumes.NotNull(ilGenerator);
305 ilGenerator.Emit(OpCodes.Ldc_I4, value);
308 private static void LoadLong(this ILGenerator ilGenerator, long value)
310 Assumes.NotNull(ilGenerator);
311 ilGenerator.Emit(OpCodes.Ldc_I8, value);
314 private static void LoadFloat(this ILGenerator ilGenerator, float value)
316 Assumes.NotNull(ilGenerator);
317 ilGenerator.Emit(OpCodes.Ldc_R4, value);
320 private static void LoadDouble(this ILGenerator ilGenerator, double value)
322 Assumes.NotNull(ilGenerator);
323 ilGenerator.Emit(OpCodes.Ldc_R8, value);
326 private static void LoadTypeOf(this ILGenerator ilGenerator, Type type)
328 Assumes.NotNull(ilGenerator);
329 //typeofs() translate into ldtoken and Type::GetTypeFromHandle call
330 ilGenerator.Emit(OpCodes.Ldtoken, type);
331 ilGenerator.EmitCall(OpCodes.Call, GenerationServices._typeGetTypeFromHandleMethod, null);