svn path=/trunk/mcs/; revision=75242
[mono.git] / mcs / class / System.Core / System.Linq.Expressions / Expression.cs
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 // 
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 // 
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 //
19 // Authors:
20 //        Marek Safar (marek.safar@seznam.cz)
21 //        Antonello Provenzano  <antonello@deveel.com>
22 //
23
24 using System.Collections.Generic;
25 using System.Collections.ObjectModel;
26 using System.Reflection;
27 using System.Text;
28
29 namespace System.Linq.Expressions
30 {
31     public abstract class Expression
32     {
33         #region .ctor
34         protected Expression(ExpressionType nodeType, Type type)
35         {
36             this.nodeType = nodeType;
37             this.type = type;
38         }
39         #endregion
40
41         #region Fields
42         private Type type;
43         private ExpressionType nodeType;
44         #endregion
45
46         #region Properties
47         public Type Type
48         {
49             get { return type; }
50         }
51
52         public ExpressionType NodeType
53         {
54             get { return nodeType; }
55         }
56         #endregion
57
58         #region Private Static Methods
59         private static void CheckLeftRight(Expression left, Expression right)
60         {
61             if (left == null)
62                 throw new ArgumentNullException("left");
63             if (right == null)
64                 throw new ArgumentNullException("right");
65         }
66
67         #endregion
68
69         #region Internal Methods
70         internal virtual void BuildString(StringBuilder builder)
71         {
72             builder.Append("[" + nodeType + "]");
73         }
74         #endregion
75
76         #region Public Methods
77         public override string ToString()
78         {
79             //TODO: check this...
80             StringBuilder builder = new StringBuilder();
81             BuildString(builder);
82             return builder.ToString();
83         }
84         #endregion
85
86         #region Internal Static Methos
87         internal static Type GetNonNullableType(Type type)
88         {
89             if (IsNullableType(type))
90             {
91                 //TODO: check this... should we return just the first argument?
92                 Type[] argTypes = type.GetGenericArguments();
93                 return argTypes[0];
94             }
95
96             return type;
97         }
98
99         internal static bool IsNullableType(Type type)
100         {
101             if (type == null)
102                 throw new ArgumentNullException("type");
103
104             if (type.IsGenericType)
105             {
106                 Type genType = type.GetGenericTypeDefinition();
107                 return typeof(Nullable<>).IsAssignableFrom(genType);
108             }
109
110             return false;
111         }
112         #endregion
113
114         #region Public Static Methods
115         public static BinaryExpression Add(Expression left, Expression right, MethodInfo method)
116         {
117             CheckLeftRight(left, right);
118
119             // sine both the expressions define the same numeric type we don't have
120             // to look for the "op_Addition" method...
121             if (left.type == right.type &&
122                 ExpressionUtil.IsNumber (left.type))
123                 return new BinaryExpression(ExpressionType.Add, left, right, left.type);
124
125             if (method == null)
126                 method = ExpressionUtil.GetOperatorMethod("op_Addition", left.type, right.type);
127
128             // ok if even op_Addition is not defined we need to throw an exception...
129             if (method == null)
130                 throw new InvalidOperationException();
131
132             return new BinaryExpression(ExpressionType.Add, left, right, method, method.ReturnType);
133         }
134
135         public static BinaryExpression Add(Expression left, Expression right)
136         {
137             return Add(left, right, null);
138         }
139
140         public static BinaryExpression AddChecked(Expression left, Expression right, MethodInfo method)
141         {
142             CheckLeftRight(left, right);
143
144             // sine both the expressions define the same numeric type we don't have
145             // to look for the "op_Addition" method...
146             if (left.type == right.type &&
147                 ExpressionUtil.IsNumber(left.type))
148                 return new BinaryExpression(ExpressionType.AddChecked, left, right, left.type);
149
150             if (method == null)
151             {
152                 // in the case of a non-specified method we have to 'check'
153                 method = ExpressionUtil.GetOperatorMethod("op_Addition", left.type, right.type);
154                 if (method == null)
155                     throw new InvalidOperationException();
156
157                 if (method.ReturnType.IsValueType && !IsNullableType(method.ReturnType))
158                 {
159                     Type retType = method.ReturnType;
160                     if (retType != typeof(bool))
161                     {
162                         // in case the returned type is not a boolean
163                         // we want to use a nullable version of the type...
164                         if (IsNullableType(retType))
165                         {
166                             Type[] genTypes = retType.GetGenericArguments();
167                             if (genTypes.Length > 1) //TODO: should we just ignore it if is it an array greater
168                                                      //      than 1?
169                                 throw new InvalidOperationException();
170                             retType = genTypes[0];
171                         }
172
173                         retType = ExpressionUtil.GetNullable(retType);
174                     }
175                     return new BinaryExpression(ExpressionType.AddChecked, left, right, method, retType);
176                 }
177             }
178
179             return new BinaryExpression(ExpressionType.AddChecked, left, right, method, method.ReturnType);
180         }
181
182         public static BinaryExpression AddChecked(Expression left, Expression right)
183         {
184             return AddChecked(left, right, null);
185         }
186
187         public static BinaryExpression And(Expression left, Expression right, MethodInfo method)
188         {
189             CheckLeftRight(left, right);
190
191             // sine both the expressions define the same numeric type or is a boolean
192             // we don't have to look for the "op_BitwiseAnd" method...
193             if (left.type == right.type &&
194                 (ExpressionUtil.IsInteger(left.type) || left.type == typeof(bool)))
195                 return new BinaryExpression(ExpressionType.AddChecked, left, right, left.type);
196
197             if (method == null)
198                 method = ExpressionUtil.GetOperatorMethod("op_BitwiseAnd", left.type, right.type);
199
200             // ok if even op_BitwiseAnd is not defined we need to throw an exception...
201             if (method == null)
202                 throw new InvalidOperationException();
203
204             return new BinaryExpression(ExpressionType.And, left, right, method, method.ReturnType);
205         }
206
207         public static BinaryExpression And(Expression left, Expression right)
208         {
209             return And(left, right, null);
210         }
211
212         public static MethodCallExpression Call(Expression instance, MethodInfo method)
213         {
214             return Call(instance, method, (Expression[])null);
215         }
216
217         public static MethodCallExpression Call(MethodInfo method, params Expression[] arguments)
218         {
219             return Call(null, method, Enumerable.ToReadOnlyCollection<Expression>(arguments));
220         }
221
222         public static MethodCallExpression Call(Expression instance, MethodInfo method, params Expression[] arguments)
223         {
224             return Call(instance, method, Enumerable.ToReadOnlyCollection<Expression>(arguments));
225         }
226
227         public static MethodCallExpression Call(Expression instance, MethodInfo method, IEnumerable<Expression> arguments)
228         {
229             if (arguments == null)
230                 throw new ArgumentNullException("arguments");
231             if (method == null)
232                 throw new ArgumentNullException("method");
233             if (method.IsGenericMethodDefinition)
234                     throw new ArgumentException();
235             if (method.ContainsGenericParameters)
236                     throw new ArgumentException();
237             if (!method.IsStatic && instance == null)
238                 throw new ArgumentNullException("instance");
239             if (instance != null && !instance.type.IsAssignableFrom(method.DeclaringType))
240                 throw new ArgumentException();
241
242             ReadOnlyCollection<Expression> roArgs = Enumerable.ToReadOnlyCollection<Expression>(arguments);
243
244             ParameterInfo[] pars = method.GetParameters();
245             if (Enumerable.Count<Expression>(arguments) != pars.Length)
246                 throw new ArgumentException();
247
248             if (pars.Length > 0)
249             {
250                 //TODO: validate the parameters against the arguments...
251             }
252
253             return new MethodCallExpression(ExpressionType.Call, method, instance, roArgs);
254         }
255
256         public static ConditionalExpression Condition(Expression test, Expression ifTrue, Expression ifFalse)
257         {
258             if (test == null)
259                 throw new ArgumentNullException("test");
260             if (ifTrue == null)
261                 throw new ArgumentNullException("ifTrue");
262             if (ifFalse == null)
263                 throw new ArgumentNullException("ifFalse");
264             if (test.type != typeof(bool))
265                 throw new ArgumentException();
266             if (ifTrue.type != ifFalse.type)
267                 throw new ArgumentException();
268
269             return new ConditionalExpression(test, ifTrue, ifFalse, ifTrue.type);
270         }
271
272         public static ConstantExpression Constant(object value)
273         {
274             Type valueType = null;
275             if (value != null)
276                 valueType = value.GetType();
277             return Constant(value, valueType);
278         }
279
280         public static ConstantExpression Constant(object value, Type type)
281         {
282             if (type == null)
283                 throw new ArgumentNullException("type");
284             if (value == null && !IsNullableType(type))
285                 throw new ArgumentException();
286
287             return new ConstantExpression(value, type);
288         }
289
290         public static BinaryExpression Divide(Expression left, Expression right)
291         {
292             return Divide(left, right, null);
293         }
294
295         public static BinaryExpression Divide(Expression left, Expression right, MethodInfo method)
296         {
297             CheckLeftRight(left, right);
298
299             // sine both the expressions define the same numeric type we don't have 
300             // to look for the "op_Division" method...
301             if (left.type == right.type &&
302                 ExpressionUtil.IsNumber(left.type))
303                 return new BinaryExpression(ExpressionType.Divide, left, right, left.type);
304
305             if (method == null)
306                 method = ExpressionUtil.GetOperatorMethod("op_Division", left.type, right.type);
307
308             // ok if even op_Division is not defined we need to throw an exception...
309             if (method == null)
310                 throw new InvalidOperationException();
311
312             return new BinaryExpression(ExpressionType.Divide, left, right, method, method.ReturnType);
313         }
314
315         public static MemberExpression Field(Expression expression, FieldInfo field)
316         {
317             if (field == null)
318                 throw new ArgumentNullException("field");
319
320             return new MemberExpression(expression, field, field.FieldType);
321         }
322
323         public static MemberExpression Field(Expression expression, string fieldName)
324         {
325             if (expression == null)
326                 throw new ArgumentNullException("expression");
327
328             FieldInfo field = expression.Type.GetField(fieldName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
329             if (field == null)
330                 throw new ArgumentException();
331
332             return Field(expression, field);
333         }
334
335         public static FuncletExpression Funclet(Funclet funclet, Type type)
336         {
337             if (funclet == null)
338                 throw new ArgumentNullException("funclet");
339             if (type == null)
340                 throw new ArgumentNullException("type");
341
342             return new FuncletExpression(funclet, type);
343         }
344
345         public static Type GetFuncType(params Type[] typeArgs)
346         {
347             if (typeArgs == null)
348                 throw new ArgumentNullException("typeArgs");
349             if (typeArgs.Length > 5)
350                 throw new ArgumentException();
351
352             return typeof(Func<,,,,>).MakeGenericType(typeArgs);
353         }
354
355         public static BinaryExpression LeftShift(Expression left, Expression right, MethodInfo method)
356         {
357             CheckLeftRight(left, right);
358
359             // since the left expression is of an integer type and the right is of
360             // an integer we don't have to look for the "op_LeftShift" method...
361             if (ExpressionUtil.IsInteger(left.type) && right.type == typeof(int))
362                 return new BinaryExpression(ExpressionType.LeftShift, left, right, left.type);
363
364             if (method == null)
365                 method = ExpressionUtil.GetOperatorMethod("op_LeftShift", left.type, right.type);
366
367             // ok if even op_Division is not defined we need to throw an exception...
368             if (method == null)
369                 throw new InvalidOperationException();
370
371             return new BinaryExpression(ExpressionType.LeftShift, left, right, method, method.ReturnType);
372         }
373
374         public static BinaryExpression LeftShift(Expression left, Expression right)
375         {
376             return LeftShift(left, right, null);
377         }
378
379         public static ListInitExpression ListInit(NewExpression newExpression, params Expression[] initializers)
380         {
381             if (initializers == null)
382                 throw new ArgumentNullException("inizializers");
383
384             return ListInit(newExpression, Enumerable.ToReadOnlyCollection<Expression>(initializers));
385         }
386
387         public static ListInitExpression ListInit(NewExpression newExpression, IEnumerable<Expression> initializers)
388         {
389             if (newExpression == null)
390                 throw new ArgumentNullException("newExpression");
391             if (initializers == null)
392                 throw new ArgumentNullException("inizializers");
393
394             return new ListInitExpression(newExpression, Enumerable.ToReadOnlyCollection<Expression>(initializers));
395         }
396
397         public static MemberInitExpression MemberInit(NewExpression newExpression, IEnumerable<MemberBinding> bindings)
398         {
399             if (newExpression == null)
400                 throw new ArgumentNullException("newExpression");
401
402             if (bindings == null)
403                 throw new ArgumentNullException("bindings");
404
405             return new MemberInitExpression(newExpression, Enumerable.ToReadOnlyCollection<MemberBinding>(bindings));
406         }
407
408         public static MemberExpression Property(Expression expression, PropertyInfo property)
409         {
410             if (property == null)
411                 throw new ArgumentNullException("property");
412
413             MethodInfo getMethod = property.GetGetMethod(true);
414             if (getMethod == null)
415                 throw new ArgumentException(); // to access the property we need to have
416                                                // a get method...
417
418             return new MemberExpression(expression, property, property.PropertyType);
419         }
420
421         public static UnaryExpression Quote(Expression expression)
422         {
423             if (expression == null)
424                 throw new ArgumentNullException("expression");
425
426             return new UnaryExpression(ExpressionType.Quote, expression, expression.GetType());
427         }
428
429
430         public static MemberExpression Property(Expression expression, string propertyName)
431         {
432             if (expression == null)
433                 throw new ArgumentNullException("expression");
434
435             PropertyInfo property = expression.Type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
436
437             if (property == null)
438                 throw new ArgumentException();
439
440             return Property(expression, property);
441         }
442
443         public static MemberExpression PropertyOrField(Expression expression, string propertyOrFieldName)
444         {
445             if (expression == null)
446                 throw new ArgumentNullException("expression");
447
448             PropertyInfo property = expression.Type.GetProperty(propertyOrFieldName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
449             if (property != null)
450                 return Property(expression, property);
451
452             FieldInfo field = expression.Type.GetField(propertyOrFieldName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
453             if (field != null)
454                 return Field(expression, field);
455                 
456             //TODO: should we return <null> here?
457             // the name is not defined in the Type of the expression given...
458             throw new ArgumentException();
459         }
460
461
462         public static TypeBinaryExpression TypeIs(Expression expression, Type type)
463         {
464             if (expression == null)
465                 throw new ArgumentNullException("expression");
466             if (type == null)
467                 throw new ArgumentNullException("type"); 
468             
469             return new TypeBinaryExpression(ExpressionType.TypeIs, expression, type, typeof(bool));
470         }
471
472         public static UnaryExpression TypeAs(Expression expression, Type type)
473         {
474             if (expression == null)
475                 throw new ArgumentNullException("expression");
476             if (type == null)
477                 throw new ArgumentNullException("type");
478
479             return new UnaryExpression(ExpressionType.TypeAs, expression, type);
480         }
481         #endregion
482     }
483 }