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