Merge pull request #799 from kebby/master
[mono.git] / mcs / class / System.ComponentModel.Composition.4.5 / src / ComponentModel / Microsoft / Internal / GenerationServices.cs
1 #if !FULL_AOT_RUNTIME
2 // -----------------------------------------------------------------------
3 // Copyright (c) Microsoft Corporation.  All rights reserved.
4 // -----------------------------------------------------------------------
5
6 using System;
7 using System.Collections;
8 using System.Collections.Generic;
9 using System.ComponentModel.Composition;
10 using System.Globalization;
11 using System.Linq;
12 using System.Text;
13 using System.Reflection;
14 using System.Reflection.Emit;
15
16 namespace Microsoft.Internal
17 {
18     internal static class GenerationServices
19     {
20         // Type.GetTypeFromHandle
21         private static readonly MethodInfo _typeGetTypeFromHandleMethod = typeof(Type).GetMethod("GetTypeFromHandle");
22
23
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);
41
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);
45
46         public static ILGenerator CreateGeneratorForPublicConstructor(this TypeBuilder typeBuilder, Type[] ctrArgumentTypes)
47         {
48             ConstructorBuilder ctorBuilder = typeBuilder.DefineConstructor(
49                 MethodAttributes.Public,
50                 CallingConventions.Standard,
51                 ctrArgumentTypes);
52
53             ILGenerator ctorIL = ctorBuilder.GetILGenerator();
54             ctorIL.Emit(OpCodes.Ldarg_0);
55             ctorIL.Emit(OpCodes.Call, ObjectCtor);
56
57             return ctorIL;
58         }
59         
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
62         /// on its type.
63         /// We support:
64         /// 1. All primitive types
65         /// 2. Strings
66         /// 3. Enums
67         /// 4. typeofs
68         /// 5. nulls
69         /// 6. Enumerables
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)
78         {
79             Assumes.NotNull(ilGenerator);
80
81             //
82             // Get nulls out of the way - they are basically typeless, so we just load null
83             //
84             if (value == null)
85             {
86                 ilGenerator.LoadNull();
87                 return;
88             }
89
90             //
91             // Prepare for literal loading - decide whether we should box, and handle enums properly
92             //
93             Type valueType = value.GetType();
94             object rawValue = value;
95             if (valueType.IsEnum)
96             {
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();
100             }
101
102             //
103             // Generate IL depending on the valueType - this is messier than it should ever be, but sadly necessary
104             //
105             if (valueType == GenerationServices.StringType)
106             {
107                 // we need to check for strings before enumerables, because strings are IEnumerable<char>
108                 ilGenerator.LoadString((string)rawValue);
109             }
110             else if (GenerationServices.TypeType.IsAssignableFrom(valueType))
111             {
112                 ilGenerator.LoadTypeOf((Type)rawValue);
113             }
114             else if (GenerationServices.IEnumerableType.IsAssignableFrom(valueType))
115             {
116                 // NOTE : strings and dictionaries are also enumerables, but we have already handled those
117                 ilGenerator.LoadEnumerable((IEnumerable) rawValue);
118             }
119             else if (
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)
127                 )
128             {
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));
134             }
135             else if (valueType == GenerationServices.UInt32Type)
136             {
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)));
141             }
142             else if (valueType == GenerationServices.Int64Type)
143             {
144                 ilGenerator.LoadLong((long)rawValue);
145             }
146             else if (valueType == GenerationServices.UInt64Type)
147             {
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)));
152             }
153             else if (valueType == GenerationServices.SingleType)
154             {
155                 ilGenerator.LoadFloat((float)rawValue);
156             }
157             else if (valueType == GenerationServices.DoubleType)
158             {
159                 ilGenerator.LoadDouble((double)rawValue);
160             }
161             else
162             {
163                 throw new InvalidOperationException(
164                     string.Format(CultureInfo.CurrentCulture, Strings.InvalidMetadataValue, value.GetType().FullName));
165             }
166         }
167
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)
175         {
176             Assumes.NotNull(ilGenerator);
177             Assumes.NotNull(dictionary);
178             Assumes.NotNull(key);
179             Assumes.NotNull(value);
180
181             ilGenerator.Emit(OpCodes.Ldloc, dictionary);
182             ilGenerator.LoadValue(key);
183             ilGenerator.LoadValue(value);
184             ilGenerator.Emit(OpCodes.Callvirt, DictionaryAdd);
185         }
186
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)
194         {
195             Assumes.NotNull(ilGenerator);
196             Assumes.NotNull(dictionary);
197             Assumes.NotNull(key);
198             Assumes.NotNull(value);
199
200             ilGenerator.Emit(OpCodes.Ldloc, dictionary);
201             ilGenerator.LoadValue(key);
202             ilGenerator.Emit(OpCodes.Ldloc, value);
203             ilGenerator.Emit(OpCodes.Callvirt, DictionaryAdd);
204         }
205
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)
213         {
214             Assumes.NotNull(ilGenerator);
215             Assumes.NotNull(exception);
216             Assumes.NotNull(dataStore);
217
218             ilGenerator.Emit(OpCodes.Ldloc, exception);
219             ilGenerator.Emit(OpCodes.Callvirt, ExceptionGetData);
220             ilGenerator.Emit(OpCodes.Stloc, dataStore);
221         }
222
223         private static void LoadEnumerable(this ILGenerator ilGenerator, IEnumerable enumerable)
224         {
225             Assumes.NotNull(ilGenerator);
226             Assumes.NotNull(enumerable);
227
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))
232             {
233                 elementType = closedType.GetGenericArguments()[0];
234             }
235             else
236             {
237                 elementType = typeof(object);
238             }
239
240             //
241             // elem[] array = new elem[<enumerable.Count()>]
242             //
243             Type generatedArrayType = elementType.MakeArrayType();
244             LocalBuilder generatedArrayLocal = ilGenerator.DeclareLocal(generatedArrayType);
245
246             ilGenerator.LoadInt(enumerable.Cast<object>().Count());
247             ilGenerator.Emit(OpCodes.Newarr, elementType);
248             ilGenerator.Emit(OpCodes.Stloc, generatedArrayLocal);
249             
250             int index = 0;
251             foreach (object value in enumerable)
252             {
253                 //
254                 //array[<index>] = value;
255                 //
256                 ilGenerator.Emit(OpCodes.Ldloc, generatedArrayLocal);
257                 ilGenerator.LoadInt(index);
258                 ilGenerator.LoadValue(value);
259                 if (GenerationServices.IsBoxingRequiredForValue(value) && !elementType.IsValueType)
260                 {
261                     ilGenerator.Emit(OpCodes.Box, value.GetType());
262                 }
263                 ilGenerator.Emit(OpCodes.Stelem, elementType);
264                 index++;
265             }
266
267             ilGenerator.Emit(OpCodes.Ldloc, generatedArrayLocal);
268         }
269
270         private static bool IsBoxingRequiredForValue(object value)
271         {
272             if (value == null)
273             {
274                 return false;
275             }
276             else
277             {
278                 return value.GetType().IsValueType;
279             }
280         }
281
282
283         private static void LoadNull(this ILGenerator ilGenerator)
284         {
285             ilGenerator.Emit(OpCodes.Ldnull);
286         }
287
288         private static void LoadString(this ILGenerator ilGenerator, string s)
289         {
290             Assumes.NotNull(ilGenerator);
291             if (s == null)
292             {
293                 ilGenerator.LoadNull();
294             }
295             else
296             {
297                 ilGenerator.Emit(OpCodes.Ldstr, s);
298             }
299         }
300
301
302         private static void LoadInt(this ILGenerator ilGenerator, int value)
303         {
304             Assumes.NotNull(ilGenerator);
305             ilGenerator.Emit(OpCodes.Ldc_I4, value);
306         }
307
308         private static void LoadLong(this ILGenerator ilGenerator, long value)
309         {
310             Assumes.NotNull(ilGenerator);
311             ilGenerator.Emit(OpCodes.Ldc_I8, value);
312         }
313
314         private static void LoadFloat(this ILGenerator ilGenerator, float value)
315         {
316             Assumes.NotNull(ilGenerator);
317             ilGenerator.Emit(OpCodes.Ldc_R4, value);
318         }
319
320         private static void LoadDouble(this ILGenerator ilGenerator, double value)
321         {
322             Assumes.NotNull(ilGenerator);
323             ilGenerator.Emit(OpCodes.Ldc_R8, value);
324         }
325
326         private static void LoadTypeOf(this ILGenerator ilGenerator, Type type)
327         {
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);
332         }
333     }
334 }
335 #endif