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