[release/8.0] Fix calling existing ctor with MethodInvoker; share tests with invokers...
authorgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Fri, 25 Aug 2023 22:09:46 +0000 (15:09 -0700)
committerGitHub <noreply@github.com>
Fri, 25 Aug 2023 22:09:46 +0000 (15:09 -0700)
* Fix calling existing ctor with MethodInvoker; share tests with invokers

* Remove unnecessary semicolon (non functional)

---------

Co-authored-by: Steve Harter <steveharter@users.noreply.github.com>
12 files changed:
src/libraries/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.cs
src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvoker.cs
src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvokerCommon.cs
src/libraries/System.Reflection/tests/ConstructorCommonTests.cs [new file with mode: 0644]
src/libraries/System.Reflection/tests/ConstructorInfoTests.cs
src/libraries/System.Reflection/tests/ConstructorInvokerTests.cs
src/libraries/System.Reflection/tests/InvokeEmit/System.Reflection.InvokeEmit.Tests.csproj
src/libraries/System.Reflection/tests/InvokeInterpreted/System.Reflection.InvokeInterpreted.Tests.csproj
src/libraries/System.Reflection/tests/MethodCommonTests.cs [new file with mode: 0644]
src/libraries/System.Reflection/tests/MethodInfoTests.cs
src/libraries/System.Reflection/tests/MethodInvokerTests.cs
src/libraries/System.Reflection/tests/System.Reflection.Tests.csproj

index 7f3c1a2..3fad9dc 100644 (file)
@@ -155,7 +155,7 @@ namespace System.Reflection
 
         private object InvokeImpl(object? arg1, object? arg2, object? arg3, object? arg4)
         {
-            if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers)) != 0)
+            if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers | InvocationFlags.NoConstructorInvoke)) != 0)
             {
                 _method.ThrowNoInvokeException();
             }
index 66a35db..0c8d9c5 100644 (file)
@@ -66,7 +66,12 @@ namespace System.Reflection
             {
                 // This is useful for calling a constructor on an already-initialized object
                 // such as created from RuntimeHelpers.GetUninitializedObject(Type).
-                return new MethodInvoker(rci);
+                MethodInvoker invoker = new MethodInvoker(rci);
+
+                // Use the interpreted version to avoid having to generate a new method that doesn't allocate.
+                invoker._strategy = GetStrategyForUsingInterpreted();
+
+                return invoker;
             }
 
             throw new ArgumentException(SR.Argument_MustBeRuntimeMethod, nameof(method));
@@ -181,7 +186,7 @@ namespace System.Reflection
 
         private object? InvokeImpl(object? obj, object? arg1, object? arg2, object? arg3, object? arg4)
         {
-            if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers)) != 0)
+            if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers | InvocationFlags.NoConstructorInvoke)) != 0)
             {
                 ThrowForBadInvocationFlags();
             }
index 191228b..813e89f 100644 (file)
@@ -18,13 +18,14 @@ namespace System.Reflection
         {
             if (LocalAppContextSwitches.ForceInterpretedInvoke && !LocalAppContextSwitches.ForceEmitInvoke)
             {
-                // Always use the native invoke; useful for testing.
-                strategy = InvokerStrategy.StrategyDetermined_Obj4Args | InvokerStrategy.StrategyDetermined_ObjSpanArgs | InvokerStrategy.StrategyDetermined_RefArgs;
+                // Always use the native interpreted invoke.
+                // Useful for testing, to avoid startup overhead of emit, or for calling a ctor on already initialized object.
+                strategy = GetStrategyForUsingInterpreted();
             }
             else if (LocalAppContextSwitches.ForceEmitInvoke && !LocalAppContextSwitches.ForceInterpretedInvoke)
             {
                 // Always use emit invoke (if IsDynamicCodeSupported == true); useful for testing.
-                strategy = InvokerStrategy.HasBeenInvoked_Obj4Args | InvokerStrategy.HasBeenInvoked_ObjSpanArgs | InvokerStrategy.HasBeenInvoked_RefArgs;
+                strategy = GetStrategyForUsingEmit();
             }
             else
             {
@@ -69,6 +70,18 @@ namespace System.Reflection
             }
         }
 
+        internal static InvokerStrategy GetStrategyForUsingInterpreted()
+        {
+            // This causes the default strategy, which is interpreted, to always be used.
+            return InvokerStrategy.StrategyDetermined_Obj4Args | InvokerStrategy.StrategyDetermined_ObjSpanArgs | InvokerStrategy.StrategyDetermined_RefArgs;
+        }
+
+        private static InvokerStrategy GetStrategyForUsingEmit()
+        {
+            // This causes the emit strategy, if supported, to be used on the first call as well as subsequent calls.
+            return InvokerStrategy.HasBeenInvoked_Obj4Args | InvokerStrategy.HasBeenInvoked_ObjSpanArgs | InvokerStrategy.HasBeenInvoked_RefArgs;
+        }
+
         /// <summary>
         /// Confirm member invocation has an instance and is of the correct type
         /// </summary>
