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 EmitLiftedLogical (ec, and, short_circuit);
207 ec.ig.Emit (and ? OpCodes.And : OpCodes.Or);
210 void EmitCoalesce (EmitContext ec)
212 ILGenerator ig = ec.ig;
217 MethodInfo has_value = left.Type.GetMethod ("get_HasValue");
219 Label exit = ig.DefineLabel ();
220 Label try_right = ig.DefineLabel ();
221 Label setup_null = ig.DefineLabel ();
223 vleft = ec.EmitStored (left);
224 if (IsNullable (left.Type)){
225 ig.Emit (OpCodes.Ldloca, vleft);
226 ig.Emit (OpCodes.Call, has_value);
228 ig.Emit (OpCodes.Ldloc, vleft);
230 ig.Emit (OpCodes.Brfalse, try_right);
231 ig.Emit (OpCodes.Ldloc, vleft);
232 ig.Emit (OpCodes.Br, exit);
235 ig.MarkLabel (try_right);
236 vright = ec.EmitStored (right);
237 if (IsNullable (right.Type)){
238 ig.Emit (OpCodes.Ldloca, vright);
239 ig.Emit (OpCodes.Call, has_value);
241 ig.Emit (OpCodes.Ldloc, vright);
243 ig.Emit (OpCodes.Brfalse, setup_null);
244 ig.Emit (OpCodes.Ldloc, vright);
245 ig.Emit (OpCodes.Br, exit);
248 ig.MarkLabel (setup_null);
249 LocalBuilder ret = ig.DeclareLocal (Type);
250 ig.Emit (OpCodes.Ldloca, ret);
251 ig.Emit (OpCodes.Initobj, Type);
252 ig.Emit (OpCodes.Ldloc, ret);
258 void EmitBinaryOperator (EmitContext ec)
262 bool is_unsigned = IsUnsigned (left.Type);
265 case ExpressionType.Add:
266 opcode = OpCodes.Add;
269 case ExpressionType.AddChecked:
270 if (left.Type == typeof (int) || left.Type == typeof (long))
271 opcode = OpCodes.Add_Ovf;
272 else if (is_unsigned)
273 opcode = OpCodes.Add_Ovf_Un;
275 opcode = OpCodes.Add;
278 case ExpressionType.Subtract:
279 opcode = OpCodes.Sub;
282 case ExpressionType.SubtractChecked:
283 if (left.Type == typeof (int) || left.Type == typeof (long))
284 opcode = OpCodes.Sub_Ovf;
285 else if (is_unsigned)
286 opcode = OpCodes.Sub_Ovf_Un;
288 opcode = OpCodes.Sub;
291 case ExpressionType.Multiply:
292 opcode = OpCodes.Mul;
295 case ExpressionType.MultiplyChecked:
296 if (left.Type == typeof (int) || left.Type == typeof (long))
297 opcode = OpCodes.Mul_Ovf;
298 else if (is_unsigned)
299 opcode = OpCodes.Mul_Ovf_Un;
301 opcode = OpCodes.Mul;
304 case ExpressionType.Divide:
306 opcode = OpCodes.Div_Un;
308 opcode = OpCodes.Div;
311 case ExpressionType.Modulo:
313 opcode = OpCodes.Rem_Un;
315 opcode = OpCodes.Rem;
318 case ExpressionType.RightShift:
320 opcode = OpCodes.Shr_Un;
322 opcode = OpCodes.Shr;
325 case ExpressionType.LeftShift:
326 opcode = OpCodes.Shl;
329 case ExpressionType.And:
330 opcode = OpCodes.And;
333 case ExpressionType.Or:
337 case ExpressionType.ExclusiveOr:
338 opcode = OpCodes.Xor;
341 case ExpressionType.GreaterThan:
343 opcode = OpCodes.Cgt_Un;
345 opcode = OpCodes.Cgt;
348 case ExpressionType.GreaterThanOrEqual:
351 if (is_unsigned || (le == typeof (double) || le == typeof (float)))
352 ig.Emit (OpCodes.Clt_Un);
354 ig.Emit (OpCodes.Clt);
356 ig.Emit (OpCodes.Ldc_I4_0);
358 opcode = OpCodes.Ceq;
361 case ExpressionType.LessThan:
363 opcode = OpCodes.Clt_Un;
365 opcode = OpCodes.Clt;
368 case ExpressionType.LessThanOrEqual:
371 if (is_unsigned || (lt == typeof (double) || lt == typeof (float)))
372 ig.Emit (OpCodes.Cgt_Un);
374 ig.Emit (OpCodes.Cgt);
375 ig.Emit (OpCodes.Ldc_I4_0);
377 opcode = OpCodes.Ceq;
380 case ExpressionType.Equal:
381 opcode = OpCodes.Ceq;
384 case ExpressionType.NotEqual:
385 ig.Emit (OpCodes.Ceq);
386 ig.Emit (OpCodes.Ldc_I4_0);
388 opcode = OpCodes.Ceq;
392 throw new InvalidOperationException (string.Format ("Internal error: BinaryExpression contains non-Binary nodetype {0}", NodeType));
398 void EmitLiftedArithmeticBinary (EmitContext ec)
400 EmitLiftedToNullBinary (ec);
403 void EmitLiftedToNullBinary (EmitContext ec)
406 var left = ec.EmitStored (this.left);
407 var right = ec.EmitStored (this.right);
408 var result = ig.DeclareLocal (Type);
410 var has_value = ig.DefineLabel ();
411 var done = ig.DefineLabel ();
413 ec.EmitNullableHasValue (left);
414 ec.EmitNullableHasValue (right);
415 ig.Emit (OpCodes.And);
416 ig.Emit (OpCodes.Brtrue, has_value);
418 ig.Emit (OpCodes.Ldloca, result);
419 ig.Emit (OpCodes.Initobj, result.LocalType);
420 ig.Emit (OpCodes.Ldloc, result);
422 ig.Emit (OpCodes.Br, done);
424 ig.MarkLabel (has_value);
426 ec.EmitNullableGetValueOrDefault (left);
427 ec.EmitNullableGetValueOrDefault (right);
429 EmitBinaryOperator (ec);
431 ec.EmitNullableNew (result.LocalType);
436 void EmitLiftedRelationalBinary (EmitContext ec)
439 var left = ec.EmitStored (this.left);
440 var right = ec.EmitStored (this.right);
442 var ret = ig.DefineLabel ();
443 var done = ig.DefineLabel ();
445 ec.EmitNullableGetValueOrDefault (left);
446 ec.EmitNullableGetValueOrDefault (right);
449 case ExpressionType.Equal:
450 case ExpressionType.NotEqual:
451 ig.Emit (OpCodes.Bne_Un, ret);
454 EmitBinaryOperator (ec);
455 ig.Emit (OpCodes.Brfalse, ret);
459 ec.EmitNullableHasValue (left);
460 ec.EmitNullableHasValue (right);
463 case ExpressionType.Equal:
464 ig.Emit (OpCodes.Ceq);
466 case ExpressionType.NotEqual:
467 ig.Emit (OpCodes.Ceq);
468 ig.Emit (OpCodes.Ldc_I4_0);
469 ig.Emit (OpCodes.Ceq);
472 ig.Emit (OpCodes.And);
476 ig.Emit (OpCodes.Br, done);
480 ig.Emit (NodeType == ExpressionType.NotEqual ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
485 void EmitArithmeticBinary (EmitContext ec)
490 EmitBinaryOperator (ec);
492 EmitLiftedArithmeticBinary (ec);
495 void EmitRelationalBinary (EmitContext ec)
500 EmitBinaryOperator (ec);
501 } else if (IsLiftedToNull)
502 EmitLiftedToNullBinary (ec);
504 EmitLiftedRelationalBinary (ec);
507 internal override void Emit (EmitContext ec)
515 case ExpressionType.ArrayIndex:
516 EmitArrayAccess (ec);
519 case ExpressionType.And:
520 EmitLogical (ec, true, false);
523 case ExpressionType.Or:
524 EmitLogical (ec, false, false);
527 case ExpressionType.AndAlso:
528 EmitLogical (ec, true, true);
531 case ExpressionType.OrElse:
532 EmitLogical (ec, false, true);
535 case ExpressionType.Coalesce:
539 case ExpressionType.Power:
540 // likely broken if lifted
543 ec.EmitCall (typeof (Math).GetMethod ("Pow"));
546 case ExpressionType.Add:
547 case ExpressionType.AddChecked:
548 case ExpressionType.Divide:
549 case ExpressionType.ExclusiveOr:
550 case ExpressionType.LeftShift:
551 case ExpressionType.Modulo:
552 case ExpressionType.Multiply:
553 case ExpressionType.MultiplyChecked:
554 case ExpressionType.RightShift:
555 case ExpressionType.Subtract:
556 case ExpressionType.SubtractChecked:
557 EmitArithmeticBinary (ec);
559 case ExpressionType.Equal:
560 case ExpressionType.GreaterThan:
561 case ExpressionType.GreaterThanOrEqual:
562 case ExpressionType.LessThan:
563 case ExpressionType.LessThanOrEqual:
564 case ExpressionType.NotEqual:
565 EmitRelationalBinary (ec);
568 throw new NotSupportedException (this.NodeType.ToString ());