#if NET20 || NET30 || !NET_4_6 // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Dynamic.Utils; using System.Reflection; using LinqInternal.Collections; using LinqInternal.Collections.ThreadSafe; using LinqInternal.Core; namespace System.Linq.Expressions.Reimplement { #if NET20 || NET30 || !NET_4_6 /// /// Represents a call to either static or an instance method. /// [DebuggerTypeProxy(typeof(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 Call(@object, Method, arguments); } internal virtual ReadOnlyCollection GetOrMakeArguments() { throw ContractUtils.Unreachable; } protected internal override Expression Accept(ExpressionVisitor visitor) { return visitor.VisitMethodCall(this); } internal virtual MethodCallExpression Rewrite(Expression instance, IList args) { throw ContractUtils.Unreachable; } #region IArgumentProvider Members public virtual Expression GetArgument(int index) { throw ContractUtils.Unreachable; } public virtual int ArgumentCount { get { throw ContractUtils.Unreachable; } } #endregion IArgumentProvider Members } #endif #region Specialized Subclasses internal class MethodCallExpressionN : MethodCallExpression, IArgumentProvider { private IList _arguments; public MethodCallExpressionN(MethodInfo method, IList args) : base(method) { _arguments = args; } public override Expression GetArgument(int index) { return _arguments[index]; } public override int 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 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; } public override Expression GetArgument(int index) { return _arguments[index]; } public override int 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 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; } public override Expression GetArgument(int index) { switch (index) { case 0: return ReturnObject(_arg0); default: throw new InvalidOperationException(); } } public override int 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 Call(Method, args[0]); } return 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; } public override Expression GetArgument(int index) { switch (index) { case 0: return ReturnObject(_arg0); case 1: return _arg1; default: throw new InvalidOperationException(); } } public override int 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 Call(Method, args[0], args[1]); } return 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; } public override Expression GetArgument(int index) { switch (index) { case 0: return ReturnObject(_arg0); case 1: return _arg1; case 2: return _arg2; default: throw new InvalidOperationException(); } } public override int 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 Call(Method, args[0], args[1], args[2]); } return 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; } public override Expression 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(); } } public override int 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 Call(Method, args[0], args[1], args[2], args[3]); } return 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; } public override Expression 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(); } } public override int 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 Call(Method, args[0], args[1], args[2], args[3], args[4]); } return 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; } public override Expression GetArgument(int index) { switch (index) { case 0: return ReturnObject(_arg0); case 1: return _arg1; default: throw new InvalidOperationException(); } } public override int 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 Call(instance, Method, args[0], args[1]); } return 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; } public override Expression GetArgument(int index) { switch (index) { case 0: return ReturnObject(_arg0); case 1: return _arg1; case 2: return _arg2; default: throw new InvalidOperationException(); } } public override int 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 Call(instance, Method, args[0], args[1], args[2]); } return Call(instance, Method, ReturnObject(_arg0), _arg1, _arg2); } } #endregion Specialized Subclasses 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"); var 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"); var 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"); var 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"); var 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"); var 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, EmptyCollection.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"); var 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"); var 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 = ArrayReservoir.EmptyArray; } const System.Reflection.BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy; return InternalCall(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 = ArrayReservoir.EmptyArray; } const System.Reflection.BindingFlags flags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy; return InternalCall(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"); var 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); } } internal static MethodCallExpression InternalCall(Expression instance, MethodInfo method, IEnumerable arguments) { ContractUtils.RequiresNotNull(method, "method"); var argList = arguments.ToReadOnly(); ValidateMethodInfo(method); ValidateStaticOrInstanceMethodAlternative(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 ArgumentNullException(Strings.OnlyStaticMethodsHaveNullInstance, "method"); } RequiresCanRead(instance, "instance"); ValidateCallInstanceType(instance.Type, method); } } private static void ValidateStaticOrInstanceMethodAlternative(Expression instance, MethodInfo method) { if (method.IsStatic) { if (instance != null) { throw new InvalidOperationException(Strings.OnlyStaticMethodsHaveNullInstance); } } else { if (instance == null) { throw new InvalidOperationException(Strings.OnlyStaticMethodsHaveNullInstance); } RequiresCanRead(instance, "instance"); ValidateCallInstanceType(instance.Type, method); } } private static void ValidateCallInstanceType(Type instanceType, MethodInfo method) { if (!TypeHelper.IsValidInstanceType(method, instanceType)) { throw Error.InstanceAndMethodTypeMismatch(method, method.DeclaringType, instanceType); } } public static void ValidateArgumentTypes(MethodBase method, ExpressionType nodeKind, ref ReadOnlyCollection arguments) { ExpressionUtils.ValidateArgumentTypes(method, nodeKind, ref arguments); } private static ParameterInfo[] GetParametersForValidation(MethodBase method, ExpressionType nodeKind) { return ExpressionUtils.GetParametersForValidation(method, nodeKind); } public static void ValidateArgumentCount(MethodBase method, ExpressionType nodeKind, int count, ParameterInfo[] pis) { ExpressionUtils.ValidateArgumentCount(method, nodeKind, count, pis); } public static Expression ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arg, ParameterInfo pi) { return ExpressionUtils.ValidateOneArgument(method, nodeKind, arg, pi); } // Attempts to auto-quote the expression tree. Returns true if it succeeded, false otherwise. private static bool TryQuote(Type parameterType, ref Expression argument) { return ExpressionUtils.TryQuote(parameterType, ref argument); } private static MethodInfo FindMethod(Type type, string methodName, Type[] typeArgs, Expression[] args, BindingFlags flags) { MemberInfo[] members = type.GetMethodsIgnoreCase(flags, methodName); if (members == null || members.Length == 0) { throw Error.MethodDoesNotExistOnType(methodName, type); } MethodInfo method; var methodInfos = members.Map(t => (MethodInfo)t); var 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) { var count = 0; method = null; foreach (var mi in methods) { var 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) { var parms = m.GetParameters(); if (parms.Length != args.Length) { return false; } for (var i = 0; i < args.Length; i++) { var arg = args[i]; ContractUtils.RequiresNotNull(arg, "argument"); var argType = arg.Type; var pType = parms[i].ParameterType; if (pType.IsByRef) { pType = pType.GetElementType(); } if (!TypeHelper.AreReferenceAssignable(pType, argType) && !(pType.IsSameOrSubclassOf(typeof(LambdaExpression)) && pType.IsAssignableFrom(arg.GetType()))) { if (pType.IsExpression()) { argType = arg.GetType(); if (!TypeHelper.AreReferenceAssignable(pType, argType) && !(pType.IsSameOrSubclassOf(typeof(LambdaExpression)) && pType.IsAssignableFrom(arg.GetType()))) { return false; } } else { 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 Call #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"); var arrayType = array.Type; if (!arrayType.IsArray) { throw Error.ArgumentMustBeArray(); } var indexList = indexes.ToReadOnly(); if (arrayType.GetArrayRank() != indexList.Count) { throw Error.IncorrectNumberOfIndexes(); } foreach (var e in indexList) { RequiresCanRead(e, "indexes"); if (e.Type != typeof(int)) { throw Error.ArgumentMustBeArrayIndexType(); } } var mi = array.Type.GetMethod("Get", BindingFlags.Public | BindingFlags.Instance); return Call(array, mi, indexList); } #endregion ArrayIndex } } #endif