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