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