@@ -678,6 +678,7 @@ HRESULT CLR_RT_ExecutionEngine::Execute(wchar_t *entryPointArgs, int maxContextS
678678 NANOCLR_CHECK_HRESULT (WaitForDebugger ());
679679
680680 // m_cctorThread is nullptr before call and inialized by the SpawnStaticConstructor
681+ // This will execute both non-generic and generic type static constructors
681682 SpawnStaticConstructor (m_cctorThread);
682683
683684 while (true )
@@ -809,6 +810,28 @@ void CLR_RT_ExecutionEngine::StaticConstructorTerminationCallback(void *arg)
809810 (void )arg;
810811
811812 NATIVE_PROFILE_CLR_CORE ();
813+
814+ // If the completed .cctor was for a generic type, mark it as executed
815+ CLR_RT_HeapBlock_Delegate *dlg = g_CLR_RT_ExecutionEngine.m_cctorThread ->m_dlg ;
816+ if (dlg != nullptr && dlg->m_genericTypeSpec .data != 0 )
817+ {
818+ // This was a generic type .cctor - compute hash and mark as executed
819+ CLR_RT_TypeSpec_Instance genericTypeInstance{};
820+ if (genericTypeInstance.InitializeFromIndex (dlg->m_genericTypeSpec ))
821+ {
822+ CLR_UINT32 hash = g_CLR_RT_TypeSystem.ComputeHashForClosedGenericType (genericTypeInstance);
823+ CLR_RT_GenericCctorExecutionRecord *record =
824+ g_CLR_RT_TypeSystem.FindOrCreateGenericCctorRecord (hash, nullptr );
825+
826+ if (record != nullptr )
827+ {
828+ // Clear scheduled flag and set executed flag
829+ record->m_flags &= ~CLR_RT_GenericCctorExecutionRecord::c_Scheduled;
830+ record->m_flags |= CLR_RT_GenericCctorExecutionRecord::c_Executed;
831+ }
832+ }
833+ }
834+
812835 g_CLR_RT_ExecutionEngine.SpawnStaticConstructor (g_CLR_RT_ExecutionEngine.m_cctorThread );
813836}
814837
@@ -965,10 +988,136 @@ bool CLR_RT_ExecutionEngine::SpawnStaticConstructorHelper(
965988 }
966989 }
967990
991+ // Set flag to indicate regular static constructors have been processed
968992 assembly->flags |= CLR_RT_Assembly::StaticConstructorsExecuted;
969993 return false ;
970994}
971995
996+ bool CLR_RT_ExecutionEngine::SpawnGenericTypeStaticConstructorsHelper (
997+ CLR_RT_Assembly *assembly,
998+ const CLR_RT_TypeSpec_Index &startTypeSpecIndex)
999+ {
1000+ NATIVE_PROFILE_CLR_CORE ();
1001+
1002+ _ASSERTE (m_cctorThread != nullptr );
1003+ _ASSERTE (m_cctorThread->CanThreadBeReused ());
1004+ _ASSERTE (assembly != nullptr );
1005+
1006+ // Crawl TypeSpecs in this assembly to find closed generic instantiations that need .cctor execution
1007+ int numTypeSpec = assembly->tablesSize [TBL_TypeSpec];
1008+
1009+ // Start from the specified TypeSpec index (to resume iteration after a .cctor completes)
1010+ CLR_UINT32 startIndex = startTypeSpecIndex.TypeSpec ();
1011+ CLR_RT_TypeSpec_CrossReference *tsCross = assembly->crossReferenceTypeSpec + startIndex;
1012+
1013+ for (int iTs = startIndex; iTs < numTypeSpec; iTs++, tsCross++)
1014+ {
1015+ // Build a TypeSpec_Instance to check if this is a closed generic instantiation
1016+ CLR_RT_TypeSpec_Instance genericTypeInstance{};
1017+ CLR_RT_TypeSpec_Index tsIndex;
1018+ tsIndex.Set (assembly->assemblyIndex , iTs);
1019+
1020+ if (!genericTypeInstance.InitializeFromIndex (tsIndex))
1021+ {
1022+ continue ;
1023+ }
1024+
1025+ // Only for closed generic instantiations (have genericTypeDef)
1026+ if (!genericTypeInstance.IsClosedGenericType ())
1027+ {
1028+ continue ;
1029+ }
1030+
1031+ // Get the generic type definition
1032+ CLR_RT_TypeDef_Index typeDef = genericTypeInstance.genericTypeDef ;
1033+
1034+ // Check if the generic type definition has a static constructor
1035+ CLR_RT_Assembly *ownerAsm = g_CLR_RT_TypeSystem.m_assemblies [typeDef.Assembly () - 1 ];
1036+ if (!ownerAsm->HasStaticConstructor (typeDef))
1037+ {
1038+ continue ;
1039+ }
1040+
1041+ // Find the static constructor method for this generic type definition
1042+ const CLR_RECORD_TYPEDEF *ownerTd = ownerAsm->GetTypeDef (typeDef.Type ());
1043+ const CLR_RECORD_METHODDEF *md = ownerAsm->GetMethodDef (ownerTd->firstMethod );
1044+
1045+ // Calculate total method count for this type
1046+ int methodCount = ownerTd->virtualMethodCount + ownerTd->instanceMethodCount + ownerTd->staticMethodCount ;
1047+
1048+ CLR_RT_MethodDef_Index cctorIndex;
1049+ bool foundCctor = false ;
1050+
1051+ for (int i = 0 ; i < methodCount; i++, md++)
1052+ {
1053+ if (md->flags & CLR_RECORD_METHODDEF::MD_StaticConstructor)
1054+ {
1055+ cctorIndex.Set (ownerAsm->assemblyIndex , ownerTd->firstMethod + i);
1056+ foundCctor = true ;
1057+ break ;
1058+ }
1059+ }
1060+
1061+ if (!foundCctor)
1062+ {
1063+ continue ;
1064+ }
1065+
1066+ // Compute hash for the closed generic type to check if .cctor already scheduled/executed
1067+ CLR_UINT32 hash = g_CLR_RT_TypeSystem.ComputeHashForClosedGenericType (genericTypeInstance);
1068+
1069+ // Find or create the .cctor execution record for this closed type
1070+ bool recordCreated = false ;
1071+ CLR_RT_GenericCctorExecutionRecord *record =
1072+ g_CLR_RT_TypeSystem.FindOrCreateGenericCctorRecord (hash, &recordCreated);
1073+
1074+ if (record == nullptr )
1075+ {
1076+ // Out of memory - skip this .cctor
1077+ continue ;
1078+ }
1079+
1080+ // Check if .cctor already scheduled or executed
1081+ if (record->m_flags &
1082+ (CLR_RT_GenericCctorExecutionRecord::c_Scheduled | CLR_RT_GenericCctorExecutionRecord::c_Executed))
1083+ {
1084+ // Already handled - skip to next TypeSpec
1085+ continue ;
1086+ }
1087+
1088+ // Mark as scheduled to prevent duplicate scheduling
1089+ record->m_flags |= CLR_RT_GenericCctorExecutionRecord::c_Scheduled;
1090+
1091+ // Create delegate for the generic type .cctor
1092+ CLR_RT_HeapBlock_Delegate *dlg;
1093+ CLR_RT_HeapBlock refDlg;
1094+
1095+ refDlg.SetObjectReference (nullptr );
1096+ CLR_RT_ProtectFromGC gc (refDlg);
1097+
1098+ if (SUCCEEDED (CLR_RT_HeapBlock_Delegate::CreateInstance (refDlg, cctorIndex, nullptr )))
1099+ {
1100+ dlg = refDlg.DereferenceDelegate ();
1101+
1102+ // Store the current closed generic TypeSpec index for correct resumption
1103+ dlg->m_genericTypeSpec = tsIndex;
1104+
1105+ if (SUCCEEDED (m_cctorThread->PushThreadProcDelegate (dlg)))
1106+ {
1107+ m_cctorThread->m_terminationCallback = StaticConstructorTerminationCallback;
1108+ return true ;
1109+ }
1110+ }
1111+
1112+ // If we failed to schedule, clear the scheduled flag
1113+ record->m_flags &= ~CLR_RT_GenericCctorExecutionRecord::c_Scheduled;
1114+ }
1115+
1116+ // No more generic type .cctors for this assembly - set flag
1117+ assembly->flags |= CLR_RT_Assembly::StaticGenericConstructorsExecuted;
1118+ return false ;
1119+ }
1120+
9721121void CLR_RT_ExecutionEngine::SpawnStaticConstructor (CLR_RT_Thread *&pCctorThread)
9731122{
9741123 NATIVE_PROFILE_CLR_CORE ();
@@ -988,37 +1137,91 @@ void CLR_RT_ExecutionEngine::SpawnStaticConstructor(CLR_RT_Thread *&pCctorThread
9881137 _ASSERTE (NANOCLR_INDEX_IS_VALID (index));
9891138 _SIDE_ASSERTE (inst.InitializeFromIndex (index));
9901139
991- // This is ok if index is no longer valid. SpawnStaticConstructorHelper will call FindNextStaticConstructor
992- // which will fail
993- index.data ++;
1140+ // Check if this is a generic type .cctor (has m_genericTypeSpec.data != 0 in the delegate)
1141+ if (dlg->m_genericTypeSpec .data != 0 )
1142+ {
1143+ // Extract the TypeSpec index from the delegate and increment to next TypeSpec
1144+ CLR_RT_TypeSpec_Index tsIndex = dlg->m_genericTypeSpec ;
1145+ CLR_RT_Assembly *assembly = g_CLR_RT_TypeSystem.m_assemblies [tsIndex.Assembly () - 1 ];
9941146
995- if (SpawnStaticConstructorHelper (inst.assembly , index))
996- return ;
1147+ // Increment to next TypeSpec (same pattern as regular .cctor)
1148+ tsIndex.data ++;
1149+
1150+ if (SpawnGenericTypeStaticConstructorsHelper (assembly, tsIndex))
1151+ {
1152+ return ;
1153+ }
1154+ }
1155+ else
1156+ {
1157+ // Regular static constructor - increment to next method index
1158+ index.data ++;
1159+
1160+ if (SpawnStaticConstructorHelper (inst.assembly , index))
1161+ {
1162+ return ;
1163+ }
1164+ }
9971165 }
9981166
9991167 // first, find the AppDomainAssembly to run. (what about appdomains!!!)
10001168 NANOCLR_FOREACH_ASSEMBLY (g_CLR_RT_TypeSystem)
10011169 {
1002- // Find an AppDomainAssembly that does not have it's static constructor bit set...
1170+ // Check if regular static constructors need to be executed
10031171 if ((pASSM->flags & CLR_RT_Assembly::StaticConstructorsExecuted) == 0 )
10041172 {
10051173 CLR_RT_MethodDef_Index index;
10061174 index.Set (pASSM->assemblyIndex , 0 );
1007- bool fDepedenciesRun = true ;
1175+ bool dependenciesSatisfied = true ;
10081176
1009- // Check that all dependent assemblies have had static constructors run.
1177+ // Check that all dependent assemblies have had regular static constructors run
10101178 CLR_RT_AssemblyRef_CrossReference *ar = pASSM->crossReferenceAssemblyRef ;
10111179 for (int i = 0 ; i < pASSM->tablesSize [TBL_AssemblyRef]; i++, ar++)
10121180 {
10131181 if ((ar->target ->flags & CLR_RT_Assembly::StaticConstructorsExecuted) == 0 )
10141182 {
1015- fDepedenciesRun = true ;
1183+ dependenciesSatisfied = false ;
10161184 break ;
10171185 }
10181186 }
10191187
1020- if (fDepedenciesRun && SpawnStaticConstructorHelper (pASSM, index))
1021- return ;
1188+ if (dependenciesSatisfied)
1189+ {
1190+ // Run regular static constructors for this assembly
1191+ if (SpawnStaticConstructorHelper (pASSM, index))
1192+ {
1193+ return ;
1194+ }
1195+ }
1196+ }
1197+
1198+ // Check if generic type static constructors need to be executed
1199+ if ((pASSM->flags & CLR_RT_Assembly::StaticGenericConstructorsExecuted) == 0 )
1200+ {
1201+ bool dependenciesSatisfied = true ;
1202+
1203+ // Check that all dependent assemblies have had regular static constructors run
1204+ CLR_RT_AssemblyRef_CrossReference *ar = pASSM->crossReferenceAssemblyRef ;
1205+ for (int i = 0 ; i < pASSM->tablesSize [TBL_AssemblyRef]; i++, ar++)
1206+ {
1207+ if ((ar->target ->flags & CLR_RT_Assembly::StaticConstructorsExecuted) == 0 )
1208+ {
1209+ dependenciesSatisfied = false ;
1210+ break ;
1211+ }
1212+ }
1213+
1214+ if (dependenciesSatisfied)
1215+ {
1216+ // Run generic type static constructors for this assembly (starting from index 0)
1217+ CLR_RT_TypeSpec_Index startIndex;
1218+ startIndex.Set (pASSM->assemblyIndex , 0 );
1219+
1220+ if (SpawnGenericTypeStaticConstructorsHelper (pASSM, startIndex))
1221+ {
1222+ return ;
1223+ }
1224+ }
10221225 }
10231226 }
10241227 NANOCLR_FOREACH_ASSEMBLY_END ();
0 commit comments