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 EmitMethod (EmitContext ec)
104 ec.EmitCall (method);
107 static MethodInfo GetMethodNoPar (Type t, string name)
109 var method = t.GetMethod (name, Type.EmptyTypes);
111 throw new ArgumentException (
112 string.Format ("Internal error: method {0} with no parameters not found on {1}", name, t));
117 void EmitArrayAccess (EmitContext ec)
121 ec.ig.Emit (OpCodes.Ldelem, this.Type);
124 void EmitLiftedLogical (EmitContext ec, bool and, bool short_circuit)
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");
132 vleft = ec.EmitStored (left);
134 vright = ec.EmitStored (right);
136 Label left_is_null = ig.DefineLabel ();
137 Label right_is_null = ig.DefineLabel ();
138 Label create = ig.DefineLabel ();
139 Label exit = ig.DefineLabel ();
140 Label both_are_null = ig.DefineLabel ();
144 ig.Emit (OpCodes.Ldloca, vleft);
145 ig.Emit (OpCodes.Call, has_value);
146 ig.Emit (OpCodes.Brfalse, left_is_null);
148 ig.Emit (OpCodes.Ldloca, vleft);
149 ig.Emit (OpCodes.Call, get_value);
150 ig.Emit (OpCodes.Dup);
152 ig.Emit (and ? OpCodes.Brfalse : OpCodes.Brtrue, create);
156 vright = ec.EmitStored (right);
158 ig.Emit (OpCodes.Ldloca, vright);
159 ig.Emit (OpCodes.Call, has_value);
160 ig.Emit (OpCodes.Brfalse, right_is_null);
162 ig.Emit (OpCodes.Ldloca, vright);
163 ig.Emit (OpCodes.Call, get_value);
165 ig.Emit (and ? OpCodes.And : OpCodes.Or);
166 ig.Emit (OpCodes.Br, create);
169 ig.MarkLabel (left_is_null);
171 ig.Emit (OpCodes.Ldloca, vright);
172 ig.Emit (OpCodes.Call, has_value);
173 ig.Emit (OpCodes.Brfalse, both_are_null);
174 ig.Emit (OpCodes.Ldloca, vright);
175 ig.Emit (OpCodes.Call, get_value);
176 ig.Emit (OpCodes.Dup);
177 ig.Emit (and ? OpCodes.Brfalse : OpCodes.Brtrue, create);
180 ig.MarkLabel (right_is_null);
181 ig.Emit (OpCodes.Pop);
184 ig.MarkLabel (both_are_null);
185 ig.Emit (OpCodes.Ldloca, ret);
186 ig.Emit (OpCodes.Initobj, Type);
187 ig.Emit (OpCodes.Ldloc, ret);
188 ig.Emit (OpCodes.Br, exit);
191 ig.MarkLabel (create);
192 ig.Emit (OpCodes.Newobj, Type.GetConstructors () [0]);
198 void EmitLogical (EmitContext ec, bool and, bool short_circuit)
201 EmitNonLiftedBinary (ec);
203 EmitLiftedLogical (ec, and, short_circuit);
206 void EmitCoalesce (EmitContext ec)
208 ILGenerator ig = ec.ig;
213 MethodInfo has_value = left.Type.GetMethod ("get_HasValue");
215 Label exit = ig.DefineLabel ();
216 Label try_right = ig.DefineLabel ();
217 Label setup_null = ig.DefineLabel ();
219 vleft = ec.EmitStored (left);
220 if (IsNullable (left.Type)){
221 ig.Emit (OpCodes.Ldloca, vleft);
222 ig.Emit (OpCodes.Call, has_value);
224 ig.Emit (OpCodes.Ldloc, vleft);
226 ig.Emit (OpCodes.Brfalse, try_right);
227 ig.Emit (OpCodes.Ldloc, vleft);
228 ig.Emit (OpCodes.Br, exit);
231 ig.MarkLabel (try_right);
232 vright = ec.EmitStored (right);
233 if (IsNullable (right.Type)){
234 ig.Emit (OpCodes.Ldloca, vright);
235 ig.Emit (OpCodes.Call, has_value);
237 ig.Emit (OpCodes.Ldloc, vright);
239 ig.Emit (OpCodes.Brfalse, setup_null);
240 ig.Emit (OpCodes.Ldloc, vright);
241 ig.Emit (OpCodes.Br, exit);
244 ig.MarkLabel (setup_null);
245 LocalBuilder ret = ig.DeclareLocal (Type);
246 ig.Emit (OpCodes.Ldloca, ret);
247 ig.Emit (OpCodes.Initobj, Type);
248 ig.Emit (OpCodes.Ldloc, ret);
254 void EmitBinaryOperator (EmitContext ec)
258 bool is_unsigned = IsUnsigned (left.Type);
261 case ExpressionType.Add:
262 opcode = OpCodes.Add;
264 case ExpressionType.AddChecked:
265 if (left.Type == typeof (int) || left.Type == typeof (long))
266 opcode = OpCodes.Add_Ovf;
268 opcode = is_unsigned ? OpCodes.Add_Ovf_Un : OpCodes.Add;
270 case ExpressionType.Subtract:
271 opcode = OpCodes.Sub;
273 case ExpressionType.SubtractChecked:
274 if (left.Type == typeof (int) || left.Type == typeof (long))
275 opcode = OpCodes.Sub_Ovf;
277 opcode = is_unsigned ? OpCodes.Sub_Ovf_Un : OpCodes.Sub;
279 case ExpressionType.Multiply:
280 opcode = OpCodes.Mul;
282 case ExpressionType.MultiplyChecked:
283 if (left.Type == typeof (int) || left.Type == typeof (long))
284 opcode = OpCodes.Mul_Ovf;
286 opcode = is_unsigned ? OpCodes.Mul_Ovf_Un : OpCodes.Mul;
288 case ExpressionType.Divide:
289 opcode = is_unsigned ? OpCodes.Div_Un : OpCodes.Div;
291 case ExpressionType.Modulo:
292 opcode = is_unsigned ? OpCodes.Rem_Un : OpCodes.Rem;
294 case ExpressionType.RightShift:
295 opcode = is_unsigned ? OpCodes.Shr_Un : OpCodes.Shr;
297 case ExpressionType.LeftShift:
298 opcode = OpCodes.Shl;
300 case ExpressionType.And:
301 case ExpressionType.AndAlso:
302 opcode = OpCodes.And;
304 case ExpressionType.Or:
305 case ExpressionType.OrElse:
308 case ExpressionType.ExclusiveOr:
309 opcode = OpCodes.Xor;
311 case ExpressionType.GreaterThan:
312 opcode = is_unsigned ? OpCodes.Cgt_Un : OpCodes.Cgt;
314 case ExpressionType.GreaterThanOrEqual:
317 if (is_unsigned || (le == typeof (double) || le == typeof (float)))
318 ig.Emit (OpCodes.Clt_Un);
320 ig.Emit (OpCodes.Clt);
322 ig.Emit (OpCodes.Ldc_I4_0);
324 opcode = OpCodes.Ceq;
326 case ExpressionType.LessThan:
327 opcode = is_unsigned ? OpCodes.Clt_Un : OpCodes.Clt;
329 case ExpressionType.LessThanOrEqual:
332 if (is_unsigned || (lt == typeof (double) || lt == typeof (float)))
333 ig.Emit (OpCodes.Cgt_Un);
335 ig.Emit (OpCodes.Cgt);
337 ig.Emit (OpCodes.Ldc_I4_0);
339 opcode = OpCodes.Ceq;
341 case ExpressionType.Equal:
342 opcode = OpCodes.Ceq;
344 case ExpressionType.NotEqual:
345 ig.Emit (OpCodes.Ceq);
346 ig.Emit (OpCodes.Ldc_I4_0);
347 opcode = OpCodes.Ceq;
350 throw new InvalidOperationException (string.Format ("Internal error: BinaryExpression contains non-Binary nodetype {0}", NodeType));
356 void EmitLiftedArithmeticBinary (EmitContext ec)
358 EmitLiftedToNullBinary (ec);
361 void EmitLiftedToNullBinary (EmitContext ec)
364 var left = ec.EmitStored (this.left);
365 var right = ec.EmitStored (this.right);
366 var result = ig.DeclareLocal (Type);
368 var has_value = ig.DefineLabel ();
369 var done = ig.DefineLabel ();
371 ec.EmitNullableHasValue (left);
372 ec.EmitNullableHasValue (right);
373 ig.Emit (OpCodes.And);
374 ig.Emit (OpCodes.Brtrue, has_value);
376 ec.EmitNullableInitialize (result);
378 ig.Emit (OpCodes.Br, done);
380 ig.MarkLabel (has_value);
382 ec.EmitNullableGetValueOrDefault (left);
383 ec.EmitNullableGetValueOrDefault (right);
385 EmitBinaryOperator (ec);
387 ec.EmitNullableNew (result.LocalType);
392 void EmitLiftedRelationalBinary (EmitContext ec)
395 var left = ec.EmitStored (this.left);
396 var right = ec.EmitStored (this.right);
398 var ret = ig.DefineLabel ();
399 var done = ig.DefineLabel ();
401 ec.EmitNullableGetValueOrDefault (left);
402 ec.EmitNullableGetValueOrDefault (right);
405 case ExpressionType.Equal:
406 case ExpressionType.NotEqual:
407 ig.Emit (OpCodes.Bne_Un, ret);
410 EmitBinaryOperator (ec);
411 ig.Emit (OpCodes.Brfalse, ret);
415 ec.EmitNullableHasValue (left);
416 ec.EmitNullableHasValue (right);
419 case ExpressionType.Equal:
420 ig.Emit (OpCodes.Ceq);
422 case ExpressionType.NotEqual:
423 ig.Emit (OpCodes.Ceq);
424 ig.Emit (OpCodes.Ldc_I4_0);
425 ig.Emit (OpCodes.Ceq);
428 ig.Emit (OpCodes.And);
432 ig.Emit (OpCodes.Br, done);
436 ig.Emit (NodeType == ExpressionType.NotEqual ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
441 void EmitArithmeticBinary (EmitContext ec)
444 EmitNonLiftedBinary (ec);
446 EmitLiftedArithmeticBinary (ec);
449 void EmitNonLiftedBinary (EmitContext ec)
453 EmitBinaryOperator (ec);
456 void EmitRelationalBinary (EmitContext ec)
459 EmitNonLiftedBinary (ec);
460 else if (IsLiftedToNull)
461 EmitLiftedToNullBinary (ec);
463 EmitLiftedRelationalBinary (ec);
466 internal override void Emit (EmitContext ec)
474 case ExpressionType.ArrayIndex:
475 EmitArrayAccess (ec);
477 case ExpressionType.And:
478 EmitLogical (ec, true, false);
480 case ExpressionType.Or:
481 EmitLogical (ec, false, false);
483 case ExpressionType.AndAlso:
484 EmitLogical (ec, true, true);
486 case ExpressionType.OrElse:
487 EmitLogical (ec, false, true);
489 case ExpressionType.Coalesce:
492 case ExpressionType.Power:
493 // likely broken if lifted
496 ec.EmitCall (typeof (Math).GetMethod ("Pow"));
498 case ExpressionType.Add:
499 case ExpressionType.AddChecked:
500 case ExpressionType.Divide:
501 case ExpressionType.ExclusiveOr:
502 case ExpressionType.LeftShift:
503 case ExpressionType.Modulo:
504 case ExpressionType.Multiply:
505 case ExpressionType.MultiplyChecked:
506 case ExpressionType.RightShift:
507 case ExpressionType.Subtract:
508 case ExpressionType.SubtractChecked:
509 EmitArithmeticBinary (ec);
511 case ExpressionType.Equal:
512 case ExpressionType.GreaterThan:
513 case ExpressionType.GreaterThanOrEqual:
514 case ExpressionType.LessThan:
515 case ExpressionType.LessThanOrEqual:
516 case ExpressionType.NotEqual:
517 EmitRelationalBinary (ec);
520 throw new NotSupportedException (this.NodeType.ToString ());