5 // Jb Evain (jbevain@novell.com)
6 // Miguel de Icaza (miguel@novell.com)
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)
13 // (C) 2001-2003 Ximian, Inc.
14 // (C) 2004-2008 Novell, Inc. (http://www.novell.com)
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:
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
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.
37 using System.Reflection;
38 using System.Reflection.Emit;
40 namespace System.Linq.Expressions {
42 public sealed class BinaryExpression : Expression {
46 LambdaExpression conversion;
48 bool lift_to_null, is_lifted;
50 public Expression Left {
54 public Expression Right {
58 public MethodInfo Method {
59 get { return method; }
62 public bool IsLifted {
63 get { return is_lifted; }
66 public bool IsLiftedToNull {
67 get { return lift_to_null; }
70 public LambdaExpression Conversion {
71 get { return conversion; }
74 internal BinaryExpression (ExpressionType node_type, Type type, Expression left, Expression right)
75 : base (node_type, type)
81 internal BinaryExpression (ExpressionType node_type, Type type, Expression left, Expression right, MethodInfo method)
82 : base (node_type, type)
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)
95 this.conversion = conversion;
96 this.lift_to_null = lift_to_null;
97 this.is_lifted = is_lifted;
100 void EmitArrayAccess (EmitContext ec)
104 ec.ig.Emit (OpCodes.Ldelem, this.Type);
107 void EmitLogicalBinary (EmitContext ec)
110 case ExpressionType.And:
111 case ExpressionType.Or:
114 else if (Type == typeof (bool?))
115 EmitLiftedLogical (ec);
117 EmitLiftedArithmeticBinary (ec);
119 case ExpressionType.AndAlso:
120 case ExpressionType.OrElse:
122 EmitLogicalShortCircuit (ec);
124 EmitLiftedLogicalShortCircuit (ec);
129 void EmitLogical (EmitContext ec)
131 EmitNonLiftedBinary (ec);
134 void EmitLiftedLogical (EmitContext ec)
137 var and = NodeType == ExpressionType.And;
138 var left = ec.EmitStored (this.left);
139 var right = ec.EmitStored (this.right);
141 var ret_from_left = ig.DefineLabel ();
142 var ret_from_right = ig.DefineLabel ();
143 var done = ig.DefineLabel ();
145 ec.EmitNullableGetValueOrDefault (left);
146 ig.Emit (OpCodes.Brtrue, ret_from_left);
147 ec.EmitNullableGetValueOrDefault (right);
148 ig.Emit (OpCodes.Brtrue, ret_from_right);
150 ec.EmitNullableHasValue (left);
151 ig.Emit (OpCodes.Brfalse, ret_from_left);
153 ig.MarkLabel (ret_from_right);
154 ec.EmitLoad (and ? left : right);
155 ig.Emit (OpCodes.Br, done);
157 ig.MarkLabel (ret_from_left);
158 ec.EmitLoad (and ? right : left);
163 void EmitLogicalShortCircuit (EmitContext ec)
166 var and = NodeType == ExpressionType.AndAlso;
167 var ret = ig.DefineLabel ();
168 var done = ig.DefineLabel ();
171 ig.Emit (and ? OpCodes.Brfalse : OpCodes.Brtrue, ret);
175 ig.Emit (OpCodes.Br, done);
178 ig.Emit (and ? OpCodes.Ldc_I4_0 : OpCodes.Ldc_I4_1);
183 MethodInfo GetFalseOperator ()
185 return GetFalseOperator (left.Type.GetNotNullableType ());
188 MethodInfo GetTrueOperator ()
190 return GetTrueOperator (left.Type.GetNotNullableType ());
193 void EmitUserDefinedLogicalShortCircuit (EmitContext ec)
196 var and = NodeType == ExpressionType.AndAlso;
198 var done = ig.DefineLabel ();
200 var left = ec.EmitStored (this.left);
203 ig.Emit (OpCodes.Dup);
204 ec.EmitCall (and ? GetFalseOperator () : GetTrueOperator ());
205 ig.Emit (OpCodes.Brtrue, done);
207 ec.Emit (this.right);
208 ec.EmitCall (method);
213 void EmitLiftedLogicalShortCircuit (EmitContext ec)
216 var and = NodeType == ExpressionType.AndAlso;
217 var left_is_null = ig.DefineLabel ();
218 var ret_from_left = ig.DefineLabel ();
219 var ret_null = ig.DefineLabel ();
220 var ret_new = ig.DefineLabel();
221 var done = ig.DefineLabel();
223 var left = ec.EmitStored (this.left);
225 ec.EmitNullableHasValue (left);
226 ig.Emit (OpCodes.Brfalse, left_is_null);
228 ec.EmitNullableGetValueOrDefault (left);
230 ig.Emit (OpCodes.Ldc_I4_0);
231 ig.Emit (OpCodes.Ceq);
232 ig.Emit (and ? OpCodes.Brtrue : OpCodes.Brfalse, ret_from_left);
234 ig.MarkLabel (left_is_null);
235 var right = ec.EmitStored (this.right);
237 ec.EmitNullableHasValue (right);
238 ig.Emit (OpCodes.Brfalse_S, ret_null);
240 ec.EmitNullableGetValueOrDefault (right);
242 ig.Emit (OpCodes.Ldc_I4_0);
243 ig.Emit (OpCodes.Ceq);
245 ig.Emit (and ? OpCodes.Brtrue : OpCodes.Brfalse, ret_from_left);
247 ec.EmitNullableHasValue (left);
248 ig.Emit (OpCodes.Brfalse, ret_null);
250 ig.Emit (and ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
251 ig.Emit (OpCodes.Br_S, ret_new);
253 ig.MarkLabel (ret_from_left);
254 ig.Emit (and ? OpCodes.Ldc_I4_0 : OpCodes.Ldc_I4_1);
256 ig.MarkLabel (ret_new);
257 ec.EmitNullableNew (Type);
258 ig.Emit (OpCodes.Br, done);
260 ig.MarkLabel (ret_null);
261 var ret = ig.DeclareLocal (Type);
262 ec.EmitNullableInitialize (ret);
267 void EmitCoalesce (EmitContext ec)
270 var done = ig.DefineLabel ();
271 var load_right = ig.DefineLabel ();
273 var left = ec.EmitStored (this.left);
274 var left_is_nullable = left.LocalType.IsNullable ();
276 if (left_is_nullable)
277 ec.EmitNullableHasValue (left);
281 ig.Emit (OpCodes.Brfalse, load_right);
283 if (left_is_nullable && !Type.IsNullable ())
284 ec.EmitNullableGetValue (left);
288 ig.Emit (OpCodes.Br, done);
290 ig.MarkLabel (load_right);
291 ec.Emit (this.right);
296 void EmitConvertedCoalesce (EmitContext ec)
299 var done = ig.DefineLabel ();
300 var load_right = ig.DefineLabel ();
302 var left = ec.EmitStored (this.left);
304 if (left.LocalType.IsNullable ())
305 ec.EmitNullableHasValue (left);
309 ig.Emit (OpCodes.Brfalse, load_right);
311 ec.Emit (conversion);
313 ig.Emit (OpCodes.Callvirt, conversion.Type.GetInvokeMethod ());
315 ig.Emit (OpCodes.Br, done);
317 ig.MarkLabel (load_right);
318 ec.Emit (this.right);
323 static bool IsInt32OrInt64 (Type type)
325 return type == typeof (int) || type == typeof (long);
328 static bool IsSingleOrDouble (Type type)
330 return type == typeof (float) || type == typeof (double);
333 void EmitBinaryOperator (EmitContext ec)
336 bool is_unsigned = IsUnsigned (left.Type);
339 case ExpressionType.Add:
340 ig.Emit (OpCodes.Add);
342 case ExpressionType.AddChecked:
343 if (IsInt32OrInt64 (left.Type))
344 ig.Emit (OpCodes.Add_Ovf);
346 ig.Emit (is_unsigned ? OpCodes.Add_Ovf_Un : OpCodes.Add);
348 case ExpressionType.Subtract:
349 ig.Emit (OpCodes.Sub);
351 case ExpressionType.SubtractChecked:
352 if (IsInt32OrInt64 (left.Type))
353 ig.Emit (OpCodes.Sub_Ovf);
355 ig.Emit (is_unsigned ? OpCodes.Sub_Ovf_Un : OpCodes.Sub);
357 case ExpressionType.Multiply:
358 ig.Emit (OpCodes.Mul);
360 case ExpressionType.MultiplyChecked:
361 if (IsInt32OrInt64 (left.Type))
362 ig.Emit (OpCodes.Mul_Ovf);
364 ig.Emit (is_unsigned ? OpCodes.Mul_Ovf_Un : OpCodes.Mul);
366 case ExpressionType.Divide:
367 ig.Emit (is_unsigned ? OpCodes.Div_Un : OpCodes.Div);
369 case ExpressionType.Modulo:
370 ig.Emit (is_unsigned ? OpCodes.Rem_Un : OpCodes.Rem);
372 case ExpressionType.RightShift:
373 case ExpressionType.LeftShift:
374 ig.Emit (OpCodes.Ldc_I4, left.Type == typeof (int) ? 0x1f : 0x3f);
375 ig.Emit (OpCodes.And);
376 if (NodeType == ExpressionType.RightShift)
377 ig.Emit (is_unsigned ? OpCodes.Shr_Un : OpCodes.Shr);
379 ig.Emit (OpCodes.Shl);
381 case ExpressionType.And:
382 ig.Emit (OpCodes.And);
384 case ExpressionType.Or:
385 ig.Emit (OpCodes.Or);
387 case ExpressionType.ExclusiveOr:
388 ig.Emit (OpCodes.Xor);
390 case ExpressionType.GreaterThan:
391 ig.Emit (is_unsigned ? OpCodes.Cgt_Un : OpCodes.Cgt);
393 case ExpressionType.GreaterThanOrEqual:
394 if (is_unsigned || IsSingleOrDouble (left.Type))
395 ig.Emit (OpCodes.Clt_Un);
397 ig.Emit (OpCodes.Clt);
399 ig.Emit (OpCodes.Ldc_I4_0);
400 ig.Emit (OpCodes.Ceq);
402 case ExpressionType.LessThan:
403 ig.Emit (is_unsigned ? OpCodes.Clt_Un : OpCodes.Clt);
405 case ExpressionType.LessThanOrEqual:
406 if (is_unsigned || IsSingleOrDouble (left.Type))
407 ig.Emit (OpCodes.Cgt_Un);
409 ig.Emit (OpCodes.Cgt);
411 ig.Emit (OpCodes.Ldc_I4_0);
412 ig.Emit (OpCodes.Ceq);
414 case ExpressionType.Equal:
415 ig.Emit (OpCodes.Ceq);
417 case ExpressionType.NotEqual:
418 ig.Emit (OpCodes.Ceq);
419 ig.Emit (OpCodes.Ldc_I4_0);
420 ig.Emit (OpCodes.Ceq);
422 case ExpressionType.Power:
423 ig.Emit (OpCodes.Call, typeof (Math).GetMethod ("Pow"));
426 throw new InvalidOperationException (
427 string.Format ("Internal error: BinaryExpression contains non-Binary nodetype {0}", NodeType));
431 bool IsLeftLiftedBinary ()
433 return left.Type.IsNullable () && !right.Type.IsNullable ();
436 void EmitLeftLiftedToNullBinary (EmitContext ec)
440 var ret = ig.DefineLabel ();
441 var done = ig.DefineLabel ();
443 var left = ec.EmitStored (this.left);
445 ec.EmitNullableHasValue (left);
446 ig.Emit (OpCodes.Brfalse, ret);
448 ec.EmitNullableGetValueOrDefault (left);
451 EmitBinaryOperator (ec);
453 ec.EmitNullableNew (Type);
455 ig.Emit (OpCodes.Br, done);
459 var temp = ig.DeclareLocal (Type);
460 ec.EmitNullableInitialize (temp);
465 void EmitLiftedArithmeticBinary (EmitContext ec)
467 if (IsLeftLiftedBinary ())
468 EmitLeftLiftedToNullBinary (ec);
470 EmitLiftedToNullBinary (ec);
473 void EmitLiftedToNullBinary (EmitContext ec)
476 var left = ec.EmitStored (this.left);
477 var right = ec.EmitStored (this.right);
478 var result = ig.DeclareLocal (Type);
480 var has_value = ig.DefineLabel ();
481 var done = ig.DefineLabel ();
483 ec.EmitNullableHasValue (left);
484 ec.EmitNullableHasValue (right);
485 ig.Emit (OpCodes.And);
486 ig.Emit (OpCodes.Brtrue, has_value);
488 ec.EmitNullableInitialize (result);
490 ig.Emit (OpCodes.Br, done);
492 ig.MarkLabel (has_value);
494 ec.EmitNullableGetValueOrDefault (left);
495 ec.EmitNullableGetValueOrDefault (right);
497 EmitBinaryOperator (ec);
499 ec.EmitNullableNew (result.LocalType);
504 void EmitLiftedRelationalBinary (EmitContext ec)
507 var left = ec.EmitStored (this.left);
508 var right = ec.EmitStored (this.right);
510 var ret = ig.DefineLabel ();
511 var done = ig.DefineLabel ();
513 ec.EmitNullableGetValueOrDefault (left);
514 ec.EmitNullableGetValueOrDefault (right);
517 case ExpressionType.Equal:
518 case ExpressionType.NotEqual:
519 ig.Emit (OpCodes.Bne_Un, ret);
522 EmitBinaryOperator (ec);
523 ig.Emit (OpCodes.Brfalse, ret);
527 ec.EmitNullableHasValue (left);
528 ec.EmitNullableHasValue (right);
531 case ExpressionType.Equal:
532 ig.Emit (OpCodes.Ceq);
534 case ExpressionType.NotEqual:
535 ig.Emit (OpCodes.Ceq);
536 ig.Emit (OpCodes.Ldc_I4_0);
537 ig.Emit (OpCodes.Ceq);
540 ig.Emit (OpCodes.And);
544 ig.Emit (OpCodes.Br, done);
548 ig.Emit (NodeType == ExpressionType.NotEqual ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
553 void EmitArithmeticBinary (EmitContext ec)
556 EmitNonLiftedBinary (ec);
558 EmitLiftedArithmeticBinary (ec);
561 void EmitNonLiftedBinary (EmitContext ec)
565 EmitBinaryOperator (ec);
568 void EmitRelationalBinary (EmitContext ec)
571 EmitNonLiftedBinary (ec);
572 else if (IsLiftedToNull)
573 EmitLiftedToNullBinary (ec);
575 EmitLiftedRelationalBinary (ec);
578 void EmitLiftedUserDefinedOperator (EmitContext ec)
582 var ret_true = ig.DefineLabel ();
583 var ret_false = ig.DefineLabel ();
584 var done = ig.DefineLabel ();
586 var left = ec.EmitStored (this.left);
587 var right = ec.EmitStored (this.right);
589 ec.EmitNullableHasValue (left);
590 ec.EmitNullableHasValue (right);
592 case ExpressionType.Equal:
593 ig.Emit (OpCodes.Bne_Un, ret_false);
594 ec.EmitNullableHasValue (left);
595 ig.Emit (OpCodes.Brfalse, ret_true);
597 case ExpressionType.NotEqual:
598 ig.Emit (OpCodes.Bne_Un, ret_true);
599 ec.EmitNullableHasValue (left);
600 ig.Emit (OpCodes.Brfalse, ret_false);
603 ig.Emit (OpCodes.And);
604 ig.Emit (OpCodes.Brfalse, ret_false);
608 ec.EmitNullableGetValueOrDefault (left);
609 ec.EmitNullableGetValueOrDefault (right);
610 ec.EmitCall (method);
611 ig.Emit (OpCodes.Br, done);
613 ig.MarkLabel (ret_true);
614 ig.Emit (OpCodes.Ldc_I4_1);
615 ig.Emit (OpCodes.Br, done);
617 ig.MarkLabel (ret_false);
618 ig.Emit (OpCodes.Ldc_I4_0);
619 ig.Emit (OpCodes.Br, done);
624 void EmitLiftedToNullUserDefinedOperator (EmitContext ec)
628 var ret = ig.DefineLabel ();
629 var done = ig.DefineLabel ();
631 var left = ec.EmitStored (this.left);
632 var right = ec.EmitStored (this.right);
634 ec.EmitNullableHasValue (left);
635 ec.EmitNullableHasValue (right);
636 ig.Emit (OpCodes.And);
637 ig.Emit (OpCodes.Brfalse, ret);
639 ec.EmitNullableGetValueOrDefault (left);
640 ec.EmitNullableGetValueOrDefault (right);
641 ec.EmitCall (method);
642 ec.EmitNullableNew (Type);
643 ig.Emit (OpCodes.Br, done);
646 var temp = ig.DeclareLocal (Type);
647 ec.EmitNullableInitialize (temp);
652 void EmitUserDefinedLiftedLogicalShortCircuit (EmitContext ec)
655 var and = NodeType == ExpressionType.AndAlso;
657 var left_is_null = ig.DefineLabel ();
658 var ret_left = ig.DefineLabel ();
659 var ret_null = ig.DefineLabel ();
660 var done = ig.DefineLabel ();
662 var left = ec.EmitStored (this.left);
664 ec.EmitNullableHasValue (left);
665 ig.Emit (OpCodes.Brfalse, and ? ret_null : left_is_null);
667 ec.EmitNullableGetValueOrDefault (left);
668 ec.EmitCall (and ? GetFalseOperator () : GetTrueOperator ());
669 ig.Emit (OpCodes.Brtrue, ret_left);
671 ig.MarkLabel (left_is_null);
672 var right = ec.EmitStored (this.right);
673 ec.EmitNullableHasValue (right);
674 ig.Emit (OpCodes.Brfalse, ret_null);
676 ec.EmitNullableGetValueOrDefault (left);
677 ec.EmitNullableGetValueOrDefault (right);
678 ec.EmitCall (method);
680 ec.EmitNullableNew (Type);
681 ig.Emit (OpCodes.Br, done);
683 ig.MarkLabel (ret_left);
685 ig.Emit (OpCodes.Br, done);
687 ig.MarkLabel (ret_null);
688 var ret = ig.DeclareLocal (Type);
689 ec.EmitNullableInitialize (ret);
694 void EmitUserDefinedOperator (EmitContext ec)
698 case ExpressionType.AndAlso:
699 case ExpressionType.OrElse:
700 EmitUserDefinedLogicalShortCircuit (ec);
705 ec.EmitCall (method);
708 } else if (IsLiftedToNull) {
710 case ExpressionType.AndAlso:
711 case ExpressionType.OrElse:
712 EmitUserDefinedLiftedLogicalShortCircuit (ec);
715 EmitLiftedToNullUserDefinedOperator (ec);
719 EmitLiftedUserDefinedOperator (ec);
722 internal override void Emit (EmitContext ec)
724 if (method != null) {
725 EmitUserDefinedOperator (ec);
730 case ExpressionType.ArrayIndex:
731 EmitArrayAccess (ec);
733 case ExpressionType.Coalesce:
734 if (conversion != null)
735 EmitConvertedCoalesce (ec);
739 case ExpressionType.Power:
740 case ExpressionType.Add:
741 case ExpressionType.AddChecked:
742 case ExpressionType.Divide:
743 case ExpressionType.ExclusiveOr:
744 case ExpressionType.LeftShift:
745 case ExpressionType.Modulo:
746 case ExpressionType.Multiply:
747 case ExpressionType.MultiplyChecked:
748 case ExpressionType.RightShift:
749 case ExpressionType.Subtract:
750 case ExpressionType.SubtractChecked:
751 EmitArithmeticBinary (ec);
753 case ExpressionType.Equal:
754 case ExpressionType.GreaterThan:
755 case ExpressionType.GreaterThanOrEqual:
756 case ExpressionType.LessThan:
757 case ExpressionType.LessThanOrEqual:
758 case ExpressionType.NotEqual:
759 EmitRelationalBinary (ec);
761 case ExpressionType.And:
762 case ExpressionType.Or:
763 case ExpressionType.AndAlso:
764 case ExpressionType.OrElse:
765 EmitLogicalBinary (ec);
768 throw new NotSupportedException (this.NodeType.ToString ());