diff --git a/src/libraries/System.Reflection/tests/ConstructorCommonTests.cs b/src/libraries/System.Reflection/tests/ConstructorCommonTests.cs
new file mode 100644 (file)
index 0000000..5c71f79
--- /dev/null
@@ -0,0 +1,167 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Linq;
+using Xunit;
+
+namespace System.Reflection.Tests
+{
+    /// <summary>
+    /// These tests are shared with ConstructorInfo.Invoke and ConstructorInvoker.Invoke by using
+    /// the abstract Invoke(...) methods below.
+    /// </summary>
+    public abstract class ConstructorCommonTests
+    {
+        public abstract object Invoke(ConstructorInfo constructorInfo, object?[]? parameters);
+
+        protected abstract bool IsExceptionWrapped { get; }
+
+        /// <summary>
+        /// Invoke constructor on an existing instance. Should return null.
+        /// </summary>
+        public abstract object? Invoke(ConstructorInfo constructorInfo, object obj, object?[]? parameters);
+
+        public static ConstructorInfo[] GetConstructors(Type type)
+        {
+            return type.GetTypeInfo().DeclaredConstructors.ToArray();
+        }
+
+        [Fact]
+        public void SimpleInvoke()
+        {
+            ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
+            Assert.Equal(3, constructors.Length);
+            ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[0], null);
+            Assert.NotNull(obj);
+        }
+
+        [Fact]
+        [ActiveIssue("https://github.com/mono/mono/issues/15024", TestRuntimes.Mono)]
+        public void Invoke_StaticConstructor_ThrowsMemberAccessException()
+        {
+            ConstructorInfo[] constructors = GetConstructors(typeof(ClassWithStaticConstructor));
+            Assert.Equal(1, constructors.Length);
+            Assert.Throws<MemberAccessException>(() => Invoke(constructors[0], new object[0]));
+        }
+
+        [Fact]
+        public void Invoke_OneDimensionalArray()
+        {
+            ConstructorInfo[] constructors = GetConstructors(typeof(object[]));
+            int[] arraylength = { 1, 2, 99, 65535 };
+
+            // Try to invoke Array ctors with different lengths
+            foreach (int length in arraylength)
+            {
+                // Create big Array with elements
+                object[] arr = (object[])Invoke(constructors[0], new object[] { length });
+                Assert.Equal(arr.Length, length);
+            }
+        }
+
+        [Fact]
+        public void Invoke_OneDimensionalArray_NegativeLengths_ThrowsOverflowException()
+        {
+            ConstructorInfo[] constructors = GetConstructors(typeof(object[]));
+            int[] arraylength = new int[] { -1, -2, -99 };
+            // Try to invoke Array ctors with different lengths
+            foreach (int length in arraylength)
+            {
+                // Create big Array with elements
+                if (IsExceptionWrapped)
+                {
+                    Exception ex = Assert.Throws<TargetInvocationException>(() => Invoke(constructors[0], new object[] { length }));
+                    Assert.IsType<OverflowException>(ex.InnerException);
+                }
+                else
+                {
+                    Assert.Throws<OverflowException>(() => Invoke(constructors[0], new object[] { length }));
+                }
+            }
+        }
+
+        [Fact]
+        public void Invoke_OneParameter()
+        {
+            ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
+            ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[1], new object[] { 100 });
+            Assert.Equal(100, obj.intValue);
+        }
+
+        [Fact]
+        public void Invoke_TwoParameters()
+        {
+            ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
+            ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[2], new object[] { 101, "hello" });
+            Assert.Equal(101, obj.intValue);
+            Assert.Equal("hello", obj.stringValue);
+        }
+
+        [Fact]
+        public void Invoke_NoParameters_ThowsTargetParameterCountException()
+        {
+            ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
+            Assert.Throws<TargetParameterCountException>(() => Invoke(constructors[2], new object[0]));
+        }
+
+        [Fact]
+        public void Invoke_ParameterMismatch_ThrowsTargetParameterCountException()
+        {
+            ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
+            Assert.Throws<TargetParameterCountException>(() => (ClassWith3Constructors)Invoke(constructors[2], new object[] { 121 }));
+        }
+
+        [Fact]
+        public void Invoke_ParameterWrongType_ThrowsArgumentException()
+        {
+            ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
+            AssertExtensions.Throws<ArgumentException>(null, () => (ClassWith3Constructors)Invoke(constructors[1], new object[] { "hello" }));
+        }
+
+        [Fact]
+        public void Invoke_ExistingInstance()
+        {
+            // Should not produce a second object.
+            ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
+            ClassWith3Constructors obj1 = new ClassWith3Constructors(100, "hello");
+            ClassWith3Constructors obj2 = (ClassWith3Constructors)Invoke(constructors[2], obj1, new object[] { 999, "initialized" });
+            Assert.Null(obj2);
+            Assert.Equal(999, obj1.intValue);
+            Assert.Equal("initialized", obj1.stringValue);
+        }
+
+        [Fact]
+        public void Invoke_NullForObj()
+        {
+            ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
+            Assert.Throws<TargetException>(() => Invoke(constructors[2], obj: null, new object[] { 999, "initialized" }));
+        }
+
+        [Fact]
+        [ActiveIssue("https://github.com/mono/mono/issues/15026", TestRuntimes.Mono)]
+        public void Invoke_AbstractClass_ThrowsMemberAccessException()
+        {
+            ConstructorInfo[] constructors = GetConstructors(typeof(ConstructorInfoAbstractBase));
+            Assert.Throws<MemberAccessException>(() => (ConstructorInfoAbstractBase)Invoke(constructors[0], new object[0]));
+        }
+
+        [Fact]
+        public void Invoke_SubClass()
+        {
+            ConstructorInfo[] constructors = GetConstructors(typeof(ConstructorInfoDerived));
+            ConstructorInfoDerived obj = null;
+            obj = (ConstructorInfoDerived)Invoke(constructors[0], new object[] { });
+            Assert.NotNull(obj);
+        }
+
+        [Fact]
+        public void Invoke_Struct()
+        {
+            ConstructorInfo[] constructors = GetConstructors(typeof(StructWith1Constructor));
+            StructWith1Constructor obj;
+            obj = (StructWith1Constructor)Invoke(constructors[0], new object[] { 1, 2 });
+            Assert.Equal(1, obj.x);
+            Assert.Equal(2, obj.y);
+        }
+    }
+}
index 34d0490..3749834 100644 (file)
@@ -2,15 +2,29 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System.Collections.Generic;
-using System.Linq;
 using Xunit;
 
 #pragma warning disable 0414
 
 namespace System.Reflection.Tests
 {
-    public class ConstructorInfoTests
+    /// <summary>
+    /// These tests use the shared tests from the base class with ConstructorInfo.Invoke.
+    /// </summary>
+    public sealed class ConstructorInfoTests : ConstructorCommonTests
     {
+        public override object Invoke(ConstructorInfo constructorInfo, object?[]? parameters)
+        {
+            return constructorInfo.Invoke(parameters);
+        }
+
+        public override object? Invoke(ConstructorInfo constructorInfo, object obj, object?[]? parameters)
+        {
+            return constructorInfo.Invoke(obj, parameters);
+        }
+
+        protected override bool IsExceptionWrapped => true;
+
         [Fact]
         public void ConstructorName()
         {
@@ -50,15 +64,6 @@ namespace System.Reflection.Tests
             }
         }
 
-        [Fact]
-        public void Invoke()
-        {
-            ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
-            Assert.Equal(3, constructors.Length);
-            ClassWith3Constructors obj = (ClassWith3Constructors)constructors[0].Invoke(null);
-            Assert.NotNull(obj);
-        }
-
         [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsInvokingStaticConstructorsSupported))]
         public void Invoke_StaticConstructor_NullObject_NullParameters()
         {
@@ -89,44 +94,6 @@ namespace System.Reflection.Tests
         }
 
         [Fact]
