/* **************************************************************************** * * Copyright (c) Microsoft Corporation. * * This source code is subject to terms and conditions of the Apache License, Version 2.0. A * copy of the license can be found in the License.html file at the root of this distribution. If * you cannot locate the Apache License, Version 2.0, please send an email to * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound * by the terms of the Apache License, Version 2.0. * * You must not remove this notice, or any other, from this software. * * * ***************************************************************************/ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Dynamic.Utils; using System.Reflection; using System.Runtime.CompilerServices; #if !FEATURE_CORE_DLR namespace Microsoft.Scripting.Ast { #else namespace System.Linq.Expressions { #endif /// /// Represents a call to either static or an instance method. /// [DebuggerTypeProxy(typeof(Expression.MethodCallExpressionProxy))] public class MethodCallExpression : Expression, IArgumentProvider { private readonly MethodInfo _method; internal MethodCallExpression(MethodInfo method) { _method = method; } internal virtual Expression GetInstance() { return null; } /// /// Returns the node type of this . (Inherited from .) /// /// The that represents this expression. public sealed override ExpressionType NodeType { get { return ExpressionType.Call; } } /// /// Gets the static type of the expression that this represents. (Inherited from .) /// /// The that represents the static type of the expression. public sealed override Type Type { get { return _method.ReturnType; } } /// /// Gets the for the method to be called. /// public MethodInfo Method { get { return _method; } } /// /// Gets the that represents the instance /// for instance method calls or null for static method cals. /// public Expression Object { get { return GetInstance(); } } /// /// Gets a collection of expressions that represent arguments to the method call. /// public ReadOnlyCollection Arguments { get { return GetOrMakeArguments(); } } /// /// Creates a new expression that is like this one, but using the /// supplied children. If all of the children are the same, it will /// return this expression. /// /// The property of the result. /// The property of the result. /// This expression if no children changed, or an expression with the updated children. public MethodCallExpression Update(Expression @object, IEnumerable arguments) { if (@object == Object && arguments == Arguments) { return this; } return Expression.Call(@object, Method, arguments); } internal virtual ReadOnlyCollection GetOrMakeArguments() { throw ContractUtils.Unreachable; } /// /// Dispatches to the specific visit method for this node type. /// protected internal override Expression Accept(ExpressionVisitor visitor) { return visitor.VisitMethodCall(this); } /// /// Returns a new MethodCallExpression replacing the existing instance/args with the /// newly provided instance and args. Arguments can be null to use the existing /// arguments. /// /// This helper is provided to allow re-writing of nodes to not depend on the specific optimized /// subclass of MethodCallExpression which is being used. /// internal virtual MethodCallExpression Rewrite(Expression instance, IList args) { throw ContractUtils.Unreachable; } #region IArgumentProvider Members Expression IArgumentProvider.GetArgument(int index) { throw ContractUtils.Unreachable; } int IArgumentProvider.ArgumentCount { get { throw ContractUtils.Unreachable; } } #endregion } #region Specialized Subclasses internal class MethodCallExpressionN : MethodCallExpression, IArgumentProvider { private IList _arguments; public MethodCallExpressionN(MethodInfo method, IList args) : base(method) { _arguments = args; } Expression IArgumentProvider.GetArgument(int index) { return _arguments[index]; } int IArgumentProvider.ArgumentCount { get { return _arguments.Count; } } internal override ReadOnlyCollection GetOrMakeArguments() { return ReturnReadOnly(ref _arguments); } internal override MethodCallExpression Rewrite(Expression instance, IList args) { Debug.Assert(instance == null); Debug.Assert(args == null || args.Count == _arguments.Count); return Expression.Call(Method, args ?? _arguments); } } internal class InstanceMethodCallExpressionN : MethodCallExpression, IArgumentProvider { private IList _arguments; private readonly Expression _instance; public InstanceMethodCallExpressionN(MethodInfo method, Expression instance, IList args) : base(method) { _instance = instance; _arguments = args; } Expression IArgumentProvider.GetArgument(int index) { return _arguments[index]; } int IArgumentProvider.ArgumentCount { get { return _arguments.Count; } } internal override Expression GetInstance() { return _instance; } internal override ReadOnlyCollection GetOrMakeArguments() { return ReturnReadOnly(ref _arguments); } internal override MethodCallExpression Rewrite(Expression instance, IList args) { Debug.Assert(instance != null); Debug.Assert(args == null || args.Count == _arguments.Count); return Expression.Call(instance, Method, args ?? _arguments); } } internal class MethodCallExpression1 : MethodCallExpression, IArgumentProvider { private object _arg0; // storage for the 1st argument or a readonly collection. See IArgumentProvider public MethodCallExpression1(MethodInfo method, Expression arg0) : base(method) { _arg0 = arg0; } Expression IArgumentProvider.GetArgument(int index) { switch (index) { case 0: return ReturnObject(_arg0); default: throw new InvalidOperationException(); } } int IArgumentProvider.ArgumentCount { get { return 1; } } internal override ReadOnlyCollection GetOrMakeArguments() { return ReturnReadOnly(this, ref _arg0); } internal override MethodCallExpression Rewrite(Expression instance, IList args) { Debug.Assert(instance == null); Debug.Assert(args == null || args.Count == 1); if (args != null) { return Expression.Call(Method, args[0]); } return Expression.Call(Method, ReturnObject(_arg0)); } } internal class MethodCallExpression2 : MethodCallExpression, IArgumentProvider { private object _arg0; // storage for the 1st argument or a readonly collection. See IArgumentProvider private readonly Expression _arg1; // storage for the 2nd arg public MethodCallExpression2(MethodInfo method, Expression arg0, Expression arg1) : base(method) { _arg0 = arg0; _arg1 = arg1; } Expression IArgumentProvider.GetArgument(int index) { switch (index) { case 0: return ReturnObject(_arg0); case 1: return _arg1; default: throw new InvalidOperationException(); } } int IArgumentProvider.ArgumentCount { get { return 2; } } internal override ReadOnlyCollection GetOrMakeArguments() { return ReturnReadOnly(this, ref _arg0); } internal override MethodCallExpression Rewrite(Expression instance, IList args) { Debug.Assert(instance == null); Debug.Assert(args == null || args.Count == 2); if (args != null) { return Expression.Call(Method, args[0], args[1]); } return Expression.Call(Method, ReturnObject(_arg0), _arg1); } } internal class MethodCallExpression3 : MethodCallExpression, IArgumentProvider { private object _arg0; // storage for the 1st argument or a readonly collection. See IArgumentProvider private readonly Expression _arg1, _arg2; // storage for the 2nd - 3rd args. public MethodCallExpression3(MethodInfo method, Expression arg0, Expression arg1, Expression arg2) : base(method) { _arg0 = arg0; _arg1 = arg1; _arg2 = arg2; } Expression IArgumentProvider.GetArgument(int index) { switch (index) { case 0: return ReturnObject(_arg0); case 1: return _arg1; case 2: return _arg2; default: throw new InvalidOperationException(); } } int IArgumentProvider.ArgumentCount { get { return 3; } } internal override ReadOnlyCollection GetOrMakeArguments() { return ReturnReadOnly(this, ref _arg0); } internal override MethodCallExpression Rewrite(Expression instance, IList args) { Debug.Assert(instance == null); Debug.Assert(args == null || args.Count == 3); if (args != null) { return Expression.Call(Method, args[0], args[1], args[2]); } return Expression.Call(Method, ReturnObject(_arg0), _arg1, _arg2); } } internal class MethodCallExpression4 : MethodCallExpression, IArgumentProvider { private object _arg0; // storage for the 1st argument or a readonly collection. See IArgumentProvider private readonly Expression _arg1, _arg2, _arg3; // storage for the 2nd - 4th args. public MethodCallExpression4(MethodInfo method, Expression arg0, Expression arg1, Expression arg2, Expression arg3) : base(method) { _arg0 = arg0; _arg1 = arg1; _arg2 = arg2; _arg3 = arg3; } Expression IArgumentProvider.GetArgument(int index) { switch (index) { case 0: return ReturnObject(_arg0); case 1: return _arg1; case 2: return _arg2; case 3: return _arg3; default: throw new InvalidOperationException(); } } int IArgumentProvider.ArgumentCount { get { return 4; } } internal override ReadOnlyCollection GetOrMakeArguments() { return ReturnReadOnly(this, ref _arg0); } internal override MethodCallExpression Rewrite(Expression instance, IList args) { Debug.Assert(instance == null); Debug.Assert(args == null || args.Count == 4); if (args != null) { return Expression.Call(Method, args[0], args[1], args[2], args[3]); } return Expression.Call(Method, ReturnObject(_arg0), _arg1, _arg2, _arg3); } } internal class MethodCallExpression5 : MethodCallExpression, IArgumentProvider { private object _arg0; // storage for the 1st argument or a readonly collection. See IArgumentProvider private readonly Expression _arg1, _arg2, _arg3, _arg4; // storage for the 2nd - 5th args. public MethodCallExpression5(MethodInfo method, Expression arg0, Expression arg1, Expression arg2, Expression arg3, Expression arg4) : base(method) { _arg0 = arg0; _arg1 = arg1; _arg2 = arg2; _arg3 = arg3; _arg4 = arg4; } Expression IArgumentProvider.GetArgument(int index) { switch (index) { case 0: return ReturnObject(_arg0); case 1: return _arg1; case 2: return _arg2; case 3: return _arg3; case 4: return _arg4; default: throw new InvalidOperationException(); } } int IArgumentProvider.ArgumentCount { get { return 5; } } internal override ReadOnlyCollection GetOrMakeArguments() { return ReturnReadOnly(this, ref _arg0); } internal override MethodCallExpression Rewrite(Expression instance, IList args) { Debug.Assert(instance == null); Debug.Assert(args == null || args.Count == 5); if (args != null) { return Expression.Call(Method, args[0], args[1], args[2], args[3], args[4]); } return Expression.Call(Method, ReturnObject(_arg0), _arg1, _arg2, _arg3, _arg4); } } internal class InstanceMethodCallExpression2 : MethodCallExpression, IArgumentProvider { private readonly Expression _instance; private object _arg0; // storage for the 1st argument or a readonly collection. See IArgumentProvider private readonly Expression _arg1; // storage for the 2nd argument public InstanceMethodCallExpression2(MethodInfo method, Expression instance, Expression arg0, Expression arg1) : base(method) { Debug.Assert(instance != null); _instance = instance; _arg0 = arg0; _arg1 = arg1; } Expression IArgumentProvider.GetArgument(int index) { switch (index) { case 0: return ReturnObject(_arg0); case 1: return _arg1; default: throw new InvalidOperationException(); } } int IArgumentProvider.ArgumentCount { get { return 2; } } internal override Expression GetInstance() { return _instance; } internal override ReadOnlyCollection GetOrMakeArguments() { return ReturnReadOnly(this, ref _arg0); } internal override MethodCallExpression Rewrite(Expression instance, IList args) { Debug.Assert(instance != null); Debug.Assert(args == null || args.Count == 2); if (args != null) { return Expression.Call(instance, Method, args[0], args[1]); } return Expression.Call(instance, Method, ReturnObject(_arg0), _arg1); } } internal class InstanceMethodCallExpression3 : MethodCallExpression, IArgumentProvider { private readonly Expression _instance; private object _arg0; // storage for the 1st argument or a readonly collection. See IArgumentProvider private readonly Expression _arg1, _arg2; // storage for the 2nd - 3rd argument public InstanceMethodCallExpression3(MethodInfo method, Expression instance, Expression arg0, Expression arg1, Expression arg2) : base(method) { Debug.Assert(instance != null); _instance = instance; _arg0 = arg0; _arg1 = arg1; _arg2 = arg2; } Expression IArgumentProvider.GetArgument(int index) { switch (index) { case 0: return ReturnObject(_arg0); case 1: return _arg1; case 2: return _arg2; default: throw new InvalidOperationException(); } } int IArgumentProvider.ArgumentCount { get { return 3; } } internal override Expression GetInstance() { return _instance; } internal override ReadOnlyCollection GetOrMakeArguments() { return ReturnReadOnly(this, ref _arg0); } internal override MethodCallExpression Rewrite(Expression instance, IList args) { Debug.Assert(instance != null); Debug.Assert(args == null || args.Count == 3); if (args != null) { return Expression.Call(instance, Method, args[0], args[1], args[2]); } return Expression.Call(instance, Method, ReturnObject(_arg0), _arg1, _arg2); } } #endregion public partial class Expression { #region Call ///Creates a that represents a call to a static method that takes one argument. ///A that has the property equal to and the and properties set to the specified values. ///A to set the property equal to. ///The that represents the first argument. /// /// is null. public static MethodCallExpression Call(MethodInfo method, Expression arg0) { ContractUtils.RequiresNotNull(method, "method"); ContractUtils.RequiresNotNull(arg0, "arg0"); ParameterInfo[] pis = ValidateMethodAndGetParameters(null, method); ValidateArgumentCount(method, ExpressionType.Call, 1, pis); arg0 = ValidateOneArgument(method, ExpressionType.Call, arg0, pis[0]); return new MethodCallExpression1(method, arg0); } ///Creates a that represents a call to a static method that takes two arguments. ///A that has the property equal to and the and properties set to the specified values. ///A to set the property equal to. ///The that represents the first argument. ///The that represents the second argument. /// /// is null. public static MethodCallExpression Call(MethodInfo method, Expression arg0, Expression arg1) { ContractUtils.RequiresNotNull(method, "method"); ContractUtils.RequiresNotNull(arg0, "arg0"); ContractUtils.RequiresNotNull(arg1, "arg1"); ParameterInfo[] pis = ValidateMethodAndGetParameters(null, method); ValidateArgumentCount(method, ExpressionType.Call, 2, pis); arg0 = ValidateOneArgument(method, ExpressionType.Call, arg0, pis[0]); arg1 = ValidateOneArgument(method, ExpressionType.Call, arg1, pis[1]); return new MethodCallExpression2(method, arg0, arg1); } ///Creates a that represents a call to a static method that takes three arguments. ///A that has the property equal to and the and properties set to the specified values. ///A to set the property equal to. ///The that represents the first argument. ///The that represents the second argument. ///The that represents the third argument. /// /// is null. public static MethodCallExpression Call(MethodInfo method, Expression arg0, Expression arg1, Expression arg2) { ContractUtils.RequiresNotNull(method, "method"); ContractUtils.RequiresNotNull(arg0, "arg0"); ContractUtils.RequiresNotNull(arg1, "arg1"); ContractUtils.RequiresNotNull(arg2, "arg2"); ParameterInfo[] pis = ValidateMethodAndGetParameters(null, method); ValidateArgumentCount(method, ExpressionType.Call, 3, pis); arg0 = ValidateOneArgument(method, ExpressionType.Call, arg0, pis[0]); arg1 = ValidateOneArgument(method, ExpressionType.Call, arg1, pis[1]); arg2 = ValidateOneArgument(method, ExpressionType.Call, arg2, pis[2]); return new MethodCallExpression3(method, arg0, arg1, arg2); } ///Creates a that represents a call to a static method that takes four arguments. ///A that has the property equal to and the and properties set to the specified values. ///A to set the property equal to. ///The that represents the first argument. ///The that represents the second argument. ///The that represents the third argument. ///The that represents the fourth argument. /// /// is null. public static MethodCallExpression Call(MethodInfo method, Expression arg0, Expression arg1, Expression arg2, Expression arg3) { ContractUtils.RequiresNotNull(method, "method"); ContractUtils.RequiresNotNull(arg0, "arg0"); ContractUtils.RequiresNotNull(arg1, "arg1"); ContractUtils.RequiresNotNull(arg2, "arg2"); ContractUtils.RequiresNotNull(arg3, "arg3"); ParameterInfo[] pis = ValidateMethodAndGetParameters(null, method); ValidateArgumentCount(method, ExpressionType.Call, 4, pis); arg0 = ValidateOneArgument(method, ExpressionType.Call, arg0, pis[0]); arg1 = ValidateOneArgument(method, ExpressionType.Call, arg1, pis[1]); arg2 = ValidateOneArgument(method, ExpressionType.Call, arg2, pis[2]); arg3 = ValidateOneArgument(method, ExpressionType.Call, arg3, pis[3]); return new MethodCallExpression4(method, arg0, arg1, arg2, arg3); } ///Creates a that represents a call to a static method that takes five arguments. ///A that has the property equal to and the and properties set to the specified values. ///A to set the property equal to. ///The that represents the first argument. ///The that represents the second argument. ///The that represents the third argument. ///The that represents the fourth argument. ///The that represents the fifth argument. /// /// is null. ///A that has the property equal to and the and properties set to the specified values. public static MethodCallExpression Call(MethodInfo method, Expression arg0, Expression arg1, Expression arg2, Expression arg3, Expression arg4) { ContractUtils.RequiresNotNull(method, "method"); ContractUtils.RequiresNotNull(arg0, "arg0"); ContractUtils.RequiresNotNull(arg1, "arg1"); ContractUtils.RequiresNotNull(arg2, "arg2"); ContractUtils.RequiresNotNull(arg3, "arg3"); ContractUtils.RequiresNotNull(arg4, "arg4"); ParameterInfo[] pis = ValidateMethodAndGetParameters(null, method); ValidateArgumentCount(method, ExpressionType.Call, 5, pis); arg0 = ValidateOneArgument(method, ExpressionType.Call, arg0, pis[0]); arg1 = ValidateOneArgument(method, ExpressionType.Call, arg1, pis[1]); arg2 = ValidateOneArgument(method, ExpressionType.Call, arg2, pis[2]); arg3 = ValidateOneArgument(method, ExpressionType.Call, arg3, pis[3]); arg4 = ValidateOneArgument(method, ExpressionType.Call, arg4, pis[4]); return new MethodCallExpression5(method, arg0, arg1, arg2, arg3, arg4); } /// /// Creates a that represents a call to a static (Shared in Visual Basic) method. /// /// The that represents the target method. /// The array of one or more of that represents the call arguments. ///A that has the property equal to and the and properties set to the specified values. public static MethodCallExpression Call(MethodInfo method, params Expression[] arguments) { return Call(null, method, arguments); } /// /// Creates a that represents a call to a static (Shared in Visual Basic) method. /// /// The that represents the target method. /// A collection of that represents the call arguments. ///A that has the property equal to and the and properties set to the specified values. public static MethodCallExpression Call(MethodInfo method, IEnumerable arguments) { return Call(null, method, arguments); } /// /// Creates a that represents a call to a method that takes no arguments. /// /// An that specifies the instance for an instance call. (pass null for a static (Shared in Visual Basic) method). /// The that represents the target method. ///A that has the property equal to and the and properties set to the specified values. public static MethodCallExpression Call(Expression instance, MethodInfo method) { return Call(instance, method, EmptyReadOnlyCollection.Instance); } /// /// Creates a that represents a method call. /// /// An that specifies the instance for an instance call. (pass null for a static (Shared in Visual Basic) method). /// The that represents the target method. /// An array of one or more of that represents the call arguments. ///A that has the property equal to and the and properties set to the specified values. public static MethodCallExpression Call(Expression instance, MethodInfo method, params Expression[] arguments) { return Call(instance, method, (IEnumerable)arguments); } /// /// Creates a that represents a call to a method that takes two arguments. /// /// An that specifies the instance for an instance call. (pass null for a static (Shared in Visual Basic) method). /// The that represents the target method. /// The that represents the first argument. /// The that represents the second argument. ///A that has the property equal to and the and properties set to the specified values. public static MethodCallExpression Call(Expression instance, MethodInfo method, Expression arg0, Expression arg1) { ContractUtils.RequiresNotNull(method, "method"); ContractUtils.RequiresNotNull(arg0, "arg0"); ContractUtils.RequiresNotNull(arg1, "arg1"); ParameterInfo[] pis = ValidateMethodAndGetParameters(instance, method); ValidateArgumentCount(method, ExpressionType.Call, 2, pis); arg0 = ValidateOneArgument(method, ExpressionType.Call, arg0, pis[0]); arg1 = ValidateOneArgument(method, ExpressionType.Call, arg1, pis[1]); if (instance != null) { return new InstanceMethodCallExpression2(method, instance, arg0, arg1); } return new MethodCallExpression2(method, arg0, arg1); } /// /// Creates a that represents a call to a method that takes three arguments. /// /// An that specifies the instance for an instance call. (pass null for a static (Shared in Visual Basic) method). /// The that represents the target method. /// The that represents the first argument. /// The that represents the second argument. /// The that represents the third argument. ///A that has the property equal to and the and properties set to the specified values. public static MethodCallExpression Call(Expression instance, MethodInfo method, Expression arg0, Expression arg1, Expression arg2) { ContractUtils.RequiresNotNull(method, "method"); ContractUtils.RequiresNotNull(arg0, "arg0"); ContractUtils.RequiresNotNull(arg1, "arg1"); ContractUtils.RequiresNotNull(arg2, "arg2"); ParameterInfo[] pis = ValidateMethodAndGetParameters(instance, method); ValidateArgumentCount(method, ExpressionType.Call, 3, pis); arg0 = ValidateOneArgument(method, ExpressionType.Call, arg0, pis[0]); arg1 = ValidateOneArgument(method, ExpressionType.Call, arg1, pis[1]); arg2 = ValidateOneArgument(method, ExpressionType.Call, arg2, pis[2]); if (instance != null) { return new InstanceMethodCallExpression3(method, instance, arg0, arg1, arg2); } return new MethodCallExpression3(method, arg0, arg1, arg2); } ///Creates a that represents a call to an instance method by calling the appropriate factory method. ///A that has the property equal to , the property equal to , set to the that represents the specified instance method, and set to the specified arguments. ///An whose property value will be searched for a specific method. ///The name of the method. /// ///An array of objects that specify the type parameters of the generic method. ///This argument should be null when specifies a non-generic method. /// ///An array of objects that represents the arguments to the method. /// /// or is null. ///No method whose name is , whose type parameters match , and whose parameter types match is found in .Type or its base types.-or-More than one method whose name is , whose type parameters match , and whose parameter types match is found in .Type or its base types. public static MethodCallExpression Call(Expression instance, string methodName, Type[] typeArguments, params Expression[] arguments) { ContractUtils.RequiresNotNull(instance, "instance"); ContractUtils.RequiresNotNull(methodName, "methodName"); if (arguments == null) { arguments = new Expression[0]; } BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy; return Expression.Call(instance, FindMethod(instance.Type, methodName, typeArguments, arguments, flags), arguments); } ///Creates a that represents a call to a static (Shared in Visual Basic) method by calling the appropriate factory method. ///A that has the property equal to , the property set to the that represents the specified static (Shared in Visual Basic) method, and the property set to the specified arguments. ///The that specifies the type that contains the specified static (Shared in Visual Basic) method. ///The name of the method. /// ///An array of objects that specify the type parameters of the generic method. ///This argument should be null when specifies a non-generic method. /// ///An array of objects that represent the arguments to the method. /// /// or is null. ///No method whose name is , whose type parameters match , and whose parameter types match is found in or its base types.-or-More than one method whose name is , whose type parameters match , and whose parameter types match is found in or its base types. public static MethodCallExpression Call(Type type, string methodName, Type[] typeArguments, params Expression[] arguments) { ContractUtils.RequiresNotNull(type, "type"); ContractUtils.RequiresNotNull(methodName, "methodName"); if (arguments == null) arguments = new Expression[] { }; BindingFlags flags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy; return Expression.Call(null, FindMethod(type, methodName, typeArguments, arguments, flags), arguments); } ///Creates a that represents a method call. ///A that has the property equal to and the , , and properties set to the specified values. ///An to set the property equal to (pass null for a static (Shared in Visual Basic) method). ///A to set the property equal to. ///An that contains objects to use to populate the collection. /// /// is null.-or- is null and represents an instance method. /// ///.Type is not assignable to the declaring type of the method represented by .-or-The number of elements in does not equal the number of parameters for the method represented by .-or-One or more of the elements of is not assignable to the corresponding parameter for the method represented by . public static MethodCallExpression Call(Expression instance, MethodInfo method, IEnumerable arguments) { ContractUtils.RequiresNotNull(method, "method"); ReadOnlyCollection argList = arguments.ToReadOnly(); ValidateMethodInfo(method); ValidateStaticOrInstanceMethod(instance, method); ValidateArgumentTypes(method, ExpressionType.Call, ref argList); if (instance == null) { return new MethodCallExpressionN(method, argList); } else { return new InstanceMethodCallExpressionN(method, instance, argList); } } private static ParameterInfo[] ValidateMethodAndGetParameters(Expression instance, MethodInfo method) { ValidateMethodInfo(method); ValidateStaticOrInstanceMethod(instance, method); return GetParametersForValidation(method, ExpressionType.Call); } private static void ValidateStaticOrInstanceMethod(Expression instance, MethodInfo method) { if (method.IsStatic) { if (instance != null) throw new ArgumentException(Strings.OnlyStaticMethodsHaveNullInstance, "instance"); } else { if (instance == null) throw new ArgumentException(Strings.OnlyStaticMethodsHaveNullInstance, "method"); RequiresCanRead(instance, "instance"); ValidateCallInstanceType(instance.Type, method); } } private static void ValidateCallInstanceType(Type instanceType, MethodInfo method) { if (!TypeUtils.IsValidInstanceType(method, instanceType)) { throw Error.InstanceAndMethodTypeMismatch(method, method.DeclaringType, instanceType); } } private static void ValidateArgumentTypes(MethodBase method, ExpressionType nodeKind, ref ReadOnlyCollection arguments) { Debug.Assert(nodeKind == ExpressionType.Invoke || nodeKind == ExpressionType.Call || nodeKind == ExpressionType.Dynamic || nodeKind == ExpressionType.New); ParameterInfo[] pis = GetParametersForValidation(method, nodeKind); ValidateArgumentCount(method, nodeKind, arguments.Count, pis); Expression[] newArgs = null; for (int i = 0, n = pis.Length; i < n; i++) { Expression arg = arguments[i]; ParameterInfo pi = pis[i]; arg = ValidateOneArgument(method, nodeKind, arg, pi); if (newArgs == null && arg != arguments[i]) { newArgs = new Expression[arguments.Count]; for (int j = 0; j < i; j++) { newArgs[j] = arguments[j]; } } if (newArgs != null) { newArgs[i] = arg; } } if (newArgs != null) { arguments = new TrueReadOnlyCollection(newArgs); } } private static ParameterInfo[] GetParametersForValidation(MethodBase method, ExpressionType nodeKind) { ParameterInfo[] pis = method.GetParametersCached(); if (nodeKind == ExpressionType.Dynamic) { pis = pis.RemoveFirst(); // ignore CallSite argument } return pis; } private static void ValidateArgumentCount(MethodBase method, ExpressionType nodeKind, int count, ParameterInfo[] pis) { if (pis.Length != count) { // Throw the right error for the node we were given switch (nodeKind) { case ExpressionType.New: throw Error.IncorrectNumberOfConstructorArguments(); case ExpressionType.Invoke: throw Error.IncorrectNumberOfLambdaArguments(); case ExpressionType.Dynamic: case ExpressionType.Call: throw Error.IncorrectNumberOfMethodCallArguments(method); default: throw ContractUtils.Unreachable; } } } private static Expression ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arg, ParameterInfo pi) { RequiresCanRead(arg, "arguments"); Type pType = pi.ParameterType; if (pType.IsByRef) { pType = pType.GetElementType(); } TypeUtils.ValidateType(pType); if (!TypeUtils.AreReferenceAssignable(pType, arg.Type)) { if (!TryQuote(pType, ref arg)) { // Throw the right error for the node we were given switch (nodeKind) { case ExpressionType.New: throw Error.ExpressionTypeDoesNotMatchConstructorParameter(arg.Type, pType); case ExpressionType.Invoke: throw Error.ExpressionTypeDoesNotMatchParameter(arg.Type, pType); case ExpressionType.Dynamic: case ExpressionType.Call: throw Error.ExpressionTypeDoesNotMatchMethodParameter(arg.Type, pType, method); default: throw ContractUtils.Unreachable; } } } return arg; } // Attempts to auto-quote the expression tree. Returns true if it succeeded, false otherwise. private static bool TryQuote(Type parameterType, ref Expression argument) { // We used to allow quoting of any expression, but the behavior of // quote (produce a new tree closed over parameter values), only // works consistently for lambdas Type quoteable = typeof(LambdaExpression); if (TypeUtils.IsSameOrSubclass(quoteable, parameterType) && parameterType.IsAssignableFrom(argument.GetType())) { argument = Expression.Quote(argument); return true; } return false; } private static MethodInfo FindMethod(Type type, string methodName, Type[] typeArgs, Expression[] args, BindingFlags flags) { MemberInfo[] members = type.FindMembers(MemberTypes.Method, flags, Type.FilterNameIgnoreCase, methodName); if (members == null || members.Length == 0) throw Error.MethodDoesNotExistOnType(methodName, type); MethodInfo method; var methodInfos = members.Map(t => (MethodInfo)t); int count = FindBestMethod(methodInfos, typeArgs, args, out method); if (count == 0) { if (typeArgs != null && typeArgs.Length > 0) { throw Error.GenericMethodWithArgsDoesNotExistOnType(methodName, type); } else { throw Error.MethodWithArgsDoesNotExistOnType(methodName, type); } } if (count > 1) throw Error.MethodWithMoreThanOneMatch(methodName, type); return method; } private static int FindBestMethod(IEnumerable methods, Type[] typeArgs, Expression[] args, out MethodInfo method) { int count = 0; method = null; foreach (MethodInfo mi in methods) { MethodInfo moo = ApplyTypeArgs(mi, typeArgs); if (moo != null && IsCompatible(moo, args)) { // favor public over non-public methods if (method == null || (!method.IsPublic && moo.IsPublic)) { method = moo; count = 1; } // only count it as additional method if they both public or both non-public else if (method.IsPublic == moo.IsPublic) { count++; } } } return count; } private static bool IsCompatible(MethodBase m, Expression[] args) { ParameterInfo[] parms = m.GetParametersCached(); if (parms.Length != args.Length) return false; for (int i = 0; i < args.Length; i++) { Expression arg = args[i]; ContractUtils.RequiresNotNull(arg, "argument"); Type argType = arg.Type; Type pType = parms[i].ParameterType; if (pType.IsByRef) { pType = pType.GetElementType(); } if (!TypeUtils.AreReferenceAssignable(pType, argType) && !(TypeUtils.IsSameOrSubclass(typeof(LambdaExpression), pType) && pType.IsAssignableFrom(arg.GetType()))) { return false; } } return true; } private static MethodInfo ApplyTypeArgs(MethodInfo m, Type[] typeArgs) { if (typeArgs == null || typeArgs.Length == 0) { if (!m.IsGenericMethodDefinition) return m; } else { if (m.IsGenericMethodDefinition && m.GetGenericArguments().Length == typeArgs.Length) return m.MakeGenericMethod(typeArgs); } return null; } #endregion #region ArrayIndex ///Creates a that represents applying an array index operator to a multi-dimensional array. ///A that has the property equal to and the and properties set to the specified values. ///An array of instances - indexes for the array index operation. ///An array that contains objects to use to populate the collection. public static MethodCallExpression ArrayIndex(Expression array, params Expression[] indexes) { return ArrayIndex(array, (IEnumerable)indexes); } ///Creates a that represents applying an array index operator to an array of rank more than one. ///A that has the property equal to and the and properties set to the specified values. ///An to set the property equal to. ///An that contains objects to use to populate the collection. /// /// or is null. /// ///.Type does not represent an array type.-or-The rank of .Type does not match the number of elements in .-or-The property of one or more elements of does not represent the type. public static MethodCallExpression ArrayIndex(Expression array, IEnumerable indexes) { RequiresCanRead(array, "array"); ContractUtils.RequiresNotNull(indexes, "indexes"); Type arrayType = array.Type; if (!arrayType.IsArray) { throw Error.ArgumentMustBeArray(); } ReadOnlyCollection indexList = indexes.ToReadOnly(); if (arrayType.GetArrayRank() != indexList.Count) { throw Error.IncorrectNumberOfIndexes(); } foreach (Expression e in indexList) { RequiresCanRead(e, "indexes"); if (e.Type != typeof(int)) { throw Error.ArgumentMustBeArrayIndexType(); } } MethodInfo mi = array.Type.GetMethod("Get", BindingFlags.Public | BindingFlags.Instance); return Call(array, mi, indexList); } #endregion } }