1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Collections.Generic;
6 using System.Reflection;
7 using Xunit;
8 
9 namespace System.Linq.Expressions.Tests
10 {
11     public static class CallTests
12     {
13         private struct Mutable
14         {
15             private int x;
16 
17             public int X
18             {
19                 get { return x; }
20                 set { x = value; }
21             }
22 
23             public int this[int i]
24             {
25                 get { return x; }
26                 set { x = value; }
27             }
28 
FooSystem.Linq.Expressions.Tests.CallTests.Mutable29             public int Foo()
30             {
31                 return x++;
32             }
33         }
34 
35         private class Wrapper<T>
36         {
37             public const int Zero = 0;
38             public T Field;
39 #pragma warning disable 649 // For testing purposes
40             public readonly T ReadOnlyField;
41 #pragma warning restore
42             public T Property
43             {
44                 get { return Field; }
45                 set { Field = value; }
46             }
47         }
48 
49         private static class Methods
50         {
ByRef(ref int x)51             public static void ByRef(ref int x) { ++x; }
52         }
53 
54         [Theory]
55         [ClassData(typeof(CompilationTypes))]
UnboxReturnsReference(bool useInterpreter)56         public static void UnboxReturnsReference(bool useInterpreter)
57         {
58             ParameterExpression p = Expression.Parameter(typeof(object));
59             UnaryExpression unbox = Expression.Unbox(p, typeof(Mutable));
60             MethodCallExpression call = Expression.Call(unbox, typeof(Mutable).GetMethod("Foo"));
61             Func<object, int> lambda = Expression.Lambda<Func<object, int>>(call, p).Compile(useInterpreter);
62 
63             object boxed = new Mutable();
64             Assert.Equal(0, lambda(boxed));
65             Assert.Equal(1, lambda(boxed));
66             Assert.Equal(2, lambda(boxed));
67             Assert.Equal(3, lambda(boxed));
68         }
69 
70         [Theory]
71         [ClassData(typeof(CompilationTypes))]
ArrayWriteBack(bool useInterpreter)72         public static void ArrayWriteBack(bool useInterpreter)
73         {
74             ParameterExpression p = Expression.Parameter(typeof(Mutable[]));
75             BinaryExpression indexed = Expression.ArrayIndex(p, Expression.Constant(0));
76             MethodCallExpression call = Expression.Call(indexed, typeof(Mutable).GetMethod("Foo"));
77             Func<Mutable[], int> lambda = Expression.Lambda<Func<Mutable[], int>>(call, p).Compile(useInterpreter);
78 
79             var array = new Mutable[1];
80             Assert.Equal(0, lambda(array));
81             Assert.Equal(1, lambda(array));
82             Assert.Equal(2, lambda(array));
83         }
84 
85         [Theory]
86         [ClassData(typeof(CompilationTypes))]
MultiRankArrayWriteBack(bool useInterpreter)87         public static void MultiRankArrayWriteBack(bool useInterpreter)
88         {
89             ParameterExpression p = Expression.Parameter(typeof(Mutable[,]));
90             MethodCallExpression indexed = Expression.ArrayIndex(p, Expression.Constant(0), Expression.Constant(0));
91             MethodCallExpression call = Expression.Call(indexed, typeof(Mutable).GetMethod("Foo"));
92             Func<Mutable[,], int> lambda = Expression.Lambda<Func<Mutable[,], int>>(call, p).Compile(useInterpreter);
93 
94             var array = new Mutable[1, 1];
95             Assert.Equal(0, lambda(array));
96             Assert.Equal(1, lambda(array));
97             Assert.Equal(2, lambda(array));
98         }
99 
100         [Theory]
101         [ClassData(typeof(CompilationTypes))]
ArrayAccessWriteBack(bool useInterpreter)102         public static void ArrayAccessWriteBack(bool useInterpreter)
103         {
104             ParameterExpression p = Expression.Parameter(typeof(Mutable[]));
105             IndexExpression indexed = Expression.ArrayAccess(p, Expression.Constant(0));
106             MethodCallExpression call = Expression.Call(indexed, typeof(Mutable).GetMethod("Foo"));
107             Func<Mutable[], int> lambda = Expression.Lambda<Func<Mutable[], int>>(call, p).Compile(useInterpreter);
108 
109             var array = new Mutable[1];
110             Assert.Equal(0, lambda(array));
111             Assert.Equal(1, lambda(array));
112             Assert.Equal(2, lambda(array));
113         }
114 
115         [Theory]
116         [ClassData(typeof(CompilationTypes))]
MultiRankArrayAccessWriteBack(bool useInterpreter)117         public static void MultiRankArrayAccessWriteBack(bool useInterpreter)
118         {
119             ParameterExpression p = Expression.Parameter(typeof(Mutable[,]));
120             IndexExpression indexed = Expression.ArrayAccess(p, Expression.Constant(0), Expression.Constant(0));
121             MethodCallExpression call = Expression.Call(indexed, typeof(Mutable).GetMethod("Foo"));
122             Func<Mutable[,], int> lambda = Expression.Lambda<Func<Mutable[,], int>>(call, p).Compile(useInterpreter);
123 
124             var array = new Mutable[1, 1];
125             Assert.Equal(0, lambda(array));
126             Assert.Equal(1, lambda(array));
127             Assert.Equal(2, lambda(array));
128         }
129 
130         [Theory]
131         [ClassData(typeof(CompilationTypes))]
IndexedPropertyAccessNoWriteBack(bool useInterpreter)132         public static void IndexedPropertyAccessNoWriteBack(bool useInterpreter)
133         {
134             ParameterExpression p = Expression.Parameter(typeof(List<Mutable>));
135             IndexExpression indexed = Expression.Property(p, typeof(List<Mutable>).GetProperty("Item"), Expression.Constant(0));
136             MethodCallExpression call = Expression.Call(indexed, typeof(Mutable).GetMethod("Foo"));
137             Func<List<Mutable>, int> lambda = Expression.Lambda<Func<List<Mutable>, int>>(call, p).Compile(useInterpreter);
138 
139             var list = new List<Mutable> { new Mutable() };
140             Assert.Equal(0, lambda(list));
141             Assert.Equal(0, lambda(list));
142         }
143 
144         [Theory]
145         [ClassData(typeof(CompilationTypes))]
FieldAccessWriteBack(bool useInterpreter)146         public static void FieldAccessWriteBack(bool useInterpreter)
147         {
148             ParameterExpression p = Expression.Parameter(typeof(Wrapper<Mutable>));
149             MemberExpression member = Expression.Field(p, typeof(Wrapper<Mutable>).GetField("Field"));
150             MethodCallExpression call = Expression.Call(member, typeof(Mutable).GetMethod("Foo"));
151             Func<Wrapper<Mutable>, int> lambda = Expression.Lambda<Func<Wrapper<Mutable>, int>>(call, p).Compile(useInterpreter);
152 
153             var wrapper = new Wrapper<Mutable>();
154             Assert.Equal(0, lambda(wrapper));
155             Assert.Equal(1, lambda(wrapper));
156             Assert.Equal(2, lambda(wrapper));
157         }
158 
159         [Theory]
160         [ClassData(typeof(CompilationTypes))]
PropertyAccessNoWriteBack(bool useInterpreter)161         public static void PropertyAccessNoWriteBack(bool useInterpreter)
162         {
163             ParameterExpression p = Expression.Parameter(typeof(Wrapper<Mutable>));
164             MemberExpression member = Expression.Property(p, typeof(Wrapper<Mutable>).GetProperty("Property"));
165             MethodCallExpression call = Expression.Call(member, typeof(Mutable).GetMethod("Foo"));
166             Func<Wrapper<Mutable>, int> lambda = Expression.Lambda<Func<Wrapper<Mutable>, int>>(call, p).Compile(useInterpreter);
167 
168             var wrapper = new Wrapper<Mutable>();
169             Assert.Equal(0, lambda(wrapper));
170             Assert.Equal(0, lambda(wrapper));
171         }
172 
173         [Theory]
174         [ClassData(typeof(CompilationTypes))]
ReadonlyFieldAccessWriteBack(bool useInterpreter)175         public static void ReadonlyFieldAccessWriteBack(bool useInterpreter)
176         {
177             ParameterExpression p = Expression.Parameter(typeof(Wrapper<Mutable>));
178             MemberExpression member = Expression.Field(p, typeof(Wrapper<Mutable>).GetField("ReadOnlyField"));
179             MethodCallExpression call = Expression.Call(member, typeof(Mutable).GetMethod("Foo"));
180             Func<Wrapper<Mutable>, int> lambda = Expression.Lambda<Func<Wrapper<Mutable>, int>>(call, p).Compile(useInterpreter);
181 
182             var wrapper = new Wrapper<Mutable>();
183             Assert.Equal(0, lambda(wrapper));
184             Assert.Equal(0, lambda(wrapper));
185             Assert.Equal(0, lambda(wrapper));
186         }
187 
188         [Theory]
189         [ClassData(typeof(CompilationTypes))]
ConstFieldAccessWriteBack(bool useInterpreter)190         public static void ConstFieldAccessWriteBack(bool useInterpreter)
191         {
192             MemberExpression member = Expression.Field(null, typeof(Wrapper<Mutable>).GetField("Zero"));
193             MethodCallExpression call = Expression.Call(member, typeof(int).GetMethod("GetType"));
194             Func<Type> lambda = Expression.Lambda<Func<Type>>(call).Compile(useInterpreter);
195 
196             var wrapper = new Wrapper<Mutable>();
197             Assert.Equal(typeof(int), lambda());
198         }
199 
200         [Theory]
201         [ClassData(typeof(CompilationTypes))]
CallByRefMutableStructPropertyWriteBack(bool useInterpreter)202         public static void CallByRefMutableStructPropertyWriteBack(bool useInterpreter)
203         {
204             ParameterExpression p = Expression.Parameter(typeof(Mutable));
205             MemberExpression x = Expression.Property(p, "X");
206             MethodCallExpression call = Expression.Call(typeof(Methods).GetMethod("ByRef"), x);
207             BlockExpression body = Expression.Block(call, x);
208             Func<Mutable, int> lambda = Expression.Lambda<Func<Mutable, int>>(body, p).Compile(useInterpreter);
209 
210             var m = new Mutable() { X = 41 };
211             Assert.Equal(42, lambda(m));
212         }
213 
214         [Theory]
215         [ClassData(typeof(CompilationTypes))]
CallByRefMutableStructIndexWriteBack(bool useInterpreter)216         public static void CallByRefMutableStructIndexWriteBack(bool useInterpreter)
217         {
218             // Should not produce tail-call, but should still succeed
219             ParameterExpression p = Expression.Parameter(typeof(Mutable));
220             IndexExpression x = Expression.MakeIndex(p, typeof(Mutable).GetProperty("Item"), new[] { Expression.Constant(0) });
221             MethodCallExpression call = Expression.Call(typeof(Methods).GetMethod("ByRef"), x);
222             Action<Mutable> act = Expression.Lambda<Action<Mutable>>(call, true, p).Compile(useInterpreter);
223 
224             Mutable m = new Mutable { X = 41 };
225             act(m);
226         }
227 
228         [Theory]
229         [ClassData(typeof(CompilationTypes))]
CallByRefAttemptTailCall(bool useInterpreter)230         public static void CallByRefAttemptTailCall(bool useInterpreter)
231         {
232             ParameterExpression p = Expression.Parameter(typeof(Mutable));
233             IndexExpression x = Expression.MakeIndex(p, typeof(Mutable).GetProperty("Item"), new[] { Expression.Constant(0) });
234             MethodCallExpression call = Expression.Call(typeof(Methods).GetMethod("ByRef"), x);
235             BlockExpression body = Expression.Block(call, x);
236             Func<Mutable, int> lambda = Expression.Lambda<Func<Mutable, int>>(body, p).Compile(useInterpreter);
237 
238             var m = new Mutable() { X = 41 };
239             Assert.Equal(42, lambda(m));
240         }
241 
242         [Theory]
243         [ClassData(typeof(CompilationTypes))]
Call_InstanceNullInside_ThrowsNullReferenceExceptionOnInvocation(bool useInterpreter)244         public static void Call_InstanceNullInside_ThrowsNullReferenceExceptionOnInvocation(bool useInterpreter)
245         {
246             Expression call = Expression.Call(Expression.Constant(null, typeof(NonGenericClass)), typeof(NonGenericClass).GetMethod(nameof(NonGenericClass.InstanceMethod)));
247             Action compiledDelegate = Expression.Lambda<Action>(call).Compile(useInterpreter);
248             TargetInvocationException exception = Assert.Throws<TargetInvocationException>(() => compiledDelegate.DynamicInvoke());
249             Assert.IsType<NullReferenceException>(exception.InnerException);
250         }
251 
Call_NoParameters_TestData()252         public static IEnumerable<object[]> Call_NoParameters_TestData()
253         {
254             // Basic
255             yield return new object[] { Expression.Constant(new ClassWithInterface1()), typeof(ClassWithInterface1).GetMethod(nameof(ClassWithInterface1.Method)), 2 };
256             yield return new object[] { Expression.Constant(new StructWithInterface1()), typeof(StructWithInterface1).GetMethod(nameof(StructWithInterface1.Method)), 2 };
257 
258             // Object method
259             yield return new object[] { Expression.Constant(new ClassWithInterface1()), typeof(object).GetMethod(nameof(object.GetType)), typeof(ClassWithInterface1) };
260             yield return new object[] { Expression.Constant(new StructWithInterface1()), typeof(object).GetMethod(nameof(object.GetType)), typeof(StructWithInterface1) };
261             yield return new object[] { Expression.Constant(Int32Enum.A), typeof(object).GetMethod(nameof(object.GetType)), typeof(Int32Enum) };
262 
263             // ValueType method from struct
264             yield return new object[] { Expression.Constant(new StructWithInterface1()), typeof(ValueType).GetMethod(nameof(ValueType.ToString)), new StructWithInterface1().ToString() };
265 
266             // Enum method from enum
267             yield return new object[] { Expression.Constant(Int32Enum.A), typeof(Enum).GetMethod(nameof(Enum.ToString), new Type[0]), "A" };
268 
269             // Interface method
270             yield return new object[] { Expression.Constant(new ClassWithInterface1()), typeof(Interface1).GetMethod(nameof(Interface1.InterfaceMethod)), 1 };
271             yield return new object[] { Expression.Constant(new StructWithInterface1()), typeof(Interface1).GetMethod(nameof(Interface1.InterfaceMethod)), 1 };
272             yield return new object[] { Expression.Constant(new ClassWithCompoundInterface()), typeof(Interface1).GetMethod(nameof(Interface1.InterfaceMethod)), 1 };
273             yield return new object[] { Expression.Constant(new StructWithCompoundInterface()), typeof(Interface1).GetMethod(nameof(Interface1.InterfaceMethod)), 1 };
274 
275             // Interface method, interface type
276             yield return new object[] { Expression.Constant(new ClassWithInterface1(), typeof(Interface1)), typeof(Interface1).GetMethod(nameof(Interface1.InterfaceMethod)), 1 };
277             yield return new object[] { Expression.Constant(new StructWithInterface1(), typeof(Interface1)), typeof(Interface1).GetMethod(nameof(Interface1.InterfaceMethod)), 1 };
278         }
279 
280         [Theory]
281         [PerCompilationType(nameof(Call_NoParameters_TestData))]
Call_NoParameters(Expression instance, MethodInfo method, object expected, bool useInterpreter)282         public static void Call_NoParameters(Expression instance, MethodInfo method, object expected, bool useInterpreter)
283         {
284             Expression call = Expression.Call(instance, method);
285             Delegate compiledDelegate = Expression.Lambda(call).Compile(useInterpreter);
286             Assert.Equal(expected, compiledDelegate.DynamicInvoke());
287         }
288 
289         private static Expression s_valid => Expression.Constant(5);
290 
291         private static MethodInfo s_method0 = typeof(NonGenericClass).GetMethod(nameof(NonGenericClass.Method0));
292         private static MethodInfo s_method1 = typeof(NonGenericClass).GetMethod(nameof(NonGenericClass.Method1));
293         private static MethodInfo s_method2 = typeof(NonGenericClass).GetMethod(nameof(NonGenericClass.Method2));
294         private static MethodInfo s_method3 = typeof(NonGenericClass).GetMethod(nameof(NonGenericClass.Method3));
295         private static MethodInfo s_method4 = typeof(NonGenericClass).GetMethod(nameof(NonGenericClass.Method4));
296         private static MethodInfo s_method5 = typeof(NonGenericClass).GetMethod(nameof(NonGenericClass.Method5));
297         private static MethodInfo s_method6 = typeof(NonGenericClass).GetMethod(nameof(NonGenericClass.Method6));
298         private static MethodInfo s_method7 = typeof(NonGenericClass).GetMethod(nameof(NonGenericClass.Method7));
299 
Method_Invalid_TestData()300         public static IEnumerable<object[]> Method_Invalid_TestData()
301         {
302             yield return new object[] { null, typeof(ArgumentNullException) };
303             yield return new object[] { typeof(GenericClass<>).GetMethod(nameof(GenericClass<string>.NonGenericMethod)), typeof(ArgumentException) };
304             yield return new object[] { typeof(NonGenericClass).GetMethod(nameof(NonGenericClass.GenericMethod)), typeof(ArgumentException) };
305         }
306 
307         [Theory]
308         [MemberData(nameof(Method_Invalid_TestData))]
Method_Invalid_ThrowsArgumentException(MethodInfo method, Type exceptionType)309         public static void Method_Invalid_ThrowsArgumentException(MethodInfo method, Type exceptionType)
310         {
311             AssertArgumentException(() => Expression.Call(null, method), exceptionType, "method");
312             AssertArgumentException(() => Expression.Call(null, method, s_valid, s_valid), exceptionType, "method");
313             AssertArgumentException(() => Expression.Call(null, method, s_valid, s_valid, s_valid), exceptionType, "method");
314             AssertArgumentException(() => Expression.Call(null, method, new Expression[0]), exceptionType, "method");
315             AssertArgumentException(() => Expression.Call(null, method, (IEnumerable<Expression>)new Expression[0]), exceptionType, "method");
316 
317             AssertArgumentException(() => Expression.Call(method, s_valid), exceptionType, "method");
318             AssertArgumentException(() => Expression.Call(method, s_valid, s_valid), exceptionType, "method");
319             AssertArgumentException(() => Expression.Call(method, s_valid, s_valid, s_valid), exceptionType, "method");
320             AssertArgumentException(() => Expression.Call(method, s_valid, s_valid, s_valid, s_valid), exceptionType, "method");
321             AssertArgumentException(() => Expression.Call(method, s_valid, s_valid, s_valid, s_valid, s_valid), exceptionType, "method");
322             AssertArgumentException(() => Expression.Call(method, new Expression[0]), exceptionType, "method");
323             AssertArgumentException(() => Expression.Call(method, (IEnumerable<Expression>)new Expression[0]), exceptionType, "method");
324         }
325 
326         [Fact]
Method_Invalid_Via_Name()327         public static void Method_Invalid_Via_Name()
328         {
329             AssertExtensions.Throws<ArgumentException>("method", () => Expression.Call(typeof(GenericClass<>), nameof(GenericClass<string>.NonGenericMethod), Type.EmptyTypes));
330             AssertExtensions.Throws<ArgumentException>("method", () => Expression.Call(typeof(GenericClass<>).MakeGenericType(typeof(GenericClass<>)), nameof(GenericClass<string>.NonGenericMethod), Type.EmptyTypes));
331         }
332 
Method_DoesntBelongToInstance_TestData()333         public static IEnumerable<object[]> Method_DoesntBelongToInstance_TestData()
334         {
335             // Different declaring type
336             yield return new object[] { Expression.Constant(new ClassWithInterface1()), typeof(OtherClassWithInterface1).GetMethod(nameof(Interface1.InterfaceMethod)) };
337             yield return new object[] { Expression.Constant(new StructWithInterface1()), typeof(OtherStructWithInterface1).GetMethod(nameof(Interface1.InterfaceMethod)) };
338 
339             // Different interface
340             yield return new object[] { Expression.Constant(new ClassWithInterface1()), typeof(Interface2).GetMethod(nameof(Interface2.InterfaceMethod)) };
341             yield return new object[] { Expression.Constant(new StructWithInterface1()), typeof(Interface2).GetMethod(nameof(Interface2.InterfaceMethod)) };
342 
343             // Custom type
344             yield return new object[] { Expression.Constant(new ClassWithInterface1(), typeof(object)), typeof(ClassWithInterface1).GetMethod(nameof(ClassWithInterface1.Method)) };
345             yield return new object[] { Expression.Constant(new StructWithInterface1(), typeof(object)), typeof(ClassWithInterface1).GetMethod(nameof(ClassWithInterface1.Method)) };
346         }
347 
348         [Theory]
349         [MemberData(nameof(Method_DoesntBelongToInstance_TestData))]
Method_DoesntBelongToInstance_ThrowsArgumentException(Expression instance, MethodInfo method)350         public static void Method_DoesntBelongToInstance_ThrowsArgumentException(Expression instance, MethodInfo method)
351         {
352             AssertExtensions.Throws<ArgumentException>(null, () => Expression.Call(instance, method));
353         }
354 
355         [Fact]
InstanceMethod_NullInstance_ThrowsArgumentException()356         public static void InstanceMethod_NullInstance_ThrowsArgumentException()
357         {
358             MethodInfo method = typeof(NonGenericClass).GetMethod(nameof(NonGenericClass.InstanceMethod));
359             AssertExtensions.Throws<ArgumentException>(null, () => Expression.Call(method, s_valid));
360             AssertExtensions.Throws<ArgumentException>(null, () => Expression.Call(method, s_valid, s_valid));
361             AssertExtensions.Throws<ArgumentException>(null, () => Expression.Call(method, s_valid, s_valid, s_valid));
362             AssertExtensions.Throws<ArgumentException>(null, () => Expression.Call(method, s_valid, s_valid, s_valid, s_valid));
363             AssertExtensions.Throws<ArgumentException>(null, () => Expression.Call(method, s_valid, s_valid, s_valid, s_valid, s_valid));
364             AssertExtensions.Throws<ArgumentException>(null, () => Expression.Call(method, new Expression[0]));
365             AssertExtensions.Throws<ArgumentException>(null, () => Expression.Call(method, (IEnumerable<Expression>)new Expression[0]));
366 
367             AssertExtensions.Throws<ArgumentException>(null, () => Expression.Call(null, method, s_valid));
368             AssertExtensions.Throws<ArgumentException>(null, () => Expression.Call(null, method, s_valid, s_valid));
369             AssertExtensions.Throws<ArgumentException>(null, () => Expression.Call(null, method, s_valid, s_valid, s_valid));
370             AssertExtensions.Throws<ArgumentException>(null, () => Expression.Call(null, method, new Expression[0]));
371             AssertExtensions.Throws<ArgumentException>(null, () => Expression.Call(null, method, (IEnumerable<Expression>)new Expression[0]));
372         }
373 
374         [Fact]
StaticMethod_NonNullInstance_ThrowsArgumentException()375         public static void StaticMethod_NonNullInstance_ThrowsArgumentException()
376         {
377             Expression instance = Expression.Constant(new NonGenericClass());
378             MethodInfo method = typeof(NonGenericClass).GetMethod(nameof(NonGenericClass.StaticMethod));
379             AssertExtensions.Throws<ArgumentException>(null, () => Expression.Call(instance, method, s_valid));
380             AssertExtensions.Throws<ArgumentException>(null, () => Expression.Call(instance, method, s_valid, s_valid));
381             AssertExtensions.Throws<ArgumentException>(null, () => Expression.Call(instance, method, s_valid, s_valid, s_valid));
382             AssertExtensions.Throws<ArgumentException>(null, () => Expression.Call(instance, method, new Expression[0]));
383             AssertExtensions.Throws<ArgumentException>(null, () => Expression.Call(instance, method, (IEnumerable<Expression>)new Expression[0]));
384         }
385 
InvalidArg_TestData()386         public static IEnumerable<object[]> InvalidArg_TestData()
387         {
388             yield return new object[] { null, typeof(ArgumentNullException) };
389             yield return new object[] { Expression.Property(null, typeof(Unreadable<string>), nameof(Unreadable<string>.WriteOnly)), typeof(ArgumentException) };
390             yield return new object[] { Expression.Constant("abc"), typeof(ArgumentException) };
391         }
392 
393         [Theory]
394         [MemberData(nameof(InvalidArg_TestData))]
Arg0_Invalid(Expression arg, Type exceptionType)395         public static void Arg0_Invalid(Expression arg, Type exceptionType)
396         {
397             AssertArgumentException(() => Expression.Call(s_method1, arg), exceptionType, "arg0");
398             AssertArgumentException(() => Expression.Call(s_method2, arg, s_valid), exceptionType, "arg0");
399             AssertArgumentException(() => Expression.Call(s_method3, arg, s_valid, s_valid), exceptionType, "arg0");
400             AssertArgumentException(() => Expression.Call(s_method4, arg, s_valid, s_valid, s_valid), exceptionType, "arg0");
401             AssertArgumentException(() => Expression.Call(s_method5, arg, s_valid, s_valid, s_valid, s_valid), exceptionType, "arg0");
402 
403             AssertArgumentException(() => Expression.Call(null, s_method1, arg), exceptionType, "arg0");
404             AssertArgumentException(() => Expression.Call(null, s_method2, arg, s_valid), exceptionType, "arg0");
405             AssertArgumentException(() => Expression.Call(null, s_method3, arg, s_valid, s_valid), exceptionType, "arg0");
406         }
407 
408         [Theory]
409         [MemberData(nameof(InvalidArg_TestData))]
Arg1_Invalid(Expression arg, Type exceptionType)410         public static void Arg1_Invalid(Expression arg, Type exceptionType)
411         {
412             AssertArgumentException(() => Expression.Call(s_method2, s_valid, arg), exceptionType, "arg1");
413             AssertArgumentException(() => Expression.Call(s_method3, s_valid, arg, s_valid), exceptionType, "arg1");
414             AssertArgumentException(() => Expression.Call(s_method4, s_valid, arg, s_valid, s_valid), exceptionType, "arg1");
415             AssertArgumentException(() => Expression.Call(s_method5, s_valid, arg, s_valid, s_valid, s_valid), exceptionType, "arg1");
416 
417             AssertArgumentException(() => Expression.Call(null, s_method2, s_valid, arg), exceptionType, "arg1");
418             AssertArgumentException(() => Expression.Call(null, s_method3, s_valid, arg, s_valid), exceptionType, "arg1");
419         }
420 
421         [Theory]
422         [MemberData(nameof(InvalidArg_TestData))]
Arg2_Invalid(Expression arg, Type exceptionType)423         public static void Arg2_Invalid(Expression arg, Type exceptionType)
424         {
425             AssertArgumentException(() => Expression.Call(s_method3, s_valid, s_valid, arg), exceptionType, "arg2");
426             AssertArgumentException(() => Expression.Call(s_method4, s_valid, s_valid, arg, s_valid), exceptionType, "arg2");
427             AssertArgumentException(() => Expression.Call(s_method5, s_valid, s_valid, arg, s_valid, s_valid), exceptionType, "arg2");
428 
429             AssertArgumentException(() => Expression.Call(null, s_method3, s_valid, s_valid, arg), exceptionType, "arg2");
430         }
431 
432         [Theory]
433         [MemberData(nameof(InvalidArg_TestData))]
Arg3_Invalid(Expression arg, Type exceptionType)434         public static void Arg3_Invalid(Expression arg, Type exceptionType)
435         {
436             AssertArgumentException(() => Expression.Call(s_method4, s_valid, s_valid, s_valid, arg), exceptionType, "arg3");
437             AssertArgumentException(() => Expression.Call(s_method5, s_valid, s_valid, s_valid, arg, s_valid), exceptionType, "arg3");
438         }
439 
440         [Theory]
441         [MemberData(nameof(InvalidArg_TestData))]
Arg4_Invalid(Expression arg, Type exceptionType)442         public static void Arg4_Invalid(Expression arg, Type exceptionType)
443         {
444             AssertArgumentException(() => Expression.Call(s_method5, s_valid, s_valid, s_valid, s_valid, arg), exceptionType, "arg4");
445         }
446 
AssertArgumentException(Action action, Type exceptionType, string paramName)447         private static void AssertArgumentException(Action action, Type exceptionType, string paramName)
448         {
449             ArgumentException ex = (ArgumentException)Assert.Throws(exceptionType, action);
450             if (!PlatformDetection.IsNetNative) // The .NET Native toolchain optimizes away exception ParamNames
451             {
452                 Assert.Equal(paramName, ex.ParamName);
453             }
454         }
455 
456         [Theory]
457         [InlineData(typeof(NonGenericClass), nameof(NonGenericClass.Method0), 0)]
458         [InlineData(typeof(NonGenericClass), nameof(NonGenericClass.Method1), 1)]
459         [InlineData(typeof(NonGenericClass), nameof(NonGenericClass.Method2), 2)]
460         [InlineData(typeof(NonGenericClass), nameof(NonGenericClass.Method3), 3)]
461         [InlineData(typeof(NonGenericClass), nameof(NonGenericClass.Method4), 4)]
462         [InlineData(typeof(NonGenericClass), nameof(NonGenericClass.Method5), 5)]
InvalidArgumentCount_ThrowsArgumentException(Type type, string name, int count)463         public static void InvalidArgumentCount_ThrowsArgumentException(Type type, string name, int count)
464         {
465             MethodInfo method = type.GetMethod(name);
466             Expression arg = Expression.Constant("abc");
467             if (count != 0)
468             {
469                 AssertExtensions.Throws<ArgumentException>("method", () => Expression.Call(method));
470                 AssertExtensions.Throws<ArgumentException>("method", () => Expression.Call(null, method));
471             }
472             if (count != 1)
473             {
474                 AssertExtensions.Throws<ArgumentException>("method", () => Expression.Call(method, arg));
475                 AssertExtensions.Throws<ArgumentException>("method", () => Expression.Call(null, method, arg));
476             }
477             if (count != 2)
478             {
479                 AssertExtensions.Throws<ArgumentException>("method", () => Expression.Call(method, arg, arg));
480                 AssertExtensions.Throws<ArgumentException>("method", () => Expression.Call(null, method, arg, arg));
481             }
482             if (count != 3)
483             {
484                 AssertExtensions.Throws<ArgumentException>("method", () => Expression.Call(method, arg, arg, arg));
485                 AssertExtensions.Throws<ArgumentException>("method", () => Expression.Call(null, method, arg, arg, arg));
486             }
487             if (count != 4)
488             {
489                 AssertExtensions.Throws<ArgumentException>("method", () => Expression.Call(method, arg, arg, arg, arg));
490             }
491             if (count != 5)
492             {
493                 AssertExtensions.Throws<ArgumentException>("method", () => Expression.Call(method, arg, arg, arg, arg, arg));
494             }
495             AssertExtensions.Throws<ArgumentException>("method", () => Expression.Call(method, Enumerable.Repeat(arg, count + 1).ToArray()));
496             AssertExtensions.Throws<ArgumentException>("method", () => Expression.Call(method, Enumerable.Repeat(arg, count + 1)));
497 
498             AssertExtensions.Throws<ArgumentException>("method", () => Expression.Call(null, method, Enumerable.Repeat(arg, count + 1).ToArray()));
499             AssertExtensions.Throws<ArgumentException>("method", () => Expression.Call(null, method, Enumerable.Repeat(arg, count + 1)));
500         }
501 
502         [Fact]
MethodName_NullInstance_ThrowsArgumentNullException()503         public static void MethodName_NullInstance_ThrowsArgumentNullException()
504         {
505             AssertExtensions.Throws<ArgumentNullException>("instance", () => Expression.Call((Expression)null, "methodName", new Type[0], new Expression[0]));
506         }
507 
508         [Fact]
MethodName_NullType_ThrowsArgumentNullException()509         public static void MethodName_NullType_ThrowsArgumentNullException()
510         {
511             AssertExtensions.Throws<ArgumentNullException>("type", () => Expression.Call((Type)null, "methodName", new Type[0], new Expression[0]));
512         }
513 
514         [Fact]
NullMethodName_ThrowsArgumentNullException()515         public static void NullMethodName_ThrowsArgumentNullException()
516         {
517             AssertExtensions.Throws<ArgumentNullException>("methodName", () => Expression.Call(Expression.Constant(new NonGenericClass()), null, new Type[0], new Expression[0]));
518             AssertExtensions.Throws<ArgumentNullException>("methodName", () => Expression.Call(typeof(NonGenericClass), null, new Type[0], new Expression[0]));
519         }
520 
521         [Fact]
MethodName_DoesNotExist_ThrowsInvalidOperationException()522         public static void MethodName_DoesNotExist_ThrowsInvalidOperationException()
523         {
524             Assert.Throws<InvalidOperationException>(() => Expression.Call(Expression.Constant(new NonGenericClass()), "NoSuchMethod", null));
525             Assert.Throws<InvalidOperationException>(() => Expression.Call(typeof(NonGenericClass), "NoSuchMethod", null));
526         }
527 
InvalidTypeArgs_TestData()528         public static IEnumerable<object[]> InvalidTypeArgs_TestData()
529         {
530             yield return new object[] { null };
531             yield return new object[] { new Type[0] };
532             yield return new object[] { new Type[2] };
533         }
534 
535         [Theory]
536         [MemberData(nameof(InvalidTypeArgs_TestData))]
MethodName_NoSuchGenericMethodWithTypeArgs_ThrowsInvalidOperationException(Type[] typeArgs)537         public static void MethodName_NoSuchGenericMethodWithTypeArgs_ThrowsInvalidOperationException(Type[] typeArgs)
538         {
539             Assert.Throws<InvalidOperationException>(() => Expression.Call(Expression.Constant(new NonGenericClass()), nameof(NonGenericClass.GenericInstanceMethod), typeArgs));
540             Assert.Throws<InvalidOperationException>(() => Expression.Call(typeof(NonGenericClass), nameof(NonGenericClass.GenericStaticMethod), typeArgs));
541         }
542 
543         [Fact]
MethodName_TypeArgsDontMatchConstraints_ThrowsArgumentException()544         public static void MethodName_TypeArgsDontMatchConstraints_ThrowsArgumentException()
545         {
546             AssertExtensions.Throws<ArgumentException>(null, () => Expression.Call(Expression.Constant(new NonGenericClass()), nameof(NonGenericClass.ConstrainedInstanceMethod), new Type[] { typeof(object) }));
547             AssertExtensions.Throws<ArgumentException>(null, () => Expression.Call(typeof(NonGenericClass), nameof(NonGenericClass.ConstrainedStaticMethod), new Type[] { typeof(object) }));
548         }
549 
550         [Fact]
MethodName_NonGenericMethodHasTypeArgs_ThrowsInvalidOperationException()551         public static void MethodName_NonGenericMethodHasTypeArgs_ThrowsInvalidOperationException()
552         {
553             Assert.Throws<InvalidOperationException>(() => Expression.Call(Expression.Constant(new NonGenericClass()), nameof(NonGenericClass.InstanceMethod), new Type[1]));
554             Assert.Throws<InvalidOperationException>(() => Expression.Call(typeof(NonGenericClass), nameof(NonGenericClass.StaticMethod), new Type[1]));
555         }
556 
557         [Fact]
MethodName_TypeArgsHasNullValue_ThrowsArgumentNullException()558         public static void MethodName_TypeArgsHasNullValue_ThrowsArgumentNullException()
559         {
560             AssertExtensions.Throws<ArgumentNullException>(null, () => Expression.Call(Expression.Constant(new NonGenericClass()), nameof(NonGenericClass.GenericInstanceMethod), new Type[] { null }));
561             AssertExtensions.Throws<ArgumentNullException>(null, () => Expression.Call(typeof(NonGenericClass), nameof(NonGenericClass.GenericStaticMethod), new Type[] { null }));
562         }
563 
564         [Fact]
MethodName_ArgumentsHasNullValue_ThrowsArgumentNullException()565         public static void MethodName_ArgumentsHasNullValue_ThrowsArgumentNullException()
566         {
567             AssertExtensions.Throws<ArgumentNullException>("arguments", () => Expression.Call(Expression.Constant(new NonGenericClass()), nameof(NonGenericClass.InstanceMethod1), new Type[0], new Expression[] { null }));
568             AssertExtensions.Throws<ArgumentNullException>("arguments", () => Expression.Call(typeof(NonGenericClass), nameof(NonGenericClass.StaticMethod1), new Type[0], new Expression[] { null }));
569         }
570 
571         [Fact]
MethodName_ArgumentsHasNullValueButDifferentCount_ThrowsInvalidOperationException()572         public static void MethodName_ArgumentsHasNullValueButDifferentCount_ThrowsInvalidOperationException()
573         {
574             Assert.Throws<InvalidOperationException>(() => Expression.Call(Expression.Constant(new NonGenericClass()), nameof(NonGenericClass.InstanceMethod1), new Type[0], new Expression[] { null, Expression.Constant("") }));
575             Assert.Throws<InvalidOperationException>(() => Expression.Call(typeof(NonGenericClass), nameof(NonGenericClass.StaticMethod1), new Type[0], new Expression[] { null, Expression.Constant("") }));
576         }
577 
578         [Fact]
ToStringTest()579         public static void ToStringTest()
580         {
581             // NB: Static methods are inconsistent compared to static members; the declaring type is not included
582 
583             MethodCallExpression e1 = Expression.Call(null, typeof(SomeMethods).GetMethod(nameof(SomeMethods.S0), BindingFlags.Static | BindingFlags.Public));
584             Assert.Equal("S0()", e1.ToString());
585 
586             MethodCallExpression e2 = Expression.Call(null, typeof(SomeMethods).GetMethod(nameof(SomeMethods.S1), BindingFlags.Static | BindingFlags.Public), Expression.Parameter(typeof(int), "x"));
587             Assert.Equal("S1(x)", e2.ToString());
588 
589             MethodCallExpression e3 = Expression.Call(null, typeof(SomeMethods).GetMethod(nameof(SomeMethods.S2), BindingFlags.Static | BindingFlags.Public), Expression.Parameter(typeof(int), "x"), Expression.Parameter(typeof(int), "y"));
590             Assert.Equal("S2(x, y)", e3.ToString());
591 
592             MethodCallExpression e4 = Expression.Call(Expression.Parameter(typeof(SomeMethods), "o"), typeof(SomeMethods).GetMethod(nameof(SomeMethods.I0), BindingFlags.Instance | BindingFlags.Public));
593             Assert.Equal("o.I0()", e4.ToString());
594 
595             MethodCallExpression e5 = Expression.Call(Expression.Parameter(typeof(SomeMethods), "o"), typeof(SomeMethods).GetMethod(nameof(SomeMethods.I1), BindingFlags.Instance | BindingFlags.Public), Expression.Parameter(typeof(int), "x"));
596             Assert.Equal("o.I1(x)", e5.ToString());
597 
598             MethodCallExpression e6 = Expression.Call(Expression.Parameter(typeof(SomeMethods), "o"), typeof(SomeMethods).GetMethod(nameof(SomeMethods.I2), BindingFlags.Instance | BindingFlags.Public), Expression.Parameter(typeof(int), "x"), Expression.Parameter(typeof(int), "y"));
599             Assert.Equal("o.I2(x, y)", e6.ToString());
600 
601             MethodCallExpression e7 = Expression.Call(null, typeof(ExtensionMethods).GetMethod(nameof(ExtensionMethods.E0), BindingFlags.Static | BindingFlags.Public), Expression.Parameter(typeof(int), "x"));
602             Assert.Equal("x.E0()", e7.ToString());
603 
604             MethodCallExpression e8 = Expression.Call(null, typeof(ExtensionMethods).GetMethod(nameof(ExtensionMethods.E1), BindingFlags.Static | BindingFlags.Public), Expression.Parameter(typeof(int), "x"), Expression.Parameter(typeof(int), "y"));
605             Assert.Equal("x.E1(y)", e8.ToString());
606 
607             MethodCallExpression e9 = Expression.Call(null, typeof(ExtensionMethods).GetMethod(nameof(ExtensionMethods.E2), BindingFlags.Static | BindingFlags.Public), Expression.Parameter(typeof(int), "x"), Expression.Parameter(typeof(int), "y"), Expression.Parameter(typeof(int), "z"));
608             Assert.Equal("x.E2(y, z)", e9.ToString());
609         }
610 
611         [Fact]
GetArguments()612         public static void GetArguments()
613         {
614             VerifyGetArguments(Expression.Call(null, s_method0));
615             VerifyGetArguments(Expression.Call(null, s_method1, Expression.Constant(0)));
616             VerifyGetArguments(
617                 Expression.Call(null, s_method2, Enumerable.Range(0, 2).Select(i => Expression.Constant(i))));
618             VerifyGetArguments(
619                 Expression.Call(null, s_method3, Enumerable.Range(0, 3).Select(i => Expression.Constant(i))));
620             VerifyGetArguments(
621                 Expression.Call(null, s_method4, Enumerable.Range(0, 4).Select(i => Expression.Constant(i))));
622             VerifyGetArguments(
623                 Expression.Call(null, s_method5, Enumerable.Range(0, 5).Select(i => Expression.Constant(i))));
624             VerifyGetArguments(
625                 Expression.Call(null, s_method6, Enumerable.Range(0, 6).Select(i => Expression.Constant(i))));
626             VerifyGetArguments(
627                 Expression.Call(null, s_method7, Enumerable.Range(0, 7).Select(i => Expression.Constant(i))));
628             var site = Expression.Default(typeof(NonGenericClass));
629             VerifyGetArguments(Expression.Call(site, nameof(NonGenericClass.InstanceMethod0), null));
630             VerifyGetArguments(
631                 Expression.Call(site, nameof(NonGenericClass.InstanceMethod1), null, Expression.Constant(0)));
632             VerifyGetArguments(
633                 Expression.Call(
634                     site, nameof(NonGenericClass.InstanceMethod2), null,
635                     Enumerable.Range(0, 2).Select(i => Expression.Constant(i)).ToArray()));
636             VerifyGetArguments(
637                 Expression.Call(
638                     site, nameof(NonGenericClass.InstanceMethod3), null,
639                     Enumerable.Range(0, 3).Select(i => Expression.Constant(i)).ToArray()));
640             VerifyGetArguments(
641                 Expression.Call(
642                     site, nameof(NonGenericClass.InstanceMethod4), null,
643                     Enumerable.Range(0, 4).Select(i => Expression.Constant(i)).ToArray()));
644         }
645 
VerifyGetArguments(MethodCallExpression call)646         private static void VerifyGetArguments(MethodCallExpression call)
647         {
648             var args = call.Arguments;
649             Assert.Equal(args.Count, call.ArgumentCount);
650             AssertExtensions.Throws<ArgumentOutOfRangeException>("index", () => call.GetArgument(-1));
651             AssertExtensions.Throws<ArgumentOutOfRangeException>("index", () => call.GetArgument(args.Count));
652             for (int i = 0; i != args.Count; ++i)
653             {
654                 Assert.Same(args[i], call.GetArgument(i));
655                 Assert.Equal(i, ((ConstantExpression)call.GetArgument(i)).Value);
656             }
657         }
658 
659         public class GenericClass<T>
660         {
NonGenericMethod()661             public static void NonGenericMethod() { }
662         }
663 
664         public static class Unreadable<T>
665         {
666             public static T WriteOnly { set { } }
667         }
668 
669         public class NonGenericClass
670         {
GenericMethod()671             public static void GenericMethod<T>() { }
InstanceMethod()672             public void InstanceMethod() { }
StaticMethod()673             public static void StaticMethod() { }
674 
Method0()675             public static void Method0() { }
Method1(int i1)676             public static void Method1(int i1) { }
Method2(int i1, int i2)677             public static void Method2(int i1, int i2) { }
Method3(int i1, int i2, int i3)678             public static void Method3(int i1, int i2, int i3) { }
Method4(int i1, int i2, int i3, int i4)679             public static void Method4(int i1, int i2, int i3, int i4) { }
Method5(int i1, int i2, int i3, int i4, int i5)680             public static void Method5(int i1, int i2, int i3, int i4, int i5) { }
Method6(int i1, int i2, int i3, int i4, int i5, int i6)681             public static void Method6(int i1, int i2, int i3, int i4, int i5, int i6) { }
Method7(int i1, int i2, int i3, int i4, int i5, int i6, int i7)682             public static void Method7(int i1, int i2, int i3, int i4, int i5, int i6, int i7) { }
683 
staticSameName(uint i1)684             public void staticSameName(uint i1) { }
instanceSameName(int i1)685             public void instanceSameName(int i1) { }
686 
StaticSameName(uint i1)687             public static void StaticSameName(uint i1) { }
staticSameName(int i1)688             public static void staticSameName(int i1) { }
689 
GenericInstanceMethod(T t1)690             public void GenericInstanceMethod<T>(T t1) { }
GenericStaticMethod(T t1)691             public static void GenericStaticMethod<T>(T t1) { }
692 
693             public void ConstrainedInstanceMethod<T>(T t1) where T : struct { }
694             public static void ConstrainedStaticMethod<T>(T t1) where T : struct { }
695 
InstanceMethod0()696             public void InstanceMethod0() { }
InstanceMethod1(int i1)697             public void InstanceMethod1(int i1) { }
InstanceMethod2(int i1, int i2)698             public void InstanceMethod2(int i1, int i2) { }
InstanceMethod3(int i1, int i2, int i3)699             public void InstanceMethod3(int i1, int i2, int i3) { }
InstanceMethod4(int i1, int i2, int i3, int i4)700             public void InstanceMethod4(int i1, int i2, int i3, int i4) { }
StaticMethod1(int i1)701             public static void StaticMethod1(int i1) { }
702         }
703 
704         public interface Interface1
705         {
InterfaceMethod()706             int InterfaceMethod();
707         }
708 
709         public interface Interface2
710         {
InterfaceMethod()711             int InterfaceMethod();
712         }
713 
714         public interface CompoundInterface : Interface1 { }
715 
716         public class ClassWithInterface1 : Interface1
717         {
InterfaceMethod()718             public int InterfaceMethod() => 1;
Method()719             public int Method() => 2;
720         }
721 
722         public class OtherClassWithInterface1 : Interface1
723         {
InterfaceMethod()724             public int InterfaceMethod() => 1;
725         }
726 
727         public struct StructWithInterface1 : Interface1
728         {
InterfaceMethodSystem.Linq.Expressions.Tests.CallTests.StructWithInterface1729             public int InterfaceMethod() => 1;
MethodSystem.Linq.Expressions.Tests.CallTests.StructWithInterface1730             public int Method() => 2;
731         }
732 
733         public struct OtherStructWithInterface1 : Interface1
734         {
InterfaceMethodSystem.Linq.Expressions.Tests.CallTests.OtherStructWithInterface1735             public int InterfaceMethod() => 1;
736         }
737 
738         public class ClassWithCompoundInterface : CompoundInterface
739         {
InterfaceMethod()740             public int InterfaceMethod() => 1;
741         }
742 
743         public struct StructWithCompoundInterface : CompoundInterface
744         {
InterfaceMethodSystem.Linq.Expressions.Tests.CallTests.StructWithCompoundInterface745             public int InterfaceMethod() => 1;
746         }
747     }
748 
749     class SomeMethods
750     {
S0()751         public static void S0() { }
S1(int x)752         public static void S1(int x) { }
S2(int x, int y)753         public static void S2(int x, int y) { }
754 
I0()755         public void I0() { }
I1(int x)756         public void I1(int x) { }
I2(int x, int y)757         public void I2(int x, int y) { }
758     }
759 
760     static class ExtensionMethods
761     {
E0(this int x)762         public static void E0(this int x) { }
E1(this int x, int y)763         public static void E1(this int x, int y) { }
E2(this int x, int y, int z)764         public static void E2(this int x, int y, int z) { }
765     }
766 }
767