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