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.Collections.Generic; 6 using System.Linq; 7 using System.Linq.Expressions; 8 using System.Linq.Expressions.Tests; 9 using Microsoft.CSharp.RuntimeBinder; 10 using Xunit; 11 12 namespace System.Dynamic.Tests 13 { 14 public class BinaryOperationTests 15 { 16 private class MinimumOverrideBinaryOperationBinder : BinaryOperationBinder 17 { MinimumOverrideBinaryOperationBinder(ExpressionType operation)18 public MinimumOverrideBinaryOperationBinder(ExpressionType operation) : base(operation) 19 { 20 } 21 FallbackBinaryOperation( DynamicMetaObject target, DynamicMetaObject arg, DynamicMetaObject errorSuggestion)22 public override DynamicMetaObject FallbackBinaryOperation( 23 DynamicMetaObject target, DynamicMetaObject arg, DynamicMetaObject errorSuggestion) 24 { 25 throw new NotSupportedException(); 26 } 27 } 28 29 private static readonly int[] SomeInt32 = {0, 1, 2, -1, int.MinValue, int.MaxValue, int.MaxValue - 1}; 30 CrossJoinInt32()31 private static IEnumerable<object[]> CrossJoinInt32() 32 => from i in SomeInt32 from j in SomeInt32 select new object[] {i, j}; 33 34 private static readonly double[] SomeDouble = {0.0, 1.0, 2.0, -1.0, double.PositiveInfinity, double.NaN}; 35 CrossJoinDouble()36 private static IEnumerable<object[]> CrossJoinDouble() 37 => from i in SomeDouble from j in SomeDouble select new object[] {i, j}; 38 BinaryExpressionTypes()39 private static IEnumerable<object[]> BinaryExpressionTypes() 40 { 41 yield return new object[] {ExpressionType.Add}; 42 yield return new object[] {ExpressionType.And}; 43 yield return new object[] {ExpressionType.Divide}; 44 yield return new object[] {ExpressionType.Equal}; 45 yield return new object[] {ExpressionType.ExclusiveOr}; 46 yield return new object[] {ExpressionType.GreaterThan}; 47 yield return new object[] {ExpressionType.GreaterThanOrEqual}; 48 yield return new object[] {ExpressionType.LeftShift}; 49 yield return new object[] {ExpressionType.LessThan}; 50 yield return new object[] {ExpressionType.LessThanOrEqual}; 51 yield return new object[] {ExpressionType.Modulo}; 52 yield return new object[] {ExpressionType.Multiply}; 53 yield return new object[] {ExpressionType.NotEqual}; 54 yield return new object[] {ExpressionType.Or}; 55 yield return new object[] {ExpressionType.Power}; 56 yield return new object[] {ExpressionType.RightShift}; 57 yield return new object[] {ExpressionType.Subtract}; 58 yield return new object[] {ExpressionType.Extension}; 59 yield return new object[] {ExpressionType.AddAssign}; 60 yield return new object[] {ExpressionType.AndAssign}; 61 yield return new object[] {ExpressionType.DivideAssign}; 62 yield return new object[] {ExpressionType.ExclusiveOrAssign}; 63 yield return new object[] {ExpressionType.LeftShiftAssign}; 64 yield return new object[] {ExpressionType.ModuloAssign}; 65 yield return new object[] {ExpressionType.MultiplyAssign}; 66 yield return new object[] {ExpressionType.OrAssign}; 67 yield return new object[] {ExpressionType.PowerAssign}; 68 yield return new object[] {ExpressionType.RightShiftAssign}; 69 yield return new object[] {ExpressionType.SubtractAssign}; 70 } 71 NonBinaryExpressionTypes()72 private static IEnumerable<object[]> NonBinaryExpressionTypes() 73 { 74 yield return new object[] {ExpressionType.AddChecked}; 75 yield return new object[] {ExpressionType.AndAlso}; 76 yield return new object[] {ExpressionType.ArrayLength}; 77 yield return new object[] {ExpressionType.ArrayIndex}; 78 yield return new object[] {ExpressionType.Call}; 79 yield return new object[] {ExpressionType.Coalesce}; 80 yield return new object[] {ExpressionType.Conditional}; 81 yield return new object[] {ExpressionType.Constant}; 82 yield return new object[] {ExpressionType.Convert}; 83 yield return new object[] {ExpressionType.ConvertChecked}; 84 yield return new object[] {ExpressionType.Invoke}; 85 yield return new object[] {ExpressionType.Lambda}; 86 yield return new object[] {ExpressionType.ListInit}; 87 yield return new object[] {ExpressionType.MemberAccess}; 88 yield return new object[] {ExpressionType.MemberInit}; 89 yield return new object[] {ExpressionType.MultiplyChecked}; 90 yield return new object[] {ExpressionType.Negate}; 91 yield return new object[] {ExpressionType.UnaryPlus}; 92 yield return new object[] {ExpressionType.NegateChecked}; 93 yield return new object[] {ExpressionType.New}; 94 yield return new object[] {ExpressionType.NewArrayInit}; 95 yield return new object[] {ExpressionType.NewArrayBounds}; 96 yield return new object[] {ExpressionType.Not}; 97 yield return new object[] {ExpressionType.OrElse}; 98 yield return new object[] {ExpressionType.Parameter}; 99 yield return new object[] {ExpressionType.Quote}; 100 yield return new object[] {ExpressionType.SubtractChecked}; 101 yield return new object[] {ExpressionType.TypeAs}; 102 yield return new object[] {ExpressionType.TypeIs}; 103 yield return new object[] {ExpressionType.Assign}; 104 yield return new object[] {ExpressionType.Block}; 105 yield return new object[] {ExpressionType.DebugInfo}; 106 yield return new object[] {ExpressionType.Decrement}; 107 yield return new object[] {ExpressionType.Dynamic}; 108 yield return new object[] {ExpressionType.Default}; 109 yield return new object[] {ExpressionType.Goto}; 110 yield return new object[] {ExpressionType.Increment}; 111 yield return new object[] {ExpressionType.Index}; 112 yield return new object[] {ExpressionType.Label}; 113 yield return new object[] {ExpressionType.RuntimeVariables}; 114 yield return new object[] {ExpressionType.Loop}; 115 yield return new object[] {ExpressionType.Switch}; 116 yield return new object[] {ExpressionType.Throw}; 117 yield return new object[] {ExpressionType.Try}; 118 yield return new object[] {ExpressionType.Unbox}; 119 yield return new object[] {ExpressionType.AddAssignChecked}; 120 yield return new object[] {ExpressionType.MultiplyAssignChecked}; 121 yield return new object[] {ExpressionType.SubtractAssignChecked}; 122 yield return new object[] {ExpressionType.PreIncrementAssign}; 123 yield return new object[] {ExpressionType.PreDecrementAssign}; 124 yield return new object[] {ExpressionType.PostIncrementAssign}; 125 yield return new object[] {ExpressionType.PostDecrementAssign}; 126 yield return new object[] {ExpressionType.TypeEqual}; 127 yield return new object[] {ExpressionType.OnesComplement}; 128 yield return new object[] {ExpressionType.IsTrue}; 129 yield return new object[] {ExpressionType.IsFalse}; 130 } 131 132 [Theory, MemberData(nameof(CrossJoinInt32))] AddInt32(int x, int y)133 public void AddInt32(int x, int y) 134 { 135 dynamic dX = x; 136 dynamic dY = y; 137 Assert.Equal(unchecked(x + y), unchecked(dX + dY)); 138 } 139 140 [Theory, MemberData(nameof(CrossJoinInt32))] AddOvfInt32(int x, int y)141 public void AddOvfInt32(int x, int y) 142 { 143 dynamic dX = x; 144 dynamic dY = y; 145 int result; 146 try 147 { 148 result = checked(x + y); 149 } 150 catch (OverflowException) 151 { 152 Assert.Throws<OverflowException>(() => checked(dX + dY)); 153 return; 154 } 155 156 Assert.Equal(result, checked(dX + dY)); 157 } 158 159 [Theory, MemberData(nameof(CrossJoinInt32))] AndInt32(int x, int y)160 public void AndInt32(int x, int y) 161 { 162 dynamic dX = x; 163 dynamic dY = y; 164 Assert.Equal(x & y, dX & dY); 165 } 166 167 [Theory, MemberData(nameof(CrossJoinInt32))] DivideInt32(int x, int y)168 public void DivideInt32(int x, int y) 169 { 170 dynamic dX = x; 171 dynamic dY = y; 172 if (y == 0) 173 Assert.Throws<DivideByZeroException>(() => dX / dY); 174 else if (y == -1 && x == int.MinValue) 175 Assert.Throws<OverflowException>(() => dX / dY); 176 else 177 Assert.Equal(x / y, dX / dY); 178 } 179 180 [Theory, MemberData(nameof(CrossJoinInt32))] EqualInt32(int x, int y)181 public void EqualInt32(int x, int y) 182 { 183 dynamic dX = x; 184 dynamic dY = y; 185 Assert.Equal(x == y, dX == dY); 186 } 187 188 [Theory, MemberData(nameof(CrossJoinInt32))] ExclusiveOrInt32(int x, int y)189 public void ExclusiveOrInt32(int x, int y) 190 { 191 dynamic dX = x; 192 dynamic dY = y; 193 Assert.Equal(x ^ y, dX ^ dY); 194 } 195 196 [Theory, MemberData(nameof(CrossJoinInt32))] GreaterThanInt32(int x, int y)197 public void GreaterThanInt32(int x, int y) 198 { 199 dynamic dX = x; 200 dynamic dY = y; 201 Assert.Equal(x > y, dX > dY); 202 } 203 204 [Theory, MemberData(nameof(CrossJoinInt32))] GreaterThanOrEqualInt32(int x, int y)205 public void GreaterThanOrEqualInt32(int x, int y) 206 { 207 dynamic dX = x; 208 dynamic dY = y; 209 Assert.Equal(x >= y, dX >= dY); 210 } 211 212 [Theory, MemberData(nameof(CrossJoinInt32))] LeftShiftInt32(int x, int y)213 public void LeftShiftInt32(int x, int y) 214 { 215 dynamic dX = x; 216 dynamic dY = y; 217 Assert.Equal(x << y, dX << dY); 218 } 219 220 [Theory, MemberData(nameof(CrossJoinInt32))] LessThanInt32(int x, int y)221 public void LessThanInt32(int x, int y) 222 { 223 dynamic dX = x; 224 dynamic dY = y; 225 Assert.Equal(x < y, dX < dY); 226 } 227 228 [Theory, MemberData(nameof(CrossJoinInt32))] LessThanOrEqualInt32(int x, int y)229 public void LessThanOrEqualInt32(int x, int y) 230 { 231 dynamic dX = x; 232 dynamic dY = y; 233 Assert.Equal(x <= y, dX <= dY); 234 } 235 236 [Theory, MemberData(nameof(CrossJoinInt32))] ModuloInt32(int x, int y)237 public void ModuloInt32(int x, int y) 238 { 239 dynamic dX = x; 240 dynamic dY = y; 241 if (y == 0) 242 Assert.Throws<DivideByZeroException>(() => dX % dY); 243 else if (y == -1 && x == int.MinValue) 244 Assert.Throws<OverflowException>(() => dX % dY); 245 else 246 Assert.Equal(x % y, dX % dY); 247 } 248 249 [Theory, MemberData(nameof(CrossJoinInt32))] MultiplyInt32(int x, int y)250 public void MultiplyInt32(int x, int y) 251 { 252 dynamic dX = x; 253 dynamic dY = y; 254 Assert.Equal(unchecked(x * y), unchecked(dX * dY)); 255 } 256 257 [Theory, MemberData(nameof(CrossJoinInt32))] MultiplyOvfInt32(int x, int y)258 public void MultiplyOvfInt32(int x, int y) 259 { 260 dynamic dX = x; 261 dynamic dY = y; 262 int result; 263 try 264 { 265 result = checked(x * y); 266 } 267 catch (OverflowException) 268 { 269 Assert.Throws<OverflowException>(() => checked(dX * dY)); 270 return; 271 } 272 273 Assert.Equal(result, dX * dY); 274 } 275 276 [Theory, MemberData(nameof(CrossJoinInt32))] NotEqualInt32(int x, int y)277 public void NotEqualInt32(int x, int y) 278 { 279 dynamic dX = x; 280 dynamic dY = y; 281 Assert.Equal(x != y, dX != dY); 282 } 283 284 [Theory, MemberData(nameof(CrossJoinInt32))] OrInt32(int x, int y)285 public void OrInt32(int x, int y) 286 { 287 dynamic dX = x; 288 dynamic dY = y; 289 Assert.Equal(x | y, dX | dY); 290 } 291 292 [Theory, MemberData(nameof(CrossJoinInt32))] RightShiftInt32(int x, int y)293 public void RightShiftInt32(int x, int y) 294 { 295 dynamic dX = x; 296 dynamic dY = y; 297 Assert.Equal(x >> y, dX >> dY); 298 } 299 300 [Theory, MemberData(nameof(CrossJoinInt32))] SubtractInt32(int x, int y)301 public void SubtractInt32(int x, int y) 302 { 303 dynamic dX = x; 304 dynamic dY = y; 305 Assert.Equal(unchecked(x - y), unchecked(dX - dY)); 306 } 307 308 [Theory, MemberData(nameof(CrossJoinInt32))] SubtractOvfInt32(int x, int y)309 public void SubtractOvfInt32(int x, int y) 310 { 311 dynamic dX = x; 312 dynamic dY = y; 313 int result; 314 try 315 { 316 result = checked(x - y); 317 } 318 catch (OverflowException) 319 { 320 Assert.Throws<OverflowException>(() => checked(dX - dY)); 321 return; 322 } 323 324 Assert.Equal(result, checked(dX - dY)); 325 } 326 327 [Theory, MemberData(nameof(CrossJoinInt32))] AddInt32Assign(int x, int y)328 public void AddInt32Assign(int x, int y) 329 { 330 dynamic dX = x; 331 dynamic dY = y; 332 333 unchecked 334 { 335 dX += dY; 336 Assert.Equal(x + y, dX); 337 } 338 } 339 340 [Theory, MemberData(nameof(CrossJoinInt32))] AddOvfInt32Assign(int x, int y)341 public void AddOvfInt32Assign(int x, int y) 342 { 343 dynamic dX = x; 344 dynamic dY = y; 345 int result; 346 try 347 { 348 result = checked(x + y); 349 } 350 catch (OverflowException) 351 { 352 Assert.Throws<OverflowException>(() => checked(dX += dY)); 353 return; 354 } 355 356 checked 357 { 358 dX += dY; 359 } 360 Assert.Equal(result, dX); 361 } 362 363 [Theory, MemberData(nameof(CrossJoinInt32))] AndInt32Assign(int x, int y)364 public void AndInt32Assign(int x, int y) 365 { 366 dynamic dX = x; 367 dynamic dY = y; 368 dX &= dY; 369 Assert.Equal(x & y, dX); 370 } 371 372 [Theory, MemberData(nameof(CrossJoinInt32))] DivideInt32Assign(int x, int y)373 public void DivideInt32Assign(int x, int y) 374 { 375 dynamic dX = x; 376 dynamic dY = y; 377 if (y == 0) 378 Assert.Throws<DivideByZeroException>(() => dX /= dY); 379 else if (y == -1 && x == int.MinValue) 380 Assert.Throws<OverflowException>(() => dX /= dY); 381 else 382 { 383 dX /= dY; 384 Assert.Equal(x / y, dX); 385 } 386 } 387 388 [Theory, MemberData(nameof(CrossJoinInt32))] ExclusiveOrInt32Assign(int x, int y)389 public void ExclusiveOrInt32Assign(int x, int y) 390 { 391 dynamic dX = x; 392 dynamic dY = y; 393 dX ^= dY; 394 Assert.Equal(x ^ y, dX); 395 } 396 397 [Theory, MemberData(nameof(CrossJoinInt32))] LeftShiftInt32Assign(int x, int y)398 public void LeftShiftInt32Assign(int x, int y) 399 { 400 dynamic dX = x; 401 dynamic dY = y; 402 dX <<= dY; 403 Assert.Equal(x << y, dX); 404 } 405 406 [Theory, MemberData(nameof(CrossJoinInt32))] ModuloInt32Assign(int x, int y)407 public void ModuloInt32Assign(int x, int y) 408 { 409 dynamic dX = x; 410 dynamic dY = y; 411 if (y == 0) 412 Assert.Throws<DivideByZeroException>(() => dX %= dY); 413 else if (y == -1 && x == int.MinValue) 414 Assert.Throws<OverflowException>(() => dX %= dY); 415 else 416 { 417 dX %= dY; 418 Assert.Equal(x % y, dX); 419 } 420 } 421 422 [Theory, MemberData(nameof(CrossJoinInt32))] MultiplyInt32Assign(int x, int y)423 public void MultiplyInt32Assign(int x, int y) 424 { 425 dynamic dX = x; 426 dynamic dY = y; 427 428 unchecked 429 { 430 dX *= dY; 431 Assert.Equal(x * y, dX); 432 } 433 } 434 435 [Theory, MemberData(nameof(CrossJoinInt32))] MultiplyOvfInt32Assign(int x, int y)436 public void MultiplyOvfInt32Assign(int x, int y) 437 { 438 dynamic dX = x; 439 dynamic dY = y; 440 int result; 441 try 442 { 443 result = checked(x * y); 444 } 445 catch (OverflowException) 446 { 447 Assert.Throws<OverflowException>(() => checked(dX *= dY)); 448 return; 449 } 450 451 dX *= dY; 452 Assert.Equal(result, dX); 453 } 454 455 456 [Theory, MemberData(nameof(CrossJoinInt32))] OrInt32Assign(int x, int y)457 public void OrInt32Assign(int x, int y) 458 { 459 dynamic dX = x; 460 dynamic dY = y; 461 dX |= dY; 462 Assert.Equal(x | y, dX); 463 } 464 465 [Theory, MemberData(nameof(CrossJoinInt32))] RightShiftInt32Assign(int x, int y)466 public void RightShiftInt32Assign(int x, int y) 467 { 468 dynamic dX = x; 469 dynamic dY = y; 470 dX >>= dY; 471 Assert.Equal(x >> y, dX); 472 } 473 474 [Theory, MemberData(nameof(CrossJoinInt32))] SubtractInt32Assign(int x, int y)475 public void SubtractInt32Assign(int x, int y) 476 { 477 dynamic dX = x; 478 dynamic dY = y; 479 480 unchecked 481 { 482 dX -= dY; 483 Assert.Equal(x - y, dX); 484 } 485 } 486 487 [Theory, MemberData(nameof(CrossJoinInt32))] SubtractOvfInt32Assign(int x, int y)488 public void SubtractOvfInt32Assign(int x, int y) 489 { 490 dynamic dX = x; 491 dynamic dY = y; 492 int result; 493 try 494 { 495 result = checked(x - y); 496 } 497 catch (OverflowException) 498 { 499 Assert.Throws<OverflowException>(() => checked(dX -= dY)); 500 return; 501 } 502 503 checked 504 { 505 dX -= dY; 506 } 507 Assert.Equal(result, dX); 508 } 509 510 [Theory, MemberData(nameof(CrossJoinDouble))] AddDouble(double x, double y)511 public void AddDouble(double x, double y) 512 { 513 dynamic dX = x; 514 dynamic dY = y; 515 Assert.Equal(x + y, dX + dY); 516 } 517 518 [Theory, MemberData(nameof(CrossJoinDouble))] DivideDouble(double x, double y)519 public void DivideDouble(double x, double y) 520 { 521 dynamic dX = x; 522 dynamic dY = y; 523 Assert.Equal(x / y, dX / dY); 524 } 525 526 [Theory, MemberData(nameof(CrossJoinDouble))] EqualDouble(double x, double y)527 public void EqualDouble(double x, double y) 528 { 529 dynamic dX = x; 530 dynamic dY = y; 531 Assert.Equal(x == y, dX == dY); 532 } 533 534 [Theory, MemberData(nameof(CrossJoinDouble))] GreaterThanDouble(double x, double y)535 public void GreaterThanDouble(double x, double y) 536 { 537 dynamic dX = x; 538 dynamic dY = y; 539 Assert.Equal(x > y, dX > dY); 540 } 541 542 [Theory, MemberData(nameof(CrossJoinDouble))] GreaterThanOrEqualDouble(double x, double y)543 public void GreaterThanOrEqualDouble(double x, double y) 544 { 545 dynamic dX = x; 546 dynamic dY = y; 547 Assert.Equal(x >= y, dX >= dY); 548 } 549 550 [Theory, MemberData(nameof(CrossJoinDouble))] LessThanDouble(double x, double y)551 public void LessThanDouble(double x, double y) 552 { 553 dynamic dX = x; 554 dynamic dY = y; 555 Assert.Equal(x < y, dX < dY); 556 } 557 558 [Theory, MemberData(nameof(CrossJoinDouble))] LessThanOrEqualDouble(double x, double y)559 public void LessThanOrEqualDouble(double x, double y) 560 { 561 dynamic dX = x; 562 dynamic dY = y; 563 Assert.Equal(x <= y, dX <= dY); 564 } 565 566 [Theory, MemberData(nameof(CrossJoinDouble))] ModuloDouble(double x, double y)567 public void ModuloDouble(double x, double y) 568 { 569 dynamic dX = x; 570 dynamic dY = y; 571 Assert.Equal(x % y, dX % dY); 572 } 573 574 [Theory, MemberData(nameof(CrossJoinDouble))] MultiplyDouble(double x, double y)575 public void MultiplyDouble(double x, double y) 576 { 577 dynamic dX = x; 578 dynamic dY = y; 579 Assert.Equal(x * y, dX * dY); 580 } 581 582 583 [Theory, MemberData(nameof(CrossJoinDouble))] NotEqualDouble(double x, double y)584 public void NotEqualDouble(double x, double y) 585 { 586 dynamic dX = x; 587 dynamic dY = y; 588 Assert.Equal(x != y, dX != dY); 589 } 590 591 [Theory, MemberData(nameof(CrossJoinDouble))] SubtractDouble(double x, double y)592 public void SubtractDouble(double x, double y) 593 { 594 dynamic dX = x; 595 dynamic dY = y; 596 Assert.Equal(x - y, dX - dY); 597 } 598 599 [Theory, MemberData(nameof(CrossJoinDouble))] AddDoubleAssign(double x, double y)600 public void AddDoubleAssign(double x, double y) 601 { 602 dynamic dX = x; 603 dynamic dY = y; 604 dX += dY; 605 Assert.Equal(x + y, dX); 606 } 607 608 [Theory, MemberData(nameof(CrossJoinDouble))] DivideDoubleAssign(double x, double y)609 public void DivideDoubleAssign(double x, double y) 610 { 611 dynamic dX = x; 612 dynamic dY = y; 613 dX /= dY; 614 Assert.Equal(x / y, dX); 615 } 616 617 [Theory, MemberData(nameof(CrossJoinDouble))] ModuloDoubleAssign(double x, double y)618 public void ModuloDoubleAssign(double x, double y) 619 { 620 dynamic dX = x; 621 dynamic dY = y; 622 dX %= dY; 623 Assert.Equal(x % y, dX); 624 } 625 626 [Theory, MemberData(nameof(CrossJoinDouble))] MultiplyDoubleAssign(double x, double y)627 public void MultiplyDoubleAssign(double x, double y) 628 { 629 dynamic dX = x; 630 dynamic dY = y; 631 dX *= dY; 632 Assert.Equal(x * y, dX); 633 } 634 635 [Theory, MemberData(nameof(CrossJoinDouble))] SubtractDoubleAssign(double x, double y)636 public void SubtractDoubleAssign(double x, double y) 637 { 638 dynamic dX = x; 639 dynamic dY = y; 640 dX -= dY; 641 Assert.Equal(x - y, dX); 642 } 643 644 [Theory, MemberData(nameof(NonBinaryExpressionTypes))] NonBinaryOperations(ExpressionType type)645 public void NonBinaryOperations(ExpressionType type) 646 { 647 AssertExtensions.Throws<ArgumentException>("operation", () => new MinimumOverrideBinaryOperationBinder(type)); 648 } 649 650 [Theory, MemberData(nameof(BinaryExpressionTypes))] ReturnType(ExpressionType type)651 public void ReturnType(ExpressionType type) 652 { 653 Assert.Equal(typeof(object), new MinimumOverrideBinaryOperationBinder(type).ReturnType); 654 } 655 656 [Theory, MemberData(nameof(BinaryExpressionTypes))] ExpressionTypeMatches(ExpressionType type)657 public void ExpressionTypeMatches(ExpressionType type) 658 { 659 Assert.Equal(type, new MinimumOverrideBinaryOperationBinder(type).Operation); 660 } 661 662 [Fact] NullTarget()663 public void NullTarget() 664 { 665 var binder = new MinimumOverrideBinaryOperationBinder(ExpressionType.Add); 666 var arg = new DynamicMetaObject(Expression.Parameter(typeof(object), null), BindingRestrictions.Empty); 667 AssertExtensions.Throws<ArgumentNullException>("target", () => binder.Bind(null, new[] {arg})); 668 } 669 670 [Fact] NullArgumentArrayPassed()671 public void NullArgumentArrayPassed() 672 { 673 var target = new DynamicMetaObject(Expression.Parameter(typeof(object), null), BindingRestrictions.Empty); 674 var binder = new MinimumOverrideBinaryOperationBinder(ExpressionType.Add); 675 AssertExtensions.Throws<ArgumentNullException>("args", () => binder.Bind(target, null)); 676 } 677 678 [Fact] NoArgumentsPassed()679 public void NoArgumentsPassed() 680 { 681 var target = new DynamicMetaObject(Expression.Parameter(typeof(object), null), BindingRestrictions.Empty); 682 var binder = new MinimumOverrideBinaryOperationBinder(ExpressionType.Add); 683 AssertExtensions.Throws<ArgumentException>("args", () => binder.Bind(target, Array.Empty<DynamicMetaObject>())); 684 } 685 686 [Fact] TooManyArgumentArrayPassed()687 public void TooManyArgumentArrayPassed() 688 { 689 var target = new DynamicMetaObject(Expression.Parameter(typeof(object), null), BindingRestrictions.Empty); 690 var binder = new MinimumOverrideBinaryOperationBinder(ExpressionType.Add); 691 var arg0 = new DynamicMetaObject(Expression.Parameter(typeof(object), null), BindingRestrictions.Empty); 692 var arg1 = new DynamicMetaObject(Expression.Parameter(typeof(object), null), BindingRestrictions.Empty); 693 AssertExtensions.Throws<ArgumentException>("args", () => binder.Bind(target, new[] {arg0, arg1})); 694 } 695 696 [Fact] SingleNullArgumentPassed()697 public void SingleNullArgumentPassed() 698 { 699 var target = new DynamicMetaObject(Expression.Parameter(typeof(object), null), BindingRestrictions.Empty); 700 var binder = new MinimumOverrideBinaryOperationBinder(ExpressionType.Add); 701 AssertExtensions.Throws<ArgumentNullException>("args", () => binder.Bind(target, new DynamicMetaObject[1])); 702 } 703 704 [Fact] InvalidOperationForType()705 public void InvalidOperationForType() 706 { 707 dynamic dX = "23"; 708 dynamic dY = "49"; 709 Assert.Throws<RuntimeBinderException>(() => dX * dY); 710 dX = 23; 711 dY = 49; 712 Assert.Throws<RuntimeBinderException>(() => dX && dY); 713 } 714 715 [Fact] LiteralDoubleNaN()716 public void LiteralDoubleNaN() 717 { 718 dynamic d = double.NaN; 719 Assert.False(d == double.NaN); 720 Assert.True(d != double.NaN); 721 d = 3.0; 722 Assert.True(double.IsNaN(d + double.NaN)); 723 } 724 725 [Fact] LiteralSingleNaN()726 public void LiteralSingleNaN() 727 { 728 dynamic d = float.NaN; 729 Assert.False(d == float.NaN); 730 Assert.True(d != float.NaN); 731 d = 3.0F; 732 Assert.True(float.IsNaN(d + float.NaN)); 733 } 734 735 [Theory] 736 [ClassData(typeof(CompilationTypes))] BinaryCallSiteBinder_DynamicExpression(bool useInterpreter)737 public void BinaryCallSiteBinder_DynamicExpression(bool useInterpreter) 738 { 739 DynamicExpression expression = DynamicExpression.Dynamic( 740 new BinaryCallSiteBinder(), 741 typeof(object), 742 Expression.Constant(40, typeof(object)), 743 Expression.Constant(2, typeof(object))); 744 Func<object> func = Expression.Lambda<Func<object>>(expression).Compile(useInterpreter); 745 Assert.Equal("42", func().ToString()); 746 } 747 748 private class BinaryCallSiteBinder : BinaryOperationBinder 749 { BinaryCallSiteBinder()750 public BinaryCallSiteBinder() : base(ExpressionType.Add) {} 751 FallbackBinaryOperation(DynamicMetaObject target, DynamicMetaObject arg, DynamicMetaObject errorSuggestion)752 public override DynamicMetaObject FallbackBinaryOperation(DynamicMetaObject target, DynamicMetaObject arg, DynamicMetaObject errorSuggestion) 753 { 754 return new DynamicMetaObject( 755 Expression.Convert( 756 Expression.Add( 757 Expression.Convert(target.Expression, typeof(int)), 758 Expression.Convert(arg.Expression, typeof(int)) 759 ), typeof(object)), 760 761 BindingRestrictions.GetTypeRestriction(target.Expression, typeof(int)).Merge( 762 BindingRestrictions.GetTypeRestriction(arg.Expression, typeof(int)) 763 )); 764 } 765 } 766 } 767 } 768