-        [ActiveIssue("https://github.com/mono/mono/issues/15024", TestRuntimes.Mono)]
-        public void Invoke_StaticConstructor_ThrowsMemberAccessException()
-        {
-            ConstructorInfo[] constructors = GetConstructors(typeof(ClassWithStaticConstructor));
-            Assert.Equal(1, constructors.Length);
-            Assert.Throws<MemberAccessException>(() => constructors[0].Invoke(new object[0]));
-        }
-
-        [Fact]
-        public void Invoke_OneDimensionalArray()
-        {
-            ConstructorInfo[] constructors = GetConstructors(typeof(object[]));
-            int[] arraylength = { 1, 2, 99, 65535 };
-
-            // Try to invoke Array ctors with different lengths
-            foreach (int length in arraylength)
-            {
-                // Create big Array with elements
-                object[] arr = (object[])constructors[0].Invoke(new object[] { length });
-                Assert.Equal(arr.Length, length);
-            }
-        }
-
-        [Fact]
-        public void Invoke_OneDimensionalArray_NegativeLengths_ThrowsOverflowException()
-        {
-            ConstructorInfo[] constructors = GetConstructors(typeof(object[]));
-            int[] arraylength = new int[] { -1, -2, -99 };
-            // Try to invoke Array ctors with different lengths
-            foreach (int length in arraylength)
-            {
-                // Create big Array with elements
-                Exception ex = Assert.Throws<TargetInvocationException>(() => constructors[0].Invoke(new object[] { length }));
-                Assert.IsType<OverflowException>(ex.InnerException);
-            }
-        }
-
-        [Fact]
         [ActiveIssue("https://github.com/dotnet/runtime/issues/67531", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
         public void Invoke_TwoDimensionalArray_CustomBinder_IncorrectTypeArguments()
         {
@@ -139,23 +106,6 @@ namespace System.Reflection.Tests
         }
 
         [Fact]
-        public void Invoke_OneParameter()
-        {
-            ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
-            ClassWith3Constructors obj = (ClassWith3Constructors)constructors[1].Invoke(new object[] { 100 });
-            Assert.Equal(100, obj.intValue);
-        }
-
-        [Fact]
-        public void Invoke_TwoParameters()
-        {
-            ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
-            ClassWith3Constructors obj = (ClassWith3Constructors)constructors[2].Invoke(new object[] { 101, "hello" });
-            Assert.Equal(101, obj.intValue);
-            Assert.Equal("hello", obj.stringValue);
-        }
-
-        [Fact]
         [ActiveIssue("https://github.com/dotnet/runtime/issues/67531", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
         public void Invoke_TwoParameters_CustomBinder_IncorrectTypeArgument()
         {
@@ -170,66 +120,6 @@ namespace System.Reflection.Tests
         }
 
         [Fact]
-        public void Invoke_NoParameters_ThowsTargetParameterCountException()
-        {
-            ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
-            Assert.Throws<TargetParameterCountException>(() => constructors[2].Invoke(new object[0]));
-        }
-
-        [Fact]
-        public void Invoke_ParameterMismatch_ThrowsTargetParameterCountException()
-        {
-            ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
-            Assert.Throws<TargetParameterCountException>(() => (ClassWith3Constructors)constructors[2].Invoke(new object[] { 121 }));
-        }
-
-        [Fact]
-        public void Invoke_ParameterWrongType_ThrowsArgumentException()
-        {
-            ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
-            AssertExtensions.Throws<ArgumentException>(null, () => (ClassWith3Constructors)constructors[1].Invoke(new object[] { "hello" }));
-        }
-
-        [Fact]
-        public void Invoke_ExistingInstance()
-        {
-            // Should not produce a second object.
-            ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
-            ClassWith3Constructors obj1 = new ClassWith3Constructors(100, "hello");
-            ClassWith3Constructors obj2 = (ClassWith3Constructors)constructors[2].Invoke(obj1, new object[] { 999, "initialized" });
-            Assert.Null(obj2);
-            Assert.Equal(999, obj1.intValue);
-            Assert.Equal("initialized", obj1.stringValue);
-        }
-
-        [Fact]
-        [ActiveIssue("https://github.com/mono/mono/issues/15026", TestRuntimes.Mono)]
-        public void Invoke_AbstractClass_ThrowsMemberAccessException()
-        {
-            ConstructorInfo[] constructors = GetConstructors(typeof(ConstructorInfoAbstractBase));
-            Assert.Throws<MemberAccessException>(() => (ConstructorInfoAbstractBase)constructors[0].Invoke(new object[0]));
-        }
-
-        [Fact]
-        public void Invoke_SubClass()
-        {
-            ConstructorInfo[] constructors = GetConstructors(typeof(ConstructorInfoDerived));
-            ConstructorInfoDerived obj = null;
-            obj = (ConstructorInfoDerived)constructors[0].Invoke(new object[] { });
-            Assert.NotNull(obj);
-        }
-
-        [Fact]
-        public void Invoke_Struct()
-        {
-            ConstructorInfo[] constructors = GetConstructors(typeof(StructWith1Constructor));
-            StructWith1Constructor obj;
-            obj = (StructWith1Constructor)constructors[0].Invoke(new object[] { 1, 2 });
-            Assert.Equal(1, obj.x);
-            Assert.Equal(2, obj.y);
-        }
-
-        [Fact]
         public void IsConstructor_ReturnsTrue()
         {
             ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
@@ -243,9 +133,18 @@ namespace System.Reflection.Tests
             Assert.True(constructors[0].IsPublic);
         }
 
-        public static ConstructorInfo[] GetConstructors(Type type)
+        // Use this class only from the Invoke_StaticConstructorMultipleTimes method
+        public static class ClassWithStaticConstructorThatIsCalledMultipleTimesViaReflection
         {
-            return type.GetTypeInfo().DeclaredConstructors.ToArray();
+            public static class VisibleStatics
+            {
+                public static int s_cctorCallCount;
+            }
+
+            static ClassWithStaticConstructorThatIsCalledMultipleTimesViaReflection()
+            {
+                VisibleStatics.s_cctorCallCount++;
+            }
         }
     }
 
@@ -281,20 +180,6 @@ namespace System.Reflection.Tests
         static ClassWithStaticConstructor() { }
     }
 
-    // Use this class only from the Invoke_StaticConstructorMultipleTimes method
-    public static class ClassWithStaticConstructorThatIsCalledMultipleTimesViaReflection
-    {
-        public static class VisibleStatics
-        {
-            public static int s_cctorCallCount;
-        }
-
-        static ClassWithStaticConstructorThatIsCalledMultipleTimesViaReflection()
-        {
-            VisibleStatics.s_cctorCallCount++;
-        }
-    }
-
     public struct StructWith1Constructor
     {
         public int x;
index 86fa1c4..f5313bd 100644 (file)
@@ -1,13 +1,26 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-using System.Runtime.CompilerServices;
 using Xunit;
 
 namespace System.Reflection.Tests
 {
-    public class ConstructorInvokerTests
+    /// <summary>
+    /// These tests use the shared tests from the base class with ConstructorInvoker.Invoke.
+    /// </summary>
+    public sealed class ConstructorInvokerTests : ConstructorCommonTests
     {
+        public override object Invoke(ConstructorInfo constructorInfo, object?[]? parameters)
+        {
+            return ConstructorInvoker.Create(constructorInfo).Invoke(new Span<object>(parameters));
+        }
+
+        public override object? Invoke(ConstructorInfo constructorInfo, object obj, object?[]? parameters)
+        {
+            return MethodInvoker.Create(constructorInfo).Invoke(obj, new Span<object>(parameters));
+        }
+
+        protected override bool IsExceptionWrapped => false;
 
         [Fact]
         public void Args_0()
@@ -162,16 +175,13 @@ namespace System.Reflection.Tests
         }
 
         [Fact]
-        public void ExistingInstance()
+        public void Invoke_StaticConstructor_NullObject_NullParameters()
         {
-            ConstructorInfo ci = typeof(TestClass).GetConstructor(BindingFlags.Public | BindingFlags.Instance, Type.EmptyTypes);
-            TestClass tc = (TestClass)RuntimeHelpers.GetUninitializedObject(typeof(TestClass));
-            Assert.Null(tc._args);
+            ConstructorInfo[] constructors = GetConstructors(typeof(ClassWithStaticConstructor));
+            Assert.Equal(1, constructors.Length);
 
-            MethodInvoker invoker = MethodInvoker.Create(ci);
-            object? obj = invoker.Invoke(tc);
-            Assert.Equal("0", tc._args);
-            Assert.Null(obj);
+            // Invoker classes do not support calling class constructors; use standard reflection for that.
+            Assert.Throws<MemberAccessException>(() => Invoke(constructors[0], null, new object[] { }));
         }
 
         private class TestClass
index 7b1e610..757b631 100644 (file)
@@ -7,8 +7,12 @@
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="..\Common.cs" />
+    <Compile Include="..\ConstructorCommonTests.cs" />
     <Compile Include="..\ConstructorInfoTests.cs" />
+    <Compile Include="..\ConstructorInvokerTests.cs" />
+    <Compile Include="..\MethodCommonTests.cs" />
     <Compile Include="..\MethodInfoTests.cs" />
+    <Compile Include="..\MethodInvokerTests.cs" />
     <Compile Include="..\PropertyInfoTests.cs" />
     <Compile Include="$(CommonTestPath)\System\Reflection\InvokeEmitTests.cs" />
   </ItemGroup>
index 64dc87d..5d71379 100644 (file)
@@ -7,8 +7,12 @@
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="..\Common.cs" />
+    <Compile Include="..\ConstructorCommonTests.cs" />
     <Compile Include="..\ConstructorInfoTests.cs" />
+    <Compile Include="..\ConstructorInvokerTests.cs" />
+    <Compile Include="..\MethodCommonTests.cs" />
     <Compile Include="..\MethodInfoTests.cs" />
+    <Compile Include="..\MethodInvokerTests.cs" />
     <Compile Include="..\PropertyInfoTests.cs" />
     <Compile Include="$(CommonTestPath)\System\Reflection\InvokeInterpretedTests.cs" />
   </ItemGroup>
diff --git a/src/libraries/System.Reflection/tests/MethodCommonTests.cs b/src/libraries/System.Reflection/tests/MethodCommonTests.cs
new file mode 100644 (file)
index 0000000..b2e8e2f
--- /dev/null
@@ -0,0 +1,297 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Linq;
+using Xunit;
+
+namespace System.Reflection.Tests
+{
+    /// <summary>
+    /// These tests are shared with MethodInfo.Invoke and MethodInvoker.Invoke by using
+    /// the abstract Invoke(...) method below.
+    /// </summary>
+    public abstract class MethodCommonTests
+    {
+        public abstract object? Invoke(MethodInfo methodInfo, object? obj, object?[]? parameters);
+
+        protected abstract bool SupportsMissing { get; }
+
+        protected static MethodInfo GetMethod(Type type, string name)
+        {
+            return type.GetTypeInfo().GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).First(method => method.Name.Equals(name));
+        }
+
+        [Fact]
+        public void InvokeNullableRefs()
+        {
+            object?[] args;
+
+            int? iNull = null;
+            args = new object[] { iNull };
+            Assert.True((bool)Invoke(GetMethod(nameof(NullableRefMethods.Null)), null, args));
+            Assert.Null(args[0]);
+            Assert.False(((int?)args[0]).HasValue);
+
+            args = new object[] { iNull };
+            Assert.True((bool)Invoke(GetMethod(nameof(NullableRefMethods.NullBoxed)), null, args));
+            Assert.Null(args[0]);
+
+            args = new object[] { iNull, 10 };
+            Assert.True((bool)Invoke(GetMethod(nameof(NullableRefMethods.NullToValue)), null, args));
+            Assert.IsType<int>(args[0]);
+            Assert.Equal(10, (int)args[0]);
+
+            iNull = 42;
+            args = new object[] { iNull, 42 };
+            Assert.True((bool)Invoke(GetMethod(nameof(NullableRefMethods.ValueToNull)), null, args));
+            Assert.Null(args[0]);
+
+            iNull = null;
+            args = new object[] { iNull, 10 };
+            Assert.True((bool)Invoke(GetMethod(nameof(NullableRefMethods.NullToValueBoxed)), null, args));
+            Assert.IsType<int>(args[0]);
+            Assert.Equal(10, (int)args[0]);
+
+            static MethodInfo GetMethod(string name) => typeof(NullableRefMethods).GetMethod(
+                name, BindingFlags.Public | BindingFlags.Static)!;
+        }
+
+        [Fact]
+        public void InvokeBoxedNullableRefs()
+        {
+            object?[] args;
+
+            object? iNull = null;
+            args = new object[] { iNull };
+            Assert.True((bool)Invoke(GetMethod(nameof(NullableRefMethods.Null)), null, args));
+            Assert.Null(args[0]);
+
+            args = new object[] { iNull };
+            Assert.True((bool)Invoke(GetMethod(nameof(NullableRefMethods.NullBoxed)), null, args));
+            Assert.Null(args[0]);
+
+            args = new object[] { iNull, 10 };
+            Assert.True((bool)Invoke(GetMethod(nameof(NullableRefMethods.NullToValue)), null, args));
+            Assert.IsType<int>(args[0]);
+            Assert.Equal(10, (int)args[0]);
+
+            iNull = 42;
+            args = new object[] { iNull, 42 };
+            Assert.True((bool)Invoke(GetMethod(nameof(NullableRefMethods.ValueToNull)), null, args));
+            Assert.Null(args[0]);
+
+            iNull = null;
+            args = new object[] { iNull, 10 };
+            Assert.True((bool)Invoke(GetMethod(nameof(NullableRefMethods.NullToValueBoxed)), null, args));
+            Assert.IsType<int>(args[0]);
+            Assert.Equal(10, (int)args[0]);
+
+            static MethodInfo GetMethod(string name) => typeof(NullableRefMethods).GetMethod(
+                name, BindingFlags.Public | BindingFlags.Static)!;
+        }
+
+        [Fact]
+        public void InvokeEnum()
+        {
+            // Enums only need to match by primitive type.
+            Assert.True((bool)GetMethod(nameof(EnumMethods.PassColorsInt)).
+                Invoke(null, new object[] { OtherColorsInt.Red }));
+
+            // Widening allowed
+            Assert.True((bool)GetMethod(nameof(EnumMethods.PassColorsInt)).
+                Invoke(null, new object[] { ColorsShort.Red }));
+
+            // Narrowing not allowed
+            Assert.Throws<ArgumentException>(() => GetMethod(nameof(EnumMethods.PassColorsShort)).
+                Invoke(null, new object[] { OtherColorsInt.Red }));
+
+            static MethodInfo GetMethod(string name) => typeof(EnumMethods).GetMethod(
+                name, BindingFlags.Public | BindingFlags.Static)!;
+        }
+
+        [Fact]
+        public void InvokeNullableEnumParameterDefaultNo()
+        {
+            MethodInfo method = typeof(EnumMethods).GetMethod("NullableEnumDefaultNo", BindingFlags.Static | BindingFlags.NonPublic);
+
+            Assert.Null(Invoke(method, null, new object?[] { default(object) }));
+            Assert.Equal(YesNo.No, Invoke(method, null, new object?[] { YesNo.No }));
+            Assert.Equal(YesNo.Yes, Invoke(method, null, new object?[] { YesNo.Yes }));
+
+            if (SupportsMissing)
+            {
+                Assert.Equal(YesNo.No, Invoke(method, null, new object?[] { Type.Missing }));
+            }
+        }
+
+        [Fact]
+        public void InvokeNullableEnumParameterDefaultYes()
+        {
+            MethodInfo method = typeof(EnumMethods).GetMethod("NullableEnumDefaultYes", BindingFlags.Static | BindingFlags.NonPublic);
+
+            Assert.Null(Invoke(method, null, new object?[] { default(object) }));
+            Assert.Equal(YesNo.No, Invoke(method, null, new object?[] { YesNo.No }));
+            Assert.Equal(YesNo.Yes, Invoke(method, null, new object?[] { YesNo.Yes }));
+
+            if (SupportsMissing)
+            {
+                Assert.Equal(YesNo.Yes, Invoke(method, null, new object?[] { Type.Missing }));
+            }
+        }
+
+        [Fact]
+        public void InvokeNonNullableEnumParameterDefaultYes()
+        {
+            MethodInfo method = typeof(EnumMethods).GetMethod("NonNullableEnumDefaultYes", BindingFlags.Static | BindingFlags.NonPublic);
+
+            Assert.Equal(YesNo.No, Invoke(method, null, new object[] { default(object) }));
+            Assert.Equal(YesNo.No, Invoke(method, null, new object[] { YesNo.No }));
+            Assert.Equal(YesNo.Yes, Invoke(method, null, new object[] { YesNo.Yes }));
+
+            if (SupportsMissing)
+            {
+                Assert.Equal(YesNo.Yes, Invoke(method, null, new object[] { Type.Missing }));
+            }
+        }
+
+        [Fact]
+        public void InvokeNullableEnumParameterDefaultNull()
+        {
+            MethodInfo method = typeof(EnumMethods).GetMethod("NullableEnumDefaultNull", BindingFlags.Static | BindingFlags.NonPublic);
+
+            Assert.Null(Invoke(method, null, new object?[] { default(object) }));
+            Assert.Equal(YesNo.No, Invoke(method, null, new object?[] { YesNo.No }));
+            Assert.Equal(YesNo.Yes, Invoke(method, null, new object?[] { YesNo.Yes }));
+
+            if (SupportsMissing)
+            {
+                Assert.Null(Invoke(method, null, new object?[] { Type.Missing }));
+            }
+        }
+
+        [Fact]
+        public void ValueTypeMembers_WithOverrides()
+        {
+            ValueTypeWithOverrides obj = new() { Id = 1 };
+
+            // ToString is overridden.
+            Assert.Equal("Hello", (string)Invoke(GetMethod(typeof(ValueTypeWithOverrides), nameof(ValueTypeWithOverrides.ToString)),
+                obj, null));
+
+            // Ensure a normal method works.
+            Assert.Equal(1, (int)Invoke(GetMethod(typeof(ValueTypeWithOverrides), nameof(ValueTypeWithOverrides.GetId)),
+                obj, null));
+        }
+
+        [Fact]
+        public void ValueTypeMembers_WithoutOverrides()
+        {
+            ValueTypeWithoutOverrides obj = new() { Id = 1 };
+
+            // ToString is not overridden.
+            Assert.Equal(typeof(ValueTypeWithoutOverrides).ToString(), (string) Invoke(GetMethod(typeof(ValueTypeWithoutOverrides), nameof(ValueTypeWithoutOverrides.ToString)),
+                obj, null));
+
+            // Ensure a normal method works.
+            Assert.Equal(1, (int)Invoke(GetMethod(typeof(ValueTypeWithoutOverrides), nameof(ValueTypeWithoutOverrides.GetId)),
+                obj, null));
+        }
+
+        [Fact]
+        public void NullableOfTMembers()
+        {
+            // Ensure calling a method on Nullable<T> works.
+            MethodInfo mi = GetMethod(typeof(int?), nameof(Nullable<int>.GetValueOrDefault));
+            Assert.Equal(42, Invoke(mi, 42, null));
+        }
+
+        [Fact]
+        public void CopyBackWithByRefArgs()
+        {
+            object i = 42;
+            object[] args = new object[] { i };
+            Invoke(GetMethod(typeof(CopyBackMethods), nameof(CopyBackMethods.IncrementByRef)), null, args);
+            Assert.Equal(43, (int)args[0]);
+            Assert.NotSame(i, args[0]); // A copy should be made; a boxed instance should never be directly updated.
+
+            i = 42;
+            args = new object[] { i };
+            Invoke(GetMethod(typeof(CopyBackMethods), nameof(CopyBackMethods.IncrementByNullableRef)), null, args);
+            Assert.Equal(43, (int)args[0]);
+            Assert.NotSame(i, args[0]);
+
+            object o = null;
+            args = new object[] { o };
+            Invoke(GetMethod(typeof(CopyBackMethods), nameof(CopyBackMethods.SetToNonNullByRef)), null, args);
+            Assert.NotNull(args[0]);
+
+            o = new object();
+            args = new object[] { o };
+            Invoke(GetMethod(typeof(CopyBackMethods), nameof(CopyBackMethods.SetToNullByRef)), null, args);
+            Assert.Null(args[0]);
+        }
+
+        [Fact]
+        public unsafe void TestFunctionPointerDirect()
+        {
+            // Sanity checks for direct invocation.
+            void* fn = FunctionPointerMethods.GetFunctionPointer();
+            Assert.True(FunctionPointerMethods.GetFunctionPointer()(42));
+            Assert.True(FunctionPointerMethods.CallFcnPtr_IntPtr((IntPtr)fn, 42));
+            Assert.True(FunctionPointerMethods.CallFcnPtr_Void(fn, 42));
+            Assert.False(FunctionPointerMethods.GetFunctionPointer()(41));
+            Assert.False(FunctionPointerMethods.CallFcnPtr_IntPtr((IntPtr)fn, 41));
+            Assert.False(FunctionPointerMethods.CallFcnPtr_Void(fn, 41));
+        }
+
+        [Fact]
+        public unsafe void TestFunctionPointerAsIntPtrArgType()
+        {
+            void* fn = FunctionPointerMethods.GetFunctionPointer();
+
+            MethodInfo m;
+
+            m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.CallFcnPtr_IntPtr));
+            Assert.True((bool)Invoke(m, null, new object[] { (IntPtr)fn, 42 }));
+            Assert.False((bool)Invoke(m, null, new object[] { (IntPtr)fn, 41 }));
+
+            m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.CallFcnPtr_Void));
+            Assert.True((bool)Invoke(m, null, new object[] { (IntPtr)fn, 42 }));
+            Assert.False((bool)Invoke(m, null, new object[] { (IntPtr)fn, 41 }));
+        }
+
+        [Fact]
+        public unsafe void TestFunctionPointerAsUIntPtrArgType()
+        {
+            void* fn = FunctionPointerMethods.GetFunctionPointer();
+
+            MethodInfo m;
+
+            m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.CallFcnPtr_UIntPtr));
+            Assert.True((bool)Invoke(m, null, new object[] { (UIntPtr)fn, 42 }));
+            Assert.False((bool)Invoke(m, null, new object[] { (UIntPtr)fn, 41 }));
+
+            m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.CallFcnPtr_Void));
+            Assert.True((bool)Invoke(m, null, new object[] { (UIntPtr)fn, 42 }));
+            Assert.False((bool)Invoke(m, null, new object[] { (UIntPtr)fn, 41 }));
+        }
+
+        [Fact]
+        public unsafe void TestFunctionPointerAsArgType()
+        {
+            void* fn = FunctionPointerMethods.GetFunctionPointer();
+            MethodInfo m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.CallFcnPtr_FP));
+            Assert.True((bool)Invoke(m, null, new object[] { (IntPtr)fn, 42 }));
+            Assert.False((bool)Invoke(m, null, new object[] { (IntPtr)fn, 41 }));
+        }
+
+        [Fact]
+        public unsafe void TestFunctionPointerAsReturnType()
+        {
+            MethodInfo m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.GetFunctionPointer));
+            object ret = Invoke(m, null, null);
+            Assert.IsType<IntPtr>(ret);
+            Assert.True((IntPtr)ret != 0);
+        }
+    }
+}
index 769fc94..341fb7f 100644 (file)
@@ -6,12 +6,21 @@ using System.Linq;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using Xunit;
-using Xunit.Sdk;
 
 namespace System.Reflection.Tests
 {
-    public class MethodInfoTests
+    /// <summary>
+    /// These tests use the shared tests from the base class with MethodInfo.Invoke.
+    /// </summary>
+    public sealed class MethodInfoTests : MethodCommonTests
     {
+        public override object? Invoke(MethodInfo methodInfo, object? obj, object?[]? parameters)
+        {
+            return methodInfo.Invoke(obj, parameters);
+        }
+
+        protected override bool SupportsMissing => false;
+
         [Fact]
         public void CreateDelegate_PublicMethod()
         {
@@ -361,7 +370,7 @@ namespace System.Reflection.Tests
 
         [Theory]
         [MemberData(nameof(Invoke_TestData))]
-        public void Invoke(Type methodDeclaringType, string methodName, object obj, object[] parameters, object result)
+        public void InvokeWithTestData(Type methodDeclaringType, string methodName, object obj, object[] parameters, object result)
         {
             MethodInfo method = GetMethod(methodDeclaringType, methodName);
             Assert.Equal(result, method.Invoke(obj, parameters));
@@ -370,8 +379,8 @@ namespace System.Reflection.Tests
         [Fact]
         public void Invoke_ParameterSpecification_ArrayOfMissing()
         {
-            Invoke(typeof(MethodInfoDefaultParameters), "OptionalObjectParameter", new MethodInfoDefaultParameters(), new object[] { Type.Missing }, Type.Missing);
-            Invoke(typeof(MethodInfoDefaultParameters), "OptionalObjectParameter", new MethodInfoDefaultParameters(), new Missing[] { Missing.Value }, Missing.Value);
+            InvokeWithTestData(typeof(MethodInfoDefaultParameters), "OptionalObjectParameter", new MethodInfoDefaultParameters(), new object[] { Type.Missing }, Type.Missing);
+            InvokeWithTestData(typeof(MethodInfoDefaultParameters), "OptionalObjectParameter", new MethodInfoDefaultParameters(), new Missing[] { Missing.Value }, Missing.Value);
         }
 
         [Fact]
@@ -620,149 +629,6 @@ namespace System.Reflection.Tests
             Assert.Equal(expected, methodInfo.ToString());
         }
 
-        [Fact]
-        public void InvokeNullableRefs()
-        {
-            object?[] args;
-
-            int? iNull = null;
-            args = new object[] { iNull };
-            Assert.True((bool)GetMethod(nameof(NullableRefMethods.Null)).Invoke(null, args));
-            Assert.Null(args[0]);
-            Assert.False(((int?)args[0]).HasValue);
-
-            args = new object[] { iNull };
-            Assert.True((bool)GetMethod(nameof(NullableRefMethods.NullBoxed)).Invoke(null, args));
-            Assert.Null(args[0]);
-
-            args = new object[] { iNull, 10 };
-            Assert.True((bool)GetMethod(nameof(NullableRefMethods.NullToValue)).Invoke(null, args));
-            Assert.IsType<int>(args[0]);
-            Assert.Equal(10, (int)args[0]);
-
-            iNull = 42;
-            args = new object[] { iNull, 42 };
-            Assert.True((bool)GetMethod(nameof(NullableRefMethods.ValueToNull)).Invoke(null, args));
-            Assert.Null(args[0]);
-
-            iNull = null;
-            args = new object[] { iNull, 10 };
-            Assert.True((bool)GetMethod(nameof(NullableRefMethods.NullToValueBoxed)).Invoke(null, args));
-            Assert.IsType<int>(args[0]);
-            Assert.Equal(10, (int)args[0]);
-
-            static MethodInfo GetMethod(string name) => typeof(NullableRefMethods).GetMethod(
-                name, BindingFlags.Public | BindingFlags.Static)!;
-        }
-
-        [Fact]
-        public void InvokeBoxedNullableRefs()
-        {
-            object?[] args;
-
-            object? iNull = null;
-            args = new object[] { iNull };
-            Assert.True((bool)GetMethod(nameof(NullableRefMethods.Null)).Invoke(null, args));
-            Assert.Null(args[0]);
-
-            args = new object[] { iNull };
-            Assert.True((bool)GetMethod(nameof(NullableRefMethods.NullBoxed)).Invoke(null, args));
-            Assert.Null(args[0]);
-
-            args = new object[] { iNull, 10 };
-            Assert.True((bool)GetMethod(nameof(NullableRefMethods.NullToValue)).Invoke(null, args));
-            Assert.IsType<int>(args[0]);
-            Assert.Equal(10, (int)args[0]);
-
-            iNull = 42;
-            args = new object[] { iNull, 42 };
-            Assert.True((bool)GetMethod(nameof(NullableRefMethods.ValueToNull)).Invoke(null, args));
-            Assert.Null(args[0]);
-
-            iNull = null;
-            args = new object[] { iNull, 10 };
-            Assert.True((bool)GetMethod(nameof(NullableRefMethods.NullToValueBoxed)).Invoke(null, args));
-            Assert.IsType<int>(args[0]);
-            Assert.Equal(10, (int)args[0]);
-
-            static MethodInfo GetMethod(string name) => typeof(NullableRefMethods).GetMethod(
-                name, BindingFlags.Public | BindingFlags.Static)!;
-        }
-
-        [Fact]
-        public void InvokeEnum()
-        {
-            // Enums only need to match by primitive type.
-            Assert.True((bool)GetMethod(nameof(EnumMethods.PassColorsInt)).
-                Invoke(null, new object[] { OtherColorsInt.Red }));
-
-            // Widening allowed
-            Assert.True((bool)GetMethod(nameof(EnumMethods.PassColorsInt)).
-                Invoke(null, new object[] { ColorsShort.Red }));
-
-            // Narrowing not allowed
-            Assert.Throws<ArgumentException>(() => GetMethod(nameof(EnumMethods.PassColorsShort)).
-                Invoke(null, new object[] { OtherColorsInt.Red }));
-
-            static MethodInfo GetMethod(string name) => typeof(EnumMethods).GetMethod(
-                name, BindingFlags.Public | BindingFlags.Static)!;
-        }
-
-        [Fact]
-        public static void InvokeNullableEnumParameterDefaultNo()
-        {
-            MethodInfo method = typeof(EnumMethods).GetMethod("NullableEnumDefaultNo", BindingFlags.Static | BindingFlags.NonPublic);
-
-            Assert.Null(method.Invoke(null, new object?[] { default(object) }));
-            Assert.Equal(YesNo.No, method.Invoke(null, new object?[] { YesNo.No }));
-            Assert.Equal(YesNo.Yes, method.Invoke(null, new object?[] { YesNo.Yes }));
-            Assert.Equal(YesNo.No, method.Invoke(null, new object?[] { Type.Missing }));
-        }
-
-        [Fact]
-        public static void InvokeNullableEnumParameterDefaultYes()
-        {
-            MethodInfo method = typeof(EnumMethods).GetMethod("NullableEnumDefaultYes", BindingFlags.Static | BindingFlags.NonPublic);
-
-            Assert.Null(method.Invoke(null, new object?[] { default(object) }));
-            Assert.Equal(YesNo.No, method.Invoke(null, new object?[] { YesNo.No }));
-            Assert.Equal(YesNo.Yes, method.Invoke(null, new object?[] { YesNo.Yes }));
-            Assert.Equal(YesNo.Yes, method.Invoke(null, new object?[] { Type.Missing }));
-        }
-
-        [Fact]
-        public static void InvokeNonNullableEnumParameterDefaultYes()
-        {
-            MethodInfo method = typeof(EnumMethods).GetMethod("NonNullableEnumDefaultYes", BindingFlags.Static | BindingFlags.NonPublic);
-
-            Assert.Equal(YesNo.No, method.Invoke(null, new object[] { default(object) }));
-            Assert.Equal(YesNo.No, method.Invoke(null, new object[] { YesNo.No }));
-            Assert.Equal(YesNo.Yes, method.Invoke(null, new object[] { YesNo.Yes }));
-            Assert.Equal(YesNo.Yes, method.Invoke(null, new object[] { Type.Missing }));
-        }
-
-        [Fact]
-        public static void InvokeNullableEnumParameterDefaultNull()
-        {
-            MethodInfo method = typeof(EnumMethods).GetMethod("NullableEnumDefaultNull", BindingFlags.Static | BindingFlags.NonPublic);
-
-            Assert.Null(method.Invoke(null, new object?[] { default(object) }));
-            Assert.Equal(YesNo.No, method.Invoke(null, new object?[] { YesNo.No }));
-            Assert.Equal(YesNo.Yes, method.Invoke(null, new object?[] { YesNo.Yes }));
-            Assert.Null(method.Invoke(null, new object?[] { Type.Missing }));
-        }
-
-        [Fact]
-        public static void InvokeNullableEnumParameterNoDefault()
-        {
-            MethodInfo method = typeof(EnumMethods).GetMethod("NullableEnumNoDefault", BindingFlags.Static | BindingFlags.NonPublic);
-
-            Assert.Null(method.Invoke(null, new object?[] { default(object) }));
-            Assert.Equal(YesNo.No, method.Invoke(null, new object?[] { YesNo.No }));
-            Assert.Equal(YesNo.Yes, method.Invoke(null, new object?[] { YesNo.Yes }));
-            Assert.Throws<ArgumentException>(() => method.Invoke(null, new object?[] { Type.Missing }));
-        }
-
         public static IEnumerable<object[]> MethodNameAndArguments()
         {
             yield return new object[] { nameof(Sample.DefaultString), "Hello", "Hi" };
@@ -806,68 +672,6 @@ namespace System.Reflection.Tests
         }
 
         [Fact]
-        public void ValueTypeMembers_WithOverrides()
-        {
-            ValueTypeWithOverrides obj = new() { Id = 1 };
-
-            // ToString is overridden.
-            Assert.Equal("Hello", (string)GetMethod(typeof(ValueTypeWithOverrides), nameof(ValueTypeWithOverrides.ToString)).
-                Invoke(obj, null));
-
-            // Ensure a normal method works.
-            Assert.Equal(1, (int)GetMethod(typeof(ValueTypeWithOverrides), nameof(ValueTypeWithOverrides.GetId)).
-                Invoke(obj, null));
-        }
-
-        [Fact]
-        public void ValueTypeMembers_WithoutOverrides()
-        {
-            ValueTypeWithoutOverrides obj = new() { Id = 1 };
-
-            // ToString is not overridden.
-            Assert.Equal(typeof(ValueTypeWithoutOverrides).ToString(), (string)GetMethod(typeof(ValueTypeWithoutOverrides), nameof(ValueTypeWithoutOverrides.ToString)).
-                Invoke(obj, null));
-
-            // Ensure a normal method works.
-            Assert.Equal(1, (int)GetMethod(typeof(ValueTypeWithoutOverrides), nameof(ValueTypeWithoutOverrides.GetId)).
-                Invoke(obj, null));
-        }
-
-        [Fact]
-        public void NullableOfTMembers()
-        {
-            // Ensure calling a method on Nullable<T> works.
-            MethodInfo mi = GetMethod(typeof(int?), nameof(Nullable<int>.GetValueOrDefault));
-            Assert.Equal(42, mi.Invoke(42, null));
-        }
-
-        [Fact]
-        public void CopyBackWithByRefArgs()
-        {
-            object i = 42;
-            object[] args = new object[] { i };
-            GetMethod(typeof(CopyBackMethods), nameof(CopyBackMethods.IncrementByRef)).Invoke(null, args);
-            Assert.Equal(43, (int)args[0]);
-            Assert.NotSame(i, args[0]); // A copy should be made; a boxed instance should never be directly updated.
-
-            i = 42;
-            args = new object[] { i };
-            GetMethod(typeof(CopyBackMethods), nameof(CopyBackMethods.IncrementByNullableRef)).Invoke(null, args);
-            Assert.Equal(43, (int)args[0]);
-            Assert.NotSame(i, args[0]);
-
-            object o = null;
-            args = new object[] { o };
-            GetMethod(typeof(CopyBackMethods), nameof(CopyBackMethods.SetToNonNullByRef)).Invoke(null, args);
-            Assert.NotNull(args[0]);
-
-            o = new object();
-            args = new object[] { o };
-            GetMethod(typeof(CopyBackMethods), nameof(CopyBackMethods.SetToNullByRef)).Invoke(null, args);
-            Assert.Null(args[0]);
-        }
-
-        [Fact]
         [ActiveIssue("https://github.com/dotnet/runtime/issues/50957", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoInterpreter))]
         [ActiveIssue("https://github.com/dotnet/runtime/issues/69919", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
         public static void CallStackFrame_AggressiveInlining()
@@ -894,69 +698,6 @@ namespace System.Reflection.Tests
             Assert.Contains("TestAssembly", asm.ToString());
         }
 
-        [Fact]
-        private static unsafe void TestFunctionPointerDirect()
-        {
-            // Sanity checks for direct invocation.
-            void* fn = FunctionPointerMethods.GetFunctionPointer();
-            Assert.True(FunctionPointerMethods.GetFunctionPointer()(42));
-            Assert.True(FunctionPointerMethods.CallFcnPtr_IntPtr((IntPtr)fn, 42));
-            Assert.True(FunctionPointerMethods.CallFcnPtr_Void(fn, 42));
-            Assert.False(FunctionPointerMethods.GetFunctionPointer()(41));
-            Assert.False(FunctionPointerMethods.CallFcnPtr_IntPtr((IntPtr)fn, 41));
-            Assert.False(FunctionPointerMethods.CallFcnPtr_Void(fn, 41));
-        }
-
-        [Fact]
-        private static unsafe void TestFunctionPointerAsIntPtrArgType()
-        {
-            void* fn = FunctionPointerMethods.GetFunctionPointer();
-
-            MethodInfo m;
-
-            m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.CallFcnPtr_IntPtr));
-            Assert.True((bool)m.Invoke(null, new object[] { (IntPtr)fn, 42 }));
-            Assert.False((bool)m.Invoke(null, new object[] { (IntPtr)fn, 41 }));
-
-            m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.CallFcnPtr_Void));
-            Assert.True((bool)m.Invoke(null, new object[] { (IntPtr)fn, 42 }));
-            Assert.False((bool)m.Invoke(null, new object[] { (IntPtr)fn, 41 }));
-        }
-
-        [Fact]
-        private static unsafe void TestFunctionPointerAsUIntPtrArgType()
-        {
-            void* fn = FunctionPointerMethods.GetFunctionPointer();
-
-            MethodInfo m;
-
-            m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.CallFcnPtr_UIntPtr));
-            Assert.True((bool)m.Invoke(null, new object[] { (UIntPtr)fn, 42 }));
-            Assert.False((bool)m.Invoke(null, new object[] { (UIntPtr)fn, 41 }));
-
-            m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.CallFcnPtr_Void));
-            Assert.True((bool)m.Invoke(null, new object[] { (UIntPtr)fn, 42 }));
-            Assert.False((bool)m.Invoke(null, new object[] { (UIntPtr)fn, 41 }));
-        }
-
-        [Fact]
-        private static unsafe void TestFunctionPointerAsArgType()
-        {
-            void* fn = FunctionPointerMethods.GetFunctionPointer();
-            MethodInfo m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.CallFcnPtr_FP));
-            Assert.True((bool)m.Invoke(null, new object[] { (IntPtr)fn, 42 }));
-            Assert.False((bool)m.Invoke(null, new object[] { (IntPtr)fn, 41 }));
-        }
-
-        [Fact]
-        private static unsafe void TestFunctionPointerAsReturnType()
-        {
-            MethodInfo m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.GetFunctionPointer));
-            object ret = m.Invoke(null, null);
-            Assert.IsType<IntPtr>(ret);
-            Assert.True((IntPtr)ret != 0);
-        }
-
         //Methods for Reflection Metadata
         private void DummyMethod1(string str, int iValue, long lValue)
         {
@@ -965,11 +706,6 @@ namespace System.Reflection.Tests
         private void DummyMethod2()
         {
         }
-
-        private static MethodInfo GetMethod(Type type, string name)
-        {
-            return type.GetTypeInfo().GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).First(method => method.Name.Equals(name));
-        }
     }
 
 #pragma warning disable 0414
