1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Diagnostics;
6 using System.Dynamic.Utils;
7 using System.Reflection;
8 using System.Reflection.Emit;
9 using static System.Linq.Expressions.CachedReflectionInfo;
10 
11 namespace System.Linq.Expressions.Compiler
12 {
13     internal static class ILGen
14     {
Emit(this ILGenerator il, OpCode opcode, MethodBase methodBase)15         internal static void Emit(this ILGenerator il, OpCode opcode, MethodBase methodBase)
16         {
17             Debug.Assert(methodBase is MethodInfo || methodBase is ConstructorInfo);
18 
19             var ctor = methodBase as ConstructorInfo;
20             if ((object)ctor != null)
21             {
22                 il.Emit(opcode, ctor);
23             }
24             else
25             {
26                 il.Emit(opcode, (MethodInfo)methodBase);
27             }
28         }
29 
30         #region Instruction helpers
31 
EmitLoadArg(this ILGenerator il, int index)32         internal static void EmitLoadArg(this ILGenerator il, int index)
33         {
34             Debug.Assert(index >= 0);
35             Debug.Assert(index < ushort.MaxValue);
36 
37             switch (index)
38             {
39                 case 0:
40                     il.Emit(OpCodes.Ldarg_0);
41                     break;
42                 case 1:
43                     il.Emit(OpCodes.Ldarg_1);
44                     break;
45                 case 2:
46                     il.Emit(OpCodes.Ldarg_2);
47                     break;
48                 case 3:
49                     il.Emit(OpCodes.Ldarg_3);
50                     break;
51                 default:
52                     if (index <= byte.MaxValue)
53                     {
54                         il.Emit(OpCodes.Ldarg_S, (byte)index);
55                     }
56                     else
57                     {
58                         // cast to short, result is correct ushort.
59                         il.Emit(OpCodes.Ldarg, (short)index);
60                     }
61                     break;
62             }
63         }
64 
EmitLoadArgAddress(this ILGenerator il, int index)65         internal static void EmitLoadArgAddress(this ILGenerator il, int index)
66         {
67             Debug.Assert(index >= 0);
68             Debug.Assert(index < ushort.MaxValue);
69 
70             if (index <= byte.MaxValue)
71             {
72                 il.Emit(OpCodes.Ldarga_S, (byte)index);
73             }
74             else
75             {
76                 // cast to short, result is correct ushort.
77                 il.Emit(OpCodes.Ldarga, (short)index);
78             }
79         }
80 
EmitStoreArg(this ILGenerator il, int index)81         internal static void EmitStoreArg(this ILGenerator il, int index)
82         {
83             Debug.Assert(index >= 0);
84             Debug.Assert(index < ushort.MaxValue);
85 
86             if (index <= byte.MaxValue)
87             {
88                 il.Emit(OpCodes.Starg_S, (byte)index);
89             }
90             else
91             {
92                 // cast to short, result is correct ushort.
93                 il.Emit(OpCodes.Starg, (short)index);
94             }
95         }
96 
97         /// <summary>
98         /// Emits a Ldind* instruction for the appropriate type
99         /// </summary>
EmitLoadValueIndirect(this ILGenerator il, Type type)100         internal static void EmitLoadValueIndirect(this ILGenerator il, Type type)
101         {
102             Debug.Assert(type != null);
103 
104             switch (type.GetTypeCode())
105             {
106                 case TypeCode.Byte:
107                     il.Emit(OpCodes.Ldind_I1);
108                     break;
109                 case TypeCode.Boolean:
110                 case TypeCode.SByte:
111                     il.Emit(OpCodes.Ldind_U1);
112                     break;
113                 case TypeCode.Int16:
114                     il.Emit(OpCodes.Ldind_I2);
115                     break;
116                 case TypeCode.Char:
117                 case TypeCode.UInt16:
118                     il.Emit(OpCodes.Ldind_U2);
119                     break;
120                 case TypeCode.Int32:
121                     il.Emit(OpCodes.Ldind_I4);
122                     break;
123                 case TypeCode.UInt32:
124                     il.Emit(OpCodes.Ldind_U4);
125                     break;
126                 case TypeCode.Int64:
127                 case TypeCode.UInt64:
128                     il.Emit(OpCodes.Ldind_I8);
129                     break;
130                 case TypeCode.Single:
131                     il.Emit(OpCodes.Ldind_R4);
132                     break;
133                 case TypeCode.Double:
134                     il.Emit(OpCodes.Ldind_R8);
135                     break;
136                 default:
137                     if (type.IsValueType)
138                     {
139                         il.Emit(OpCodes.Ldobj, type);
140                     }
141                     else
142                     {
143                         il.Emit(OpCodes.Ldind_Ref);
144                     }
145                     break;
146             }
147         }
148 
149 
150         /// <summary>
151         /// Emits a Stind* instruction for the appropriate type.
152         /// </summary>
EmitStoreValueIndirect(this ILGenerator il, Type type)153         internal static void EmitStoreValueIndirect(this ILGenerator il, Type type)
154         {
155             Debug.Assert(type != null);
156 
157             switch (type.GetTypeCode())
158             {
159                 case TypeCode.Boolean:
160                 case TypeCode.Byte:
161                 case TypeCode.SByte:
162                     il.Emit(OpCodes.Stind_I1);
163                     break;
164                 case TypeCode.Char:
165                 case TypeCode.Int16:
166                 case TypeCode.UInt16:
167                     il.Emit(OpCodes.Stind_I2);
168                     break;
169                 case TypeCode.Int32:
170                 case TypeCode.UInt32:
171                     il.Emit(OpCodes.Stind_I4);
172                     break;
173                 case TypeCode.Int64:
174                 case TypeCode.UInt64:
175                     il.Emit(OpCodes.Stind_I8);
176                     break;
177                 case TypeCode.Single:
178                     il.Emit(OpCodes.Stind_R4);
179                     break;
180                 case TypeCode.Double:
181                     il.Emit(OpCodes.Stind_R8);
182                     break;
183                 default:
184                     if (type.IsValueType)
185                     {
186                         il.Emit(OpCodes.Stobj, type);
187                     }
188                     else
189                     {
190                         il.Emit(OpCodes.Stind_Ref);
191                     }
192                     break;
193             }
194         }
195 
196         // Emits the Ldelem* instruction for the appropriate type
197 
EmitLoadElement(this ILGenerator il, Type type)198         internal static void EmitLoadElement(this ILGenerator il, Type type)
199         {
200             Debug.Assert(type != null);
201 
202             if (!type.IsValueType)
203             {
204                 il.Emit(OpCodes.Ldelem_Ref);
205             }
206             else
207             {
208                 switch (type.GetTypeCode())
209                 {
210                     case TypeCode.Boolean:
211                     case TypeCode.SByte:
212                         il.Emit(OpCodes.Ldelem_I1);
213                         break;
214                     case TypeCode.Byte:
215                         il.Emit(OpCodes.Ldelem_U1);
216                         break;
217                     case TypeCode.Int16:
218                         il.Emit(OpCodes.Ldelem_I2);
219                         break;
220                     case TypeCode.Char:
221                     case TypeCode.UInt16:
222                         il.Emit(OpCodes.Ldelem_U2);
223                         break;
224                     case TypeCode.Int32:
225                         il.Emit(OpCodes.Ldelem_I4);
226                         break;
227                     case TypeCode.UInt32:
228                         il.Emit(OpCodes.Ldelem_U4);
229                         break;
230                     case TypeCode.Int64:
231                     case TypeCode.UInt64:
232                         il.Emit(OpCodes.Ldelem_I8);
233                         break;
234                     case TypeCode.Single:
235                         il.Emit(OpCodes.Ldelem_R4);
236                         break;
237                     case TypeCode.Double:
238                         il.Emit(OpCodes.Ldelem_R8);
239                         break;
240                     default:
241                         il.Emit(OpCodes.Ldelem, type);
242                         break;
243                 }
244             }
245         }
246 
247         /// <summary>
248         /// Emits a Stelem* instruction for the appropriate type.
249         /// </summary>
EmitStoreElement(this ILGenerator il, Type type)250         internal static void EmitStoreElement(this ILGenerator il, Type type)
251         {
252             Debug.Assert(type != null);
253 
254             switch (type.GetTypeCode())
255             {
256                 case TypeCode.Boolean:
257                 case TypeCode.SByte:
258                 case TypeCode.Byte:
259                     il.Emit(OpCodes.Stelem_I1);
260                     break;
261                 case TypeCode.Char:
262                 case TypeCode.Int16:
263                 case TypeCode.UInt16:
264                     il.Emit(OpCodes.Stelem_I2);
265                     break;
266                 case TypeCode.Int32:
267                 case TypeCode.UInt32:
268                     il.Emit(OpCodes.Stelem_I4);
269                     break;
270                 case TypeCode.Int64:
271                 case TypeCode.UInt64:
272                     il.Emit(OpCodes.Stelem_I8);
273                     break;
274                 case TypeCode.Single:
275                     il.Emit(OpCodes.Stelem_R4);
276                     break;
277                 case TypeCode.Double:
278                     il.Emit(OpCodes.Stelem_R8);
279                     break;
280                 default:
281                     if (type.IsValueType)
282                     {
283                         il.Emit(OpCodes.Stelem, type);
284                     }
285                     else
286                     {
287                         il.Emit(OpCodes.Stelem_Ref);
288                     }
289                     break;
290             }
291         }
292 
EmitType(this ILGenerator il, Type type)293         internal static void EmitType(this ILGenerator il, Type type)
294         {
295             Debug.Assert(type != null);
296 
297             il.Emit(OpCodes.Ldtoken, type);
298             il.Emit(OpCodes.Call, Type_GetTypeFromHandle);
299         }
300 
301         #endregion
302 
303         #region Fields, properties and methods
304 
EmitFieldAddress(this ILGenerator il, FieldInfo fi)305         internal static void EmitFieldAddress(this ILGenerator il, FieldInfo fi)
306         {
307             Debug.Assert(fi != null);
308 
309             il.Emit(fi.IsStatic ? OpCodes.Ldsflda : OpCodes.Ldflda, fi);
310         }
311 
EmitFieldGet(this ILGenerator il, FieldInfo fi)312         internal static void EmitFieldGet(this ILGenerator il, FieldInfo fi)
313         {
314             Debug.Assert(fi != null);
315 
316             il.Emit(fi.IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld, fi);
317         }
318 
EmitFieldSet(this ILGenerator il, FieldInfo fi)319         internal static void EmitFieldSet(this ILGenerator il, FieldInfo fi)
320         {
321             Debug.Assert(fi != null);
322 
323             il.Emit(fi.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, fi);
324         }
325 
326         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
EmitNew(this ILGenerator il, ConstructorInfo ci)327         internal static void EmitNew(this ILGenerator il, ConstructorInfo ci)
328         {
329             Debug.Assert(ci != null);
330             Debug.Assert(!ci.DeclaringType.ContainsGenericParameters);
331 
332             il.Emit(OpCodes.Newobj, ci);
333         }
334 
335         #endregion
336 
337         #region Constants
338 
EmitNull(this ILGenerator il)339         internal static void EmitNull(this ILGenerator il)
340         {
341             il.Emit(OpCodes.Ldnull);
342         }
343 
EmitString(this ILGenerator il, string value)344         internal static void EmitString(this ILGenerator il, string value)
345         {
346             Debug.Assert(value != null);
347 
348             il.Emit(OpCodes.Ldstr, value);
349         }
350 
EmitPrimitive(this ILGenerator il, bool value)351         internal static void EmitPrimitive(this ILGenerator il, bool value)
352         {
353             il.Emit(value ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
354         }
355 
EmitPrimitive(this ILGenerator il, int value)356         internal static void EmitPrimitive(this ILGenerator il, int value)
357         {
358             OpCode c;
359             switch (value)
360             {
361                 case -1:
362                     c = OpCodes.Ldc_I4_M1;
363                     break;
364                 case 0:
365                     c = OpCodes.Ldc_I4_0;
366                     break;
367                 case 1:
368                     c = OpCodes.Ldc_I4_1;
369                     break;
370                 case 2:
371                     c = OpCodes.Ldc_I4_2;
372                     break;
373                 case 3:
374                     c = OpCodes.Ldc_I4_3;
375                     break;
376                 case 4:
377                     c = OpCodes.Ldc_I4_4;
378                     break;
379                 case 5:
380                     c = OpCodes.Ldc_I4_5;
381                     break;
382                 case 6:
383                     c = OpCodes.Ldc_I4_6;
384                     break;
385                 case 7:
386                     c = OpCodes.Ldc_I4_7;
387                     break;
388                 case 8:
389                     c = OpCodes.Ldc_I4_8;
390                     break;
391                 default:
392                     if (value >= sbyte.MinValue && value <= sbyte.MaxValue)
393                     {
394                         il.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
395                     }
396                     else
397                     {
398                         il.Emit(OpCodes.Ldc_I4, value);
399                     }
400                     return;
401             }
402             il.Emit(c);
403         }
404 
EmitPrimitive(this ILGenerator il, uint value)405         private static void EmitPrimitive(this ILGenerator il, uint value)
406         {
407             il.EmitPrimitive(unchecked((int)value));
408         }
409 
EmitPrimitive(this ILGenerator il, long value)410         private static void EmitPrimitive(this ILGenerator il, long value)
411         {
412             if (int.MinValue <= value & value <= uint.MaxValue)
413             {
414                 il.EmitPrimitive(unchecked((int)value));
415                 // While often not of consequence depending on what follows, there are cases where this
416                 // casting matters. Values [0, int.MaxValue] can use either safely, but negative values
417                 // must use conv.i8 and those (int.MaxValue, uint.MaxValue] must use conv.u8, or else
418                 // the higher bits will be wrong.
419                 il.Emit(value > 0 ? OpCodes.Conv_U8 : OpCodes.Conv_I8);
420             }
421             else
422             {
423                 il.Emit(OpCodes.Ldc_I8, value);
424             }
425         }
426 
EmitPrimitive(this ILGenerator il, ulong value)427         private static void EmitPrimitive(this ILGenerator il, ulong value)
428         {
429             il.EmitPrimitive(unchecked((long)value));
430         }
431 
EmitPrimitive(this ILGenerator il, double value)432         private static void EmitPrimitive(this ILGenerator il, double value)
433         {
434             il.Emit(OpCodes.Ldc_R8, value);
435         }
436 
EmitPrimitive(this ILGenerator il, float value)437         private static void EmitPrimitive(this ILGenerator il, float value)
438         {
439             il.Emit(OpCodes.Ldc_R4, value);
440         }
441 
442         // matches TryEmitConstant
CanEmitConstant(object value, Type type)443         internal static bool CanEmitConstant(object value, Type type)
444         {
445             if (value == null || CanEmitILConstant(type))
446             {
447                 return true;
448             }
449 
450             Type t = value as Type;
451             if (t != null)
452             {
453                 return ShouldLdtoken(t);
454             }
455 
456             MethodBase mb = value as MethodBase;
457             return mb != null && ShouldLdtoken(mb);
458         }
459 
460         // matches TryEmitILConstant
CanEmitILConstant(Type type)461         private static bool CanEmitILConstant(Type type)
462         {
463             switch (type.GetNonNullableType().GetTypeCode())
464             {
465                 case TypeCode.Boolean:
466                 case TypeCode.SByte:
467                 case TypeCode.Int16:
468                 case TypeCode.Int32:
469                 case TypeCode.Int64:
470                 case TypeCode.Single:
471                 case TypeCode.Double:
472                 case TypeCode.Char:
473                 case TypeCode.Byte:
474                 case TypeCode.UInt16:
475                 case TypeCode.UInt32:
476                 case TypeCode.UInt64:
477                 case TypeCode.Decimal:
478                 case TypeCode.String:
479                     return true;
480             }
481 
482             return false;
483         }
484 
485         //
486         // Note: we support emitting more things as IL constants than
487         // Linq does
TryEmitConstant(this ILGenerator il, object value, Type type, ILocalCache locals)488         internal static bool TryEmitConstant(this ILGenerator il, object value, Type type, ILocalCache locals)
489         {
490             if (value == null)
491             {
492                 // Smarter than the Linq implementation which uses the initobj
493                 // pattern for all value types (works, but requires a local and
494                 // more IL)
495                 il.EmitDefault(type, locals);
496                 return true;
497             }
498 
499             // Handle the easy cases
500             if (il.TryEmitILConstant(value, type))
501             {
502                 return true;
503             }
504 
505             // Check for a few more types that we support emitting as constants
506             Type t = value as Type;
507             if (t != null)
508             {
509                 if (ShouldLdtoken(t))
510                 {
511                     il.EmitType(t);
512                     if (type != typeof(Type))
513                     {
514                         il.Emit(OpCodes.Castclass, type);
515                     }
516 
517                     return true;
518                 }
519 
520                 return false;
521             }
522 
523             MethodBase mb = value as MethodBase;
524             if (mb != null && ShouldLdtoken(mb))
525             {
526                 il.Emit(OpCodes.Ldtoken, mb);
527                 Type dt = mb.DeclaringType;
528                 if (dt != null && dt.IsGenericType)
529                 {
530                     il.Emit(OpCodes.Ldtoken, dt);
531                     il.Emit(OpCodes.Call, MethodBase_GetMethodFromHandle_RuntimeMethodHandle_RuntimeTypeHandle);
532                 }
533                 else
534                 {
535                     il.Emit(OpCodes.Call, MethodBase_GetMethodFromHandle_RuntimeMethodHandle);
536                 }
537 
538                 if (type != typeof(MethodBase))
539                 {
540                     il.Emit(OpCodes.Castclass, type);
541                 }
542 
543                 return true;
544             }
545 
546             return false;
547         }
548 
ShouldLdtoken(Type t)549         private static bool ShouldLdtoken(Type t)
550         {
551             // If CompileToMethod is re-enabled, t is TypeBuilder should also return
552             // true when not compiling to a DynamicMethod
553             return t.IsGenericParameter || t.IsVisible;
554         }
555 
ShouldLdtoken(MethodBase mb)556         internal static bool ShouldLdtoken(MethodBase mb)
557         {
558             // Can't ldtoken on a DynamicMethod
559             if (mb is DynamicMethod)
560             {
561                 return false;
562             }
563 
564             Type dt = mb.DeclaringType;
565             return dt == null || ShouldLdtoken(dt);
566         }
567 
TryEmitILConstant(this ILGenerator il, object value, Type type)568         private static bool TryEmitILConstant(this ILGenerator il, object value, Type type)
569         {
570             Debug.Assert(value != null);
571 
572             if (type.IsNullableType())
573             {
574                 Type nonNullType = type.GetNonNullableType();
575 
576                 if (TryEmitILConstant(il, value, nonNullType))
577                 {
578                     il.Emit(OpCodes.Newobj, type.GetConstructor(new[] { nonNullType }));
579                     return true;
580                 }
581 
582                 return false;
583             }
584 
585             switch (type.GetTypeCode())
586             {
587                 case TypeCode.Boolean:
588                     il.EmitPrimitive((bool)value);
589                     return true;
590                 case TypeCode.SByte:
591                     il.EmitPrimitive((sbyte)value);
592                     return true;
593                 case TypeCode.Int16:
594                     il.EmitPrimitive((short)value);
595                     return true;
596                 case TypeCode.Int32:
597                     il.EmitPrimitive((int)value);
598                     return true;
599                 case TypeCode.Int64:
600                     il.EmitPrimitive((long)value);
601                     return true;
602                 case TypeCode.Single:
603                     il.EmitPrimitive((float)value);
604                     return true;
605                 case TypeCode.Double:
606                     il.EmitPrimitive((double)value);
607                     return true;
608                 case TypeCode.Char:
609                     il.EmitPrimitive((char)value);
610                     return true;
611                 case TypeCode.Byte:
612                     il.EmitPrimitive((byte)value);
613                     return true;
614                 case TypeCode.UInt16:
615                     il.EmitPrimitive((ushort)value);
616                     return true;
617                 case TypeCode.UInt32:
618                     il.EmitPrimitive((uint)value);
619                     return true;
620                 case TypeCode.UInt64:
621                     il.EmitPrimitive((ulong)value);
622                     return true;
623                 case TypeCode.Decimal:
624                     il.EmitDecimal((decimal)value);
625                     return true;
626                 case TypeCode.String:
627                     il.EmitString((string)value);
628                     return true;
629                 default:
630                     return false;
631             }
632         }
633 
634         #endregion
635 
636         #region Linq Conversions
637 
EmitConvertToType(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked, ILocalCache locals)638         internal static void EmitConvertToType(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked, ILocalCache locals)
639         {
640             if (TypeUtils.AreEquivalent(typeFrom, typeTo))
641             {
642                 return;
643             }
644 
645             Debug.Assert(typeFrom != typeof(void) && typeTo != typeof(void));
646 
647             bool isTypeFromNullable = typeFrom.IsNullableType();
648             bool isTypeToNullable = typeTo.IsNullableType();
649 
650             Type nnExprType = typeFrom.GetNonNullableType();
651             Type nnType = typeTo.GetNonNullableType();
652 
653             if (typeFrom.IsInterface || // interface cast
654                typeTo.IsInterface ||
655                typeFrom == typeof(object) || // boxing cast
656                typeTo == typeof(object) ||
657                typeFrom == typeof(System.Enum) ||
658                typeFrom == typeof(System.ValueType) ||
659                TypeUtils.IsLegalExplicitVariantDelegateConversion(typeFrom, typeTo))
660             {
661                 il.EmitCastToType(typeFrom, typeTo);
662             }
663             else if (isTypeFromNullable || isTypeToNullable)
664             {
665                 il.EmitNullableConversion(typeFrom, typeTo, isChecked, locals);
666             }
667             else if (!(typeFrom.IsConvertible() && typeTo.IsConvertible()) // primitive runtime conversion
668                      &&
669                      (nnExprType.IsAssignableFrom(nnType) || // down cast
670                      nnType.IsAssignableFrom(nnExprType))) // up cast
671             {
672                 il.EmitCastToType(typeFrom, typeTo);
673             }
674             else if (typeFrom.IsArray && typeTo.IsArray)
675             {
676                 // See DevDiv Bugs #94657.
677                 il.EmitCastToType(typeFrom, typeTo);
678             }
679             else
680             {
681                 il.EmitNumericConversion(typeFrom, typeTo, isChecked);
682             }
683         }
684 
685 
EmitCastToType(this ILGenerator il, Type typeFrom, Type typeTo)686         private static void EmitCastToType(this ILGenerator il, Type typeFrom, Type typeTo)
687         {
688             if (typeFrom.IsValueType)
689             {
690                 Debug.Assert(!typeTo.IsValueType);
691                 il.Emit(OpCodes.Box, typeFrom);
692                 if (typeTo != typeof(object))
693                 {
694                     il.Emit(OpCodes.Castclass, typeTo);
695                 }
696             }
697             else
698             {
699                 il.Emit(typeTo.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, typeTo);
700             }
701         }
702 
703 
704         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
EmitNumericConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked)705         private static void EmitNumericConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked)
706         {
707             TypeCode tc = typeTo.GetTypeCode();
708             TypeCode tf = typeFrom.GetTypeCode();
709 
710             if (tc == tf)
711             {
712                 // Between enums of same underlying type, or between such an enum and the underlying type itself.
713                 // Includes bool-backed enums, which is the only valid conversion to or from bool.
714                 // Just leave the value on the stack, and treat it as the wanted type.
715                 return;
716             }
717 
718             bool isFromUnsigned = tf.IsUnsigned();
719             OpCode convCode;
720             switch (tc)
721             {
722                 case TypeCode.Single:
723                     if (isFromUnsigned)
724                         il.Emit(OpCodes.Conv_R_Un);
725                     convCode = OpCodes.Conv_R4;
726                     break;
727                 case TypeCode.Double:
728                     if (isFromUnsigned)
729                         il.Emit(OpCodes.Conv_R_Un);
730                     convCode = OpCodes.Conv_R8;
731                     break;
732                 case TypeCode.Decimal:
733 
734                     // NB: TypeUtils.IsImplicitNumericConversion makes the promise that implicit conversions
735                     //     from various integral types and char to decimal are possible. Coalesce allows the
736                     //     conversion lambda to be omitted in these cases, so we have to handle this case in
737                     //     here as well, by using the op_Implicit operator implementation on System.Decimal
738                     //     because there are no opcodes for System.Decimal.
739 
740                     Debug.Assert(typeFrom != typeTo);
741 
742                     MethodInfo method;
743 
744                     switch (tf)
745                     {
746                         case TypeCode.Byte: method = Decimal_op_Implicit_Byte; break;
747                         case TypeCode.SByte: method = Decimal_op_Implicit_SByte; break;
748                         case TypeCode.Int16: method = Decimal_op_Implicit_Int16; break;
749                         case TypeCode.UInt16: method = Decimal_op_Implicit_UInt16; break;
750                         case TypeCode.Int32: method = Decimal_op_Implicit_Int32; break;
751                         case TypeCode.UInt32: method = Decimal_op_Implicit_UInt32; break;
752                         case TypeCode.Int64: method = Decimal_op_Implicit_Int64; break;
753                         case TypeCode.UInt64: method = Decimal_op_Implicit_UInt64; break;
754                         case TypeCode.Char: method = Decimal_op_Implicit_Char; break;
755                         default:
756                             throw ContractUtils.Unreachable;
757                     }
758 
759                     il.Emit(OpCodes.Call, method);
760                     return;
761                 case TypeCode.SByte:
762                     if (isChecked)
763                     {
764                         convCode = isFromUnsigned ? OpCodes.Conv_Ovf_I1_Un : OpCodes.Conv_Ovf_I1;
765                     }
766                     else
767                     {
768                         if (tf == TypeCode.Byte)
769                         {
770                             return;
771                         }
772 
773                         convCode = OpCodes.Conv_I1;
774                     }
775 
776                     break;
777                 case TypeCode.Byte:
778                     if (isChecked)
779                     {
780                         convCode = isFromUnsigned ? OpCodes.Conv_Ovf_U1_Un : OpCodes.Conv_Ovf_U1;
781                     }
782                     else
783                     {
784                         if (tf == TypeCode.SByte)
785                         {
786                             return;
787                         }
788 
789                         convCode = OpCodes.Conv_U1;
790                     }
791 
792                     break;
793                 case TypeCode.Int16:
794                     switch (tf)
795                     {
796                         case TypeCode.SByte:
797                         case TypeCode.Byte:
798                             return;
799                         case TypeCode.Char:
800                         case TypeCode.UInt16:
801                             if (!isChecked)
802                             {
803                                 return;
804                             }
805 
806                             break;
807                     }
808 
809                     convCode = isChecked
810                         ? (isFromUnsigned ? OpCodes.Conv_Ovf_I2_Un : OpCodes.Conv_Ovf_I2)
811                         : OpCodes.Conv_I2;
812                     break;
813                 case TypeCode.Char:
814                 case TypeCode.UInt16:
815                     switch (tf)
816                     {
817                         case TypeCode.Byte:
818                         case TypeCode.Char:
819                         case TypeCode.UInt16:
820                             return;
821                         case TypeCode.SByte:
822                         case TypeCode.Int16:
823                             if (!isChecked)
824                             {
825                                 return;
826                             }
827 
828                             break;
829                     }
830 
831                     convCode = isChecked
832                         ? (isFromUnsigned ? OpCodes.Conv_Ovf_U2_Un : OpCodes.Conv_Ovf_U2)
833                         : OpCodes.Conv_U2;
834                     break;
835                 case TypeCode.Int32:
836                     switch (tf)
837                     {
838                         case TypeCode.Byte:
839                         case TypeCode.SByte:
840                         case TypeCode.Int16:
841                         case TypeCode.UInt16:
842                             return;
843                         case TypeCode.UInt32:
844                             if (!isChecked)
845                             {
846                                 return;
847                             }
848 
849                             break;
850                     }
851 
852                     convCode = isChecked
853                         ? (isFromUnsigned ? OpCodes.Conv_Ovf_I4_Un : OpCodes.Conv_Ovf_I4)
854                         : OpCodes.Conv_I4;
855                     break;
856                 case TypeCode.UInt32:
857                     switch (tf)
858                     {
859                         case TypeCode.Byte:
860                         case TypeCode.Char:
861                         case TypeCode.UInt16:
862                             return;
863                         case TypeCode.SByte:
864                         case TypeCode.Int16:
865                         case TypeCode.Int32:
866                             if (!isChecked)
867                             {
868                                 return;
869                             }
870 
871                             break;
872                     }
873 
874                     convCode = isChecked
875                         ? (isFromUnsigned ? OpCodes.Conv_Ovf_U4_Un : OpCodes.Conv_Ovf_U4)
876                         : OpCodes.Conv_U4;
877                     break;
878                 case TypeCode.Int64:
879                     if (!isChecked && tf == TypeCode.UInt64)
880                     {
881                         return;
882                     }
883 
884                     convCode = isChecked
885                         ? (isFromUnsigned ? OpCodes.Conv_Ovf_I8_Un : OpCodes.Conv_Ovf_I8)
886                         : (isFromUnsigned ? OpCodes.Conv_U8 : OpCodes.Conv_I8);
887                     break;
888                 case TypeCode.UInt64:
889                     if (!isChecked && tf == TypeCode.Int64)
890                     {
891                         return;
892                     }
893 
894                     convCode = isChecked
895                         ? (isFromUnsigned || tf.IsFloatingPoint() ? OpCodes.Conv_Ovf_U8_Un : OpCodes.Conv_Ovf_U8)
896                         : (isFromUnsigned || tf.IsFloatingPoint() ? OpCodes.Conv_U8 : OpCodes.Conv_I8);
897                     break;
898                 default:
899                     throw ContractUtils.Unreachable;
900             }
901 
902             il.Emit(convCode);
903         }
904 
EmitNullableToNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked, ILocalCache locals)905         private static void EmitNullableToNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked, ILocalCache locals)
906         {
907             Debug.Assert(typeFrom.IsNullableType());
908             Debug.Assert(typeTo.IsNullableType());
909             Label labIfNull;
910             Label labEnd;
911             LocalBuilder locFrom = locals.GetLocal(typeFrom);
912             il.Emit(OpCodes.Stloc, locFrom);
913             // test for null
914             il.Emit(OpCodes.Ldloca, locFrom);
915             il.EmitHasValue(typeFrom);
916             labIfNull = il.DefineLabel();
917             il.Emit(OpCodes.Brfalse_S, labIfNull);
918             il.Emit(OpCodes.Ldloca, locFrom);
919             locals.FreeLocal(locFrom);
920             il.EmitGetValueOrDefault(typeFrom);
921             Type nnTypeFrom = typeFrom.GetNonNullableType();
922             Type nnTypeTo = typeTo.GetNonNullableType();
923             il.EmitConvertToType(nnTypeFrom, nnTypeTo, isChecked, locals);
924             // construct result type
925             ConstructorInfo ci = typeTo.GetConstructor(new Type[] { nnTypeTo });
926             il.Emit(OpCodes.Newobj, ci);
927             labEnd = il.DefineLabel();
928             il.Emit(OpCodes.Br_S, labEnd);
929             // if null then create a default one
930             il.MarkLabel(labIfNull);
931             LocalBuilder locTo = locals.GetLocal(typeTo);
932             il.Emit(OpCodes.Ldloca, locTo);
933             il.Emit(OpCodes.Initobj, typeTo);
934             il.Emit(OpCodes.Ldloc, locTo);
935             locals.FreeLocal(locTo);
936             il.MarkLabel(labEnd);
937         }
938 
939 
EmitNonNullableToNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked, ILocalCache locals)940         private static void EmitNonNullableToNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked, ILocalCache locals)
941         {
942             Debug.Assert(!typeFrom.IsNullableType());
943             Debug.Assert(typeTo.IsNullableType());
944             Type nnTypeTo = typeTo.GetNonNullableType();
945             il.EmitConvertToType(typeFrom, nnTypeTo, isChecked, locals);
946             ConstructorInfo ci = typeTo.GetConstructor(new Type[] { nnTypeTo });
947             il.Emit(OpCodes.Newobj, ci);
948         }
949 
950 
EmitNullableToNonNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked, ILocalCache locals)951         private static void EmitNullableToNonNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked, ILocalCache locals)
952         {
953             Debug.Assert(typeFrom.IsNullableType());
954             Debug.Assert(!typeTo.IsNullableType());
955             if (typeTo.IsValueType)
956                 il.EmitNullableToNonNullableStructConversion(typeFrom, typeTo, isChecked, locals);
957             else
958                 il.EmitNullableToReferenceConversion(typeFrom);
959         }
960 
961 
EmitNullableToNonNullableStructConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked, ILocalCache locals)962         private static void EmitNullableToNonNullableStructConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked, ILocalCache locals)
963         {
964             Debug.Assert(typeFrom.IsNullableType());
965             Debug.Assert(!typeTo.IsNullableType());
966             Debug.Assert(typeTo.IsValueType);
967             LocalBuilder locFrom = locals.GetLocal(typeFrom);
968             il.Emit(OpCodes.Stloc, locFrom);
969             il.Emit(OpCodes.Ldloca, locFrom);
970             locals.FreeLocal(locFrom);
971             il.EmitGetValue(typeFrom);
972             Type nnTypeFrom = typeFrom.GetNonNullableType();
973             il.EmitConvertToType(nnTypeFrom, typeTo, isChecked, locals);
974         }
975 
976 
EmitNullableToReferenceConversion(this ILGenerator il, Type typeFrom)977         private static void EmitNullableToReferenceConversion(this ILGenerator il, Type typeFrom)
978         {
979             Debug.Assert(typeFrom.IsNullableType());
980             // We've got a conversion from nullable to Object, ValueType, Enum, etc.  Just box it so that
981             // we get the nullable semantics.
982             il.Emit(OpCodes.Box, typeFrom);
983         }
984 
985 
EmitNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked, ILocalCache locals)986         private static void EmitNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked, ILocalCache locals)
987         {
988             bool isTypeFromNullable = typeFrom.IsNullableType();
989             bool isTypeToNullable = typeTo.IsNullableType();
990             Debug.Assert(isTypeFromNullable || isTypeToNullable);
991             if (isTypeFromNullable && isTypeToNullable)
992                 il.EmitNullableToNullableConversion(typeFrom, typeTo, isChecked, locals);
993             else if (isTypeFromNullable)
994                 il.EmitNullableToNonNullableConversion(typeFrom, typeTo, isChecked, locals);
995             else
996                 il.EmitNonNullableToNullableConversion(typeFrom, typeTo, isChecked, locals);
997         }
998 
999 
EmitHasValue(this ILGenerator il, Type nullableType)1000         internal static void EmitHasValue(this ILGenerator il, Type nullableType)
1001         {
1002             MethodInfo mi = nullableType.GetMethod("get_HasValue", BindingFlags.Instance | BindingFlags.Public);
1003             Debug.Assert(nullableType.IsValueType);
1004             il.Emit(OpCodes.Call, mi);
1005         }
1006 
1007 
EmitGetValue(this ILGenerator il, Type nullableType)1008         internal static void EmitGetValue(this ILGenerator il, Type nullableType)
1009         {
1010             MethodInfo mi = nullableType.GetMethod("get_Value", BindingFlags.Instance | BindingFlags.Public);
1011             Debug.Assert(nullableType.IsValueType);
1012             il.Emit(OpCodes.Call, mi);
1013         }
1014 
1015 
EmitGetValueOrDefault(this ILGenerator il, Type nullableType)1016         internal static void EmitGetValueOrDefault(this ILGenerator il, Type nullableType)
1017         {
1018             MethodInfo mi = nullableType.GetMethod("GetValueOrDefault", System.Type.EmptyTypes);
1019             Debug.Assert(nullableType.IsValueType);
1020             il.Emit(OpCodes.Call, mi);
1021         }
1022 
1023         #endregion
1024 
1025         #region Arrays
1026 
1027 #if FEATURE_COMPILE_TO_METHODBUILDER
1028         /// <summary>
1029         /// Emits an array of constant values provided in the given array.
1030         /// The array is strongly typed.
1031         /// </summary>
EmitArray(this ILGenerator il, T[] items, ILocalCache locals)1032         internal static void EmitArray<T>(this ILGenerator il, T[] items, ILocalCache locals)
1033         {
1034             Debug.Assert(items != null);
1035 
1036             il.EmitPrimitive(items.Length);
1037             il.Emit(OpCodes.Newarr, typeof(T));
1038             for (int i = 0; i < items.Length; i++)
1039             {
1040                 il.Emit(OpCodes.Dup);
1041                 il.EmitPrimitive(i);
1042                 il.TryEmitConstant(items[i], typeof(T), locals);
1043                 il.EmitStoreElement(typeof(T));
1044             }
1045         }
1046 #endif
1047 
1048         /// <summary>
1049         /// Emits an array of values of count size.
1050         /// </summary>
EmitArray(this ILGenerator il, Type elementType, int count)1051         internal static void EmitArray(this ILGenerator il, Type elementType, int count)
1052         {
1053             Debug.Assert(elementType != null);
1054             Debug.Assert(count >= 0);
1055 
1056             il.EmitPrimitive(count);
1057             il.Emit(OpCodes.Newarr, elementType);
1058         }
1059 
1060         /// <summary>
1061         /// Emits an array construction code.
1062         /// The code assumes that bounds for all dimensions
1063         /// are already emitted.
1064         /// </summary>
EmitArray(this ILGenerator il, Type arrayType)1065         internal static void EmitArray(this ILGenerator il, Type arrayType)
1066         {
1067             Debug.Assert(arrayType != null);
1068             Debug.Assert(arrayType.IsArray);
1069 
1070             if (arrayType.IsSZArray)
1071             {
1072                 il.Emit(OpCodes.Newarr, arrayType.GetElementType());
1073             }
1074             else
1075             {
1076                 Type[] types = new Type[arrayType.GetArrayRank()];
1077                 for (int i = 0; i < types.Length; i++)
1078                 {
1079                     types[i] = typeof(int);
1080                 }
1081                 ConstructorInfo ci = arrayType.GetConstructor(types);
1082                 Debug.Assert(ci != null);
1083                 il.EmitNew(ci);
1084             }
1085         }
1086 
1087         #endregion
1088 
1089         #region Support for emitting constants
1090 
EmitDecimal(this ILGenerator il, decimal value)1091         private static void EmitDecimal(this ILGenerator il, decimal value)
1092         {
1093             int[] bits = decimal.GetBits(value);
1094             int scale = (bits[3] & int.MaxValue) >> 16;
1095             if (scale == 0)
1096             {
1097                 if (int.MinValue <= value)
1098                 {
1099                     if (value <= int.MaxValue)
1100                     {
1101                         int intValue = decimal.ToInt32(value);
1102                         switch (intValue)
1103                         {
1104                             case -1:
1105                                 il.Emit(OpCodes.Ldsfld, Decimal_MinusOne);
1106                                 return;
1107                             case 0:
1108                                 il.EmitDefault(typeof(decimal), locals: null); // locals won't be used.
1109                                 return;
1110                             case 1:
1111                                 il.Emit(OpCodes.Ldsfld, Decimal_One);
1112                                 return;
1113                             default:
1114                                 il.EmitPrimitive(intValue);
1115                                 il.EmitNew(Decimal_Ctor_Int32);
1116                                 return;
1117                         }
1118                     }
1119 
1120                     if (value <= uint.MaxValue)
1121                     {
1122                         il.EmitPrimitive(decimal.ToUInt32(value));
1123                         il.EmitNew(Decimal_Ctor_UInt32);
1124                         return;
1125                     }
1126                 }
1127 
1128                 if (long.MinValue <= value)
1129                 {
1130                     if (value <= long.MaxValue)
1131                     {
1132                         il.EmitPrimitive(decimal.ToInt64(value));
1133                         il.EmitNew(Decimal_Ctor_Int64);
1134                         return;
1135                     }
1136 
1137                     if (value <= ulong.MaxValue)
1138                     {
1139                         il.EmitPrimitive(decimal.ToUInt64(value));
1140                         il.EmitNew(Decimal_Ctor_UInt64);
1141                         return;
1142                     }
1143 
1144                     if (value == decimal.MaxValue)
1145                     {
1146                         il.Emit(OpCodes.Ldsfld, Decimal_MaxValue);
1147                         return;
1148                     }
1149                 }
1150                 else if (value == decimal.MinValue)
1151                 {
1152                     il.Emit(OpCodes.Ldsfld, Decimal_MinValue);
1153                     return;
1154                 }
1155             }
1156 
1157             il.EmitPrimitive(bits[0]);
1158             il.EmitPrimitive(bits[1]);
1159             il.EmitPrimitive(bits[2]);
1160             il.EmitPrimitive((bits[3] & 0x80000000) != 0);
1161             il.EmitPrimitive(unchecked((byte)scale));
1162             il.EmitNew(Decimal_Ctor_Int32_Int32_Int32_Bool_Byte);
1163         }
1164 
1165         /// <summary>
1166         /// Emits default(T)
1167         /// Semantics match C# compiler behavior
1168         /// </summary>
EmitDefault(this ILGenerator il, Type type, ILocalCache locals)1169         internal static void EmitDefault(this ILGenerator il, Type type, ILocalCache locals)
1170         {
1171             switch (type.GetTypeCode())
1172             {
1173                 case TypeCode.DateTime:
1174                     il.Emit(OpCodes.Ldsfld, DateTime_MinValue);
1175                     break;
1176 
1177                 case TypeCode.Object:
1178                     if (type.IsValueType)
1179                     {
1180                         // Type.GetTypeCode on an enum returns the underlying
1181                         // integer TypeCode, so we won't get here.
1182                         Debug.Assert(!type.IsEnum);
1183 
1184                         // This is the IL for default(T) if T is a generic type
1185                         // parameter, so it should work for any type. It's also
1186                         // the standard pattern for structs.
1187                         LocalBuilder lb = locals.GetLocal(type);
1188                         il.Emit(OpCodes.Ldloca, lb);
1189                         il.Emit(OpCodes.Initobj, type);
1190                         il.Emit(OpCodes.Ldloc, lb);
1191                         locals.FreeLocal(lb);
1192                         break;
1193                     }
1194 
1195                     goto case TypeCode.Empty;
1196 
1197                 case TypeCode.Empty:
1198                 case TypeCode.String:
1199                 case TypeCode.DBNull:
1200                     il.Emit(OpCodes.Ldnull);
1201                     break;
1202 
1203                 case TypeCode.Boolean:
1204                 case TypeCode.Char:
1205                 case TypeCode.SByte:
1206                 case TypeCode.Byte:
1207                 case TypeCode.Int16:
1208                 case TypeCode.UInt16:
1209                 case TypeCode.Int32:
1210                 case TypeCode.UInt32:
1211                     il.Emit(OpCodes.Ldc_I4_0);
1212                     break;
1213 
1214                 case TypeCode.Int64:
1215                 case TypeCode.UInt64:
1216                     il.Emit(OpCodes.Ldc_I4_0);
1217                     il.Emit(OpCodes.Conv_I8);
1218                     break;
1219 
1220                 case TypeCode.Single:
1221                     il.Emit(OpCodes.Ldc_R4, default(float));
1222                     break;
1223 
1224                 case TypeCode.Double:
1225                     il.Emit(OpCodes.Ldc_R8, default(double));
1226                     break;
1227 
1228                 case TypeCode.Decimal:
1229                     il.Emit(OpCodes.Ldsfld, Decimal_Zero);
1230                     break;
1231 
1232                 default:
1233                     throw ContractUtils.Unreachable;
1234             }
1235         }
1236 
1237         #endregion
1238     }
1239 }
1240