svn path=/trunk/mcs/; revision=104772
[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                         var left_is_nullable = left.LocalType.IsNullable ();
252
253                         if (left_is_nullable)
254                                 ec.EmitNullableHasValue (left);
255                         else
256                                 ec.EmitLoad (left);
257
258                         ig.Emit (OpCodes.Brfalse, load_right);
259
260                         if (left_is_nullable && !Type.IsNullable ())
261                                 ec.EmitNullableGetValue (left);
262                         else
263                                 ec.EmitLoad (left);
264
265                         ig.Emit (OpCodes.Br, done);
266
267                         ig.MarkLabel (load_right);
268                         ec.Emit (this.right);
269
270                         ig.MarkLabel (done);
271                 }
272
273                 static bool IsInt32OrInt64 (Type type)
274                 {
275                         return type == typeof (int) || type == typeof (long);
276                 }
277
278                 static bool IsSingleOrDouble (Type type)
279                 {
280                         return type == typeof (float) || type == typeof (double);
281                 }
282
283                 void EmitBinaryOperator (EmitContext ec)
284                 {
285                         var ig = ec.ig;
286                         bool is_unsigned = IsUnsigned (left.Type);
287
288                         switch (NodeType) {
289                         case ExpressionType.Add:
290                                 ig.Emit (OpCodes.Add);
291                                 break;
292                         case ExpressionType.AddChecked:
293                                 if (IsInt32OrInt64 (left.Type))
294                                         ig.Emit (OpCodes.Add_Ovf);
295                                 else
296                                         ig.Emit (is_unsigned ? OpCodes.Add_Ovf_Un : OpCodes.Add);
297                                 break;
298                         case ExpressionType.Subtract:
299                                 ig.Emit (OpCodes.Sub);
300                                 break;
301                         case ExpressionType.SubtractChecked:
302                                 if (IsInt32OrInt64 (left.Type))
303                                         ig.Emit (OpCodes.Sub_Ovf);
304                                 else
305                                         ig.Emit (is_unsigned ? OpCodes.Sub_Ovf_Un : OpCodes.Sub);
306                                 break;
307                         case ExpressionType.Multiply:
308                                 ig.Emit (OpCodes.Mul);
309                                 break;
310                         case ExpressionType.MultiplyChecked:
311                                 if (IsInt32OrInt64 (left.Type))
312                                         ig.Emit (OpCodes.Mul_Ovf);
313                                 else
314                                         ig.Emit (is_unsigned ? OpCodes.Mul_Ovf_Un : OpCodes.Mul);
315                                 break;
316                         case ExpressionType.Divide:
317                                 ig.Emit (is_unsigned ? OpCodes.Div_Un : OpCodes.Div);
318                                 break;
319                         case ExpressionType.Modulo:
320                                 ig.Emit (is_unsigned ? OpCodes.Rem_Un : OpCodes.Rem);
321                                 break;
322                         case ExpressionType.RightShift:
323                                 ig.Emit (is_unsigned ? OpCodes.Shr_Un : OpCodes.Shr);
324                                 break;
325                         case ExpressionType.LeftShift:
326                                 ig.Emit (OpCodes.Shl);
327                                 break;
328                         case ExpressionType.And:
329                                 ig.Emit (OpCodes.And);
330                                 break;
331                         case ExpressionType.Or:
332                                 ig.Emit (OpCodes.Or);
333                                 break;
334                         case ExpressionType.ExclusiveOr:
335                                 ig.Emit (OpCodes.Xor);
336                                 break;
337                         case ExpressionType.GreaterThan:
338                                 ig.Emit (is_unsigned ? OpCodes.Cgt_Un : OpCodes.Cgt);
339                                 break;
340                         case ExpressionType.GreaterThanOrEqual:
341                                 if (is_unsigned || IsSingleOrDouble (left.Type))
342                                         ig.Emit (OpCodes.Clt_Un);
343                                 else
344                                         ig.Emit (OpCodes.Clt);
345
346                                 ig.Emit (OpCodes.Ldc_I4_0);
347                                 ig.Emit (OpCodes.Ceq);
348                                 break;
349                         case ExpressionType.LessThan:
350                                 ig.Emit (is_unsigned ? OpCodes.Clt_Un : OpCodes.Clt);
351                                 break;
352                         case ExpressionType.LessThanOrEqual:
353                                 if (is_unsigned || IsSingleOrDouble (left.Type))
354                                         ig.Emit (OpCodes.Cgt_Un);
355                                 else
356                                         ig.Emit (OpCodes.Cgt);
357
358                                 ig.Emit (OpCodes.Ldc_I4_0);
359                                 ig.Emit (OpCodes.Ceq);
360                                 break;
361                         case ExpressionType.Equal:
362                                 ig.Emit (OpCodes.Ceq);
363                                 break;
364                         case ExpressionType.NotEqual:
365                                 ig.Emit (OpCodes.Ceq);
366                                 ig.Emit (OpCodes.Ldc_I4_0);
367                                 ig.Emit (OpCodes.Ceq);
368                                 break;
369                         case ExpressionType.Power:
370                                 ig.Emit (OpCodes.Call, typeof (Math).GetMethod ("Pow"));
371                                 break;
372                         default:
373                                 throw new InvalidOperationException (
374                                         string.Format ("Internal error: BinaryExpression contains non-Binary nodetype {0}", NodeType));
375                         }
376                 }
377
378                 void EmitLiftedArithmeticBinary (EmitContext ec)
379                 {
380                         EmitLiftedToNullBinary (ec);
381                 }
382
383                 void EmitLiftedToNullBinary (EmitContext ec)
384                 {
385                         var ig = ec.ig;
386                         var left = ec.EmitStored (this.left);
387                         var right = ec.EmitStored (this.right);
388                         var result = ig.DeclareLocal (Type);
389
390                         var has_value = ig.DefineLabel ();
391                         var done = ig.DefineLabel ();
392
393                         ec.EmitNullableHasValue (left);
394                         ec.EmitNullableHasValue (right);
395                         ig.Emit (OpCodes.And);
396                         ig.Emit (OpCodes.Brtrue, has_value);
397
398                         ec.EmitNullableInitialize (result);
399
400                         ig.Emit (OpCodes.Br, done);
401
402                         ig.MarkLabel (has_value);
403
404                         ec.EmitNullableGetValueOrDefault (left);
405                         ec.EmitNullableGetValueOrDefault (right);
406
407                         EmitBinaryOperator (ec);
408
409                         ec.EmitNullableNew (result.LocalType);
410
411                         ig.MarkLabel (done);
412                 }
413
414                 void EmitLiftedRelationalBinary (EmitContext ec)
415                 {
416                         var ig = ec.ig;
417                         var left = ec.EmitStored (this.left);
418                         var right = ec.EmitStored (this.right);
419
420                         var ret = ig.DefineLabel ();
421                         var done = ig.DefineLabel ();
422
423                         ec.EmitNullableGetValueOrDefault (left);
424                         ec.EmitNullableGetValueOrDefault (right);
425
426                         switch (NodeType) {
427                         case ExpressionType.Equal:
428                         case ExpressionType.NotEqual:
429                                 ig.Emit (OpCodes.Bne_Un, ret);
430                                 break;
431                         default:
432                                 EmitBinaryOperator (ec);
433                                 ig.Emit (OpCodes.Brfalse, ret);
434                                 break;
435                         }
436
437                         ec.EmitNullableHasValue (left);
438                         ec.EmitNullableHasValue (right);
439
440                         switch (NodeType) {
441                         case ExpressionType.Equal:
442                                 ig.Emit (OpCodes.Ceq);
443                                 break;
444                         case ExpressionType.NotEqual:
445                                 ig.Emit (OpCodes.Ceq);
446                                 ig.Emit (OpCodes.Ldc_I4_0);
447                                 ig.Emit (OpCodes.Ceq);
448                                 break;
449                         default:
450                                 ig.Emit (OpCodes.And);
451                                 break;
452                         }
453
454                         ig.Emit (OpCodes.Br, done);
455
456                         ig.MarkLabel (ret);
457
458                         ig.Emit (NodeType == ExpressionType.NotEqual ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
459
460                         ig.MarkLabel (done);
461                 }
462
463                 void EmitArithmeticBinary (EmitContext ec)
464                 {
465                         if (!IsLifted)
466                                 EmitNonLiftedBinary (ec);
467                         else
468                                 EmitLiftedArithmeticBinary (ec);
469                 }
470
471                 void EmitNonLiftedBinary (EmitContext ec)
472                 {
473                         ec.Emit (left);
474                         ec.Emit (right);
475                         EmitBinaryOperator (ec);
476                 }
477
478                 void EmitRelationalBinary (EmitContext ec)
479                 {
480                         if (!IsLifted)
481                                 EmitNonLiftedBinary (ec);
482                         else if (IsLiftedToNull)
483                                 EmitLiftedToNullBinary (ec);
484                         else
485                                 EmitLiftedRelationalBinary (ec);
486                 }
487
488                 internal override void Emit (EmitContext ec)
489                 {
490                         if (method != null){
491                                 EmitMethod (ec);
492                                 return;
493                         }
494
495                         switch (NodeType){
496                         case ExpressionType.ArrayIndex:
497                                 EmitArrayAccess (ec);
498                                 return;
499                         case ExpressionType.Coalesce:
500                                 EmitCoalesce (ec);
501                                 return;
502                         case ExpressionType.Power:
503                         case ExpressionType.Add:
504                         case ExpressionType.AddChecked:
505                         case ExpressionType.Divide:
506                         case ExpressionType.ExclusiveOr:
507                         case ExpressionType.LeftShift:
508                         case ExpressionType.Modulo:
509                         case ExpressionType.Multiply:
510                         case ExpressionType.MultiplyChecked:
511                         case ExpressionType.RightShift:
512                         case ExpressionType.Subtract:
513                         case ExpressionType.SubtractChecked:
514                                 EmitArithmeticBinary (ec);
515                                 return;
516                         case ExpressionType.Equal:
517                         case ExpressionType.GreaterThan:
518                         case ExpressionType.GreaterThanOrEqual:
519                         case ExpressionType.LessThan:
520                         case ExpressionType.LessThanOrEqual:
521                         case ExpressionType.NotEqual:
522                                 EmitRelationalBinary (ec);
523                                 return;
524                         case ExpressionType.And:
525                         case ExpressionType.Or:
526                         case ExpressionType.AndAlso:
527                         case ExpressionType.OrElse:
528                                 EmitLogicalBinary (ec);
529                                 return;
530                         default:
531                                 throw new NotSupportedException (this.NodeType.ToString ());
532                         }
533                 }
534         }
535 }