index 97c9865..94d7015 100644 (file)
@@ -2,13 +2,22 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System.Collections.Generic;
-using System.Linq;
 using Xunit;
 
 namespace System.Reflection.Tests
 {
-    public class MethodInvokerTests
+    /// <summary>
+    /// These tests use the shared tests from the base class with MethodInvoker.Invoke.
+    /// </summary>
+    public class MethodInvokerTests : MethodCommonTests
     {
+        public override object? Invoke(MethodInfo methodInfo, object? obj, object?[]? parameters)
+        {
+            return MethodInvoker.Create(methodInfo).Invoke(obj, new Span<object>(parameters));
+        }
+
+        protected override bool SupportsMissing => false;
+
         [Fact]
         public void NullTypeValidation()
         {
@@ -286,11 +295,6 @@ namespace System.Reflection.Tests
             Assert.Throws<TargetException>(() => invoker.Invoke(obj: null));
         }
 
-        private static MethodInfo GetMethod(Type type, string name)
-        {
-            return type.GetTypeInfo().GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).First(method => method.Name.Equals(name));
-        }
-
         public static IEnumerable<object[]> Invoke_TestData() => MethodInfoTests.Invoke_TestData();
 
         private class TestClass
index e6adafc..9553a7d 100644 (file)
     <RdXmlFile Include="default.rd.xml" />
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="$(CommonTestPath)System\Reflection\MockParameterInfo.cs"
-             Link="Common\System\Reflection\MockParameterInfo.cs" />
-    <Compile Include="$(CommonTestPath)System\MockType.cs"
-             Link="Common\System\MockType.cs" />
-    <Compile Include="$(CommonTestPath)System\IO\TempFile.cs"
-             Link="Common\System\IO\TempFile.cs" />
+    <Compile Include="$(CommonTestPath)System\Reflection\MockParameterInfo.cs" Link="Common\System\Reflection\MockParameterInfo.cs" />
+    <Compile Include="$(CommonTestPath)System\MockType.cs" Link="Common\System\MockType.cs" />
+    <Compile Include="$(CommonTestPath)System\IO\TempFile.cs" Link="Common\System\IO\TempFile.cs" />
     <Compile Include="AssemblyNameTests.cs" />
     <Compile Include="AssemblyTests.cs" />
+    <Compile Include="ConstructorCommonTests.cs" />
     <Compile Include="ConstructorInfoTests.cs" />
     <Compile Include="ConstructorInvokerTests.cs" />
     <Compile Include="CustomAttributeTests.cs" />
@@ -31,6 +29,7 @@
     <Compile Include="GetTypeTests.cs" />
     <Compile Include="ManifestResourceInfoTests.cs" />
     <Compile Include="MemberInfoTests.cs" />
+    <Compile Include="MethodCommonTests.cs" />
     <Compile Include="MethodInfoTests.cs" />
     <Compile Include="MethodInvokerTests.cs" />
     <Compile Include="ModuleTests.cs" />
@@ -42,8 +41,7 @@
     <Compile Include="ExceptionTests.cs" />
     <Compile Include="PointerTests.cs" />
     <Compile Include="TypeTests.Constraints.cs" />
-    <Compile Include="TestAssembly\EquivalentValueType.cs"
-             Link="EquivalentValueType.cs" />
+    <Compile Include="TestAssembly\EquivalentValueType.cs" Link="EquivalentValueType.cs" />
   </ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="Resources\EmbeddedImage.png">