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 static void EmitMethod ()
102 throw new NotImplementedException ("Support for MethodInfo-based BinaryExpressions not yet supported");
105 static MethodInfo GetMethodNoPar (Type t, string name)
107 MethodInfo [] methods = t.GetMethods ();
108 foreach (MethodInfo m in methods){
111 if (m.GetParameters ().Length == 0)
114 throw new Exception (String.Format ("Internal error: method {0} with no parameters not found on {1}",
118 LocalBuilder EmitStored (EmitContext ec, Expression expr)
120 LocalBuilder lb = ec.ig.DeclareLocal (expr.Type);
122 ec.ig.Emit (OpCodes.Stloc, lb);
127 void EmitLogical (EmitContext ec, bool and, bool short_circuit)
129 ILGenerator ig = ec.ig;
131 LocalBuilder ret = ig.DeclareLocal (Type);
132 LocalBuilder vleft = null, vright = null;
133 MethodInfo has_value = left.Type.GetMethod ("get_HasValue");
134 MethodInfo get_value = GetMethodNoPar (left.Type, "get_Value");
136 vleft = EmitStored (ec, left);
138 vright = EmitStored (ec, right);
140 Label left_is_null = ig.DefineLabel ();
141 Label left_is_false = ig.DefineLabel ();
142 Label right_is_null = ig.DefineLabel ();
143 Label create = ig.DefineLabel ();
144 Label exit = ig.DefineLabel ();
145 Label both_are_null = ig.DefineLabel ();
149 ig.Emit (OpCodes.Ldloca, vleft);
150 ig.Emit (OpCodes.Call, has_value);
151 ig.Emit (OpCodes.Brfalse, left_is_null);
153 ig.Emit (OpCodes.Ldloca, vleft);
154 ig.Emit (OpCodes.Call, get_value);
155 ig.Emit (OpCodes.Dup);
157 left_is_false = ig.DefineLabel ();
158 ig.Emit (and ? OpCodes.Brfalse : OpCodes.Brtrue, create);
162 vright = EmitStored (ec, right);
164 ig.Emit (OpCodes.Ldloca, vright);
165 ig.Emit (OpCodes.Call, has_value);
166 ig.Emit (OpCodes.Brfalse, right_is_null);
168 ig.Emit (OpCodes.Ldloca, vright);
169 ig.Emit (OpCodes.Call, get_value);
171 ig.Emit (and ? OpCodes.And : OpCodes.Or);
172 ig.Emit (OpCodes.Br, create);
175 ig.MarkLabel (left_is_null);
176 ig.Emit (OpCodes.Ldloca, vright);
177 ig.Emit (OpCodes.Call, has_value);
178 ig.Emit (OpCodes.Brfalse, both_are_null);
179 ig.Emit (OpCodes.Call, get_value);
180 ig.Emit (OpCodes.Dup);
181 ig.Emit (and ? OpCodes.Brfalse : OpCodes.Brtrue, create);
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.Ldloca, ret);
189 ig.Emit (OpCodes.Br, exit);
192 ig.MarkLabel (create);
193 ig.Emit (OpCodes.Newobj, Type.GetConstructors ()[0]);
200 ig.Emit (and ? OpCodes.And : OpCodes.Or);
205 internal override void Emit (EmitContext ec)
207 ILGenerator ig = ec.ig;
216 case ExpressionType.And:
217 EmitLogical (ec, true, false);
220 case ExpressionType.Or:
221 EmitLogical (ec, false, false);
224 case ExpressionType.AndAlso:
225 Console.WriteLine ("LINQ/BinaryExpression: AndAlso code path not yet reviewed");
226 EmitLogical (ec, true, true);
229 case ExpressionType.OrElse:
230 Console.WriteLine ("LINQ/BinaryExpression: OrElse code path not yet reviewed");
231 EmitLogical (ec, false, true);
235 Label? empty_value = null;
236 LocalBuilder ret = null;
239 LocalBuilder vleft, vright;
241 empty_value = ig.DefineLabel ();
242 ret = ig.DeclareLocal (Type);
244 vleft = EmitStored (ec, left);
245 vright = EmitStored (ec, right);
247 MethodInfo has_value = left.Type.GetMethod ("get_HasValue");
248 MethodInfo get_value = GetMethodNoPar (left.Type, "get_Value");
250 ig.Emit (OpCodes.Ldloca, vleft);
251 ig.Emit (OpCodes.Call, has_value);
252 ig.Emit (OpCodes.Brfalse, empty_value.Value);
253 ig.Emit (OpCodes.Ldloca, vright);
254 ig.Emit (OpCodes.Call, has_value);
255 ig.Emit (OpCodes.Brfalse, empty_value.Value);
256 ig.Emit (OpCodes.Ldloca, vleft);
257 ig.Emit (OpCodes.Call, get_value);
258 ig.Emit (OpCodes.Ldloca, vright);
259 ig.Emit (OpCodes.Call, get_value);
265 bool is_unsigned = IsUnsigned (left.Type);
268 case ExpressionType.Add:
269 opcode = OpCodes.Add;
272 case ExpressionType.AddChecked:
273 if (left.Type == typeof (int) || left.Type == typeof (long))
274 opcode = OpCodes.Add_Ovf;
275 else if (is_unsigned)
276 opcode = OpCodes.Add_Ovf_Un;
278 opcode = OpCodes.Add;
281 case ExpressionType.Subtract:
282 opcode = OpCodes.Sub;
285 case ExpressionType.SubtractChecked:
286 if (left.Type == typeof (int) || left.Type == typeof (long))
287 opcode = OpCodes.Sub_Ovf;
288 else if (is_unsigned)
289 opcode = OpCodes.Sub_Ovf_Un;
291 opcode = OpCodes.Sub;
294 case ExpressionType.Multiply:
295 opcode = OpCodes.Mul;
298 case ExpressionType.MultiplyChecked:
299 if (left.Type == typeof (int) || left.Type == typeof (long))
300 opcode = OpCodes.Mul_Ovf;
301 else if (is_unsigned)
302 opcode = OpCodes.Mul_Ovf_Un;
304 opcode = OpCodes.Mul;
307 case ExpressionType.Divide:
309 opcode = OpCodes.Div_Un;
311 opcode = OpCodes.Div;
314 case ExpressionType.Modulo:
316 opcode = OpCodes.Rem_Un;
318 opcode = OpCodes.Rem;
321 case ExpressionType.RightShift:
323 opcode = OpCodes.Shr_Un;
325 opcode = OpCodes.Shr;
328 case ExpressionType.LeftShift:
329 opcode = OpCodes.Shl;
332 case ExpressionType.And:
333 opcode = OpCodes.And;
336 case ExpressionType.Or:
340 case ExpressionType.ExclusiveOr:
341 opcode = OpCodes.Xor;
344 case ExpressionType.Coalesce:
345 case ExpressionType.Equal:
346 case ExpressionType.GreaterThan:
347 case ExpressionType.GreaterThanOrEqual:
348 case ExpressionType.LessThan:
349 case ExpressionType.LessThanOrEqual:
350 case ExpressionType.NotEqual:
351 case ExpressionType.Power:
352 throw new NotImplementedException (String.Format ("No support for {0} node yet", NodeType));
355 throw new Exception (String.Format ("Internal error: BinaryExpression contains non-Binary nodetype {0}", NodeType));
360 ig.Emit (OpCodes.Newobj, left.Type.GetConstructors ()[0]);
362 Label skip = ig.DefineLabel ();
363 ig.Emit (OpCodes.Br_S, skip);
364 ig.MarkLabel (empty_value.Value);
365 ig.Emit (OpCodes.Ldloc, ret);
366 ig.Emit (OpCodes.Ldloca, ret);
367 ig.Emit (OpCodes.Initobj, Type);