1 // -----------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 // -----------------------------------------------------------------------
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.ComponentModel.Composition;
9 using System.Globalization;
12 using System.Reflection;
13 using System.Reflection.Emit;
15 namespace Microsoft.Internal
17 internal static class GenerationServices
19 // Type.GetTypeFromHandle
20 private static readonly MethodInfo _typeGetTypeFromHandleMethod = typeof(Type).GetMethod("GetTypeFromHandle");
23 // typeofs are pretty expensive, so we cache them statically
24 private static readonly Type TypeType = typeof(System.Type);
25 private static readonly Type StringType = typeof(System.String);
26 private static readonly Type CharType = typeof(System.Char);
27 private static readonly Type BooleanType = typeof(System.Boolean);
28 private static readonly Type ByteType = typeof(System.Byte);
29 private static readonly Type SByteType = typeof(System.SByte);
30 private static readonly Type Int16Type = typeof(System.Int16);
31 private static readonly Type UInt16Type = typeof(System.UInt16);
32 private static readonly Type Int32Type = typeof(System.Int32);
33 private static readonly Type UInt32Type = typeof(System.UInt32);
34 private static readonly Type Int64Type = typeof(System.Int64);
35 private static readonly Type UInt64Type = typeof(System.UInt64);
36 private static readonly Type DoubleType = typeof(System.Double);
37 private static readonly Type SingleType = typeof(System.Single);
38 private static readonly Type IEnumerableTypeofT = typeof(System.Collections.Generic.IEnumerable<>);
39 private static readonly Type IEnumerableType = typeof(System.Collections.IEnumerable);
41 private static readonly MethodInfo ExceptionGetData = typeof(Exception).GetProperty("Data").GetGetMethod();
42 private static readonly MethodInfo DictionaryAdd = typeof(IDictionary).GetMethod("Add");
43 private static readonly ConstructorInfo ObjectCtor = typeof(object).GetConstructor(Type.EmptyTypes);
45 public static ILGenerator CreateGeneratorForPublicConstructor(this TypeBuilder typeBuilder, Type[] ctrArgumentTypes)
47 ConstructorBuilder ctorBuilder = typeBuilder.DefineConstructor(
48 MethodAttributes.Public,
49 CallingConventions.Standard,
52 ILGenerator ctorIL = ctorBuilder.GetILGenerator();
53 ctorIL.Emit(OpCodes.Ldarg_0);
54 ctorIL.Emit(OpCodes.Call, ObjectCtor);
59 /// Generates the code that loads the supplied value on the stack
60 /// This is not as simple as it seems, as different instructions need to be generated depending
63 /// 1. All primitive types
69 /// 7. Delegates on static functions or any of the above
70 /// Everything else cannot be represented as literals
71 /// <param name="ilGenerator"></param>
72 /// <param name="item"></param>
73 /// <param name="key"></param>
74 /// <param name="value"></param>
75 /// <returns></returns>
76 public static void LoadValue(this ILGenerator ilGenerator, object value)
78 Assumes.NotNull(ilGenerator);
81 // Get nulls out of the way - they are basically typeless, so we just load null
85 ilGenerator.LoadNull();
90 // Prepare for literal loading - decide whether we should box, and handle enums properly
92 Type valueType = value.GetType();
93 object rawValue = value;
96 // enums are special - we need to load the underlying constant on the stack
97 rawValue = Convert.ChangeType(value, Enum.GetUnderlyingType(valueType), null);
98 valueType = rawValue.GetType();
102 // Generate IL depending on the valueType - this is messier than it should ever be, but sadly necessary
104 if (valueType == GenerationServices.StringType)
106 // we need to check for strings before enumerables, because strings are IEnumerable<char>
107 ilGenerator.LoadString((string)rawValue);
109 else if (GenerationServices.TypeType.IsAssignableFrom(valueType))
111 ilGenerator.LoadTypeOf((Type)rawValue);
113 else if (GenerationServices.IEnumerableType.IsAssignableFrom(valueType))
115 // NOTE : strings and dictionaries are also enumerables, but we have already handled those
116 ilGenerator.LoadEnumerable((IEnumerable) rawValue);
119 (valueType == GenerationServices.CharType) ||
120 (valueType == GenerationServices.BooleanType) ||
121 (valueType == GenerationServices.ByteType) ||
122 (valueType == GenerationServices.SByteType) ||
123 (valueType == GenerationServices.Int16Type) ||
124 (valueType == GenerationServices.UInt16Type) ||
125 (valueType == GenerationServices.Int32Type)
128 // 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
129 // direct casting to (int) won't work, because the value is boxed, thus we need to use Convert.
130 // Sadly, this will not work for all cases - namely large uint32 - because they can't semantically fit into 32 signed bits
131 // We have a special case for that next
132 ilGenerator.LoadInt((int)Convert.ChangeType(rawValue, typeof(int), CultureInfo.InvariantCulture));
134 else if (valueType == GenerationServices.UInt32Type)
136 // NOTE : This one is a bit tricky. Ldc.I4 takes an Int32 as an argument, although it really treats it as a 32bit number
137 // That said, some UInt32 values are larger that Int32.MaxValue, so the Convert call above will fail, which is why
138 // we need to treat this case individually and cast to uint, and then - unchecked - to int.
139 ilGenerator.LoadInt(unchecked((int)((uint)rawValue)));
141 else if (valueType == GenerationServices.Int64Type)
143 ilGenerator.LoadLong((long)rawValue);
145 else if (valueType == GenerationServices.UInt64Type)
147 // NOTE : This one is a bit tricky. Ldc.I8 takes an Int64 as an argument, although it really treats it as a 64bit number
148 // 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
149 // we need to treat this case individually and cast to ulong, and then - unchecked - to long.
150 ilGenerator.LoadLong(unchecked((long)((ulong)rawValue)));
152 else if (valueType == GenerationServices.SingleType)
154 ilGenerator.LoadFloat((float)rawValue);
156 else if (valueType == GenerationServices.DoubleType)
158 ilGenerator.LoadDouble((double)rawValue);
162 throw new InvalidOperationException(
163 string.Format(CultureInfo.CurrentCulture, Strings.InvalidMetadataValue, value.GetType().FullName));
167 /// Generates the code that adds an object to a dictionary stored in a local variable
168 /// <param name="ilGenerator"></param>
169 /// <param name="dictionary"></param>
170 /// <param name="key"></param>
171 /// <param name="value"></param>
172 /// <returns></returns>
173 public static void AddItemToLocalDictionary(this ILGenerator ilGenerator, LocalBuilder dictionary, object key, object value)
175 Assumes.NotNull(ilGenerator);
176 Assumes.NotNull(dictionary);
177 Assumes.NotNull(key);
178 Assumes.NotNull(value);
180 ilGenerator.Emit(OpCodes.Ldloc, dictionary);
181 ilGenerator.LoadValue(key);
182 ilGenerator.LoadValue(value);
183 ilGenerator.Emit(OpCodes.Callvirt, DictionaryAdd);
186 /// Generates the code that adds an object from a local variable to a dictionary also stored in a local
187 /// <param name="ilGenerator"></param>
188 /// <param name="dictionary"></param>
189 /// <param name="key"></param>
190 /// <param name="value"></param>
191 /// <returns></returns>
192 public static void AddLocalToLocalDictionary(this ILGenerator ilGenerator, LocalBuilder dictionary, object key, LocalBuilder value)
194 Assumes.NotNull(ilGenerator);
195 Assumes.NotNull(dictionary);
196 Assumes.NotNull(key);
197 Assumes.NotNull(value);
199 ilGenerator.Emit(OpCodes.Ldloc, dictionary);
200 ilGenerator.LoadValue(key);
201 ilGenerator.Emit(OpCodes.Ldloc, value);
202 ilGenerator.Emit(OpCodes.Callvirt, DictionaryAdd);
205 /// Generates the code to get the type of an object and store it in a local
206 /// <param name="ilGenerator"></param>
207 /// <param name="dictionary"></param>
208 /// <param name="key"></param>
209 /// <param name="value"></param>
210 /// <returns></returns>
211 public static void GetExceptionDataAndStoreInLocal(this ILGenerator ilGenerator, LocalBuilder exception, LocalBuilder dataStore)
213 Assumes.NotNull(ilGenerator);
214 Assumes.NotNull(exception);
215 Assumes.NotNull(dataStore);
217 ilGenerator.Emit(OpCodes.Ldloc, exception);
218 ilGenerator.Emit(OpCodes.Callvirt, ExceptionGetData);
219 ilGenerator.Emit(OpCodes.Stloc, dataStore);
222 private static void LoadEnumerable(this ILGenerator ilGenerator, IEnumerable enumerable)
224 Assumes.NotNull(ilGenerator);
225 Assumes.NotNull(enumerable);
227 // We load enumerable as an array - this is the most compact and efficient way of representing it
228 Type elementType = null;
229 Type closedType = null;
230 if (ReflectionServices.TryGetGenericInterfaceType(enumerable.GetType(), GenerationServices.IEnumerableTypeofT, out closedType))
232 elementType = closedType.GetGenericArguments()[0];
236 elementType = typeof(object);
240 // elem[] array = new elem[<enumerable.Count()>]
242 Type generatedArrayType = elementType.MakeArrayType();
243 LocalBuilder generatedArrayLocal = ilGenerator.DeclareLocal(generatedArrayType);
245 ilGenerator.LoadInt(enumerable.Cast<object>().Count());
246 ilGenerator.Emit(OpCodes.Newarr, elementType);
247 ilGenerator.Emit(OpCodes.Stloc, generatedArrayLocal);
250 foreach (object value in enumerable)
253 //array[<index>] = value;
255 ilGenerator.Emit(OpCodes.Ldloc, generatedArrayLocal);
256 ilGenerator.LoadInt(index);
257 ilGenerator.LoadValue(value);
258 if (GenerationServices.IsBoxingRequiredForValue(value) && !elementType.IsValueType)
260 ilGenerator.Emit(OpCodes.Box, value.GetType());
262 ilGenerator.Emit(OpCodes.Stelem, elementType);
266 ilGenerator.Emit(OpCodes.Ldloc, generatedArrayLocal);
269 private static bool IsBoxingRequiredForValue(object value)
277 return value.GetType().IsValueType;
282 private static void LoadNull(this ILGenerator ilGenerator)
284 ilGenerator.Emit(OpCodes.Ldnull);
287 private static void LoadString(this ILGenerator ilGenerator, string s)
289 Assumes.NotNull(ilGenerator);
292 ilGenerator.LoadNull();
296 ilGenerator.Emit(OpCodes.Ldstr, s);
301 private static void LoadInt(this ILGenerator ilGenerator, int value)
303 Assumes.NotNull(ilGenerator);
304 ilGenerator.Emit(OpCodes.Ldc_I4, value);
307 private static void LoadLong(this ILGenerator ilGenerator, long value)
309 Assumes.NotNull(ilGenerator);
310 ilGenerator.Emit(OpCodes.Ldc_I8, value);
313 private static void LoadFloat(this ILGenerator ilGenerator, float value)
315 Assumes.NotNull(ilGenerator);
316 ilGenerator.Emit(OpCodes.Ldc_R4, value);
319 private static void LoadDouble(this ILGenerator ilGenerator, double value)
321 Assumes.NotNull(ilGenerator);
322 ilGenerator.Emit(OpCodes.Ldc_R8, value);
325 private static void LoadTypeOf(this ILGenerator ilGenerator, Type type)
327 Assumes.NotNull(ilGenerator);
328 //typeofs() translate into ldtoken and Type::GetTypeFromHandle call
329 ilGenerator.Emit(OpCodes.Ldtoken, type);
330 ilGenerator.EmitCall(OpCodes.Call, GenerationServices._typeGetTypeFromHandleMethod, null);