diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.cs index 7f3c1a227e371..3fad9dc8dba85 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.cs @@ -155,7 +155,7 @@ public object Invoke(object? arg1, object? arg2, object? arg3, object? arg4) 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(); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvoker.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvoker.cs index 66a35dbd39c74..0c8d9c5958070 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvoker.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvoker.cs @@ -66,7 +66,12 @@ public static MethodInvoker Create(MethodBase method) { // 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 @@ private MethodInvoker(MethodBase method, RuntimeType[] argumentTypes) 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(); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvokerCommon.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvokerCommon.cs index 191228b688faa..813e89f80f834 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvokerCommon.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvokerCommon.cs @@ -18,13 +18,14 @@ internal static void Initialize( { 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 @@ internal static void Initialize( } } + 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; + } + /// /// Confirm member invocation has an instance and is of the correct type /// diff --git a/src/libraries/System.Reflection/tests/ConstructorCommonTests.cs b/src/libraries/System.Reflection/tests/ConstructorCommonTests.cs new file mode 100644 index 0000000000000..5c71f79da014c --- /dev/null +++ b/src/libraries/System.Reflection/tests/ConstructorCommonTests.cs @@ -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 +{ + /// + /// These tests are shared with ConstructorInfo.Invoke and ConstructorInvoker.Invoke by using + /// the abstract Invoke(...) methods below. + /// + public abstract class ConstructorCommonTests + { + public abstract object Invoke(ConstructorInfo constructorInfo, object?[]? parameters); + + protected abstract bool IsExceptionWrapped { get; } + + /// + /// Invoke constructor on an existing instance. Should return null. + /// + 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(() => 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(() => Invoke(constructors[0], new object[] { length })); + Assert.IsType(ex.InnerException); + } + else + { + Assert.Throws(() => 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(() => Invoke(constructors[2], new object[0])); + } + + [Fact] + public void Invoke_ParameterMismatch_ThrowsTargetParameterCountException() + { + ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors)); + Assert.Throws(() => (ClassWith3Constructors)Invoke(constructors[2], new object[] { 121 })); + } + + [Fact] + public void Invoke_ParameterWrongType_ThrowsArgumentException() + { + ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors)); + AssertExtensions.Throws(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(() => 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(() => (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); + } + } +} diff --git a/src/libraries/System.Reflection/tests/ConstructorInfoTests.cs b/src/libraries/System.Reflection/tests/ConstructorInfoTests.cs index 34d04906a8055..37498347ac48f 100644 --- a/src/libraries/System.Reflection/tests/ConstructorInfoTests.cs +++ b/src/libraries/System.Reflection/tests/ConstructorInfoTests.cs @@ -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 + /// + /// These tests use the shared tests from the base class with ConstructorInfo.Invoke. + /// + 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 @@ public void GetHashCodeTest() } } - [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() { @@ -88,44 +93,6 @@ public void Invoke_StaticConstructorMultipleTimes() Assert.Equal(1, ClassWithStaticConstructorThatIsCalledMultipleTimesViaReflection.VisibleStatics.s_cctorCallCount); } - [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(() => 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(() => constructors[0].Invoke(new object[] { length })); - Assert.IsType(ex.InnerException); - } - } - [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/67531", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))] public void Invoke_TwoDimensionalArray_CustomBinder_IncorrectTypeArguments() @@ -138,23 +105,6 @@ public void Invoke_TwoDimensionalArray_CustomBinder_IncorrectTypeArguments() Assert.True(args[1] is int); } - [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() @@ -169,66 +119,6 @@ public void Invoke_TwoParameters_CustomBinder_IncorrectTypeArgument() Assert.True(args[1] is string); } - [Fact] - public void Invoke_NoParameters_ThowsTargetParameterCountException() - { - ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors)); - Assert.Throws(() => constructors[2].Invoke(new object[0])); - } - - [Fact] - public void Invoke_ParameterMismatch_ThrowsTargetParameterCountException() - { - ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors)); - Assert.Throws(() => (ClassWith3Constructors)constructors[2].Invoke(new object[] { 121 })); - } - - [Fact] - public void Invoke_ParameterWrongType_ThrowsArgumentException() - { - ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors)); - AssertExtensions.Throws(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(() => (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() { @@ -243,9 +133,18 @@ public void IsPublic() 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 @@ public static class ClassWithStaticConstructor 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; diff --git a/src/libraries/System.Reflection/tests/ConstructorInvokerTests.cs b/src/libraries/System.Reflection/tests/ConstructorInvokerTests.cs index 86fa1c4012c27..f5313bde57984 100644 --- a/src/libraries/System.Reflection/tests/ConstructorInvokerTests.cs +++ b/src/libraries/System.Reflection/tests/ConstructorInvokerTests.cs @@ -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 + /// + /// These tests use the shared tests from the base class with ConstructorInvoker.Invoke. + /// + public sealed class ConstructorInvokerTests : ConstructorCommonTests { + public override object Invoke(ConstructorInfo constructorInfo, object?[]? parameters) + { + return ConstructorInvoker.Create(constructorInfo).Invoke(new Span(parameters)); + } + + public override object? Invoke(ConstructorInfo constructorInfo, object obj, object?[]? parameters) + { + return MethodInvoker.Create(constructorInfo).Invoke(obj, new Span(parameters)); + } + + protected override bool IsExceptionWrapped => false; [Fact] public void Args_0() @@ -162,16 +175,13 @@ public void ThrowsNonWrappedException_5() } [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(() => Invoke(constructors[0], null, new object[] { })); } private class TestClass diff --git a/src/libraries/System.Reflection/tests/InvokeEmit/System.Reflection.InvokeEmit.Tests.csproj b/src/libraries/System.Reflection/tests/InvokeEmit/System.Reflection.InvokeEmit.Tests.csproj index 7b1e61038ccb5..757b631140f2d 100644 --- a/src/libraries/System.Reflection/tests/InvokeEmit/System.Reflection.InvokeEmit.Tests.csproj +++ b/src/libraries/System.Reflection/tests/InvokeEmit/System.Reflection.InvokeEmit.Tests.csproj @@ -7,8 +7,12 @@ + + + + diff --git a/src/libraries/System.Reflection/tests/InvokeInterpreted/System.Reflection.InvokeInterpreted.Tests.csproj b/src/libraries/System.Reflection/tests/InvokeInterpreted/System.Reflection.InvokeInterpreted.Tests.csproj index 64dc87d71c086..5d71379e2d5ca 100644 --- a/src/libraries/System.Reflection/tests/InvokeInterpreted/System.Reflection.InvokeInterpreted.Tests.csproj +++ b/src/libraries/System.Reflection/tests/InvokeInterpreted/System.Reflection.InvokeInterpreted.Tests.csproj @@ -7,8 +7,12 @@ + + + + diff --git a/src/libraries/System.Reflection/tests/MethodCommonTests.cs b/src/libraries/System.Reflection/tests/MethodCommonTests.cs new file mode 100644 index 0000000000000..b2e8e2f84fbc2 --- /dev/null +++ b/src/libraries/System.Reflection/tests/MethodCommonTests.cs @@ -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 +{ + /// + /// These tests are shared with MethodInfo.Invoke and MethodInvoker.Invoke by using + /// the abstract Invoke(...) method below. + /// + 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(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(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(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(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(() => 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 works. + MethodInfo mi = GetMethod(typeof(int?), nameof(Nullable.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(ret); + Assert.True((IntPtr)ret != 0); + } + } +} diff --git a/src/libraries/System.Reflection/tests/MethodInfoTests.cs b/src/libraries/System.Reflection/tests/MethodInfoTests.cs index 769fc94266a08..341fb7fcb9de1 100644 --- a/src/libraries/System.Reflection/tests/MethodInfoTests.cs +++ b/src/libraries/System.Reflection/tests/MethodInfoTests.cs @@ -6,12 +6,21 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Xunit; -using Xunit.Sdk; namespace System.Reflection.Tests { - public class MethodInfoTests + /// + /// These tests use the shared tests from the base class with MethodInfo.Invoke. + /// + 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 @@ public static IEnumerable Invoke_TestData() [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 @@ public void Invoke(Type methodDeclaringType, string methodName, object obj, obje [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 @@ public void ToStringTest_ByMethodInfo(MethodInfo methodInfo, string expected) 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(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(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(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(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(() => 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(() => method.Invoke(null, new object?[] { Type.Missing })); - } - public static IEnumerable MethodNameAndArguments() { yield return new object[] { nameof(Sample.DefaultString), "Hello", "Hi" }; @@ -805,68 +671,6 @@ public static void InvokeCopiesBackMissingParameterAndArgument() Assert.Null(args[0]); } - [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 works. - MethodInfo mi = GetMethod(typeof(int?), nameof(Nullable.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))] @@ -894,69 +698,6 @@ private static void SecondCall(MethodInfo mi) 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(ret); - Assert.True((IntPtr)ret != 0); - } - //Methods for Reflection Metadata private void DummyMethod1(string str, int iValue, long lValue) { @@ -965,11 +706,6 @@ private void DummyMethod1(string str, int iValue, long lValue) 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 diff --git a/src/libraries/System.Reflection/tests/MethodInvokerTests.cs b/src/libraries/System.Reflection/tests/MethodInvokerTests.cs index 97c9865a64fda..94d701507eac1 100644 --- a/src/libraries/System.Reflection/tests/MethodInvokerTests.cs +++ b/src/libraries/System.Reflection/tests/MethodInvokerTests.cs @@ -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 + /// + /// These tests use the shared tests from the base class with MethodInvoker.Invoke. + /// + public class MethodInvokerTests : MethodCommonTests { + public override object? Invoke(MethodInfo methodInfo, object? obj, object?[]? parameters) + { + return MethodInvoker.Create(methodInfo).Invoke(obj, new Span(parameters)); + } + + protected override bool SupportsMissing => false; + [Fact] public void NullTypeValidation() { @@ -286,11 +295,6 @@ public void VerifyThisObj_Null() Assert.Throws(() => 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 Invoke_TestData() => MethodInfoTests.Invoke_TestData(); private class TestClass diff --git a/src/libraries/System.Reflection/tests/System.Reflection.Tests.csproj b/src/libraries/System.Reflection/tests/System.Reflection.Tests.csproj index e6adafcbaac1d..9553a7dacc482 100644 --- a/src/libraries/System.Reflection/tests/System.Reflection.Tests.csproj +++ b/src/libraries/System.Reflection/tests/System.Reflection.Tests.csproj @@ -14,14 +14,12 @@ - - - + + + + @@ -31,6 +29,7 @@ + @@ -42,8 +41,7 @@ - +