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 left_is_false = ig.DefineLabel ();
138 Label right_is_null = ig.DefineLabel ();
139 Label create = ig.DefineLabel ();
140 Label exit = ig.DefineLabel ();
141 Label both_are_null = ig.DefineLabel ();
145 ig.Emit (OpCodes.Ldloca, vleft);
146 ig.Emit (OpCodes.Call, has_value);
147 ig.Emit (OpCodes.Brfalse, left_is_null);
149 ig.Emit (OpCodes.Ldloca, vleft);
150 ig.Emit (OpCodes.Call, get_value);
151 ig.Emit (OpCodes.Dup);
153 ig.Emit (and ? OpCodes.Brfalse : OpCodes.Brtrue, create);
157 vright = ec.EmitStored (right);
159 ig.Emit (OpCodes.Ldloca, vright);
160 ig.Emit (OpCodes.Call, has_value);
161 ig.Emit (OpCodes.Brfalse, right_is_null);
163 ig.Emit (OpCodes.Ldloca, vright);
164 ig.Emit (OpCodes.Call, get_value);
166 ig.Emit (and ? OpCodes.And : OpCodes.Or);
167 ig.Emit (OpCodes.Br, create);
170 ig.MarkLabel (left_is_null);
172 ig.Emit (OpCodes.Ldloca, vright);
173 ig.Emit (OpCodes.Call, has_value);
174 ig.Emit (OpCodes.Brfalse, both_are_null);
175 ig.Emit (OpCodes.Ldloca, vright);
176 ig.Emit (OpCodes.Call, get_value);
177 ig.Emit (OpCodes.Dup);
178 ig.Emit (and ? OpCodes.Brfalse : OpCodes.Brtrue, create);
181 ig.MarkLabel (right_is_null);
182 ig.Emit (OpCodes.Pop);
185 ig.MarkLabel (both_are_null);
186 ig.Emit (OpCodes.Ldloca, ret);
187 ig.Emit (OpCodes.Initobj, Type);
188 ig.Emit (OpCodes.Ldloc, ret);
189 ig.Emit (OpCodes.Br, exit);
192 ig.MarkLabel (create);
193 ig.Emit (OpCodes.Newobj, Type.GetConstructors () [0]);
199 void EmitLogical (EmitContext ec, bool and, bool short_circuit)
202 EmitLiftedLogical (ec, and, short_circuit);
208 ec.ig.Emit (and ? OpCodes.And : OpCodes.Or);
211 void EmitCoalesce (EmitContext ec)
213 ILGenerator ig = ec.ig;
218 MethodInfo has_value = left.Type.GetMethod ("get_HasValue");
220 Label exit = ig.DefineLabel ();
221 Label try_right = ig.DefineLabel ();
222 Label setup_null = ig.DefineLabel ();
224 vleft = ec.EmitStored (left);
225 if (IsNullable (left.Type)){
226 ig.Emit (OpCodes.Ldloca, vleft);
227 ig.Emit (OpCodes.Call, has_value);
229 ig.Emit (OpCodes.Ldloc, vleft);
231 ig.Emit (OpCodes.Brfalse, try_right);
232 ig.Emit (OpCodes.Ldloc, vleft);
233 ig.Emit (OpCodes.Br, exit);
236 ig.MarkLabel (try_right);
237 vright = ec.EmitStored (right);
238 if (IsNullable (right.Type)){
239 ig.Emit (OpCodes.Ldloca, vright);
240 ig.Emit (OpCodes.Call, has_value);
242 ig.Emit (OpCodes.Ldloc, vright);
244 ig.Emit (OpCodes.Brfalse, setup_null);
245 ig.Emit (OpCodes.Ldloc, vright);
246 ig.Emit (OpCodes.Br, exit);
249 ig.MarkLabel (setup_null);
250 LocalBuilder ret = ig.DeclareLocal (Type);
251 ig.Emit (OpCodes.Ldloca, ret);
252 ig.Emit (OpCodes.Initobj, Type);
253 ig.Emit (OpCodes.Ldloc, ret);
259 void EmitBinaryOperator (EmitContext ec)
263 bool is_unsigned = IsUnsigned (left.Type);
266 case ExpressionType.Add:
267 opcode = OpCodes.Add;
270 case ExpressionType.AddChecked:
271 if (left.Type == typeof (int) || left.Type == typeof (long))
272 opcode = OpCodes.Add_Ovf;
273 else if (is_unsigned)
274 opcode = OpCodes.Add_Ovf_Un;
276 opcode = OpCodes.Add;
279 case ExpressionType.Subtract:
280 opcode = OpCodes.Sub;
283 case ExpressionType.SubtractChecked:
284 if (left.Type == typeof (int) || left.Type == typeof (long))
285 opcode = OpCodes.Sub_Ovf;
286 else if (is_unsigned)
287 opcode = OpCodes.Sub_Ovf_Un;
289 opcode = OpCodes.Sub;
292 case ExpressionType.Multiply:
293 opcode = OpCodes.Mul;
296 case ExpressionType.MultiplyChecked:
297 if (left.Type == typeof (int) || left.Type == typeof (long))
298 opcode = OpCodes.Mul_Ovf;
299 else if (is_unsigned)
300 opcode = OpCodes.Mul_Ovf_Un;
302 opcode = OpCodes.Mul;
305 case ExpressionType.Divide:
307 opcode = OpCodes.Div_Un;
309 opcode = OpCodes.Div;
312 case ExpressionType.Modulo:
314 opcode = OpCodes.Rem_Un;
316 opcode = OpCodes.Rem;
319 case ExpressionType.RightShift:
321 opcode = OpCodes.Shr_Un;
323 opcode = OpCodes.Shr;
326 case ExpressionType.LeftShift:
327 opcode = OpCodes.Shl;
330 case ExpressionType.And:
331 opcode = OpCodes.And;
334 case ExpressionType.Or:
338 case ExpressionType.ExclusiveOr:
339 opcode = OpCodes.Xor;
342 case ExpressionType.GreaterThan:
344 opcode = OpCodes.Cgt_Un;
346 opcode = OpCodes.Cgt;
349 case ExpressionType.GreaterThanOrEqual:
352 if (is_unsigned || (le == typeof (double) || le == typeof (float)))
353 ig.Emit (OpCodes.Clt_Un);
355 ig.Emit (OpCodes.Clt);
357 ig.Emit (OpCodes.Ldc_I4_0);
359 opcode = OpCodes.Ceq;
362 case ExpressionType.LessThan:
364 opcode = OpCodes.Clt_Un;
366 opcode = OpCodes.Clt;
369 case ExpressionType.LessThanOrEqual:
372 if (is_unsigned || (lt == typeof (double) || lt == typeof (float)))
373 ig.Emit (OpCodes.Cgt_Un);
375 ig.Emit (OpCodes.Cgt);
376 ig.Emit (OpCodes.Ldc_I4_0);
378 opcode = OpCodes.Ceq;
381 case ExpressionType.Equal:
382 opcode = OpCodes.Ceq;
385 case ExpressionType.NotEqual:
386 ig.Emit (OpCodes.Ceq);
387 ig.Emit (OpCodes.Ldc_I4_0);
389 opcode = OpCodes.Ceq;
393 throw new InvalidOperationException (string.Format ("Internal error: BinaryExpression contains non-Binary nodetype {0}", NodeType));
399 void EmitLiftedSimpleBinary (EmitContext ec)
402 LocalBuilder ret = null;
404 LocalBuilder vleft, vright;
407 empty_value = ig.DefineLabel ();
408 ret = ig.DeclareLocal (Type);
410 vleft = ec.EmitStored (left);
411 vright = ec.EmitStored (right);
413 MethodInfo has_value = left.Type.GetMethod ("get_HasValue");
414 MethodInfo get_value = GetMethodNoPar (left.Type, "get_Value");
416 ig.Emit (OpCodes.Ldloca, vleft);
417 ig.Emit (OpCodes.Call, has_value);
418 ig.Emit (OpCodes.Brfalse, empty_value);
419 ig.Emit (OpCodes.Ldloca, vright);
420 ig.Emit (OpCodes.Call, has_value);
421 ig.Emit (OpCodes.Brfalse, empty_value);
422 ig.Emit (OpCodes.Ldloca, vleft);
423 ig.Emit (OpCodes.Call, get_value);
424 ig.Emit (OpCodes.Ldloca, vright);
425 ig.Emit (OpCodes.Call, get_value);
427 EmitBinaryOperator (ec);
429 ig.Emit (OpCodes.Newobj, left.Type.GetConstructors () [0]);
431 Label skip = ig.DefineLabel ();
432 ig.Emit (OpCodes.Br_S, skip);
433 ig.MarkLabel (empty_value);
434 ig.Emit (OpCodes.Ldloc, ret);
435 ig.Emit (OpCodes.Ldloca, ret);
436 ig.Emit (OpCodes.Initobj, Type);
441 void EmitSimpleBinary (EmitContext ec)
444 EmitLiftedSimpleBinary (ec);
450 EmitBinaryOperator (ec);
453 internal override void Emit (EmitContext ec)
461 case ExpressionType.ArrayIndex:
462 EmitArrayAccess (ec);
465 case ExpressionType.And:
466 EmitLogical (ec, true, false);
469 case ExpressionType.Or:
470 EmitLogical (ec, false, false);
473 case ExpressionType.AndAlso:
474 EmitLogical (ec, true, true);
477 case ExpressionType.OrElse:
478 EmitLogical (ec, false, true);
481 case ExpressionType.Coalesce:
485 case ExpressionType.Power:
488 ec.EmitCall (typeof (Math).GetMethod ("Pow"));
492 EmitSimpleBinary (ec);