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