Added tests for Task.WhenAll w/ empty list
[mono.git] / mcs / class / System.Core / System.Linq.Expressions / BinaryExpression.cs
1 //
2 // BinaryExpression.cs
3 //
4 // Author:
5 //   Jb Evain (jbevain@novell.com)
6 //   Miguel de Icaza (miguel@novell.com)
7 //
8 // Contains code from the Mono C# compiler:
9 //   Marek Safar (marek.safar@seznam.cz)
10 //   Martin Baulig (martin@ximian.com)
11 //   Raja Harinath (harinath@gmail.com)
12 //
13 // (C) 2001-2003 Ximian, Inc.
14 // (C) 2004-2008 Novell, Inc. (http://www.novell.com)
15 //
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
23 //
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 //
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 //
35
36 using System;
37 using System.Reflection;
38 using System.Reflection.Emit;
39
40 namespace System.Linq.Expressions {
41
42         public sealed class BinaryExpression : Expression {
43
44                 Expression left;
45                 Expression right;
46                 LambdaExpression conversion;
47                 MethodInfo method;
48                 bool lift_to_null, is_lifted;
49
50                 public Expression Left {
51                         get { return left; }
52                 }
53
54                 public Expression Right {
55                         get { return right; }
56                 }
57
58                 public MethodInfo Method {
59                         get { return method; }
60                 }
61
62                 public bool IsLifted {
63                         get { return is_lifted;  }
64                 }
65
66                 public bool IsLiftedToNull {
67                         get { return lift_to_null; }
68                 }
69
70                 public LambdaExpression Conversion {
71                         get { return conversion; }
72                 }
73
74                 internal BinaryExpression (ExpressionType node_type, Type type, Expression left, Expression right)
75                         : base (node_type, type)
76                 {
77                         this.left = left;
78                         this.right = right;
79                 }
80
81                 internal BinaryExpression (ExpressionType node_type, Type type, Expression left, Expression right, MethodInfo method)
82                         : base (node_type, type)
83                 {
84                         this.left = left;
85                         this.right = right;
86                         this.method = method;
87                 }
88
89                 internal BinaryExpression (ExpressionType node_type, Type type, Expression left, Expression right, bool lift_to_null,
90                         bool is_lifted, MethodInfo method, LambdaExpression conversion) : base (node_type, type)
91                 {
92                         this.left = left;
93                         this.right = right;
94                         this.method = method;
95                         this.conversion = conversion;
96                         this.lift_to_null = lift_to_null;
97                         this.is_lifted = is_lifted;
98                 }
99
100 #if !FULL_AOT_RUNTIME
101                 void EmitArrayAccess (EmitContext ec)
102                 {
103                         left.Emit (ec);
104                         right.Emit (ec);
105                         ec.ig.Emit (OpCodes.Ldelem, this.Type);
106                 }
107
108                 void EmitLogicalBinary (EmitContext ec)
109                 {
110                         switch (NodeType) {
111                         case ExpressionType.And:
112                         case ExpressionType.Or:
113                                 if (!IsLifted)
114                                         EmitLogical (ec);
115                                 else if (Type == typeof (bool?))
116                                         EmitLiftedLogical (ec);
117                                 else
118                                         EmitLiftedArithmeticBinary (ec);
119                                 break;
120                         case ExpressionType.AndAlso:
121                         case ExpressionType.OrElse:
122                                 if (!IsLifted)
123                                         EmitLogicalShortCircuit (ec);
124                                 else
125                                         EmitLiftedLogicalShortCircuit (ec);
126                                 break;
127                         }
128                 }
129
130                 void EmitLogical (EmitContext ec)
131                 {
132                         EmitNonLiftedBinary (ec);
133                 }
134
135                 void EmitLiftedLogical (EmitContext ec)
136                 {
137                         var ig = ec.ig;
138                         var and = NodeType == ExpressionType.And;
139                         var left = ec.EmitStored (this.left);
140                         var right = ec.EmitStored (this.right);
141
142                         var ret_from_left = ig.DefineLabel ();
143                         var ret_from_right = ig.DefineLabel ();
144                         var done = ig.DefineLabel ();
145
146                         ec.EmitNullableGetValueOrDefault (left);
147                         ig.Emit (OpCodes.Brtrue, ret_from_left);
148                         ec.EmitNullableGetValueOrDefault (right);
149                         ig.Emit (OpCodes.Brtrue, ret_from_right);
150
151                         ec.EmitNullableHasValue (left);
152                         ig.Emit (OpCodes.Brfalse, ret_from_left);
153
154                         ig.MarkLabel (ret_from_right);
155                         ec.EmitLoad (and ? left : right);
156                         ig.Emit (OpCodes.Br, done);
157
158                         ig.MarkLabel (ret_from_left);
159                         ec.EmitLoad (and ? right : left);
160
161                         ig.MarkLabel (done);
162                 }
163
164                 void EmitLogicalShortCircuit (EmitContext ec)
165                 {
166                         var ig = ec.ig;
167                         var and = NodeType == ExpressionType.AndAlso;
168                         var ret = ig.DefineLabel ();
169                         var done = ig.DefineLabel ();
170
171                         ec.Emit (left);
172                         ig.Emit (and ? OpCodes.Brfalse : OpCodes.Brtrue, ret);
173
174                         ec.Emit (right);
175
176                         ig.Emit (OpCodes.Br, done);
177
178                         ig.MarkLabel (ret);
179                         ig.Emit (and ? OpCodes.Ldc_I4_0 : OpCodes.Ldc_I4_1);
180
181                         ig.MarkLabel (done);
182                 }
183
184                 MethodInfo GetFalseOperator ()
185                 {
186                         return GetFalseOperator (left.Type.GetNotNullableType ());
187                 }
188
189                 MethodInfo GetTrueOperator ()
190                 {
191                         return GetTrueOperator (left.Type.GetNotNullableType ());
192                 }
193
194                 void EmitUserDefinedLogicalShortCircuit (EmitContext ec)
195                 {
196                         var ig = ec.ig;
197                         var and = NodeType == ExpressionType.AndAlso;
198
199                         var done = ig.DefineLabel ();
200
201                         var left = ec.EmitStored (this.left);
202
203                         ec.EmitLoad (left);
204                         ig.Emit (OpCodes.Dup);
205                         ec.EmitCall (and ? GetFalseOperator () : GetTrueOperator ());
206                         ig.Emit (OpCodes.Brtrue, done);
207
208                         ec.Emit (this.right);
209                         ec.EmitCall (method);
210
211                         ig.MarkLabel (done);
212                 }
213
214                 void EmitLiftedLogicalShortCircuit (EmitContext ec)
215                 {
216                         var ig = ec.ig;
217                         var and = NodeType == ExpressionType.AndAlso;
218                         var left_is_null = ig.DefineLabel ();
219                         var ret_from_left = ig.DefineLabel ();
220                         var ret_null = ig.DefineLabel ();
221                         var ret_new = ig.DefineLabel();
222                         var done = ig.DefineLabel();
223
224                         var left = ec.EmitStored (this.left);
225
226                         ec.EmitNullableHasValue (left);
227                         ig.Emit (OpCodes.Brfalse, left_is_null);
228
229                         ec.EmitNullableGetValueOrDefault (left);
230
231                         ig.Emit (OpCodes.Ldc_I4_0);
232                         ig.Emit (OpCodes.Ceq);
233                         ig.Emit (and ? OpCodes.Brtrue : OpCodes.Brfalse, ret_from_left);
234
235                         ig.MarkLabel (left_is_null);
236                         var right = ec.EmitStored (this.right);
237
238                         ec.EmitNullableHasValue (right);
239                         ig.Emit (OpCodes.Brfalse_S, ret_null);
240
241                         ec.EmitNullableGetValueOrDefault (right);
242
243                         ig.Emit (OpCodes.Ldc_I4_0);
244                         ig.Emit (OpCodes.Ceq);
245
246                         ig.Emit (and ? OpCodes.Brtrue : OpCodes.Brfalse, ret_from_left);
247
248                         ec.EmitNullableHasValue (left);
249                         ig.Emit (OpCodes.Brfalse, ret_null);
250
251                         ig.Emit (and ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
252                         ig.Emit (OpCodes.Br_S, ret_new);
253
254                         ig.MarkLabel (ret_from_left);
255                         ig.Emit (and ? OpCodes.Ldc_I4_0 : OpCodes.Ldc_I4_1);
256
257                         ig.MarkLabel (ret_new);
258                         ec.EmitNullableNew (Type);
259                         ig.Emit (OpCodes.Br, done);
260
261                         ig.MarkLabel (ret_null);
262                         var ret = ig.DeclareLocal (Type);
263                         ec.EmitNullableInitialize (ret);
264
265                         ig.MarkLabel (done);
266                 }
267
268                 void EmitCoalesce (EmitContext ec)
269                 {
270                         var ig = ec.ig;
271                         var done = ig.DefineLabel ();
272                         var load_right = ig.DefineLabel ();
273
274                         var left = ec.EmitStored (this.left);
275                         var left_is_nullable = left.LocalType.IsNullable ();
276
277                         if (left_is_nullable)
278                                 ec.EmitNullableHasValue (left);
279                         else
280                                 ec.EmitLoad (left);
281
282                         ig.Emit (OpCodes.Brfalse, load_right);
283
284                         if (left_is_nullable && !Type.IsNullable ())
285                                 ec.EmitNullableGetValue (left);
286                         else
287                                 ec.EmitLoad (left);
288
289                         ig.Emit (OpCodes.Br, done);
290
291                         ig.MarkLabel (load_right);
292                         ec.Emit (this.right);
293
294                         ig.MarkLabel (done);
295                 }
296
297                 void EmitConvertedCoalesce (EmitContext ec)
298                 {
299                         var ig = ec.ig;
300                         var done = ig.DefineLabel ();
301                         var load_right = ig.DefineLabel ();
302
303                         var left = ec.EmitStored (this.left);
304
305                         if (left.LocalType.IsNullable ())
306                                 ec.EmitNullableHasValue (left);
307                         else
308                                 ec.EmitLoad (left);
309
310                         ig.Emit (OpCodes.Brfalse, load_right);
311
312                         ec.Emit (conversion);
313                         ec.EmitLoad (left);
314                         ig.Emit (OpCodes.Callvirt, conversion.Type.GetInvokeMethod ());
315
316                         ig.Emit (OpCodes.Br, done);
317
318                         ig.MarkLabel (load_right);
319                         ec.Emit (this.right);
320
321                         ig.MarkLabel (done);
322                 }
323
324                 static bool IsInt32OrInt64 (Type type)
325                 {
326                         return type == typeof (int) || type == typeof (long);
327                 }
328
329                 static bool IsSingleOrDouble (Type type)
330                 {
331                         return type == typeof (float) || type == typeof (double);
332                 }
333
334                 void EmitBinaryOperator (EmitContext ec)
335                 {
336                         var ig = ec.ig;
337                         bool is_unsigned = IsUnsigned (left.Type);
338
339                         switch (NodeType) {
340                         case ExpressionType.Add:
341                                 ig.Emit (OpCodes.Add);
342                                 break;
343                         case ExpressionType.AddChecked:
344                                 if (IsInt32OrInt64 (left.Type))
345                                         ig.Emit (OpCodes.Add_Ovf);
346                                 else
347                                         ig.Emit (is_unsigned ? OpCodes.Add_Ovf_Un : OpCodes.Add);
348                                 break;
349                         case ExpressionType.Subtract:
350                                 ig.Emit (OpCodes.Sub);
351                                 break;
352                         case ExpressionType.SubtractChecked:
353                                 if (IsInt32OrInt64 (left.Type))
354                                         ig.Emit (OpCodes.Sub_Ovf);
355                                 else
356                                         ig.Emit (is_unsigned ? OpCodes.Sub_Ovf_Un : OpCodes.Sub);
357                                 break;
358                         case ExpressionType.Multiply:
359                                 ig.Emit (OpCodes.Mul);
360                                 break;
361                         case ExpressionType.MultiplyChecked:
362                                 if (IsInt32OrInt64 (left.Type))
363                                         ig.Emit (OpCodes.Mul_Ovf);
364                                 else
365                                         ig.Emit (is_unsigned ? OpCodes.Mul_Ovf_Un : OpCodes.Mul);
366                                 break;
367                         case ExpressionType.Divide:
368                                 ig.Emit (is_unsigned ? OpCodes.Div_Un : OpCodes.Div);
369                                 break;
370                         case ExpressionType.Modulo:
371                                 ig.Emit (is_unsigned ? OpCodes.Rem_Un : OpCodes.Rem);
372                                 break;
373                         case ExpressionType.RightShift:
374                         case ExpressionType.LeftShift:
375                                 ig.Emit (OpCodes.Ldc_I4, left.Type == typeof (int) ? 0x1f : 0x3f);
376                                 ig.Emit (OpCodes.And);
377                                 if (NodeType == ExpressionType.RightShift)
378                                         ig.Emit (is_unsigned ? OpCodes.Shr_Un : OpCodes.Shr);
379                                 else
380                                         ig.Emit (OpCodes.Shl);
381                                 break;
382                         case ExpressionType.And:
383                                 ig.Emit (OpCodes.And);
384                                 break;
385                         case ExpressionType.Or:
386                                 ig.Emit (OpCodes.Or);
387                                 break;
388                         case ExpressionType.ExclusiveOr:
389                                 ig.Emit (OpCodes.Xor);
390                                 break;
391                         case ExpressionType.GreaterThan:
392                                 ig.Emit (is_unsigned ? OpCodes.Cgt_Un : OpCodes.Cgt);
393                                 break;
394                         case ExpressionType.GreaterThanOrEqual:
395                                 if (is_unsigned || IsSingleOrDouble (left.Type))
396                                         ig.Emit (OpCodes.Clt_Un);
397                                 else
398                                         ig.Emit (OpCodes.Clt);
399
400                                 ig.Emit (OpCodes.Ldc_I4_0);
401                                 ig.Emit (OpCodes.Ceq);
402                                 break;
403                         case ExpressionType.LessThan:
404                                 ig.Emit (is_unsigned ? OpCodes.Clt_Un : OpCodes.Clt);
405                                 break;
406                         case ExpressionType.LessThanOrEqual:
407                                 if (is_unsigned || IsSingleOrDouble (left.Type))
408                                         ig.Emit (OpCodes.Cgt_Un);
409                                 else
410                                         ig.Emit (OpCodes.Cgt);
411
412                                 ig.Emit (OpCodes.Ldc_I4_0);
413                                 ig.Emit (OpCodes.Ceq);
414                                 break;
415                         case ExpressionType.Equal:
416                                 ig.Emit (OpCodes.Ceq);
417                                 break;
418                         case ExpressionType.NotEqual:
419                                 ig.Emit (OpCodes.Ceq);
420                                 ig.Emit (OpCodes.Ldc_I4_0);
421                                 ig.Emit (OpCodes.Ceq);
422                                 break;
423                         case ExpressionType.Power:
424                                 ig.Emit (OpCodes.Call, typeof (Math).GetMethod ("Pow"));
425                                 break;
426                         default:
427                                 throw new InvalidOperationException (
428                                         string.Format ("Internal error: BinaryExpression contains non-Binary nodetype {0}", NodeType));
429                         }
430                 }
431
432                 bool IsLeftLiftedBinary ()
433                 {
434                         return left.Type.IsNullable () && !right.Type.IsNullable ();
435                 }
436
437                 void EmitLeftLiftedToNullBinary (EmitContext ec)
438                 {
439                         var ig = ec.ig;
440
441                         var ret = ig.DefineLabel ();
442                         var done = ig.DefineLabel ();
443
444                         var left = ec.EmitStored (this.left);
445
446                         ec.EmitNullableHasValue (left);
447                         ig.Emit (OpCodes.Brfalse, ret);
448
449                         ec.EmitNullableGetValueOrDefault (left);
450                         ec.Emit (right);
451
452                         EmitBinaryOperator (ec);
453
454                         ec.EmitNullableNew (Type);
455
456                         ig.Emit (OpCodes.Br, done);
457
458                         ig.MarkLabel (ret);
459
460                         var temp = ig.DeclareLocal (Type);
461                         ec.EmitNullableInitialize (temp);
462
463                         ig.MarkLabel (done);
464                 }
465
466                 void EmitLiftedArithmeticBinary (EmitContext ec)
467                 {
468                         if (IsLeftLiftedBinary ())
469                                 EmitLeftLiftedToNullBinary (ec);
470                         else
471                                 EmitLiftedToNullBinary (ec);
472                 }
473
474                 void EmitLiftedToNullBinary (EmitContext ec)
475                 {
476                         var ig = ec.ig;
477                         var left = ec.EmitStored (this.left);
478                         var right = ec.EmitStored (this.right);
479                         var result = ig.DeclareLocal (Type);
480
481                         var has_value = ig.DefineLabel ();
482                         var done = ig.DefineLabel ();
483
484                         ec.EmitNullableHasValue (left);
485                         ec.EmitNullableHasValue (right);
486                         ig.Emit (OpCodes.And);
487                         ig.Emit (OpCodes.Brtrue, has_value);
488
489                         ec.EmitNullableInitialize (result);
490
491                         ig.Emit (OpCodes.Br, done);
492
493                         ig.MarkLabel (has_value);
494
495                         ec.EmitNullableGetValueOrDefault (left);
496                         ec.EmitNullableGetValueOrDefault (right);
497
498                         EmitBinaryOperator (ec);
499
500                         ec.EmitNullableNew (result.LocalType);
501
502                         ig.MarkLabel (done);
503                 }
504
505                 void EmitLiftedRelationalBinary (EmitContext ec)
506                 {
507                         var ig = ec.ig;
508                         var left = ec.EmitStored (this.left);
509                         var right = ec.EmitStored (this.right);
510
511                         var ret = ig.DefineLabel ();
512                         var done = ig.DefineLabel ();
513
514                         ec.EmitNullableGetValueOrDefault (left);
515                         ec.EmitNullableGetValueOrDefault (right);
516
517                         switch (NodeType) {
518                         case ExpressionType.Equal:
519                         case ExpressionType.NotEqual:
520                                 ig.Emit (OpCodes.Bne_Un, ret);
521                                 break;
522                         default:
523                                 EmitBinaryOperator (ec);
524                                 ig.Emit (OpCodes.Brfalse, ret);
525                                 break;
526                         }
527
528                         ec.EmitNullableHasValue (left);
529                         ec.EmitNullableHasValue (right);
530
531                         switch (NodeType) {
532                         case ExpressionType.Equal:
533                                 ig.Emit (OpCodes.Ceq);
534                                 break;
535                         case ExpressionType.NotEqual:
536                                 ig.Emit (OpCodes.Ceq);
537                                 ig.Emit (OpCodes.Ldc_I4_0);
538                                 ig.Emit (OpCodes.Ceq);
539                                 break;
540                         default:
541                                 ig.Emit (OpCodes.And);
542                                 break;
543                         }
544
545                         ig.Emit (OpCodes.Br, done);
546
547                         ig.MarkLabel (ret);
548
549                         ig.Emit (NodeType == ExpressionType.NotEqual ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
550
551                         ig.MarkLabel (done);
552                 }
553
554                 void EmitArithmeticBinary (EmitContext ec)
555                 {
556                         if (!IsLifted)
557                                 EmitNonLiftedBinary (ec);
558                         else
559                                 EmitLiftedArithmeticBinary (ec);
560                 }
561
562                 void EmitNonLiftedBinary (EmitContext ec)
563                 {
564                         ec.Emit (left);
565                         ec.Emit (right);
566                         EmitBinaryOperator (ec);
567                 }
568
569                 void EmitRelationalBinary (EmitContext ec)
570                 {
571                         if (!IsLifted)
572                                 EmitNonLiftedBinary (ec);
573                         else if (IsLiftedToNull)
574                                 EmitLiftedToNullBinary (ec);
575                         else
576                                 EmitLiftedRelationalBinary (ec);
577                 }
578
579                 void EmitLiftedUserDefinedOperator (EmitContext ec)
580                 {
581                         var ig = ec.ig;
582
583                         var ret_true = ig.DefineLabel ();
584                         var ret_false = ig.DefineLabel ();
585                         var done = ig.DefineLabel ();
586
587                         var left = ec.EmitStored (this.left);
588                         var right = ec.EmitStored (this.right);
589
590                         ec.EmitNullableHasValue (left);
591                         ec.EmitNullableHasValue (right);
592                         switch (NodeType) {
593                         case ExpressionType.Equal:
594                                 ig.Emit (OpCodes.Bne_Un, ret_false);
595                                 ec.EmitNullableHasValue (left);
596                                 ig.Emit (OpCodes.Brfalse, ret_true);
597                                 break;
598                         case ExpressionType.NotEqual:
599                                 ig.Emit (OpCodes.Bne_Un, ret_true);
600                                 ec.EmitNullableHasValue (left);
601                                 ig.Emit (OpCodes.Brfalse, ret_false);
602                                 break;
603                         default:
604                                 ig.Emit (OpCodes.And);
605                                 ig.Emit (OpCodes.Brfalse, ret_false);
606                                 break;
607                         }
608
609                         ec.EmitNullableGetValueOrDefault (left);
610                         ec.EmitNullableGetValueOrDefault (right);
611                         ec.EmitCall (method);
612                         ig.Emit (OpCodes.Br, done);
613
614                         ig.MarkLabel (ret_true);
615                         ig.Emit (OpCodes.Ldc_I4_1);
616                         ig.Emit (OpCodes.Br, done);
617
618                         ig.MarkLabel (ret_false);
619                         ig.Emit (OpCodes.Ldc_I4_0);
620                         ig.Emit (OpCodes.Br, done);
621
622                         ig.MarkLabel (done);
623                 }
624
625                 void EmitLiftedToNullUserDefinedOperator (EmitContext ec)
626                 {
627                         var ig = ec.ig;
628
629                         var ret = ig.DefineLabel ();
630                         var done = ig.DefineLabel ();
631
632                         var left = ec.EmitStored (this.left);
633                         var right = ec.EmitStored (this.right);
634
635                         ec.EmitNullableHasValue (left);
636                         ec.EmitNullableHasValue (right);
637                         ig.Emit (OpCodes.And);
638                         ig.Emit (OpCodes.Brfalse, ret);
639
640                         ec.EmitNullableGetValueOrDefault (left);
641                         ec.EmitNullableGetValueOrDefault (right);
642                         ec.EmitCall (method);
643                         ec.EmitNullableNew (Type);
644                         ig.Emit (OpCodes.Br, done);
645
646                         ig.MarkLabel (ret);
647                         var temp = ig.DeclareLocal (Type);
648                         ec.EmitNullableInitialize (temp);
649
650                         ig.MarkLabel (done);
651                 }
652
653                 void EmitUserDefinedLiftedLogicalShortCircuit (EmitContext ec)
654                 {
655                         var ig = ec.ig;
656                         var and = NodeType == ExpressionType.AndAlso;
657
658                         var left_is_null = ig.DefineLabel ();
659                         var ret_left = ig.DefineLabel ();
660                         var ret_null = ig.DefineLabel ();
661                         var done = ig.DefineLabel ();
662
663                         var left = ec.EmitStored (this.left);
664
665                         ec.EmitNullableHasValue (left);
666                         ig.Emit (OpCodes.Brfalse, and ? ret_null : left_is_null);
667
668                         ec.EmitNullableGetValueOrDefault (left);
669                         ec.EmitCall (and ? GetFalseOperator () : GetTrueOperator ());
670                         ig.Emit (OpCodes.Brtrue, ret_left);
671
672                         ig.MarkLabel (left_is_null);
673                         var right = ec.EmitStored (this.right);
674                         ec.EmitNullableHasValue (right);
675                         ig.Emit (OpCodes.Brfalse, ret_null);
676
677                         ec.EmitNullableGetValueOrDefault (left);
678                         ec.EmitNullableGetValueOrDefault (right);
679                         ec.EmitCall (method);
680
681                         ec.EmitNullableNew (Type);
682                         ig.Emit (OpCodes.Br, done);
683
684                         ig.MarkLabel (ret_left);
685                         ec.EmitLoad (left);
686                         ig.Emit (OpCodes.Br, done);
687
688                         ig.MarkLabel (ret_null);
689                         var ret = ig.DeclareLocal (Type);
690                         ec.EmitNullableInitialize (ret);
691
692                         ig.MarkLabel (done);
693                 }
694
695                 void EmitUserDefinedOperator (EmitContext ec)
696                 {
697                         if (!IsLifted) {
698                                 switch (NodeType) {
699                                 case ExpressionType.AndAlso:
700                                 case ExpressionType.OrElse:
701                                         EmitUserDefinedLogicalShortCircuit (ec);
702                                         break;
703                                 default:
704                                         left.Emit (ec);
705                                         right.Emit (ec);
706                                         ec.EmitCall (method);
707                                         break;
708                                 }
709                         } else if (IsLiftedToNull) {
710                                 switch (NodeType) {
711                                 case ExpressionType.AndAlso:
712                                 case ExpressionType.OrElse:
713                                         EmitUserDefinedLiftedLogicalShortCircuit (ec);
714                                         break;
715                                 default:
716                                         EmitLiftedToNullUserDefinedOperator (ec);
717                                         break;
718                                 }
719                         }  else
720                                 EmitLiftedUserDefinedOperator (ec);
721                 }
722
723                 internal override void Emit (EmitContext ec)
724                 {
725                         if (method != null) {
726                                 EmitUserDefinedOperator (ec);
727                                 return;
728                         }
729
730                         switch (NodeType){
731                         case ExpressionType.ArrayIndex:
732                                 EmitArrayAccess (ec);
733                                 return;
734                         case ExpressionType.Coalesce:
735                                 if (conversion != null)
736                                         EmitConvertedCoalesce (ec);
737                                 else
738                                         EmitCoalesce (ec);
739                                 return;
740                         case ExpressionType.Power:
741                         case ExpressionType.Add:
742                         case ExpressionType.AddChecked:
743                         case ExpressionType.Divide:
744                         case ExpressionType.ExclusiveOr:
745                         case ExpressionType.LeftShift:
746                         case ExpressionType.Modulo:
747                         case ExpressionType.Multiply:
748                         case ExpressionType.MultiplyChecked:
749                         case ExpressionType.RightShift:
750                         case ExpressionType.Subtract:
751                         case ExpressionType.SubtractChecked:
752                                 EmitArithmeticBinary (ec);
753                                 return;
754                         case ExpressionType.Equal:
755                         case ExpressionType.GreaterThan:
756                         case ExpressionType.GreaterThanOrEqual:
757                         case ExpressionType.LessThan:
758                         case ExpressionType.LessThanOrEqual:
759                         case ExpressionType.NotEqual:
760                                 EmitRelationalBinary (ec);
761                                 return;
762                         case ExpressionType.And:
763                         case ExpressionType.Or:
764                         case ExpressionType.AndAlso:
765                         case ExpressionType.OrElse:
766                                 EmitLogicalBinary (ec);
767                                 return;
768                         default:
769                                 throw new NotSupportedException (this.NodeType.ToString ());
770                         }
771                 }
772 #endif
773         }
774 }