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.Runtime.CompilerServices;
6 using System.Threading;
7 using Xunit;
8 
9 namespace System.Linq.Expressions.Tests
10 {
11     public static class CompilerTests
12     {
13         [Theory]
14         [ClassData(typeof(CompilationTypes))]
15         [OuterLoop("Takes over a minute to complete")]
CompileDeepTree_NoStackOverflow(bool useInterpreter)16         public static void CompileDeepTree_NoStackOverflow(bool useInterpreter)
17         {
18             var e = (Expression)Expression.Constant(0);
19 
20             int n = 10000;
21 
22             for (var i = 0; i < n; i++)
23                 e = Expression.Add(e, Expression.Constant(1));
24 
25             Func<int> f = Expression.Lambda<Func<int>>(e).Compile(useInterpreter);
26 
27             Assert.Equal(n, f());
28         }
29 
30         [Theory, ClassData(typeof(CompilationTypes))]
31         [OuterLoop("May fail with SO on Debug JIT")]
CompileDeepTree_NoStackOverflowFast(bool useInterpreter)32         public static void CompileDeepTree_NoStackOverflowFast(bool useInterpreter)
33         {
34             Expression e = Expression.Constant(0);
35 
36             int n = 100;
37 
38             for (int i = 0; i < n; i++)
39                 e = Expression.Add(e, Expression.Constant(1));
40 
41             Func<int> f = null;
42             // Request a stack size of 128KiB to get very small stack.
43             // This reduces the size of tree needed to risk a stack overflow.
44             // This though will only risk overflow once, so the outerloop test
45             // above is still needed.
46             Thread t = new Thread(() => f = Expression.Lambda<Func<int>>(e).Compile(useInterpreter), 128 * 1024);
47             t.Start();
48             t.Join();
49 
50             Assert.Equal(n, f());
51         }
52 
53 #if FEATURE_COMPILE
54         [Fact]
EmitConstantsToIL_NonNullableValueTypes()55         public static void EmitConstantsToIL_NonNullableValueTypes()
56         {
57             VerifyEmitConstantsToIL((bool)true);
58 
59             VerifyEmitConstantsToIL((char)'a');
60 
61             VerifyEmitConstantsToIL((sbyte)42);
62             VerifyEmitConstantsToIL((byte)42);
63             VerifyEmitConstantsToIL((short)42);
64             VerifyEmitConstantsToIL((ushort)42);
65             VerifyEmitConstantsToIL((int)42);
66             VerifyEmitConstantsToIL((uint)42);
67             VerifyEmitConstantsToIL((long)42);
68             VerifyEmitConstantsToIL((ulong)42);
69 
70             VerifyEmitConstantsToIL((float)3.14);
71             VerifyEmitConstantsToIL((double)3.14);
72             VerifyEmitConstantsToIL((decimal)49.95m);
73         }
74 
75         [Fact]
EmitConstantsToIL_NullableValueTypes()76         public static void EmitConstantsToIL_NullableValueTypes()
77         {
78             VerifyEmitConstantsToIL((bool?)null);
79             VerifyEmitConstantsToIL((bool?)true);
80 
81             VerifyEmitConstantsToIL((char?)null);
82             VerifyEmitConstantsToIL((char?)'a');
83 
84             VerifyEmitConstantsToIL((sbyte?)null);
85             VerifyEmitConstantsToIL((sbyte?)42);
86             VerifyEmitConstantsToIL((byte?)null);
87             VerifyEmitConstantsToIL((byte?)42);
88             VerifyEmitConstantsToIL((short?)null);
89             VerifyEmitConstantsToIL((short?)42);
90             VerifyEmitConstantsToIL((ushort?)null);
91             VerifyEmitConstantsToIL((ushort?)42);
92             VerifyEmitConstantsToIL((int?)null);
93             VerifyEmitConstantsToIL((int?)42);
94             VerifyEmitConstantsToIL((uint?)null);
95             VerifyEmitConstantsToIL((uint?)42);
96             VerifyEmitConstantsToIL((long?)null);
97             VerifyEmitConstantsToIL((long?)42);
98             VerifyEmitConstantsToIL((ulong?)null);
99             VerifyEmitConstantsToIL((ulong?)42);
100 
101             VerifyEmitConstantsToIL((float?)null);
102             VerifyEmitConstantsToIL((float?)3.14);
103             VerifyEmitConstantsToIL((double?)null);
104             VerifyEmitConstantsToIL((double?)3.14);
105             VerifyEmitConstantsToIL((decimal?)null);
106             VerifyEmitConstantsToIL((decimal?)49.95m);
107 
108             VerifyEmitConstantsToIL((DateTime?)null);
109         }
110 
111         [Fact]
EmitConstantsToIL_ReferenceTypes()112         public static void EmitConstantsToIL_ReferenceTypes()
113         {
114             VerifyEmitConstantsToIL((string)null);
115             VerifyEmitConstantsToIL((string)"bar");
116         }
117 
118         [Fact]
EmitConstantsToIL_Enums()119         public static void EmitConstantsToIL_Enums()
120         {
121             VerifyEmitConstantsToIL(ConstantsEnum.A);
122             VerifyEmitConstantsToIL((ConstantsEnum?)null);
123             VerifyEmitConstantsToIL((ConstantsEnum?)ConstantsEnum.A);
124         }
125 
126         [Fact]
EmitConstantsToIL_ShareReferences()127         public static void EmitConstantsToIL_ShareReferences()
128         {
129             var o = new object();
130             VerifyEmitConstantsToIL(Expression.Equal(Expression.Constant(o), Expression.Constant(o)), 1, true);
131         }
132 
133         [Fact]
EmitConstantsToIL_LiftedToClosure()134         public static void EmitConstantsToIL_LiftedToClosure()
135         {
136             VerifyEmitConstantsToIL(DateTime.Now, 1);
137             VerifyEmitConstantsToIL((DateTime?)DateTime.Now, 1);
138         }
139 
140         [Fact]
VariableBinder_CatchBlock_Filter1()141         public static void VariableBinder_CatchBlock_Filter1()
142         {
143             // See https://github.com/dotnet/corefx/issues/11994 for reported issue
144 
145             Verify_VariableBinder_CatchBlock_Filter(
146                 Expression.Catch(
147                     Expression.Parameter(typeof(Exception), "ex"),
148                     Expression.Empty(),
149                     Expression.Parameter(typeof(bool), "???")
150                 )
151             );
152         }
153 
154         [Fact]
VariableBinder_CatchBlock_Filter2()155         public static void VariableBinder_CatchBlock_Filter2()
156         {
157             // See https://github.com/dotnet/corefx/issues/11994 for reported issue
158 
159             Verify_VariableBinder_CatchBlock_Filter(
160                 Expression.Catch(
161                     typeof(Exception),
162                     Expression.Empty(),
163                     Expression.Parameter(typeof(bool), "???")
164                 )
165             );
166         }
167 
168         [Fact]
VerifyIL_Simple()169         public static void VerifyIL_Simple()
170         {
171             Expression<Func<int>> f = () => Math.Abs(42);
172 
173             f.VerifyIL(
174                 @".method int32 ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure)
175                   {
176                     .maxstack 1
177 
178                     IL_0000: ldc.i4.s   42
179                     IL_0002: call       int32 class [System.Private.CoreLib]System.Math::Abs(int32)
180                     IL_0007: ret
181                   }");
182         }
183 
184         [Fact]
VerifyIL_Exceptions()185         public static void VerifyIL_Exceptions()
186         {
187             ParameterExpression x = Expression.Parameter(typeof(int), "x");
188             Expression<Func<int, int>> f =
189                 Expression.Lambda<Func<int, int>>(
190                     Expression.TryCatchFinally(
191                         Expression.Call(
192                             typeof(Math).GetMethod(nameof(Math.Abs), new[] { typeof(int) }),
193                             Expression.Divide(
194                                 Expression.Constant(42),
195                                 x
196                             )
197                         ),
198                         Expression.Empty(),
199                         Expression.Catch(
200                             typeof(DivideByZeroException),
201                             Expression.Constant(-1)
202                         )
203                     ),
204                     x
205                 );
206 
207             f.VerifyIL(
208                 @".method int32 ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure,int32)
209                   {
210                     .maxstack 4
211                     .locals init (
212                       [0] int32
213                     )
214 
215                     .try
216                     {
217                       .try
218                       {
219                         IL_0000: ldc.i4.s   42
220                         IL_0002: ldarg.1
221                         IL_0003: div
222                         IL_0004: call       int32 class [System.Private.CoreLib]System.Math::Abs(int32)
223                         IL_0009: stloc.0
224                         IL_000a: leave      IL_0017
225                       }
226                       catch (class [System.Private.CoreLib]System.DivideByZeroException)
227                       {
228                         IL_000f: pop
229                         IL_0010: ldc.i4.m1
230                         IL_0011: stloc.0
231                         IL_0012: leave      IL_0017
232                       }
233                       IL_0017: leave      IL_001d
234                     }
235                     finally
236                     {
237                       IL_001c: endfinally
238                     }
239                     IL_001d: ldloc.0
240                     IL_001e: ret
241                   }");
242         }
243 
244         [Fact]
VerifyIL_Closure1()245         public static void VerifyIL_Closure1()
246         {
247             Expression<Func<Func<int>>> f = () => () => 42;
248 
249             f.VerifyIL(
250                 @".method class [System.Private.CoreLib]System.Func`1<int32> ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure)
251                   {
252                     .maxstack 3
253 
254                     IL_0000: ldarg.0
255                     IL_0001: ldfld      class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure::Constants
256                     IL_0006: ldc.i4.0
257                     IL_0007: ldelem.ref
258                     IL_0008: castclass  class [System.Private.CoreLib]System.Reflection.MethodInfo
259                     IL_000d: ldtoken    class [System.Private.CoreLib]System.Func`1<int32>
260                     IL_0012: call       class [System.Private.CoreLib]System.Type class [System.Private.CoreLib]System.Type::GetTypeFromHandle(valuetype [System.Private.CoreLib]System.RuntimeTypeHandle)
261                     IL_0017: ldnull
262                     IL_0018: callvirt   instance class [System.Private.CoreLib]System.Delegate class [System.Private.CoreLib]System.Reflection.MethodInfo::CreateDelegate(class [System.Private.CoreLib]System.Type,object)
263                     IL_001d: castclass  class [System.Private.CoreLib]System.Func`1<int32>
264                     IL_0022: ret
265                   }
266 
267                   // closure.Constants[0]
268                   .method int32 ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure)
269                   {
270                     .maxstack 1
271 
272                     IL_0000: ldc.i4.s   42
273                     IL_0002: ret
274                   }",
275                 appendInnerLambdas: true);
276         }
277 
278         [Fact]
VerifyIL_Closure2()279         public static void VerifyIL_Closure2()
280         {
281             Expression<Func<int, Func<int>>> f = x => () => x;
282 
283             f.VerifyIL(
284                 @".method class [System.Private.CoreLib]System.Func`1<int32> ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure,int32)
285                   {
286                     .maxstack 8
287                     .locals init (
288                       [0] object[]
289                     )
290 
291                     IL_0000: ldc.i4.1
292                     IL_0001: newarr     object
293                     IL_0006: dup
294                     IL_0007: ldc.i4.0
295                     IL_0008: ldarg.1
296                     IL_0009: newobj     instance void class [System.Private.CoreLib]System.Runtime.CompilerServices.StrongBox`1<int32>::.ctor(int32)
297                     IL_000e: stelem.ref
298                     IL_000f: stloc.0
299                     IL_0010: ldarg.0
300                     IL_0011: ldfld      class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure::Constants
301                     IL_0016: ldc.i4.0
302                     IL_0017: ldelem.ref
303                     IL_0018: castclass  class [System.Private.CoreLib]System.Reflection.MethodInfo
304                     IL_001d: ldtoken    class [System.Private.CoreLib]System.Func`1<int32>
305                     IL_0022: call       class [System.Private.CoreLib]System.Type class [System.Private.CoreLib]System.Type::GetTypeFromHandle(valuetype [System.Private.CoreLib]System.RuntimeTypeHandle)
306                     IL_0027: ldnull
307                     IL_0028: ldloc.0
308                     IL_0029: newobj     instance void class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure::.ctor(object[],object[])
309                     IL_002e: callvirt   instance class [System.Private.CoreLib]System.Delegate class [System.Private.CoreLib]System.Reflection.MethodInfo::CreateDelegate(class [System.Private.CoreLib]System.Type,object)
310                     IL_0033: castclass  class [System.Private.CoreLib]System.Func`1<int32>
311                     IL_0038: ret
312                   }
313 
314                   // closure.Constants[0]
315                   .method int32 ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure)
316                   {
317                     .maxstack 2
318                     .locals init (
319                       [0] object[]
320                     )
321 
322                     IL_0000: ldarg.0
323                     IL_0001: ldfld      class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure::Locals
324                     IL_0006: stloc.0
325                     IL_0007: ldloc.0
326                     IL_0008: ldc.i4.0
327                     IL_0009: ldelem.ref
328                     IL_000a: castclass  class [System.Private.CoreLib]System.Runtime.CompilerServices.StrongBox`1<int32>
329                     IL_000f: ldfld      class [System.Private.CoreLib]System.Runtime.CompilerServices.StrongBox`1<int32>::Value
330                     IL_0014: ret
331                   }",
332                 appendInnerLambdas: true);
333         }
334 
335         [Fact]
VerifyIL_Closure3()336         public static void VerifyIL_Closure3()
337         {
338             // Using an unchecked addition to ensure that an add instruction is emitted (and not add.ovf)
339             Expression<Func<int, Func<int, int>>> f = x => y => unchecked(x + y);
340 
341             f.VerifyIL(
342                 @".method class [System.Private.CoreLib]System.Func`2<int32,int32> ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure,int32)
343                   {
344                     .maxstack 8
345                     .locals init (
346                       [0] object[]
347                     )
348 
349                     IL_0000: ldc.i4.1
350                     IL_0001: newarr     object
351                     IL_0006: dup
352                     IL_0007: ldc.i4.0
353                     IL_0008: ldarg.1
354                     IL_0009: newobj     instance void class [System.Private.CoreLib]System.Runtime.CompilerServices.StrongBox`1<int32>::.ctor(int32)
355                     IL_000e: stelem.ref
356                     IL_000f: stloc.0
357                     IL_0010: ldarg.0
358                     IL_0011: ldfld      class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure::Constants
359                     IL_0016: ldc.i4.0
360                     IL_0017: ldelem.ref
361                     IL_0018: castclass  class [System.Private.CoreLib]System.Reflection.MethodInfo
362                     IL_001d: ldtoken    class [System.Private.CoreLib]System.Func`2<int32,int32>
363                     IL_0022: call       class [System.Private.CoreLib]System.Type class [System.Private.CoreLib]System.Type::GetTypeFromHandle(valuetype [System.Private.CoreLib]System.RuntimeTypeHandle)
364                     IL_0027: ldnull
365                     IL_0028: ldloc.0
366                     IL_0029: newobj     instance void class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure::.ctor(object[],object[])
367                     IL_002e: callvirt   instance class [System.Private.CoreLib]System.Delegate class [System.Private.CoreLib]System.Reflection.MethodInfo::CreateDelegate(class [System.Private.CoreLib]System.Type,object)
368                     IL_0033: castclass  class [System.Private.CoreLib]System.Func`2<int32,int32>
369                     IL_0038: ret
370                   }
371 
372                   // closure.Constants[0]
373                   .method int32 ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure,int32)
374                   {
375                     .maxstack 2
376                     .locals init (
377                       [0] object[]
378                     )
379 
380                     IL_0000: ldarg.0
381                     IL_0001: ldfld      class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure::Locals
382                     IL_0006: stloc.0
383                     IL_0007: ldloc.0
384                     IL_0008: ldc.i4.0
385                     IL_0009: ldelem.ref
386                     IL_000a: castclass  class [System.Private.CoreLib]System.Runtime.CompilerServices.StrongBox`1<int32>
387                     IL_000f: ldfld      class [System.Private.CoreLib]System.Runtime.CompilerServices.StrongBox`1<int32>::Value
388                     IL_0014: ldarg.1
389                     IL_0015: add
390                     IL_0016: ret
391                   }",
392                 appendInnerLambdas: true);
393         }
394 
VerifyIL(this LambdaExpression expression, string expected, bool appendInnerLambdas = false)395         public static void VerifyIL(this LambdaExpression expression, string expected, bool appendInnerLambdas = false)
396         {
397 #if MONO
398             // TODO: Implement ILReaderFactory and friends correctly
399             string actual = expected;
400 #else
401             string actual = expression.GetIL(appendInnerLambdas);
402 #endif
403 
404             string nExpected = Normalize(expected);
405             string nActual = Normalize(actual);
406 
407             Assert.Equal(nExpected, nActual);
408         }
409 
Normalize(string s)410         private static string Normalize(string s)
411         {
412             Collections.Generic.IEnumerable<string> lines =
413                 s
414                 .Replace("\r\n", "\n")
415                 .Split(new[] { '\n' })
416                 .Select(line => line.Trim())
417                 .Where(line => line != "" && !line.StartsWith("//"));
418 
419             return string.Join("\n", lines);
420         }
421 
VerifyEmitConstantsToIL(T value)422         private static void VerifyEmitConstantsToIL<T>(T value)
423         {
424             VerifyEmitConstantsToIL<T>(value, 0);
425         }
426 
VerifyEmitConstantsToIL(T value, int expectedCount)427         private static void VerifyEmitConstantsToIL<T>(T value, int expectedCount)
428         {
429             VerifyEmitConstantsToIL(Expression.Constant(value, typeof(T)), expectedCount, value);
430         }
431 
VerifyEmitConstantsToIL(Expression e, int expectedCount, object expectedValue)432         private static void VerifyEmitConstantsToIL(Expression e, int expectedCount, object expectedValue)
433         {
434             Delegate f = Expression.Lambda(e).Compile();
435 
436             var c = f.Target as Closure;
437             Assert.NotNull(c);
438             Assert.Equal(expectedCount, c.Constants.Length);
439 
440             object o = f.DynamicInvoke();
441             Assert.Equal(expectedValue, o);
442         }
443 
444         private static void Verify_VariableBinder_CatchBlock_Filter(CatchBlock @catch)
445         {
446             Expression<Action> e =
447                 Expression.Lambda<Action>(
448                     Expression.TryCatch(
449                         Expression.Empty(),
450                         @catch
451                     )
452                 );
453 
454             Assert.Throws<InvalidOperationException>(() => e.Compile());
455         }
456 #endif
457     }
458 
459     public enum ConstantsEnum
460     {
461         A
462     }
463 }
464