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:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
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
20 // Marek Safar (marek.safar@seznam.cz)
21 // Antonello Provenzano <antonello@deveel.com>
24 using System.Collections.Generic;
25 using System.Collections.ObjectModel;
26 using System.Reflection;
29 namespace System.Linq.Expressions
31 public abstract class Expression
34 protected Expression(ExpressionType nodeType, Type type)
36 this.nodeType = nodeType;
43 private ExpressionType nodeType;
52 public ExpressionType NodeType
54 get { return nodeType; }
58 #region Private Static Methods
59 private static void CheckLeftRight(Expression left, Expression right)
62 throw new ArgumentNullException("left");
64 throw new ArgumentNullException("right");
69 #region Internal Methods
70 internal virtual void BuildString(StringBuilder builder)
72 builder.Append("[" + nodeType + "]");
76 #region Public Methods
77 public override string ToString()
80 StringBuilder builder = new StringBuilder();
82 return builder.ToString();
86 #region Internal Static Methos
87 internal static Type GetNonNullableType(Type type)
89 if (IsNullableType(type))
91 //TODO: check this... should we return just the first argument?
92 Type[] argTypes = type.GetGenericArguments();
99 internal static bool IsNullableType(Type type)
102 throw new ArgumentNullException("type");
104 if (type.IsGenericType)
106 Type genType = type.GetGenericTypeDefinition();
107 return typeof(Nullable<>).IsAssignableFrom(genType);
114 #region Public Static Methods
115 public static BinaryExpression Add(Expression left, Expression right, MethodInfo method)
117 CheckLeftRight(left, right);
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);
126 method = ExpressionUtil.GetOperatorMethod("op_Addition", left.type, right.type);
128 // ok if even op_Addition is not defined we need to throw an exception...
130 throw new InvalidOperationException();
132 return new BinaryExpression(ExpressionType.Add, left, right, method, method.ReturnType);
135 public static BinaryExpression Add(Expression left, Expression right)
137 return Add(left, right, null);
140 public static BinaryExpression AddChecked(Expression left, Expression right, MethodInfo method)
142 CheckLeftRight(left, right);
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);
152 // in the case of a non-specified method we have to 'check'
153 method = ExpressionUtil.GetOperatorMethod("op_Addition", left.type, right.type);
155 throw new InvalidOperationException();
157 if (method.ReturnType.IsValueType && !IsNullableType(method.ReturnType))
159 Type retType = method.ReturnType;
160 if (retType != typeof(bool))
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))
166 Type[] genTypes = retType.GetGenericArguments();
167 if (genTypes.Length > 1) //TODO: should we just ignore it if is it an array greater
169 throw new InvalidOperationException();
170 retType = genTypes[0];
173 retType = ExpressionUtil.GetNullable(retType);
175 return new BinaryExpression(ExpressionType.AddChecked, left, right, method, retType);
179 return new BinaryExpression(ExpressionType.AddChecked, left, right, method, method.ReturnType);
182 public static BinaryExpression AddChecked(Expression left, Expression right)
184 return AddChecked(left, right, null);
187 public static BinaryExpression And(Expression left, Expression right, MethodInfo method)
189 CheckLeftRight(left, right);
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);
198 method = ExpressionUtil.GetOperatorMethod("op_BitwiseAnd", left.type, right.type);
200 // ok if even op_BitwiseAnd is not defined we need to throw an exception...
202 throw new InvalidOperationException();
204 return new BinaryExpression(ExpressionType.And, left, right, method, method.ReturnType);
207 public static BinaryExpression And(Expression left, Expression right)
209 return And(left, right, null);
212 public static MethodCallExpression Call(Expression instance, MethodInfo method)
214 return Call(instance, method, (Expression[])null);
217 public static MethodCallExpression Call(MethodInfo method, params Expression[] arguments)
219 return Call(null, method, Enumerable.ToReadOnlyCollection<Expression>(arguments));
222 public static MethodCallExpression Call(Expression instance, MethodInfo method, params Expression[] arguments)
224 return Call(instance, method, Enumerable.ToReadOnlyCollection<Expression>(arguments));
227 public static MethodCallExpression Call(Expression instance, MethodInfo method, IEnumerable<Expression> arguments)
229 if (arguments == null)
230 throw new ArgumentNullException("arguments");
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();
242 ReadOnlyCollection<Expression> roArgs = Enumerable.ToReadOnlyCollection<Expression>(arguments);
244 ParameterInfo[] pars = method.GetParameters();
245 if (Enumerable.Count<Expression>(arguments) != pars.Length)
246 throw new ArgumentException();
250 //TODO: validate the parameters against the arguments...
253 return new MethodCallExpression(ExpressionType.Call, method, instance, roArgs);
256 public static ConditionalExpression Condition(Expression test, Expression ifTrue, Expression ifFalse)
259 throw new ArgumentNullException("test");
261 throw new ArgumentNullException("ifTrue");
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();
269 return new ConditionalExpression(test, ifTrue, ifFalse, ifTrue.type);
272 public static ConstantExpression Constant(object value)
274 Type valueType = null;
276 valueType = value.GetType();
277 return Constant(value, valueType);
280 public static ConstantExpression Constant(object value, Type type)
283 throw new ArgumentNullException("type");
284 if (value == null && !IsNullableType(type))
285 throw new ArgumentException();
287 return new ConstantExpression(value, type);
290 public static BinaryExpression Divide(Expression left, Expression right)
292 return Divide(left, right, null);
295 public static BinaryExpression Divide(Expression left, Expression right, MethodInfo method)
297 CheckLeftRight(left, right);
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);
306 method = ExpressionUtil.GetOperatorMethod("op_Division", left.type, right.type);
308 // ok if even op_Division is not defined we need to throw an exception...
310 throw new InvalidOperationException();
312 return new BinaryExpression(ExpressionType.Divide, left, right, method, method.ReturnType);
315 public static MemberExpression Field(Expression expression, FieldInfo field)
318 throw new ArgumentNullException("field");
320 return new MemberExpression(expression, field, field.FieldType);
323 public static MemberExpression Field(Expression expression, string fieldName)
325 if (expression == null)
326 throw new ArgumentNullException("expression");
328 FieldInfo field = expression.Type.GetField(fieldName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
330 throw new ArgumentException();
332 return Field(expression, field);
335 public static FuncletExpression Funclet(Funclet funclet, Type type)
338 throw new ArgumentNullException("funclet");
340 throw new ArgumentNullException("type");
342 return new FuncletExpression(funclet, type);
345 public static Type GetFuncType(params Type[] typeArgs)
347 if (typeArgs == null)
348 throw new ArgumentNullException("typeArgs");
349 if (typeArgs.Length > 5)
350 throw new ArgumentException();
352 return typeof(Func<,,,,>).MakeGenericType(typeArgs);
355 public static BinaryExpression LeftShift(Expression left, Expression right, MethodInfo method)
357 CheckLeftRight(left, right);
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);
365 method = ExpressionUtil.GetOperatorMethod("op_LeftShift", left.type, right.type);
367 // ok if even op_Division is not defined we need to throw an exception...
369 throw new InvalidOperationException();
371 return new BinaryExpression(ExpressionType.LeftShift, left, right, method, method.ReturnType);
374 public static BinaryExpression LeftShift(Expression left, Expression right)
376 return LeftShift(left, right, null);
379 public static ListInitExpression ListInit(NewExpression newExpression, params Expression[] initializers)
381 if (initializers == null)
382 throw new ArgumentNullException("inizializers");
384 return ListInit(newExpression, Enumerable.ToReadOnlyCollection<Expression>(initializers));
387 public static ListInitExpression ListInit(NewExpression newExpression, IEnumerable<Expression> initializers)
389 if (newExpression == null)
390 throw new ArgumentNullException("newExpression");
391 if (initializers == null)
392 throw new ArgumentNullException("inizializers");
394 return new ListInitExpression(newExpression, Enumerable.ToReadOnlyCollection<Expression>(initializers));
397 public static MemberInitExpression MemberInit(NewExpression newExpression, IEnumerable<MemberBinding> bindings)
399 if (newExpression == null)
400 throw new ArgumentNullException("newExpression");
402 if (bindings == null)
403 throw new ArgumentNullException("bindings");
405 return new MemberInitExpression(newExpression, Enumerable.ToReadOnlyCollection<MemberBinding>(bindings));
408 public static MemberExpression Property(Expression expression, PropertyInfo property)
410 if (property == null)
411 throw new ArgumentNullException("property");
413 MethodInfo getMethod = property.GetGetMethod(true);
414 if (getMethod == null)
415 throw new ArgumentException(); // to access the property we need to have
418 return new MemberExpression(expression, property, property.PropertyType);
421 public static UnaryExpression Quote(Expression expression)
423 if (expression == null)
424 throw new ArgumentNullException("expression");
426 return new UnaryExpression(ExpressionType.Quote, expression, expression.GetType());
430 public static MemberExpression Property(Expression expression, string propertyName)
432 if (expression == null)
433 throw new ArgumentNullException("expression");
435 PropertyInfo property = expression.Type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
437 if (property == null)
438 throw new ArgumentException();
440 return Property(expression, property);
443 public static MemberExpression PropertyOrField(Expression expression, string propertyOrFieldName)
445 if (expression == null)
446 throw new ArgumentNullException("expression");
448 PropertyInfo property = expression.Type.GetProperty(propertyOrFieldName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
449 if (property != null)
450 return Property(expression, property);
452 FieldInfo field = expression.Type.GetField(propertyOrFieldName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
454 return Field(expression, field);
456 //TODO: should we return <null> here?
457 // the name is not defined in the Type of the expression given...
458 throw new ArgumentException();
462 public static TypeBinaryExpression TypeIs(Expression expression, Type type)
464 if (expression == null)
465 throw new ArgumentNullException("expression");
467 throw new ArgumentNullException("type");
469 return new TypeBinaryExpression(ExpressionType.TypeIs, expression, type, typeof(bool));
472 public static UnaryExpression TypeAs(Expression expression, Type type)
474 if (expression == null)
475 throw new ArgumentNullException("expression");
477 throw new ArgumentNullException("type");
479 return new UnaryExpression(ExpressionType.TypeAs, expression, type);