1 // 2 // ExpressionTest_Call.cs 3 // 4 // Author: 5 // Federico Di Gregorio <fog@initd.org> 6 // Jb Evain (jbevain@novell.com) 7 // 8 // (C) 2008 Novell, Inc. (http://www.novell.com) 9 // 10 // Permission is hereby granted, free of charge, to any person obtaining 11 // a copy of this software and associated documentation files (the 12 // "Software"), to deal in the Software without restriction, including 13 // without limitation the rights to use, copy, modify, merge, publish, 14 // distribute, sublicense, and/or sell copies of the Software, and to 15 // permit persons to whom the Software is furnished to do so, subject to 16 // the following conditions: 17 // 18 // The above copyright notice and this permission notice shall be 19 // included in all copies or substantial portions of the Software. 20 // 21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 // 29 30 using System; 31 using System.Reflection; 32 using System.Reflection.Emit; 33 using System.Collections.Generic; 34 using System.Linq; 35 using System.Linq.Expressions; 36 using NUnit.Framework; 37 38 namespace MonoTests.System.Linq.Expressions { 39 40 [TestFixture] 41 public class ExpressionTest_Call { 42 43 [Test] 44 [ExpectedException (typeof (ArgumentNullException))] Arg1Null()45 public void Arg1Null () 46 { 47 Expression.Call ((Type)null, "TestMethod", null, Expression.Constant (1)); 48 } 49 50 [Test] 51 [ExpectedException (typeof (ArgumentNullException))] Arg2Null()52 public void Arg2Null () 53 { 54 Expression.Call (typeof (MemberClass), null, null, Expression.Constant (1)); 55 } 56 57 [Test] 58 [ExpectedException (typeof (InvalidOperationException))] Arg4WrongType()59 public void Arg4WrongType () 60 { 61 Expression.Call (typeof (MemberClass), "StaticMethod", null, Expression.Constant (true)); 62 } 63 64 [Test] 65 [ExpectedException (typeof (InvalidOperationException))] InstanceMethod()66 public void InstanceMethod () 67 { 68 Expression.Call (typeof (MemberClass), "TestMethod", null, Expression.Constant (1)); 69 } 70 71 [Test] StaticMethod()72 public void StaticMethod () 73 { 74 Expression.Call (typeof (MemberClass), "StaticMethod", null, Expression.Constant (1)); 75 } 76 77 [Test] StaticGenericMethod()78 public void StaticGenericMethod () 79 { 80 Expression.Call (typeof (MemberClass), "StaticGenericMethod", new [] { typeof (int) }, Expression.Constant (1)); 81 } 82 83 [Test] 84 [ExpectedException (typeof (ArgumentNullException))] ArgMethodNull()85 public void ArgMethodNull () 86 { 87 Expression.Call (Expression.Constant (new object ()), null); 88 } 89 90 [Test] 91 [ExpectedException (typeof (ArgumentException))] ArgInstanceNullForNonStaticMethod()92 public void ArgInstanceNullForNonStaticMethod () 93 { 94 Expression.Call (null, typeof (object).GetMethod ("ToString")); 95 } 96 97 [Test] 98 [ExpectedException (typeof (ArgumentException))] InstanceTypeDoesntMatchMethodDeclaringType()99 public void InstanceTypeDoesntMatchMethodDeclaringType () 100 { 101 #if MOBILE 102 // ensure that String.Intern won't be removed by the linker 103 string s = String.Intern (String.Empty); 104 #endif 105 Expression.Call (Expression.Constant (1), typeof (string).GetMethod ("Intern")); 106 } 107 108 [Test] 109 [ExpectedException (typeof (ArgumentException))] MethodArgumentCountDoesnMatchParameterLength()110 public void MethodArgumentCountDoesnMatchParameterLength () 111 { 112 Expression.Call (Expression.Constant (new object ()), typeof (object).GetMethod ("ToString"), Expression.Constant (new object ())); 113 } 114 115 public class Foo { Bar(string s)116 public void Bar (string s) 117 { 118 } 119 } 120 121 [Test] 122 [ExpectedException (typeof (ArgumentNullException))] MethodHasNullArgument()123 public void MethodHasNullArgument () 124 { 125 Expression.Call (Expression.New (typeof (Foo)), typeof (Foo).GetMethod ("Bar"), null as Expression); 126 } 127 128 [Test] 129 [ExpectedException (typeof (ArgumentException))] MethodArgumentDoesntMatchParameterType()130 public void MethodArgumentDoesntMatchParameterType () 131 { 132 Expression.Call (Expression.New (typeof (Foo)), typeof (Foo).GetMethod ("Bar"), Expression.Constant (42)); 133 } 134 135 [Test] CallToString()136 public void CallToString () 137 { 138 var call = Expression.Call (Expression.Constant (new object ()), typeof (object).GetMethod ("ToString")); 139 Assert.AreEqual ("value(System.Object).ToString()", call.ToString ()); 140 } 141 142 [Test] CallStringIsNullOrEmpty()143 public void CallStringIsNullOrEmpty () 144 { 145 var call = Expression.Call (null, typeof (string).GetMethod ("IsNullOrEmpty"), Expression.Constant ("")); 146 Assert.AreEqual ("IsNullOrEmpty(\"\")", call.ToString ()); 147 } 148 149 [Test] 150 [Category ("NotDotNet")] // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=339351 151 [ExpectedException (typeof (ArgumentException))] CallStaticMethodWithInstanceArgument()152 public void CallStaticMethodWithInstanceArgument () 153 { 154 Expression.Call ( 155 Expression.Parameter (GetType (), "t"), 156 GetType ().GetMethod ("Identity"), 157 Expression.Constant (null)); 158 } 159 Identity(object o)160 public static object Identity (object o) 161 { 162 return o; 163 } 164 165 [Test] CompileSimpleStaticCall()166 public void CompileSimpleStaticCall () 167 { 168 var p = Expression.Parameter (typeof (object), "o"); 169 var lambda = Expression.Lambda<Func<object, object>> (Expression.Call (GetType ().GetMethod ("Identity"), p), p); 170 171 var i = lambda.Compile (); 172 173 Assert.AreEqual (2, i (2)); 174 Assert.AreEqual ("Foo", i ("Foo")); 175 } 176 177 [Test] CompileSimpleInstanceCall()178 public void CompileSimpleInstanceCall () 179 { 180 var p = Expression.Parameter (typeof (string), "p"); 181 var lambda = Expression.Lambda<Func<string, string>> ( 182 Expression.Call ( 183 p, typeof (string).GetMethod ("ToString", Type.EmptyTypes)), 184 p); 185 186 var ts = lambda.Compile (); 187 188 Assert.AreEqual ("foo", ts ("foo")); 189 Assert.AreEqual ("bar", ts ("bar")); 190 } 191 192 [Test] 193 [ExpectedException (typeof (InvalidOperationException))] CheckTypeArgsIsNotUsedForParameterLookup()194 public void CheckTypeArgsIsNotUsedForParameterLookup () 195 { 196 Expression.Call (GetType (), "EineMethod", new [] { typeof (string), typeof (int) }, "foo".ToConstant (), 2.ToConstant ()); 197 } 198 EineGenericMethod(string foo, int bar)199 public static void EineGenericMethod<X, Y> (string foo, int bar) 200 { 201 } 202 203 [Test] CheckTypeArgsIsUsedForGenericArguments()204 public void CheckTypeArgsIsUsedForGenericArguments () 205 { 206 var m = Expression.Call (GetType (), "EineGenericMethod", new [] { typeof (string), typeof (int) }, "foo".ToConstant (), 2.ToConstant ()); 207 Assert.IsNotNull (m.Method); 208 Assert.AreEqual ("Void EineGenericMethod[String,Int32](System.String, Int32)", m.Method.ToString ()); 209 } 210 211 public struct EineStrukt { 212 213 public string Foo; 214 EineStruktMonoTests.System.Linq.Expressions.ExpressionTest_Call.EineStrukt215 public EineStrukt (string foo) 216 { 217 Foo = foo; 218 } 219 GimmeFooMonoTests.System.Linq.Expressions.ExpressionTest_Call.EineStrukt220 public string GimmeFoo () 221 { 222 return Foo; 223 } 224 } 225 226 [Test] CallMethodOnStruct()227 public void CallMethodOnStruct () 228 { 229 var param = Expression.Parameter (typeof (EineStrukt), "s"); 230 var foo = Expression.Lambda<Func<EineStrukt, string>> ( 231 Expression.Call (param, typeof (EineStrukt).GetMethod ("GimmeFoo")), param).Compile (); 232 233 var s = new EineStrukt ("foo"); 234 Assert.AreEqual ("foo", foo (s)); 235 } 236 OneStaticMethod()237 public static int OneStaticMethod () 238 { 239 return 42; 240 } 241 242 [Test] CallMethodOnDateTime()243 public void CallMethodOnDateTime () 244 { 245 var left = Expression.Call (Expression.Constant (DateTime.Now), typeof(DateTime).GetMethod ("AddDays"), Expression.Constant (-5.0)); 246 var right = Expression.Constant (DateTime.Today.AddDays (1)); 247 var expr = Expression.GreaterThan (left, right); 248 249 var eq = Expression.Lambda<Func<bool>> (expr).Compile (); 250 Assert.IsFalse (eq ()); 251 } 252 253 [Test] 254 [Category ("NotDotNet")] // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=339351 255 [ExpectedException (typeof (ArgumentException))] CallStaticMethodOnNonSenseInstanceExpression()256 public void CallStaticMethodOnNonSenseInstanceExpression () 257 { 258 Expression.Call ( 259 Expression.Constant ("la la la"), 260 this.GetType ().GetMethod ("OneStaticMethod")); 261 } 262 DoSomethingWith(ref int a)263 public static int DoSomethingWith (ref int a) 264 { 265 return a + 4; 266 } 267 DoAnotherThing(ref int a, string s)268 public static string DoAnotherThing (ref int a, string s) 269 { 270 return s + a; 271 } 272 273 [Test] CallStaticMethodWithRefParameter()274 public void CallStaticMethodWithRefParameter () 275 { 276 var p = Expression.Parameter (typeof (int), "i"); 277 278 var c = Expression.Lambda<Func<int, int>> ( 279 Expression.Call (GetType ().GetMethod ("DoSomethingWith"), p), p).Compile (); 280 281 Assert.AreEqual (42, c (38)); 282 } 283 284 [Test] CallStaticMethodWithRefParameterAndOtherParameter()285 public void CallStaticMethodWithRefParameterAndOtherParameter () 286 { 287 var i = Expression.Parameter (typeof (int), "i"); 288 var s = Expression.Parameter (typeof (string), "s"); 289 290 var lamda = Expression.Lambda<Func<int, string, string>> ( 291 Expression.Call (GetType ().GetMethod ("DoAnotherThing"), i, s), i, s).Compile (); 292 293 Assert.AreEqual ("foo42", lamda (42, "foo")); 294 } 295 296 #if !FULL_AOT_RUNTIME 297 [Test] CallDynamicMethod_ToString()298 public void CallDynamicMethod_ToString () 299 { 300 // Regression test for #49686 301 var m = new DynamicMethod ("intIntId", typeof (int), new Type [] { typeof (int) }); 302 var ilg = m.GetILGenerator (); 303 ilg.Emit (OpCodes.Ldarg_0); 304 ilg.Emit (OpCodes.Ret); 305 306 var i = Expression.Parameter (typeof (int), "i"); 307 var e = Expression.Call (m, i); 308 309 Assert.IsNotNull (e.ToString ()); 310 } 311 312 [Test] CallDynamicMethod_CompileInvoke()313 public void CallDynamicMethod_CompileInvoke () 314 { 315 var m = new DynamicMethod ("intIntId", typeof (int), new Type [] { typeof (int) }); 316 var ilg = m.GetILGenerator (); 317 ilg.Emit (OpCodes.Ldarg_0); 318 ilg.Emit (OpCodes.Ret); 319 320 var i = Expression.Parameter (typeof (int), "i"); 321 var e = Expression.Call (m, i); 322 323 var lambda = Expression.Lambda<Func<int, int>> (e, i).Compile (); 324 Assert.AreEqual (42, lambda (42)); 325 } 326 #endif 327 Bang(Expression i)328 public static int Bang (Expression i) 329 { 330 return (int) (i as ConstantExpression).Value; 331 } 332 static bool fout_called = false; 333 FooOut(out int x)334 public static int FooOut (out int x) 335 { 336 fout_called = true; 337 return x = 0; 338 } 339 340 [Test] Connect282729()341 public void Connect282729 () 342 { 343 // test from https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=282729 344 345 var p = Expression.Parameter (typeof (int), "p"); 346 var lambda = Expression.Lambda<Func<int, int>> ( 347 Expression.Call ( 348 GetType ().GetMethod ("FooOut"), 349 Expression.ArrayIndex( 350 Expression.NewArrayBounds ( 351 typeof(int), 352 1.ToConstant ()), 353 0.ToConstant ())), 354 p).Compile (); 355 356 Assert.AreEqual (0, lambda (0)); 357 Assert.IsTrue (fout_called); 358 } 359 FooOut2(out int x)360 public static int FooOut2 (out int x) 361 { 362 x = 2; 363 return 3; 364 } 365 366 [Test] Connect290278()367 public void Connect290278 () 368 { 369 // test from https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=290278 370 371 var p = Expression.Parameter (typeof (int [,]), "p"); 372 var lambda = Expression.Lambda<Func<int [,], int>> ( 373 Expression.Call ( 374 GetType ().GetMethod ("FooOut2"), 375 Expression.ArrayIndex (p, 0.ToConstant (), 0.ToConstant ())), 376 p).Compile (); 377 378 int [,] data = { { 1 } }; 379 380 Assert.AreEqual (3, lambda (data)); 381 Assert.AreEqual (2, data [0, 0]); 382 } 383 FooRef(ref string s)384 public static void FooRef (ref string s) 385 { 386 } 387 388 [Test] Connect297597()389 public void Connect297597 () 390 { 391 // test from https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=297597 392 393 var strings = new string [1]; 394 395 var lambda = Expression.Lambda<Action> ( 396 Expression.Call ( 397 GetType ().GetMethod ("FooRef"), 398 Expression.ArrayIndex ( 399 Expression.Constant (strings), 0.ToConstant ()))).Compile (); 400 401 lambda (); 402 } 403 404 [Test] 405 [Category ("NotDotNet")] // https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=319190 406 [Category ("NotWorkingInterpreter")] Connect319190()407 public void Connect319190 () 408 { 409 var lambda = Expression.Lambda<Func<bool>> ( 410 Expression.TypeIs ( 411 Expression.New (typeof (TypedReference)), 412 typeof (object))).Compile (); 413 414 Assert.IsTrue (lambda ()); 415 } 416 Truc()417 public static int Truc () 418 { 419 return 42; 420 } 421 422 [Test] Connect282702()423 public void Connect282702 () 424 { 425 var lambda = Expression.Lambda<Func<Func<int>>> ( 426 Expression.Convert ( 427 Expression.Call ( 428 typeof (Delegate).GetMethod ("CreateDelegate", new [] { typeof (Type), typeof (object), typeof (MethodInfo) }), 429 Expression.Constant (typeof (Func<int>), typeof (Type)), 430 Expression.Constant (null, typeof (object)), 431 Expression.Constant (GetType ().GetMethod ("Truc"))), 432 typeof (Func<int>))).Compile (); 433 434 Assert.AreEqual (42, lambda ().Invoke ()); 435 } 436 437 [Test] CallQueryableWhere()438 public void CallQueryableWhere () 439 { 440 var queryable = new [] { 1, 2, 3 }.AsQueryable (); 441 442 var parameter = Expression.Parameter (typeof (int), "i"); 443 var lambda = Expression.Lambda<Func<int, bool>> ( 444 Expression.LessThan (parameter, Expression.Constant (2)), 445 parameter); 446 447 var selector = Expression.Quote (lambda); 448 449 var call = Expression.Call ( 450 typeof (Queryable), 451 "Where", 452 new [] { typeof (int) }, 453 queryable.Expression, 454 selector); 455 456 Assert.IsNotNull (call); 457 Assert.IsNotNull (call.Method); 458 } 459 460 [Test] CallAsQueryable()461 public void CallAsQueryable () // #537768 462 { 463 var constant = Expression.Constant ( 464 new List<string> (), 465 typeof (IEnumerable<string>)); 466 467 var call = Expression.Call ( 468 typeof (Queryable), 469 "AsQueryable", 470 new [] { typeof (string) }, 471 constant); 472 473 Assert.IsNotNull (call); 474 Assert.AreEqual (1, call.Arguments.Count); 475 Assert.AreEqual (constant, call.Arguments [0]); 476 477 var method = call.Method; 478 479 Assert.AreEqual ("AsQueryable", method.Name); 480 Assert.IsTrue (method.IsGenericMethod); 481 Assert.AreEqual (typeof (string), method.GetGenericArguments () [0]); 482 } 483 484 485 [Test] CallQueryableSelect()486 public void CallQueryableSelect () // #536637 487 { 488 var parameter = Expression.Parameter (typeof (string), "s"); 489 var string_length = Expression.Property (parameter, typeof (string).GetProperty ("Length")); 490 var lambda = Expression.Lambda (string_length, parameter); 491 492 var strings = new [] { "1", "22", "333" }; 493 494 var call = Expression.Call ( 495 typeof (Queryable), 496 "Select", 497 new [] { typeof (string), typeof (int) }, 498 Expression.Constant (strings.AsQueryable ()), 499 lambda); 500 501 Assert.IsNotNull (call); 502 503 var method = call.Method; 504 505 Assert.AreEqual ("Select", method.Name); 506 Assert.IsTrue (method.IsGenericMethod); 507 Assert.AreEqual (typeof (string), method.GetGenericArguments () [0]); 508 Assert.AreEqual (typeof (int), method.GetGenericArguments () [1]); 509 } 510 511 [Test] CallNullableGetValueOrDefault()512 public void CallNullableGetValueOrDefault () // #568989 513 { 514 #if MOBILE 515 // ensure that int?.GetValueOrDefault won't be removed by the linker 516 Assert.AreEqual (0, ((int?)0).GetValueOrDefault (3)); 517 #endif 518 519 var value = Expression.Parameter (typeof (int?), "value"); 520 var default_parameter = Expression.Parameter (typeof (int), "default"); 521 522 var getter = Expression.Lambda<Func<int?, int, int>> ( 523 Expression.Call ( 524 value, 525 "GetValueOrDefault", 526 Type.EmptyTypes, 527 default_parameter), 528 value, 529 default_parameter).Compile (); 530 531 Assert.AreEqual (2, getter (null, 2)); 532 Assert.AreEqual (4, getter (4, 2)); 533 } 534 535 [Test] CallToStringOnEnum()536 public void CallToStringOnEnum () // #625367 537 { 538 var lambda = Expression.Lambda<Func<string>> ( 539 Expression.Call ( 540 Expression.Constant (TypeCode.Boolean, typeof (TypeCode)), 541 typeof (object).GetMethod ("ToString"))).Compile (); 542 543 Assert.AreEqual ("Boolean", lambda ()); 544 } 545 AcceptsIEnumerable(IEnumerable<object> o)546 public static void AcceptsIEnumerable(IEnumerable<object> o) 547 { 548 } 549 550 [Test] CallIQueryableMethodWithNewArrayBoundExpression()551 public void CallIQueryableMethodWithNewArrayBoundExpression () // #2304 552 { 553 Expression.Call ( 554 GetType ().GetMethod ("AcceptsIEnumerable", BindingFlags.Public | BindingFlags.Static), 555 Expression.NewArrayBounds (typeof (object), Expression.Constant (0))); 556 } 557 } 558 } 559