2008-01-21 Miguel de Icaza <miguel@novell.com>
[mono.git] / mcs / class / System.Core / System.Linq.Expressions / BinaryExpression.cs
1 //
2 // BinaryExpression.cs
3 //
4 // Author:
5 //   Jb Evain (jbevain@novell.com)
6 //   Miguel de Icaza (miguel@novell.com)
7 //
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)
12 //
13 // (C) 2001-2003 Ximian, Inc.
14 // (C) 2004-2008 Novell, Inc. (http://www.novell.com)
15 //
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:
23 //
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 //
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.
34 //
35
36 using System;
37 using System.Reflection;
38 using System.Reflection.Emit;
39
40 namespace System.Linq.Expressions {
41
42         public sealed class BinaryExpression : Expression {
43
44                 Expression left;
45                 Expression right;
46                 LambdaExpression conversion;
47                 MethodInfo method;
48                 bool lift_to_null, is_lifted;
49
50                 public Expression Left {
51                         get { return left; }
52                 }
53
54                 public Expression Right {
55                         get { return right; }
56                 }
57
58                 public MethodInfo Method {
59                         get { return method; }
60                 }
61
62                 public bool IsLifted {
63                         get { return is_lifted;  }
64                 }
65
66                 public bool IsLiftedToNull {
67                         get { return lift_to_null; }
68                 }
69
70                 public LambdaExpression Conversion {
71                         get { return conversion; }
72                 }
73
74                 internal BinaryExpression (ExpressionType node_type, Type type, Expression left, Expression right)
75                         : base (node_type, type)
76                 {
77                         this.left = left;
78                         this.right = right;
79                 }
80
81                 internal BinaryExpression (ExpressionType node_type, Type type, Expression left, Expression right, MethodInfo method)
82                         : base (node_type, type)
83                 {
84                         this.left = left;
85                         this.right = right;
86                         this.method = method;
87                 }
88
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)
91                 {
92                         this.left = left;
93                         this.right = right;
94                         this.method = method;
95                         this.conversion = conversion;
96                         this.lift_to_null = lift_to_null;
97                         this.is_lifted = is_lifted;
98                 }
99
100                 static void EmitMethod ()
101                 {
102                         throw new NotImplementedException ("Support for MethodInfo-based BinaryExpressions not yet supported");
103                 }
104
105                 static MethodInfo GetMethodNoPar (Type t, string name)
106                 {
107                         MethodInfo [] methods = t.GetMethods ();
108                         foreach (MethodInfo m in methods){
109                                 if (m.Name != name)
110                                         continue;
111                                 if (m.GetParameters ().Length == 0)
112                                         return m;
113                         }
114                         throw new Exception (String.Format ("Internal error: method {0} with no parameters not found on {1}",
115                                                             name, t));
116                 }
117
118                 LocalBuilder EmitStored (EmitContext ec, Expression expr)
119                 {
120                         LocalBuilder lb = ec.ig.DeclareLocal (expr.Type);
121                         expr.Emit (ec);
122                         ec.ig.Emit (OpCodes.Stloc, lb);
123
124                         return lb;
125                 }
126                 
127                 void EmitLogical (EmitContext ec, bool and, bool short_circuit)
128                 {
129                         ILGenerator ig = ec.ig;
130                         if (IsLifted){
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");
135
136                                 vleft = EmitStored (ec, left);
137                                 if (!short_circuit)
138                                         vright = EmitStored (ec, right);
139                                 
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 ();
146                                 
147                                 // Check left
148                                 
149                                 ig.Emit (OpCodes.Ldloca, vleft);
150                                 ig.Emit (OpCodes.Call, has_value);
151                                 ig.Emit (OpCodes.Brfalse, left_is_null);
152                                 
153                                 ig.Emit (OpCodes.Ldloca, vleft);
154                                 ig.Emit (OpCodes.Call, get_value);                                      
155                                 ig.Emit (OpCodes.Dup);
156                                 
157                                 left_is_false = ig.DefineLabel ();
158                                 ig.Emit (and ? OpCodes.Brfalse : OpCodes.Brtrue, create);
159                                 
160                                 // Deal with right
161                                 if (short_circuit)
162                                         vright = EmitStored (ec, right);
163                                 
164                                 ig.Emit (OpCodes.Ldloca, vright);
165                                 ig.Emit (OpCodes.Call, has_value);
166                                 ig.Emit (OpCodes.Brfalse, right_is_null);
167                                 
168                                 ig.Emit (OpCodes.Ldloca, vright);
169                                 ig.Emit (OpCodes.Call, get_value);
170
171                                 ig.Emit (and ? OpCodes.And : OpCodes.Or);
172                                 ig.Emit (OpCodes.Br, create);
173
174                         // left_is_null:
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);
183
184                         // both_are_null:
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);
190                                 
191                         // create:
192                                 ig.MarkLabel (create);
193                                 ig.Emit (OpCodes.Newobj, Type.GetConstructors ()[0]);
194
195                         // exit:
196                                 ig.MarkLabel (exit);
197                         } else {
198                                 left.Emit (ec);
199                                 right.Emit (ec);
200                                 ig.Emit (and ? OpCodes.And : OpCodes.Or);
201                         }
202                         
203                 }
204                 
205                 internal override void Emit (EmitContext ec)
206                 {
207                         ILGenerator ig = ec.ig;
208                         OpCode opcode;
209
210                         if (method != null){
211                                 EmitMethod ();
212                                 return;
213                         }
214
215                         switch (NodeType){
216                         case ExpressionType.And:
217                                 EmitLogical (ec, true, false);
218                                 return;
219                                 
220                         case ExpressionType.Or:
221                                 EmitLogical (ec, false, false);
222                                 return;
223
224                         case ExpressionType.AndAlso:
225                                 Console.WriteLine ("LINQ/BinaryExpression: AndAlso code path not yet reviewed");
226                                 EmitLogical (ec, true, true);
227                                 return;
228
229                         case ExpressionType.OrElse:
230                                 Console.WriteLine ("LINQ/BinaryExpression: OrElse code path not yet reviewed");
231                                 EmitLogical (ec, false, true);
232                                 return;
233                         }
234                         
235                         Label? empty_value = null;
236                         LocalBuilder ret = null;
237
238                         if (IsLifted){
239                                 LocalBuilder vleft, vright;
240
241                                 empty_value = ig.DefineLabel ();
242                                 ret = ig.DeclareLocal (Type);
243
244                                 vleft = EmitStored (ec, left);
245                                 vright = EmitStored (ec, right);
246
247                                 MethodInfo has_value = left.Type.GetMethod ("get_HasValue");
248                                 MethodInfo get_value = GetMethodNoPar (left.Type, "get_Value");
249
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);
260                         } else {
261                                 left.Emit (ec);
262                                 right.Emit (ec);
263                         }
264
265                         bool is_unsigned = IsUnsigned (left.Type);
266
267                         switch (NodeType){
268                         case ExpressionType.Add:
269                                 opcode = OpCodes.Add;
270                                 break;
271
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;
277                                 else
278                                         opcode = OpCodes.Add;
279                                 break;
280
281                         case ExpressionType.Subtract:
282                                 opcode = OpCodes.Sub;
283                                 break;
284
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;
290                                 else
291                                         opcode = OpCodes.Sub;
292                                 break;
293
294                         case ExpressionType.Multiply:
295                                 opcode = OpCodes.Mul;
296                                 break;
297
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;
303                                 else
304                                         opcode = OpCodes.Mul;
305                                 break;
306
307                         case ExpressionType.Divide:
308                                 if (is_unsigned)
309                                         opcode = OpCodes.Div_Un;
310                                 else
311                                         opcode = OpCodes.Div;
312                                 break;
313
314                         case ExpressionType.Modulo:
315                                 if (is_unsigned)
316                                         opcode = OpCodes.Rem_Un;
317                                 else
318                                         opcode = OpCodes.Rem;
319                                 break;
320
321                         case ExpressionType.RightShift:
322                                 if (is_unsigned)
323                                         opcode = OpCodes.Shr_Un;
324                                 else
325                                         opcode = OpCodes.Shr;
326                                 break;
327
328                         case ExpressionType.LeftShift:
329                                 opcode = OpCodes.Shl;
330                                 break;
331
332                         case ExpressionType.And:
333                                 opcode = OpCodes.And;
334                                 break;
335
336                         case ExpressionType.Or:
337                                 opcode = OpCodes.Or;
338                                 break;
339
340                         case ExpressionType.ExclusiveOr:
341                                 opcode = OpCodes.Xor;
342                                 break;
343
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));
353
354                         default:
355                                 throw new Exception (String.Format ("Internal error: BinaryExpression contains non-Binary nodetype {0}", NodeType));
356                         }
357                         ig.Emit (opcode);
358
359                         if (IsLifted){
360                                 ig.Emit (OpCodes.Newobj, left.Type.GetConstructors ()[0]);
361
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);
368                                 
369                                 ig.MarkLabel (skip);
370                         }
371                 }
372         }
373 }