make IsNullable an extension method
[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                 void EmitMethod (EmitContext ec)
101                 {
102                         left.Emit (ec);
103                         right.Emit (ec);
104                         ec.EmitCall (method);
105                 }
106
107                 void EmitArrayAccess (EmitContext ec)
108                 {
109                         left.Emit (ec);
110                         right.Emit (ec);
111                         ec.ig.Emit (OpCodes.Ldelem, this.Type);
112                 }
113
114                 void EmitLogicalBinary (EmitContext ec)
115                 {
116                         switch (NodeType) {
117                         case ExpressionType.And:
118                         case ExpressionType.Or:
119                                 if (!IsLifted)
120                                         EmitLogical (ec);
121                                 else if (Type == typeof (bool?))
122                                         EmitLiftedLogical (ec);
123                                 else
124                                         EmitLiftedArithmeticBinary (ec);
125                                 break;
126                         case ExpressionType.AndAlso:
127                         case ExpressionType.OrElse:
128                                 if (!IsLifted)
129                                         EmitLogicalShortCircuit (ec);
130                                 else
131                                         EmitLiftedLogicalShortCircuit (ec);
132                                 break;
133                         }
134                 }
135
136                 void EmitLogical (EmitContext ec)
137                 {
138                         EmitNonLiftedBinary (ec);
139                 }
140
141                 void EmitLiftedLogical (EmitContext ec)
142                 {
143                         var ig = ec.ig;
144                         var and = NodeType == ExpressionType.And;
145                         var left = ec.EmitStored (this.left);
146                         var right = ec.EmitStored (this.right);
147
148                         var ret_from_left = ig.DefineLabel ();
149                         var ret_from_right = ig.DefineLabel ();
150                         var done = ig.DefineLabel ();
151
152                         ec.EmitNullableGetValueOrDefault (left);
153                         ig.Emit (OpCodes.Brtrue, ret_from_left);
154                         ec.EmitNullableGetValueOrDefault (right);
155                         ig.Emit (OpCodes.Brtrue, ret_from_right);
156
157                         ec.EmitNullableHasValue (left);
158                         ig.Emit (OpCodes.Brfalse, ret_from_left);
159
160                         ig.MarkLabel (ret_from_right);
161                         ec.EmitLoad (and ? left : right);
162                         ig.Emit (OpCodes.Br, done);
163
164                         ig.MarkLabel (ret_from_left);
165                         ec.EmitLoad (and ? right : left);
166
167                         ig.MarkLabel (done);
168                 }
169
170                 void EmitLogicalShortCircuit (EmitContext ec)
171                 {
172                         var ig = ec.ig;
173                         var and = NodeType == ExpressionType.AndAlso;
174                         var ret = ig.DefineLabel ();
175                         var done = ig.DefineLabel ();
176
177                         ec.Emit (left);
178                         ig.Emit (and ? OpCodes.Brfalse : OpCodes.Brtrue, ret);
179
180                         ec.Emit (right);
181
182                         ig.Emit (OpCodes.Br, done);
183
184                         ig.MarkLabel (ret);
185                         ig.Emit (and ? OpCodes.Ldc_I4_0 : OpCodes.Ldc_I4_1);
186
187                         ig.MarkLabel (done);
188                 }
189
190                 void EmitLiftedLogicalShortCircuit (EmitContext ec)
191                 {
192                         var ig = ec.ig;
193                         var and = NodeType == ExpressionType.AndAlso;
194                         var left_is_null = ig.DefineLabel ();
195                         var ret_from_left = ig.DefineLabel ();
196                         var ret_null = ig.DefineLabel ();
197                         var ret_new = ig.DefineLabel();
198                         var done = ig.DefineLabel();
199
200                         var left = ec.EmitStored (this.left);
201
202                         ec.EmitNullableHasValue (left);
203                         ig.Emit (OpCodes.Brfalse, left_is_null);
204
205                         ec.EmitNullableGetValueOrDefault (left);
206
207                         ig.Emit (OpCodes.Ldc_I4_0);
208                         ig.Emit (OpCodes.Ceq);
209                         ig.Emit (and ? OpCodes.Brtrue : OpCodes.Brfalse, ret_from_left);
210
211                         ig.MarkLabel (left_is_null);
212                         var right = ec.EmitStored (this.right);
213
214                         ec.EmitNullableHasValue (right);
215                         ig.Emit (OpCodes.Brfalse_S, ret_null);
216
217                         ec.EmitNullableGetValueOrDefault (right);
218
219                         ig.Emit (OpCodes.Ldc_I4_0);
220                         ig.Emit (OpCodes.Ceq);
221
222                         ig.Emit (and ? OpCodes.Brtrue : OpCodes.Brfalse, ret_from_left);
223
224                         ec.EmitNullableHasValue (left);
225                         ig.Emit (OpCodes.Brfalse, ret_null);
226
227                         ig.Emit (and ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
228                         ig.Emit (OpCodes.Br_S, ret_new);
229
230                         ig.MarkLabel (ret_from_left);
231                         ig.Emit (and ? OpCodes.Ldc_I4_0 : OpCodes.Ldc_I4_1);
232
233                         ig.MarkLabel (ret_new);
234                         ec.EmitNullableNew (typeof (bool?));
235                         ig.Emit (OpCodes.Br, done);
236
237                         ig.MarkLabel (ret_null);
238                         var ret = ig.DeclareLocal (typeof (bool?));
239                         ec.EmitNullableInitialize (ret);
240
241                         ig.MarkLabel (done);
242                 }
243
244                 void EmitCoalesce (EmitContext ec)
245                 {
246                         var ig = ec.ig;
247                         var done = ig.DefineLabel ();
248                         var load_right = ig.DefineLabel ();
249
250                         var left = ec.EmitStored (this.left);
251                         if (left.LocalType.IsNullable ())
252                                 ec.EmitNullableHasValue (left);
253                         else
254                                 ec.EmitLoad (left);
255
256                         ig.Emit (OpCodes.Brfalse, load_right);
257
258                         ec.EmitLoad (left);
259                         ig.Emit (OpCodes.Br, done);
260
261                         ig.MarkLabel (load_right);
262                         ec.Emit (this.right);
263
264                         ig.MarkLabel (done);
265                 }
266
267                 static bool IsInt32OrInt64 (Type type)
268                 {
269                         return type == typeof (int) || type == typeof (long);
270                 }
271
272                 static bool IsSingleOrDouble (Type type)
273                 {
274                         return type == typeof (float) || type == typeof (double);
275                 }
276
277                 void EmitBinaryOperator (EmitContext ec)
278                 {
279                         var ig = ec.ig;
280                         bool is_unsigned = IsUnsigned (left.Type);
281
282                         switch (NodeType) {
283                         case ExpressionType.Add:
284                                 ig.Emit (OpCodes.Add);
285                                 break;
286                         case ExpressionType.AddChecked:
287                                 if (IsInt32OrInt64 (left.Type))
288                                         ig.Emit (OpCodes.Add_Ovf);
289                                 else
290                                         ig.Emit (is_unsigned ? OpCodes.Add_Ovf_Un : OpCodes.Add);
291                                 break;
292                         case ExpressionType.Subtract:
293                                 ig.Emit (OpCodes.Sub);
294                                 break;
295                         case ExpressionType.SubtractChecked:
296                                 if (IsInt32OrInt64 (left.Type))
297                                         ig.Emit (OpCodes.Sub_Ovf);
298                                 else
299                                         ig.Emit (is_unsigned ? OpCodes.Sub_Ovf_Un : OpCodes.Sub);
300                                 break;
301                         case ExpressionType.Multiply:
302                                 ig.Emit (OpCodes.Mul);
303                                 break;
304                         case ExpressionType.MultiplyChecked:
305                                 if (IsInt32OrInt64 (left.Type))
306                                         ig.Emit (OpCodes.Mul_Ovf);
307                                 else
308                                         ig.Emit (is_unsigned ? OpCodes.Mul_Ovf_Un : OpCodes.Mul);
309                                 break;
310                         case ExpressionType.Divide:
311                                 ig.Emit (is_unsigned ? OpCodes.Div_Un : OpCodes.Div);
312                                 break;
313                         case ExpressionType.Modulo:
314                                 ig.Emit (is_unsigned ? OpCodes.Rem_Un : OpCodes.Rem);
315                                 break;
316                         case ExpressionType.RightShift:
317                                 ig.Emit (is_unsigned ? OpCodes.Shr_Un : OpCodes.Shr);
318                                 break;
319                         case ExpressionType.LeftShift:
320                                 ig.Emit (OpCodes.Shl);
321                                 break;
322                         case ExpressionType.And:
323                                 ig.Emit (OpCodes.And);
324                                 break;
325                         case ExpressionType.Or:
326                                 ig.Emit (OpCodes.Or);
327                                 break;
328                         case ExpressionType.ExclusiveOr:
329                                 ig.Emit (OpCodes.Xor);
330                                 break;
331                         case ExpressionType.GreaterThan:
332                                 ig.Emit (is_unsigned ? OpCodes.Cgt_Un : OpCodes.Cgt);
333                                 break;
334                         case ExpressionType.GreaterThanOrEqual:
335                                 if (is_unsigned || IsSingleOrDouble (left.Type))
336                                         ig.Emit (OpCodes.Clt_Un);
337                                 else
338                                         ig.Emit (OpCodes.Clt);
339
340                                 ig.Emit (OpCodes.Ldc_I4_0);
341                                 ig.Emit (OpCodes.Ceq);
342                                 break;
343                         case ExpressionType.LessThan:
344                                 ig.Emit (is_unsigned ? OpCodes.Clt_Un : OpCodes.Clt);
345                                 break;
346                         case ExpressionType.LessThanOrEqual:
347                                 if (is_unsigned || IsSingleOrDouble (left.Type))
348                                         ig.Emit (OpCodes.Cgt_Un);
349                                 else
350                                         ig.Emit (OpCodes.Cgt);
351
352                                 ig.Emit (OpCodes.Ldc_I4_0);
353                                 ig.Emit (OpCodes.Ceq);
354                                 break;
355                         case ExpressionType.Equal:
356                                 ig.Emit (OpCodes.Ceq);
357                                 break;
358                         case ExpressionType.NotEqual:
359                                 ig.Emit (OpCodes.Ceq);
360                                 ig.Emit (OpCodes.Ldc_I4_0);
361                                 ig.Emit (OpCodes.Ceq);
362                                 break;
363                         case ExpressionType.Power:
364                                 ig.Emit (OpCodes.Call, typeof (Math).GetMethod ("Pow"));
365                                 break;
366                         default:
367                                 throw new InvalidOperationException (
368                                         string.Format ("Internal error: BinaryExpression contains non-Binary nodetype {0}", NodeType));
369                         }
370                 }
371
372                 void EmitLiftedArithmeticBinary (EmitContext ec)
373                 {
374                         EmitLiftedToNullBinary (ec);
375                 }
376
377                 void EmitLiftedToNullBinary (EmitContext ec)
378                 {
379                         var ig = ec.ig;
380                         var left = ec.EmitStored (this.left);
381                         var right = ec.EmitStored (this.right);
382                         var result = ig.DeclareLocal (Type);
383
384                         var has_value = ig.DefineLabel ();
385                         var done = ig.DefineLabel ();
386
387                         ec.EmitNullableHasValue (left);
388                         ec.EmitNullableHasValue (right);
389                         ig.Emit (OpCodes.And);
390                         ig.Emit (OpCodes.Brtrue, has_value);
391
392                         ec.EmitNullableInitialize (result);
393
394                         ig.Emit (OpCodes.Br, done);
395
396                         ig.MarkLabel (has_value);
397
398                         ec.EmitNullableGetValueOrDefault (left);
399                         ec.EmitNullableGetValueOrDefault (right);
400
401                         EmitBinaryOperator (ec);
402
403                         ec.EmitNullableNew (result.LocalType);
404
405                         ig.MarkLabel (done);
406                 }
407
408                 void EmitLiftedRelationalBinary (EmitContext ec)
409                 {
410                         var ig = ec.ig;
411                         var left = ec.EmitStored (this.left);
412                         var right = ec.EmitStored (this.right);
413
414                         var ret = ig.DefineLabel ();
415                         var done = ig.DefineLabel ();
416
417                         ec.EmitNullableGetValueOrDefault (left);
418                         ec.EmitNullableGetValueOrDefault (right);
419
420                         switch (NodeType) {
421                         case ExpressionType.Equal:
422                         case ExpressionType.NotEqual:
423                                 ig.Emit (OpCodes.Bne_Un, ret);
424                                 break;
425                         default:
426                                 EmitBinaryOperator (ec);
427                                 ig.Emit (OpCodes.Brfalse, ret);
428                                 break;
429                         }
430
431                         ec.EmitNullableHasValue (left);
432                         ec.EmitNullableHasValue (right);
433
434                         switch (NodeType) {
435                         case ExpressionType.Equal:
436                                 ig.Emit (OpCodes.Ceq);
437                                 break;
438                         case ExpressionType.NotEqual:
439                                 ig.Emit (OpCodes.Ceq);
440                                 ig.Emit (OpCodes.Ldc_I4_0);
441                                 ig.Emit (OpCodes.Ceq);
442                                 break;
443                         default:
444                                 ig.Emit (OpCodes.And);
445                                 break;
446                         }
447
448                         ig.Emit (OpCodes.Br, done);
449
450                         ig.MarkLabel (ret);
451
452                         ig.Emit (NodeType == ExpressionType.NotEqual ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
453
454                         ig.MarkLabel (done);
455                 }
456
457                 void EmitArithmeticBinary (EmitContext ec)
458                 {
459                         if (!IsLifted)
460                                 EmitNonLiftedBinary (ec);
461                         else
462                                 EmitLiftedArithmeticBinary (ec);
463                 }
464
465                 void EmitNonLiftedBinary (EmitContext ec)
466                 {
467                         ec.Emit (left);
468                         ec.Emit (right);
469                         EmitBinaryOperator (ec);
470                 }
471
472                 void EmitRelationalBinary (EmitContext ec)
473                 {
474                         if (!IsLifted)
475                                 EmitNonLiftedBinary (ec);
476                         else if (IsLiftedToNull)
477                                 EmitLiftedToNullBinary (ec);
478                         else
479                                 EmitLiftedRelationalBinary (ec);
480                 }
481
482                 internal override void Emit (EmitContext ec)
483                 {
484                         if (method != null){
485                                 EmitMethod (ec);
486                                 return;
487                         }
488
489                         switch (NodeType){
490                         case ExpressionType.ArrayIndex:
491                                 EmitArrayAccess (ec);
492                                 return;
493                         case ExpressionType.Coalesce:
494                                 EmitCoalesce (ec);
495                                 return;
496                         case ExpressionType.Power:
497                         case ExpressionType.Add:
498                         case ExpressionType.AddChecked:
499                         case ExpressionType.Divide:
500                         case ExpressionType.ExclusiveOr:
501                         case ExpressionType.LeftShift:
502                         case ExpressionType.Modulo:
503                         case ExpressionType.Multiply:
504                         case ExpressionType.MultiplyChecked:
505                         case ExpressionType.RightShift:
506                         case ExpressionType.Subtract:
507                         case ExpressionType.SubtractChecked:
508                                 EmitArithmeticBinary (ec);
509                                 return;
510                         case ExpressionType.Equal:
511                         case ExpressionType.GreaterThan:
512                         case ExpressionType.GreaterThanOrEqual:
513                         case ExpressionType.LessThan:
514                         case ExpressionType.LessThanOrEqual:
515                         case ExpressionType.NotEqual:
516                                 EmitRelationalBinary (ec);
517                                 return;
518                         case ExpressionType.And:
519                         case ExpressionType.Or:
520                         case ExpressionType.AndAlso:
521                         case ExpressionType.OrElse:
522                                 EmitLogicalBinary (ec);
523                                 return;
524                         default:
525                                 throw new NotSupportedException (this.NodeType.ToString ());
526                         }
527                 }
528         }
529 }