/* ****************************************************************************
*
* 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
}
}