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 ());