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 #if !FULL_AOT_RUNTIME
101 void EmitArrayAccess (EmitContext ec)
105 ec.ig.Emit (OpCodes.Ldelem, this.Type);
108 void EmitLogicalBinary (EmitContext ec)
111 case ExpressionType.And:
112 case ExpressionType.Or:
115 else if (Type == typeof (bool?))
116 EmitLiftedLogical (ec);
118 EmitLiftedArithmeticBinary (ec);
120 case ExpressionType.AndAlso:
121 case ExpressionType.OrElse:
123 EmitLogicalShortCircuit (ec);
125 EmitLiftedLogicalShortCircuit (ec);
130 void EmitLogical (EmitContext ec)
132 EmitNonLiftedBinary (ec);
135 void EmitLiftedLogical (EmitContext ec)
138 var and = NodeType == ExpressionType.And;
139 var left = ec.EmitStored (this.left);
140 var right = ec.EmitStored (this.right);
142 var ret_from_left = ig.DefineLabel ();
143 var ret_from_right = ig.DefineLabel ();
144 var done = ig.DefineLabel ();
146 ec.EmitNullableGetValueOrDefault (left);
147 ig.Emit (OpCodes.Brtrue, ret_from_left);
148 ec.EmitNullableGetValueOrDefault (right);
149 ig.Emit (OpCodes.Brtrue, ret_from_right);
151 ec.EmitNullableHasValue (left);
152 ig.Emit (OpCodes.Brfalse, ret_from_left);
154 ig.MarkLabel (ret_from_right);
155 ec.EmitLoad (and ? left : right);
156 ig.Emit (OpCodes.Br, done);
158 ig.MarkLabel (ret_from_left);
159 ec.EmitLoad (and ? right : left);
164 void EmitLogicalShortCircuit (EmitContext ec)
167 var and = NodeType == ExpressionType.AndAlso;
168 var ret = ig.DefineLabel ();
169 var done = ig.DefineLabel ();
172 ig.Emit (and ? OpCodes.Brfalse : OpCodes.Brtrue, ret);
176 ig.Emit (OpCodes.Br, done);
179 ig.Emit (and ? OpCodes.Ldc_I4_0 : OpCodes.Ldc_I4_1);
184 MethodInfo GetFalseOperator ()
186 return GetFalseOperator (left.Type.GetNotNullableType ());
189 MethodInfo GetTrueOperator ()
191 return GetTrueOperator (left.Type.GetNotNullableType ());
194 void EmitUserDefinedLogicalShortCircuit (EmitContext ec)
197 var and = NodeType == ExpressionType.AndAlso;
199 var done = ig.DefineLabel ();
201 var left = ec.EmitStored (this.left);
204 ig.Emit (OpCodes.Dup);
205 ec.EmitCall (and ? GetFalseOperator () : GetTrueOperator ());
206 ig.Emit (OpCodes.Brtrue, done);
208 ec.Emit (this.right);
209 ec.EmitCall (method);
214 void EmitLiftedLogicalShortCircuit (EmitContext ec)
217 var and = NodeType == ExpressionType.AndAlso;
218 var left_is_null = ig.DefineLabel ();
219 var ret_from_left = ig.DefineLabel ();
220 var ret_null = ig.DefineLabel ();
221 var ret_new = ig.DefineLabel();
222 var done = ig.DefineLabel();
224 var left = ec.EmitStored (this.left);
226 ec.EmitNullableHasValue (left);
227 ig.Emit (OpCodes.Brfalse, left_is_null);
229 ec.EmitNullableGetValueOrDefault (left);
231 ig.Emit (OpCodes.Ldc_I4_0);
232 ig.Emit (OpCodes.Ceq);
233 ig.Emit (and ? OpCodes.Brtrue : OpCodes.Brfalse, ret_from_left);
235 ig.MarkLabel (left_is_null);
236 var right = ec.EmitStored (this.right);
238 ec.EmitNullableHasValue (right);
239 ig.Emit (OpCodes.Brfalse_S, ret_null);
241 ec.EmitNullableGetValueOrDefault (right);
243 ig.Emit (OpCodes.Ldc_I4_0);
244 ig.Emit (OpCodes.Ceq);
246 ig.Emit (and ? OpCodes.Brtrue : OpCodes.Brfalse, ret_from_left);
248 ec.EmitNullableHasValue (left);
249 ig.Emit (OpCodes.Brfalse, ret_null);
251 ig.Emit (and ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
252 ig.Emit (OpCodes.Br_S, ret_new);
254 ig.MarkLabel (ret_from_left);
255 ig.Emit (and ? OpCodes.Ldc_I4_0 : OpCodes.Ldc_I4_1);
257 ig.MarkLabel (ret_new);
258 ec.EmitNullableNew (Type);
259 ig.Emit (OpCodes.Br, done);
261 ig.MarkLabel (ret_null);
262 var ret = ig.DeclareLocal (Type);
263 ec.EmitNullableInitialize (ret);
268 void EmitCoalesce (EmitContext ec)
271 var done = ig.DefineLabel ();
272 var load_right = ig.DefineLabel ();
274 var left = ec.EmitStored (this.left);
275 var left_is_nullable = left.LocalType.IsNullable ();
277 if (left_is_nullable)
278 ec.EmitNullableHasValue (left);
282 ig.Emit (OpCodes.Brfalse, load_right);
284 if (left_is_nullable && !Type.IsNullable ())
285 ec.EmitNullableGetValue (left);
289 ig.Emit (OpCodes.Br, done);
291 ig.MarkLabel (load_right);
292 ec.Emit (this.right);
297 void EmitConvertedCoalesce (EmitContext ec)
300 var done = ig.DefineLabel ();
301 var load_right = ig.DefineLabel ();
303 var left = ec.EmitStored (this.left);
305 if (left.LocalType.IsNullable ())
306 ec.EmitNullableHasValue (left);
310 ig.Emit (OpCodes.Brfalse, load_right);
312 ec.Emit (conversion);
314 ig.Emit (OpCodes.Callvirt, conversion.Type.GetInvokeMethod ());
316 ig.Emit (OpCodes.Br, done);
318 ig.MarkLabel (load_right);
319 ec.Emit (this.right);
324 static bool IsInt32OrInt64 (Type type)
326 return type == typeof (int) || type == typeof (long);
329 static bool IsSingleOrDouble (Type type)
331 return type == typeof (float) || type == typeof (double);
334 void EmitBinaryOperator (EmitContext ec)
337 bool is_unsigned = IsUnsigned (left.Type);
340 case ExpressionType.Add:
341 ig.Emit (OpCodes.Add);
343 case ExpressionType.AddChecked:
344 if (IsInt32OrInt64 (left.Type))
345 ig.Emit (OpCodes.Add_Ovf);
347 ig.Emit (is_unsigned ? OpCodes.Add_Ovf_Un : OpCodes.Add);
349 case ExpressionType.Subtract:
350 ig.Emit (OpCodes.Sub);
352 case ExpressionType.SubtractChecked:
353 if (IsInt32OrInt64 (left.Type))
354 ig.Emit (OpCodes.Sub_Ovf);
356 ig.Emit (is_unsigned ? OpCodes.Sub_Ovf_Un : OpCodes.Sub);
358 case ExpressionType.Multiply:
359 ig.Emit (OpCodes.Mul);
361 case ExpressionType.MultiplyChecked:
362 if (IsInt32OrInt64 (left.Type))
363 ig.Emit (OpCodes.Mul_Ovf);
365 ig.Emit (is_unsigned ? OpCodes.Mul_Ovf_Un : OpCodes.Mul);
367 case ExpressionType.Divide:
368 ig.Emit (is_unsigned ? OpCodes.Div_Un : OpCodes.Div);
370 case ExpressionType.Modulo:
371 ig.Emit (is_unsigned ? OpCodes.Rem_Un : OpCodes.Rem);
373 case ExpressionType.RightShift:
374 case ExpressionType.LeftShift:
375 ig.Emit (OpCodes.Ldc_I4, left.Type == typeof (int) ? 0x1f : 0x3f);
376 ig.Emit (OpCodes.And);
377 if (NodeType == ExpressionType.RightShift)
378 ig.Emit (is_unsigned ? OpCodes.Shr_Un : OpCodes.Shr);
380 ig.Emit (OpCodes.Shl);
382 case ExpressionType.And:
383 ig.Emit (OpCodes.And);
385 case ExpressionType.Or:
386 ig.Emit (OpCodes.Or);
388 case ExpressionType.ExclusiveOr:
389 ig.Emit (OpCodes.Xor);
391 case ExpressionType.GreaterThan:
392 ig.Emit (is_unsigned ? OpCodes.Cgt_Un : OpCodes.Cgt);
394 case ExpressionType.GreaterThanOrEqual:
395 if (is_unsigned || IsSingleOrDouble (left.Type))
396 ig.Emit (OpCodes.Clt_Un);
398 ig.Emit (OpCodes.Clt);
400 ig.Emit (OpCodes.Ldc_I4_0);
401 ig.Emit (OpCodes.Ceq);
403 case ExpressionType.LessThan:
404 ig.Emit (is_unsigned ? OpCodes.Clt_Un : OpCodes.Clt);
406 case ExpressionType.LessThanOrEqual:
407 if (is_unsigned || IsSingleOrDouble (left.Type))
408 ig.Emit (OpCodes.Cgt_Un);
410 ig.Emit (OpCodes.Cgt);
412 ig.Emit (OpCodes.Ldc_I4_0);
413 ig.Emit (OpCodes.Ceq);
415 case ExpressionType.Equal:
416 ig.Emit (OpCodes.Ceq);
418 case ExpressionType.NotEqual:
419 ig.Emit (OpCodes.Ceq);
420 ig.Emit (OpCodes.Ldc_I4_0);
421 ig.Emit (OpCodes.Ceq);
423 case ExpressionType.Power:
424 ig.Emit (OpCodes.Call, typeof (Math).GetMethod ("Pow"));
427 throw new InvalidOperationException (
428 string.Format ("Internal error: BinaryExpression contains non-Binary nodetype {0}", NodeType));
432 bool IsLeftLiftedBinary ()
434 return left.Type.IsNullable () && !right.Type.IsNullable ();
437 void EmitLeftLiftedToNullBinary (EmitContext ec)
441 var ret = ig.DefineLabel ();
442 var done = ig.DefineLabel ();
444 var left = ec.EmitStored (this.left);
446 ec.EmitNullableHasValue (left);
447 ig.Emit (OpCodes.Brfalse, ret);
449 ec.EmitNullableGetValueOrDefault (left);
452 EmitBinaryOperator (ec);
454 ec.EmitNullableNew (Type);
456 ig.Emit (OpCodes.Br, done);
460 var temp = ig.DeclareLocal (Type);
461 ec.EmitNullableInitialize (temp);
466 void EmitLiftedArithmeticBinary (EmitContext ec)
468 if (IsLeftLiftedBinary ())
469 EmitLeftLiftedToNullBinary (ec);
471 EmitLiftedToNullBinary (ec);
474 void EmitLiftedToNullBinary (EmitContext ec)
477 var left = ec.EmitStored (this.left);
478 var right = ec.EmitStored (this.right);
479 var result = ig.DeclareLocal (Type);
481 var has_value = ig.DefineLabel ();
482 var done = ig.DefineLabel ();
484 ec.EmitNullableHasValue (left);
485 ec.EmitNullableHasValue (right);
486 ig.Emit (OpCodes.And);
487 ig.Emit (OpCodes.Brtrue, has_value);
489 ec.EmitNullableInitialize (result);
491 ig.Emit (OpCodes.Br, done);
493 ig.MarkLabel (has_value);
495 ec.EmitNullableGetValueOrDefault (left);
496 ec.EmitNullableGetValueOrDefault (right);
498 EmitBinaryOperator (ec);
500 ec.EmitNullableNew (result.LocalType);
505 void EmitLiftedRelationalBinary (EmitContext ec)
508 var left = ec.EmitStored (this.left);
509 var right = ec.EmitStored (this.right);
511 var ret = ig.DefineLabel ();
512 var done = ig.DefineLabel ();
514 ec.EmitNullableGetValueOrDefault (left);
515 ec.EmitNullableGetValueOrDefault (right);
518 case ExpressionType.Equal:
519 case ExpressionType.NotEqual:
520 ig.Emit (OpCodes.Bne_Un, ret);
523 EmitBinaryOperator (ec);
524 ig.Emit (OpCodes.Brfalse, ret);
528 ec.EmitNullableHasValue (left);
529 ec.EmitNullableHasValue (right);
532 case ExpressionType.Equal:
533 ig.Emit (OpCodes.Ceq);
535 case ExpressionType.NotEqual:
536 ig.Emit (OpCodes.Ceq);
537 ig.Emit (OpCodes.Ldc_I4_0);
538 ig.Emit (OpCodes.Ceq);
541 ig.Emit (OpCodes.And);
545 ig.Emit (OpCodes.Br, done);
549 ig.Emit (NodeType == ExpressionType.NotEqual ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
554 void EmitArithmeticBinary (EmitContext ec)
557 EmitNonLiftedBinary (ec);
559 EmitLiftedArithmeticBinary (ec);
562 void EmitNonLiftedBinary (EmitContext ec)
566 EmitBinaryOperator (ec);
569 void EmitRelationalBinary (EmitContext ec)
572 EmitNonLiftedBinary (ec);
573 else if (IsLiftedToNull)
574 EmitLiftedToNullBinary (ec);
576 EmitLiftedRelationalBinary (ec);
579 void EmitLiftedUserDefinedOperator (EmitContext ec)
583 var ret_true = ig.DefineLabel ();
584 var ret_false = ig.DefineLabel ();
585 var done = ig.DefineLabel ();
587 var left = ec.EmitStored (this.left);
588 var right = ec.EmitStored (this.right);
590 ec.EmitNullableHasValue (left);
591 ec.EmitNullableHasValue (right);
593 case ExpressionType.Equal:
594 ig.Emit (OpCodes.Bne_Un, ret_false);
595 ec.EmitNullableHasValue (left);
596 ig.Emit (OpCodes.Brfalse, ret_true);
598 case ExpressionType.NotEqual:
599 ig.Emit (OpCodes.Bne_Un, ret_true);
600 ec.EmitNullableHasValue (left);
601 ig.Emit (OpCodes.Brfalse, ret_false);
604 ig.Emit (OpCodes.And);
605 ig.Emit (OpCodes.Brfalse, ret_false);
609 ec.EmitNullableGetValueOrDefault (left);
610 ec.EmitNullableGetValueOrDefault (right);
611 ec.EmitCall (method);
612 ig.Emit (OpCodes.Br, done);
614 ig.MarkLabel (ret_true);
615 ig.Emit (OpCodes.Ldc_I4_1);
616 ig.Emit (OpCodes.Br, done);
618 ig.MarkLabel (ret_false);
619 ig.Emit (OpCodes.Ldc_I4_0);
620 ig.Emit (OpCodes.Br, done);
625 void EmitLiftedToNullUserDefinedOperator (EmitContext ec)
629 var ret = ig.DefineLabel ();
630 var done = ig.DefineLabel ();
632 var left = ec.EmitStored (this.left);
633 var right = ec.EmitStored (this.right);
635 ec.EmitNullableHasValue (left);
636 ec.EmitNullableHasValue (right);
637 ig.Emit (OpCodes.And);
638 ig.Emit (OpCodes.Brfalse, ret);
640 ec.EmitNullableGetValueOrDefault (left);
641 ec.EmitNullableGetValueOrDefault (right);
642 ec.EmitCall (method);
643 ec.EmitNullableNew (Type);
644 ig.Emit (OpCodes.Br, done);
647 var temp = ig.DeclareLocal (Type);
648 ec.EmitNullableInitialize (temp);
653 void EmitUserDefinedLiftedLogicalShortCircuit (EmitContext ec)
656 var and = NodeType == ExpressionType.AndAlso;
658 var left_is_null = ig.DefineLabel ();
659 var ret_left = ig.DefineLabel ();
660 var ret_null = ig.DefineLabel ();
661 var done = ig.DefineLabel ();
663 var left = ec.EmitStored (this.left);
665 ec.EmitNullableHasValue (left);
666 ig.Emit (OpCodes.Brfalse, and ? ret_null : left_is_null);
668 ec.EmitNullableGetValueOrDefault (left);
669 ec.EmitCall (and ? GetFalseOperator () : GetTrueOperator ());
670 ig.Emit (OpCodes.Brtrue, ret_left);
672 ig.MarkLabel (left_is_null);
673 var right = ec.EmitStored (this.right);
674 ec.EmitNullableHasValue (right);
675 ig.Emit (OpCodes.Brfalse, ret_null);
677 ec.EmitNullableGetValueOrDefault (left);
678 ec.EmitNullableGetValueOrDefault (right);
679 ec.EmitCall (method);
681 ec.EmitNullableNew (Type);
682 ig.Emit (OpCodes.Br, done);
684 ig.MarkLabel (ret_left);
686 ig.Emit (OpCodes.Br, done);
688 ig.MarkLabel (ret_null);
689 var ret = ig.DeclareLocal (Type);
690 ec.EmitNullableInitialize (ret);
695 void EmitUserDefinedOperator (EmitContext ec)
699 case ExpressionType.AndAlso:
700 case ExpressionType.OrElse:
701 EmitUserDefinedLogicalShortCircuit (ec);
706 ec.EmitCall (method);
709 } else if (IsLiftedToNull) {
711 case ExpressionType.AndAlso:
712 case ExpressionType.OrElse:
713 EmitUserDefinedLiftedLogicalShortCircuit (ec);
716 EmitLiftedToNullUserDefinedOperator (ec);
720 EmitLiftedUserDefinedOperator (ec);
723 internal override void Emit (EmitContext ec)
725 if (method != null) {
726 EmitUserDefinedOperator (ec);
731 case ExpressionType.ArrayIndex:
732 EmitArrayAccess (ec);
734 case ExpressionType.Coalesce:
735 if (conversion != null)
736 EmitConvertedCoalesce (ec);
740 case ExpressionType.Power:
741 case ExpressionType.Add:
742 case ExpressionType.AddChecked:
743 case ExpressionType.Divide:
744 case ExpressionType.ExclusiveOr:
745 case ExpressionType.LeftShift:
746 case ExpressionType.Modulo:
747 case ExpressionType.Multiply:
748 case ExpressionType.MultiplyChecked:
749 case ExpressionType.RightShift:
750 case ExpressionType.Subtract:
751 case ExpressionType.SubtractChecked:
752 EmitArithmeticBinary (ec);
754 case ExpressionType.Equal:
755 case ExpressionType.GreaterThan:
756 case ExpressionType.GreaterThanOrEqual:
757 case ExpressionType.LessThan:
758 case ExpressionType.LessThanOrEqual:
759 case ExpressionType.NotEqual:
760 EmitRelationalBinary (ec);
762 case ExpressionType.And:
763 case ExpressionType.Or:
764 case ExpressionType.AndAlso:
765 case ExpressionType.OrElse:
766 EmitLogicalBinary (ec);
769 throw new NotSupportedException (this.NodeType.ToString ());