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