2008-03-06 Jb Evain <jbevain@novell.com>
[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.ig.Emit (OpCodes.Call, 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 EmitLiftedLogical (EmitContext ec, bool and, bool short_circuit)
118                 {
119                         var ig = ec.ig;
120                         LocalBuilder ret = ig.DeclareLocal (Type);
121                         LocalBuilder vleft = null, vright = null;
122                         MethodInfo has_value = left.Type.GetMethod ("get_HasValue");
123                         MethodInfo get_value = GetMethodNoPar (left.Type, "get_Value");
124
125                         vleft = EmitStored (ec, left);
126                         if (!short_circuit)
127                                 vright = EmitStored (ec, right);
128
129                         Label left_is_null = ig.DefineLabel ();
130                         Label left_is_false = ig.DefineLabel ();
131                         Label right_is_null = ig.DefineLabel ();
132                         Label create = ig.DefineLabel ();
133                         Label exit = ig.DefineLabel ();
134                         Label both_are_null = ig.DefineLabel ();
135
136                         // Check left
137
138                         ig.Emit (OpCodes.Ldloca, vleft);
139                         ig.Emit (OpCodes.Call, has_value);
140                         ig.Emit (OpCodes.Brfalse, left_is_null);
141
142                         ig.Emit (OpCodes.Ldloca, vleft);
143                         ig.Emit (OpCodes.Call, get_value);
144                         ig.Emit (OpCodes.Dup);
145
146                         ig.Emit (and ? OpCodes.Brfalse : OpCodes.Brtrue, create);
147
148                         // Deal with right
149                         if (short_circuit)
150                                 vright = EmitStored (ec, right);
151
152                         ig.Emit (OpCodes.Ldloca, vright);
153                         ig.Emit (OpCodes.Call, has_value);
154                         ig.Emit (OpCodes.Brfalse, right_is_null);
155
156                         ig.Emit (OpCodes.Ldloca, vright);
157                         ig.Emit (OpCodes.Call, get_value);
158
159                         ig.Emit (and ? OpCodes.And : OpCodes.Or);
160                         ig.Emit (OpCodes.Br, create);
161
162                         // left_is_null:
163                         ig.MarkLabel (left_is_null);
164
165                         ig.Emit (OpCodes.Ldloca, vright);
166                         ig.Emit (OpCodes.Call, has_value);
167                         ig.Emit (OpCodes.Brfalse, both_are_null);
168                         ig.Emit (OpCodes.Ldloca, vright);
169                         ig.Emit (OpCodes.Call, get_value);
170                         ig.Emit (OpCodes.Dup);
171                         ig.Emit (and ? OpCodes.Brfalse : OpCodes.Brtrue, create);
172
173                         // right_is_null:
174                         ig.MarkLabel (right_is_null);
175                         ig.Emit (OpCodes.Pop);
176
177                         // both_are_null:
178                         ig.MarkLabel (both_are_null);
179                         ig.Emit (OpCodes.Ldloca, ret);
180                         ig.Emit (OpCodes.Initobj, Type);
181                         ig.Emit (OpCodes.Ldloc, ret);
182                         ig.Emit (OpCodes.Br, exit);
183
184                         // create:
185                         ig.MarkLabel (create);
186                         ig.Emit (OpCodes.Newobj, Type.GetConstructors () [0]);
187
188                         // exit:
189                         ig.MarkLabel (exit);
190                 }
191
192                 void EmitLogical (EmitContext ec, bool and, bool short_circuit)
193                 {
194                         ILGenerator ig = ec.ig;
195                         if (IsLifted) {
196                                 EmitLiftedLogical (ec, and, short_circuit);
197                                 return;
198                         }
199
200                         left.Emit (ec);
201                         right.Emit (ec);
202                         ig.Emit (and ? OpCodes.And : OpCodes.Or);
203                 }
204
205                 void EmitCoalesce (EmitContext ec)
206                 {
207                         ILGenerator ig = ec.ig;
208
209                         LocalBuilder vleft;
210                         LocalBuilder vright;
211
212                         MethodInfo has_value = left.Type.GetMethod ("get_HasValue");
213
214                         Label exit = ig.DefineLabel ();
215                         Label try_right = ig.DefineLabel ();
216                         Label setup_null = ig.DefineLabel ();
217
218                         vleft = EmitStored (ec, left);
219                         if (IsNullable (left.Type)){
220                                 ig.Emit (OpCodes.Ldloca, vleft);
221                                 ig.Emit (OpCodes.Call, has_value);
222                         } else
223                                 ig.Emit (OpCodes.Ldloc, vleft);
224
225                         ig.Emit (OpCodes.Brfalse, try_right);
226                         ig.Emit (OpCodes.Ldloc, vleft);
227                         ig.Emit (OpCodes.Br, exit);
228
229                 // try_right;
230                         ig.MarkLabel (try_right);
231                         vright = EmitStored (ec, right);
232                         if (IsNullable (right.Type)){
233                                 ig.Emit (OpCodes.Ldloca, vright);
234                                 ig.Emit (OpCodes.Call, has_value);
235                         } else
236                                 ig.Emit (OpCodes.Ldloc, vright);
237
238                         ig.Emit (OpCodes.Brfalse, setup_null);
239                         ig.Emit (OpCodes.Ldloc, vright);
240                         ig.Emit (OpCodes.Br, exit);
241
242                 // setup_null:
243                         ig.MarkLabel (setup_null);
244                         LocalBuilder ret = ig.DeclareLocal (Type);
245                         ig.Emit (OpCodes.Ldloca, ret);
246                         ig.Emit (OpCodes.Initobj, Type);
247                         ig.Emit (OpCodes.Ldloc, ret);
248
249                 // exit:
250                         ig.MarkLabel (exit);
251                 }
252
253                 void EmitBinaryOperator (EmitContext ec)
254                 {
255                         OpCode opcode;
256                         var ig = ec.ig;
257                         bool is_unsigned = IsUnsigned (left.Type);
258
259                         switch (NodeType) {
260                         case ExpressionType.Add:
261                                 opcode = OpCodes.Add;
262                                 break;
263
264                         case ExpressionType.AddChecked:
265                                 if (left.Type == typeof (int) || left.Type == typeof (long))
266                                         opcode = OpCodes.Add_Ovf;
267                                 else if (is_unsigned)
268                                         opcode = OpCodes.Add_Ovf_Un;
269                                 else
270                                         opcode = OpCodes.Add;
271                                 break;
272
273                         case ExpressionType.Subtract:
274                                 opcode = OpCodes.Sub;
275                                 break;
276
277                         case ExpressionType.SubtractChecked:
278                                 if (left.Type == typeof (int) || left.Type == typeof (long))
279                                         opcode = OpCodes.Sub_Ovf;
280                                 else if (is_unsigned)
281                                         opcode = OpCodes.Sub_Ovf_Un;
282                                 else
283                                         opcode = OpCodes.Sub;
284                                 break;
285
286                         case ExpressionType.Multiply:
287                                 opcode = OpCodes.Mul;
288                                 break;
289
290                         case ExpressionType.MultiplyChecked:
291                                 if (left.Type == typeof (int) || left.Type == typeof (long))
292                                         opcode = OpCodes.Mul_Ovf;
293                                 else if (is_unsigned)
294                                         opcode = OpCodes.Mul_Ovf_Un;
295                                 else
296                                         opcode = OpCodes.Mul;
297                                 break;
298
299                         case ExpressionType.Divide:
300                                 if (is_unsigned)
301                                         opcode = OpCodes.Div_Un;
302                                 else
303                                         opcode = OpCodes.Div;
304                                 break;
305
306                         case ExpressionType.Modulo:
307                                 if (is_unsigned)
308                                         opcode = OpCodes.Rem_Un;
309                                 else
310                                         opcode = OpCodes.Rem;
311                                 break;
312
313                         case ExpressionType.RightShift:
314                                 if (is_unsigned)
315                                         opcode = OpCodes.Shr_Un;
316                                 else
317                                         opcode = OpCodes.Shr;
318                                 break;
319
320                         case ExpressionType.LeftShift:
321                                 opcode = OpCodes.Shl;
322                                 break;
323
324                         case ExpressionType.And:
325                                 opcode = OpCodes.And;
326                                 break;
327
328                         case ExpressionType.Or:
329                                 opcode = OpCodes.Or;
330                                 break;
331
332                         case ExpressionType.ExclusiveOr:
333                                 opcode = OpCodes.Xor;
334                                 break;
335
336                         case ExpressionType.GreaterThan:
337                                 if (is_unsigned)
338                                         opcode = OpCodes.Cgt_Un;
339                                 else
340                                         opcode = OpCodes.Cgt;
341                                 break;
342
343                         case ExpressionType.GreaterThanOrEqual:
344                                 Type le = left.Type;
345
346                                 if (is_unsigned || (le == typeof (double) || le == typeof (float)))
347                                         ig.Emit (OpCodes.Clt_Un);
348                                 else
349                                         ig.Emit (OpCodes.Clt);
350
351                                 ig.Emit (OpCodes.Ldc_I4_0);
352
353                                 opcode = OpCodes.Ceq;
354                                 break;
355
356                         case ExpressionType.LessThan:
357                                 if (is_unsigned)
358                                         opcode = OpCodes.Clt_Un;
359                                 else
360                                         opcode = OpCodes.Clt;
361                                 break;
362
363                         case ExpressionType.LessThanOrEqual:
364                                 Type lt = left.Type;
365
366                                 if (is_unsigned || (lt == typeof (double) || lt == typeof (float)))
367                                         ig.Emit (OpCodes.Cgt_Un);
368                                 else
369                                         ig.Emit (OpCodes.Cgt);
370                                 ig.Emit (OpCodes.Ldc_I4_0);
371
372                                 opcode = OpCodes.Ceq;
373                                 break;
374
375                         case ExpressionType.Equal:
376                                 opcode = OpCodes.Ceq;
377                                 break;
378
379                         case ExpressionType.NotEqual:
380                                 ig.Emit (OpCodes.Ceq);
381                                 ig.Emit (OpCodes.Ldc_I4_0);
382
383                                 opcode = OpCodes.Ceq;
384                                 break;
385
386                         default:
387                                 throw new InvalidOperationException (string.Format ("Internal error: BinaryExpression contains non-Binary nodetype {0}", NodeType));
388                         }
389
390                         ig.Emit (opcode);
391                 }
392
393                 void EmitLiftedSimpleBinary (EmitContext ec)
394                 {
395                         Label empty_value;
396                         LocalBuilder ret = null;
397
398                         LocalBuilder vleft, vright;
399
400                         var ig = ec.ig;
401                         empty_value = ig.DefineLabel ();
402                         ret = ig.DeclareLocal (Type);
403
404                         vleft = EmitStored (ec, left);
405                         vright = EmitStored (ec, right);
406
407                         MethodInfo has_value = left.Type.GetMethod ("get_HasValue");
408                         MethodInfo get_value = GetMethodNoPar (left.Type, "get_Value");
409
410                         ig.Emit (OpCodes.Ldloca, vleft);
411                         ig.Emit (OpCodes.Call, has_value);
412                         ig.Emit (OpCodes.Brfalse, empty_value);
413                         ig.Emit (OpCodes.Ldloca, vright);
414                         ig.Emit (OpCodes.Call, has_value);
415                         ig.Emit (OpCodes.Brfalse, empty_value);
416                         ig.Emit (OpCodes.Ldloca, vleft);
417                         ig.Emit (OpCodes.Call, get_value);
418                         ig.Emit (OpCodes.Ldloca, vright);
419                         ig.Emit (OpCodes.Call, get_value);
420
421                         EmitBinaryOperator (ec);
422
423                         ig.Emit (OpCodes.Newobj, left.Type.GetConstructors () [0]);
424
425                         Label skip = ig.DefineLabel ();
426                         ig.Emit (OpCodes.Br_S, skip);
427                         ig.MarkLabel (empty_value);
428                         ig.Emit (OpCodes.Ldloc, ret);
429                         ig.Emit (OpCodes.Ldloca, ret);
430                         ig.Emit (OpCodes.Initobj, Type);
431
432                         ig.MarkLabel (skip);
433                 }
434
435                 void EmitSimpleBinary (EmitContext ec)
436                 {
437                         if (IsLifted) {
438                                 EmitLiftedSimpleBinary (ec);
439                                 return;
440                         }
441
442                         left.Emit (ec);
443                         right.Emit (ec);
444                         EmitBinaryOperator (ec);
445                 }
446
447                 internal override void Emit (EmitContext ec)
448                 {
449                         ILGenerator ig = ec.ig;
450                         OpCode opcode;
451
452                         if (method != null){
453                                 EmitMethod (ec);
454                                 return;
455                         }
456
457                         switch (NodeType){
458                         case ExpressionType.And:
459                                 EmitLogical (ec, true, false);
460                                 return;
461
462                         case ExpressionType.Or:
463                                 EmitLogical (ec, false, false);
464                                 return;
465
466                         case ExpressionType.AndAlso:
467                                 EmitLogical (ec, true, true);
468                                 return;
469
470                         case ExpressionType.OrElse:
471                                 EmitLogical (ec, false, true);
472                                 return;
473
474                         case ExpressionType.Coalesce:
475                                 EmitCoalesce (ec);
476                                 return;
477
478                         case ExpressionType.Power:
479                                 left.Emit (ec);
480                                 right.Emit (ec);
481                                 ig.Emit (OpCodes.Call, typeof (System.Math).GetMethod ("Pow"));
482                                 return;
483                         }
484
485                         EmitSimpleBinary (ec);
486                 }
487         }
488 }