From bb63da9fa1eeac80a854c7ac493436cb38e2beb6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 26 Jul 2024 12:40:11 -0700 Subject: [PATCH] [release/9.0-preview7] Fix throwing exception when calling RunClassConstructor on a generic type with a static constructor (#105535) * Fix throwing exception when calling RunClassConstructor on a generic type with a static constructor https://github.com/dotnet/runtime/pull/99183 seems to have done away with assuming that a generic type's static constructor was always "initialized". As a result, if you call RunClassConstructor on it, the runtime would throw an exception. Fixes https://github.com/dotnet/runtime/issues/103891 * PR feedback * Apply suggestions from code review * More feedback * Shouldn't wing it --------- Co-authored-by: Steve Pfister Co-authored-by: Jan Kotas --- src/coreclr/vm/methodtable.cpp | 2 ++ src/coreclr/vm/reflectioninvocation.cpp | 3 ++- .../Runtime/CompilerServices/RuntimeHelpersTests.cs | 12 +++++++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 9209b3cc56ce7c..a8f6bd04b8dbb9 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -3768,6 +3768,8 @@ void MethodTable::CheckRunClassInitThrowing() if (IsSharedByGenericInstantiations()) return; + _ASSERTE(!ContainsGenericVariables()); + EnsureStaticDataAllocated(); DoRunClassInitThrowing(); } diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index aec38501adf60d..eeef84d45df0e5 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -1306,7 +1306,8 @@ extern "C" void QCALLTYPE ReflectionInvocation_RunClassConstructor(QCall::TypeHa return; MethodTable *pMT = typeHnd.AsMethodTable(); - if (pMT->IsClassInited()) + // The ContainsGenericVariables check is to preserve back-compat where we assume the generic type is already initialized + if (pMT->IsClassInited() || pMT->ContainsGenericVariables()) return; BEGIN_QCALL; diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs index 2fd5267409cb6b..13ce28e4f233cd 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs @@ -6,6 +6,7 @@ using System.IO; using System.Reflection; using System.Runtime.InteropServices; +using System.Threading; using Xunit; namespace System.Runtime.CompilerServices.Tests @@ -101,7 +102,8 @@ public static void RunClassConstructor() RuntimeTypeHandle t = typeof(HasCctor).TypeHandle; RuntimeHelpers.RunClassConstructor(t); Assert.Equal("Hello", HasCctorReceiver.S); - return; + // Should not throw + RuntimeHelpers.RunClassConstructor(typeof(GenericHasCctor<>).TypeHandle); } internal class HasCctor @@ -117,6 +119,14 @@ internal class HasCctorReceiver public static string S; } + internal class GenericHasCctor + { + static GenericHasCctor() + { + Thread.Yield(); // Make sure the preinitialization optimization doesn't eat this. + } + } + [Fact] public static void PrepareMethod() {