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>
22 // Federico Di Gregorio <fog@initd.org>
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;
51 public ExpressionType NodeType {
52 get { return nodeType; }
56 #region Internal methods
57 internal virtual void BuildString (StringBuilder builder)
59 builder.Append ("[").Append (nodeType).Append ("]");
62 internal static Type GetNonNullableType(Type type)
64 // The Nullable<> class takes a single generic type so we can directly return
65 // the first element of the array (if the type is nullable.)
67 if (IsNullableType (type))
68 return type.GetGenericArguments ()[0];
73 internal static bool IsNullableType(Type type)
76 throw new ArgumentNullException("type");
78 if (type.IsGenericType) {
79 Type genType = type.GetGenericTypeDefinition();
80 return typeof(Nullable<>).IsAssignableFrom(genType);
87 #region Private support methods
88 private const BindingFlags opBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
89 private const BindingFlags methBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
90 private const BindingFlags propBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;
92 private static MethodInfo GetUserDefinedBinaryOperator (Type leftType, Type rightType, string name)
94 Type[] types = new Type[2] { leftType, rightType };
96 MethodInfo method = leftType.GetMethod (name, opBindingFlags, null, types, null);
97 if (method != null) return method;
99 method = rightType.GetMethod (name, opBindingFlags, null, types, null);
100 if (method != null) return method;
102 if (method == null && IsNullableType(leftType) && IsNullableType(rightType))
103 return GetUserDefinedBinaryOperator(GetNonNullableType(leftType), GetNonNullableType(rightType), name);
108 private static BinaryExpression GetUserDefinedBinaryOperatorOrThrow (ExpressionType nodeType, string name,
109 Expression left, Expression right)
111 MethodInfo method = GetUserDefinedBinaryOperator(left.type, right.type, name);
114 return new BinaryExpression (nodeType, left, right, method, method.ReturnType);
116 throw new InvalidOperationException(String.Format(
117 "The binary operator Add is not defined for the types '{0}' and '{1}'.", left.type, right.type));
119 // Note: here the code in ExpressionUtils has a series of checks to make sure that
120 // the method is static, that its return type is not void and that the number of
121 // parameters is 2 and they are of the right type, but we already know that! Or not?
124 private static PropertyInfo GetProperty (MethodInfo mi)
126 // If the method has the hidebysig and specialname attributes it can be a property accessor;
127 // if that's the case we try to extract the type of the property and then we use it and the
128 // property name (derived from the method name) to find the right ProprtyInfo.
130 if (mi.IsHideBySig && mi.IsSpecialName) {
131 Type propertyType = null;
132 if (mi.Name.StartsWith("set_")) {
133 ParameterInfo[] parameters = mi.GetParameters();
134 if (parameters.Length == 1)
135 propertyType = parameters[0].ParameterType;
137 else if (mi.Name.StartsWith("get_")) {
138 propertyType = mi.ReturnType;
141 if (propertyType != null) {
142 PropertyInfo pi = mi.DeclaringType.GetProperty(
143 mi.Name.Substring(4), propBindingFlags, null, propertyType, new Type[0], null);
144 if (pi != null) return pi;
148 throw new ArgumentException(String.Format(
149 "The method '{0}.{1}' is not a property accessor", mi.DeclaringType.FullName, mi.Name));
152 private static void ValidateUserDefinedConditionalLogicOperator (ExpressionType nodeType, Type left, Type right, MethodInfo method)
154 // Conditional logic need the "definitely true" and "definitely false" operators.
155 Type[] types = new Type[1] { left };
157 MethodInfo opTrue = left.GetMethod ("op_True", opBindingFlags, null, types, null);
158 MethodInfo opFalse = left.GetMethod ("op_False", opBindingFlags, null, types, null);
160 if (opTrue == null || opFalse == null)
161 throw new ArgumentException(String.Format(
162 "The user-defined operator method '{0}' for operator '{1}' must have associated boolean True and False operators.",
163 method.Name, nodeType));
166 private static void ValidateSettableFieldOrPropertyMember (MemberInfo member, out Type memberType)
168 if (member.MemberType == MemberTypes.Field) {
169 memberType = typeof (FieldInfo);
171 else if (member.MemberType == MemberTypes.Property) {
172 PropertyInfo pi = (PropertyInfo)member;
174 throw new ArgumentException(String.Format("The property '{0}' has no 'set' accessor", pi));
175 memberType = typeof(PropertyInfo);
178 throw new ArgumentException("Argument must be either a FieldInfo or PropertyInfo");
184 public override string ToString()
186 StringBuilder builder = new StringBuilder ();
187 BuildString (builder);
188 return builder.ToString ();
193 public static BinaryExpression Add(Expression left, Expression right, MethodInfo method)
196 throw new ArgumentNullException ("left");
198 throw new ArgumentNullException ("right");
201 return new BinaryExpression(ExpressionType.Add, left, right, method, method.ReturnType);
203 // Since both the expressions define the same numeric type we don't have
204 // to look for the "op_Addition" method.
205 if (left.type == right.type && ExpressionUtil.IsNumber(left.type))
206 return new BinaryExpression(ExpressionType.Add, left, right, left.type);
208 // Else we try for a user-defined operator.
209 return GetUserDefinedBinaryOperatorOrThrow (ExpressionType.Add, "op_Addition", left, right);
212 public static BinaryExpression Add(Expression left, Expression right)
214 return Add(left, right, null);
219 public static BinaryExpression AddChecked(Expression left, Expression right, MethodInfo method)
222 throw new ArgumentNullException ("left");
224 throw new ArgumentNullException ("right");
227 return new BinaryExpression(ExpressionType.AddChecked, left, right, method, method.ReturnType);
229 // Since both the expressions define the same numeric type we don't have
230 // to look for the "op_Addition" method.
231 if (left.type == right.type && ExpressionUtil.IsNumber(left.type))
232 return new BinaryExpression(ExpressionType.AddChecked, left, right, left.type);
234 method = GetUserDefinedBinaryOperator (left.type, right.type, "op_Addition");
236 throw new InvalidOperationException(String.Format(
237 "The binary operator AddChecked is not defined for the types '{0}' and '{1}'.", left.type, right.type));
239 Type retType = method.ReturnType;
241 // Note: here the code did some very strange checks for bool (but note that bool does
242 // not define an addition operator) and created nullables for value types (but the new
243 // MS code does not do that). All that has been removed.
245 return new BinaryExpression(ExpressionType.AddChecked, left, right, method, retType);
248 public static BinaryExpression AddChecked(Expression left, Expression right)
250 return AddChecked(left, right, null);
255 public static BinaryExpression And(Expression left, Expression right, MethodInfo method)
258 throw new ArgumentNullException ("left");
260 throw new ArgumentNullException ("right");
263 return new BinaryExpression(ExpressionType.And, left, right, method, method.ReturnType);
265 // Since both the expressions define the same integer or boolean type we don't have
266 // to look for the "op_BitwiseAnd" method.
267 if (left.type == right.type && (ExpressionUtil.IsInteger(left.type) || left.type == typeof(bool)))
268 return new BinaryExpression(ExpressionType.And, left, right, left.type);
270 // Else we try for a user-defined operator.
271 return GetUserDefinedBinaryOperatorOrThrow (ExpressionType.And, "op_BitwiseAnd", left, right);
274 public static BinaryExpression And(Expression left, Expression right)
276 return And(left, right, null);
281 public static BinaryExpression AndAlso(Expression left, Expression right, MethodInfo method)
284 throw new ArgumentNullException ("left");
286 throw new ArgumentNullException ("right");
288 // Since both the expressions define the same integer or boolean type we don't have
289 // to look for the "op_BitwiseAnd" method.
290 if (left.type == right.type && left.type == typeof(bool))
291 return new BinaryExpression(ExpressionType.AndAlso, left, right, left.type);
293 // Else we must validate the method to make sure it has companion "true" and "false" operators.
295 method = GetUserDefinedBinaryOperator (left.type, right.type, "op_BitwiseAnd");
297 throw new InvalidOperationException(String.Format(
298 "The binary operator AndAlso is not defined for the types '{0}' and '{1}'.", left.type, right.type));
299 ValidateUserDefinedConditionalLogicOperator(ExpressionType.AndAlso, left.type, right.type, method);
301 return new BinaryExpression(ExpressionType.AndAlso, left, right, method, method.ReturnType);
304 public static BinaryExpression AndAlso(Expression left, Expression right)
306 return AndAlso(left, right, null);
311 public static BinaryExpression ArrayIndex(Expression array, Expression index)
314 throw new ArgumentNullException ("array");
316 throw new ArgumentNullException ("index");
317 if (!array.type.IsArray)
318 throw new ArgumentException ("Argument must be array");
319 if (index.type != typeof(int))
320 throw new ArgumentException ("Argument for array index must be of type Int32");
322 return new BinaryExpression(ExpressionType.ArrayIndex, array, index, array.type.GetElementType());
325 public static MethodCallExpression ArrayIndex(Expression array, params Expression[] indexes)
327 return ArrayIndex(array, (IEnumerable<Expression>)indexes);
330 public static MethodCallExpression ArrayIndex(Expression array, IEnumerable<Expression> indexes)
333 throw new ArgumentNullException ("array");
335 throw new ArgumentNullException ("indexes");
336 if (!array.type.IsArray)
337 throw new ArgumentException ("Argument must be array");
339 // We'll need an array of typeof(Type) elements long as the array's rank later
340 // and also a generic List to hold the indexes (ReadOnlyCollection wants that.)
342 Type[] types = (Type[])Array.CreateInstance(typeof(Type), array.type.GetArrayRank());
343 Expression[] indexesList = new Expression[array.type.GetArrayRank()];
346 foreach (Expression index in indexes) {
347 if (index.type != typeof(int))
348 throw new ArgumentException ("Argument for array index must be of type Int32");
349 if (rank == array.type.GetArrayRank())
350 throw new ArgumentException ("Incorrect number of indexes");
352 types[rank] = index.type;
353 indexesList[rank] = index;
357 // If the array's rank is equalto the number of given indexes we can go on and
358 // look for a Get(Int32, ...) method with "rank" parameters to generate the
359 // MethodCallExpression.
361 MethodInfo method = array.type.GetMethod("Get", methBindingFlags, null, types, null);
363 // This should not happen, but we check anyway.
365 throw new InvalidOperationException(String.Format(
366 "The method Get(...) is not defined for the type '{0}'.", array.type));
368 return new MethodCallExpression(ExpressionType.Call, method, array, new ReadOnlyCollection<Expression>(indexesList));
373 public static UnaryExpression ArrayLength(Expression array)
376 throw new ArgumentNullException ("array");
377 if (!array.type.IsArray)
378 throw new ArgumentException ("Argument must be array");
380 return new UnaryExpression(ExpressionType.ArrayLength, array, typeof(Int32));
385 public static MemberAssignment Bind (MemberInfo member, Expression expression)
388 throw new ArgumentNullException ("member");
389 if (expression == null)
390 throw new ArgumentNullException ("expression");
393 ValidateSettableFieldOrPropertyMember(member, out memberType);
395 return new MemberAssignment(member, expression);
398 public static MemberAssignment Bind (MethodInfo propertyAccessor, Expression expression)
400 if (propertyAccessor == null)
401 throw new ArgumentNullException ("propertyAccessor");
402 if (expression == null)
403 throw new ArgumentNullException ("expression");
405 return new MemberAssignment(GetProperty(propertyAccessor), expression);
410 public static MethodCallExpression Call(Expression instance, MethodInfo method)
413 throw new ArgumentNullException("method");
414 if (instance == null && !method.IsStatic)
415 throw new ArgumentNullException("instance");
417 return Call(instance, method, (Expression[])null);
420 public static MethodCallExpression Call(MethodInfo method, params Expression[] arguments)
422 return Call(null, method, (IEnumerable<Expression>)arguments);
425 public static MethodCallExpression Call(Expression instance, MethodInfo method, params Expression[] arguments)
427 return Call(instance, method, (IEnumerable<Expression>)arguments);
430 public static MethodCallExpression Call(Expression instance, MethodInfo method, IEnumerable<Expression> arguments)
433 throw new ArgumentNullException("method");
434 if (arguments == null)
435 throw new ArgumentNullException("arguments");
436 if (instance == null && !method.IsStatic)
437 throw new ArgumentNullException("instance");
439 if (method.IsGenericMethodDefinition)
440 throw new ArgumentException();
441 if (method.ContainsGenericParameters)
442 throw new ArgumentException();
443 if (instance != null && !instance.type.IsAssignableFrom(method.DeclaringType))
444 throw new ArgumentException();
446 ReadOnlyCollection<Expression> roArgs = Enumerable.ToReadOnlyCollection<Expression>(arguments);
448 ParameterInfo[] pars = method.GetParameters();
449 if (Enumerable.Count<Expression>(arguments) != pars.Length)
450 throw new ArgumentException();
454 //TODO: validate the parameters against the arguments...
457 return new MethodCallExpression(ExpressionType.Call, method, instance, roArgs);
461 // NOTE: CallVirtual is not implemented because it is already marked as Obsolete by MS.
463 public static ConditionalExpression Condition(Expression test, Expression ifTrue, Expression ifFalse)
466 throw new ArgumentNullException("test");
468 throw new ArgumentNullException("ifTrue");
470 throw new ArgumentNullException("ifFalse");
471 if (test.type != typeof(bool))
472 throw new ArgumentException();
473 if (ifTrue.type != ifFalse.type)
474 throw new ArgumentException();
476 return new ConditionalExpression(test, ifTrue, ifFalse, ifTrue.type);
479 public static ConstantExpression Constant(object value, Type type)
482 throw new ArgumentNullException("type");
483 if (value == null && !IsNullableType(type))
484 throw new ArgumentException("Argument types do not match");
486 return new ConstantExpression(value, type);
489 public static ConstantExpression Constant(object value)
492 return new ConstantExpression(value, value.GetType());
494 return new ConstantExpression(null, typeof(object));
497 public static BinaryExpression Divide(Expression left, Expression right)
499 return Divide(left, right, null);
502 public static BinaryExpression Divide(Expression left, Expression right, MethodInfo method)
505 throw new ArgumentNullException("left");
507 throw new ArgumentNullException("right");
509 // sine both the expressions define the same numeric type we don't have
510 // to look for the "op_Division" method...
511 if (left.type == right.type &&
512 ExpressionUtil.IsNumber(left.type))
513 return new BinaryExpression(ExpressionType.Divide, left, right, left.type);
516 method = ExpressionUtil.GetOperatorMethod("op_Division", left.type, right.type);
518 // ok if even op_Division is not defined we need to throw an exception...
520 throw new InvalidOperationException();
522 return new BinaryExpression(ExpressionType.Divide, left, right, method, method.ReturnType);
525 public static MemberExpression Field(Expression expression, FieldInfo field)
528 throw new ArgumentNullException("field");
530 return new MemberExpression(expression, field, field.FieldType);
533 public static MemberExpression Field(Expression expression, string fieldName)
535 if (expression == null)
536 throw new ArgumentNullException("expression");
538 FieldInfo field = expression.Type.GetField(fieldName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
540 throw new ArgumentException();
542 return Field(expression, field);
545 public static FuncletExpression Funclet(Funclet funclet, Type type)
548 throw new ArgumentNullException("funclet");
550 throw new ArgumentNullException("type");
552 return new FuncletExpression(funclet, type);
555 public static Type GetFuncType(params Type[] typeArgs)
557 if (typeArgs == null)
558 throw new ArgumentNullException("typeArgs");
559 if (typeArgs.Length > 5)
560 throw new ArgumentException();
562 return typeof(Func<,,,,>).MakeGenericType(typeArgs);
565 public static BinaryExpression LeftShift(Expression left, Expression right, MethodInfo method)
568 throw new ArgumentNullException("left");
570 throw new ArgumentNullException("right");
572 // since the left expression is of an integer type and the right is of
573 // an integer we don't have to look for the "op_LeftShift" method...
574 if (ExpressionUtil.IsInteger(left.type) && right.type == typeof(int))
575 return new BinaryExpression(ExpressionType.LeftShift, left, right, left.type);
578 method = ExpressionUtil.GetOperatorMethod("op_LeftShift", left.type, right.type);
580 // ok if even op_Division is not defined we need to throw an exception...
582 throw new InvalidOperationException();
584 return new BinaryExpression(ExpressionType.LeftShift, left, right, method, method.ReturnType);
587 public static BinaryExpression LeftShift(Expression left, Expression right)
589 return LeftShift(left, right, null);
592 public static ListInitExpression ListInit(NewExpression newExpression, params Expression[] initializers)
594 if (initializers == null)
595 throw new ArgumentNullException("inizializers");
597 return ListInit(newExpression, Enumerable.ToReadOnlyCollection<Expression>(initializers));
600 public static ListInitExpression ListInit(NewExpression newExpression, IEnumerable<Expression> initializers)
602 if (newExpression == null)
603 throw new ArgumentNullException("newExpression");
604 if (initializers == null)
605 throw new ArgumentNullException("inizializers");
607 return new ListInitExpression(newExpression, Enumerable.ToReadOnlyCollection<Expression>(initializers));
610 public static MemberInitExpression MemberInit(NewExpression newExpression, IEnumerable<MemberBinding> bindings)
612 if (newExpression == null)
613 throw new ArgumentNullException("newExpression");
615 if (bindings == null)
616 throw new ArgumentNullException("bindings");
618 return new MemberInitExpression(newExpression, Enumerable.ToReadOnlyCollection<MemberBinding>(bindings));
621 public static MemberExpression Property(Expression expression, PropertyInfo property)
623 if (property == null)
624 throw new ArgumentNullException("property");
626 MethodInfo getMethod = property.GetGetMethod(true);
627 if (getMethod == null)
628 throw new ArgumentException(); // to access the property we need to have
631 return new MemberExpression(expression, property, property.PropertyType);
634 public static UnaryExpression Quote(Expression expression)
636 if (expression == null)
637 throw new ArgumentNullException("expression");
639 return new UnaryExpression(ExpressionType.Quote, expression, expression.GetType());
643 public static MemberExpression Property(Expression expression, string propertyName)
645 if (expression == null)
646 throw new ArgumentNullException("expression");
648 PropertyInfo property = expression.Type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
650 if (property == null)
651 throw new ArgumentException();
653 return Property(expression, property);
656 public static MemberExpression PropertyOrField(Expression expression, string propertyOrFieldName)
658 if (expression == null)
659 throw new ArgumentNullException("expression");
661 PropertyInfo property = expression.Type.GetProperty(propertyOrFieldName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
662 if (property != null)
663 return Property(expression, property);
665 FieldInfo field = expression.Type.GetField(propertyOrFieldName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
667 return Field(expression, field);
669 //TODO: should we return <null> here?
670 // the name is not defined in the Type of the expression given...
671 throw new ArgumentException();
675 public static TypeBinaryExpression TypeIs(Expression expression, Type type)
677 if (expression == null)
678 throw new ArgumentNullException("expression");
680 throw new ArgumentNullException("type");
682 return new TypeBinaryExpression(ExpressionType.TypeIs, expression, type, typeof(bool));
685 public static UnaryExpression TypeAs(Expression expression, Type type)
687 if (expression == null)
688 throw new ArgumentNullException("expression");
690 throw new ArgumentNullException("type");
692 return new UnaryExpression(ExpressionType.TypeAs, expression, type);