Skip to content
1 change: 1 addition & 0 deletions src/CLR/Core/CLR_RT_HeapBlock_Delegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ HRESULT CLR_RT_HeapBlock_Delegate::CreateInstance(
#endif

dlg->m_object.SetObjectReference(nullptr);
dlg->m_genericTypeSpec.Clear();

#if defined(NANOCLR_APPDOMAINS)
dlg->m_appDomain = g_CLR_RT_ExecutionEngine.GetCurrentAppDomain();
Expand Down
175 changes: 164 additions & 11 deletions src/CLR/Core/Execution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,7 @@ HRESULT CLR_RT_ExecutionEngine::Execute(wchar_t *entryPointArgs, int maxContextS
NANOCLR_CHECK_HRESULT(WaitForDebugger());

// m_cctorThread is nullptr before call and inialized by the SpawnStaticConstructor
// This will execute both non-generic and generic type static constructors
SpawnStaticConstructor(m_cctorThread);

while (true)
Expand Down Expand Up @@ -965,10 +966,108 @@ bool CLR_RT_ExecutionEngine::SpawnStaticConstructorHelper(
}
}

// Set flag to indicate regular static constructors have been processed
assembly->flags |= CLR_RT_Assembly::StaticConstructorsExecuted;
return false;
}

bool CLR_RT_ExecutionEngine::SpawnGenericTypeStaticConstructorsHelper(
CLR_RT_Assembly *assembly,
const CLR_RT_TypeSpec_Index &startTypeSpecIndex)
{
NATIVE_PROFILE_CLR_CORE();

_ASSERTE(m_cctorThread != nullptr);
_ASSERTE(m_cctorThread->CanThreadBeReused());
_ASSERTE(assembly != nullptr);

// Crawl TypeSpecs in this assembly to find closed generic instantiations that need .cctor execution
int numTypeSpec = assembly->tablesSize[TBL_TypeSpec];

// Start from the specified TypeSpec index (to resume iteration after a .cctor completes)
CLR_UINT32 startIndex = startTypeSpecIndex.TypeSpec();
CLR_RT_TypeSpec_CrossReference *tsCross = assembly->crossReferenceTypeSpec + startIndex;

for (int iTs = startIndex; iTs < numTypeSpec; iTs++, tsCross++)
{
// Build a TypeSpec_Instance to check if this is a closed generic instantiation
CLR_RT_TypeSpec_Instance genericTypeInstance{};
CLR_RT_TypeSpec_Index tsIndex;
tsIndex.Set(assembly->assemblyIndex, iTs);

if (!genericTypeInstance.InitializeFromIndex(tsIndex))
{
continue;
}

// Only for closed generic instantiations (have genericTypeDef)
if (!genericTypeInstance.IsClosedGenericType())
{
continue;
}

// Get the generic type definition
CLR_RT_TypeDef_Index genericTypeDef = genericTypeInstance.genericTypeDef;

// Check if the generic type definition has a static constructor
CLR_RT_Assembly *ownerAsm = g_CLR_RT_TypeSystem.m_assemblies[genericTypeDef.Assembly() - 1];
if (!ownerAsm->HasStaticConstructor(genericTypeDef))
{
continue;
}

// Find the static constructor method for this generic type definition
const CLR_RECORD_TYPEDEF *ownerTd = ownerAsm->GetTypeDef(genericTypeDef.Type());
const CLR_RECORD_METHODDEF *md = ownerAsm->GetMethodDef(ownerTd->firstMethod);

// Calculate total method count for this type
int methodCount = ownerTd->virtualMethodCount + ownerTd->instanceMethodCount + ownerTd->staticMethodCount;

CLR_RT_MethodDef_Index cctorIndex;
bool foundCctor = false;

for (int i = 0; i < methodCount; i++, md++)
{
if (md->flags & CLR_RECORD_METHODDEF::MD_StaticConstructor)
{
cctorIndex.Set(ownerAsm->assemblyIndex, ownerTd->firstMethod + i);
foundCctor = true;
break;
}
}

if (!foundCctor)
{
continue;
}

// Create delegate for the generic type .cctor
CLR_RT_HeapBlock_Delegate *dlg;
CLR_RT_HeapBlock refDlg;

refDlg.SetObjectReference(nullptr);
CLR_RT_ProtectFromGC gc(refDlg);

if (SUCCEEDED(CLR_RT_HeapBlock_Delegate::CreateInstance(refDlg, cctorIndex, nullptr)))
{
dlg = refDlg.DereferenceDelegate();

// Store the TypeSpec index in the delegate (points to persistent assembly metadata)
dlg->m_genericTypeSpec = tsCross->genericType;

if (SUCCEEDED(m_cctorThread->PushThreadProcDelegate(dlg)))
{
m_cctorThread->m_terminationCallback = StaticConstructorTerminationCallback;
return true;
}
}
}

// No more generic type .cctors for this assembly - set flag
assembly->flags |= CLR_RT_Assembly::StaticGenericConstructorsExecuted;
return false;
}

void CLR_RT_ExecutionEngine::SpawnStaticConstructor(CLR_RT_Thread *&pCctorThread)
{
NATIVE_PROFILE_CLR_CORE();
Expand All @@ -988,37 +1087,91 @@ void CLR_RT_ExecutionEngine::SpawnStaticConstructor(CLR_RT_Thread *&pCctorThread
_ASSERTE(NANOCLR_INDEX_IS_VALID(index));
_SIDE_ASSERTE(inst.InitializeFromIndex(index));

// This is ok if index is no longer valid. SpawnStaticConstructorHelper will call FindNextStaticConstructor
// which will fail
index.data++;
// Check if this is a generic type .cctor (has m_genericTypeSpec.data != 0 in the delegate)
if (dlg->m_genericTypeSpec.data != 0)
{
// Extract the TypeSpec index from the delegate and increment to next TypeSpec
CLR_RT_TypeSpec_Index tsIndex = dlg->m_genericTypeSpec;
CLR_RT_Assembly *assembly = g_CLR_RT_TypeSystem.m_assemblies[tsIndex.Assembly() - 1];

if (SpawnStaticConstructorHelper(inst.assembly, index))
return;
// Increment to next TypeSpec (same pattern as regular .cctor)
tsIndex.data++;

if (SpawnGenericTypeStaticConstructorsHelper(assembly, tsIndex))
{
return;
}
}
else
{
// Regular static constructor - increment to next method index
index.data++;

if (SpawnStaticConstructorHelper(inst.assembly, index))
{
return;
}
}
}

// first, find the AppDomainAssembly to run. (what about appdomains!!!)
NANOCLR_FOREACH_ASSEMBLY(g_CLR_RT_TypeSystem)
{
// Find an AppDomainAssembly that does not have it's static constructor bit set...
// Check if regular static constructors need to be executed
if ((pASSM->flags & CLR_RT_Assembly::StaticConstructorsExecuted) == 0)
{
CLR_RT_MethodDef_Index index;
index.Set(pASSM->assemblyIndex, 0);
bool fDepedenciesRun = true;
bool dependenciesSatisfied = true;

// Check that all dependent assemblies have had static constructors run.
// Check that all dependent assemblies have had regular static constructors run
CLR_RT_AssemblyRef_CrossReference *ar = pASSM->crossReferenceAssemblyRef;
for (int i = 0; i < pASSM->tablesSize[TBL_AssemblyRef]; i++, ar++)
{
if ((ar->target->flags & CLR_RT_Assembly::StaticConstructorsExecuted) == 0)
{
fDepedenciesRun = true;
dependenciesSatisfied = false;
break;
}
}

if (fDepedenciesRun && SpawnStaticConstructorHelper(pASSM, index))
return;
if (dependenciesSatisfied)
{
// Run regular static constructors for this assembly
if (SpawnStaticConstructorHelper(pASSM, index))
{
return;
}
}
}

// Check if generic type static constructors need to be executed
if ((pASSM->flags & CLR_RT_Assembly::StaticGenericConstructorsExecuted) == 0)
{
bool dependenciesSatisfied = true;

// Check that all dependent assemblies have had regular static constructors run
CLR_RT_AssemblyRef_CrossReference *ar = pASSM->crossReferenceAssemblyRef;
for (int i = 0; i < pASSM->tablesSize[TBL_AssemblyRef]; i++, ar++)
{
if ((ar->target->flags & CLR_RT_Assembly::StaticConstructorsExecuted) == 0)
{
dependenciesSatisfied = false;
break;
}
}

if (dependenciesSatisfied)
{
// Run generic type static constructors for this assembly (starting from index 0)
CLR_RT_TypeSpec_Index startIndex;
startIndex.Set(pASSM->assemblyIndex, 0);

if (SpawnGenericTypeStaticConstructorsHelper(pASSM, startIndex))
{
return;
}
}
}
}
NANOCLR_FOREACH_ASSEMBLY_END();
Expand Down
10 changes: 8 additions & 2 deletions src/CLR/Core/Thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,12 @@ HRESULT CLR_RT_Thread::PushThreadProcDelegate(CLR_RT_HeapBlock_Delegate *pDelega
NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE);
}

// Set generic context if the delegate has a TypeSpec stored (for generic type static constructors)
if (pDelegate->m_genericTypeSpec.data != 0)
{
inst.genericType = &pDelegate->m_genericTypeSpec;
}

#if defined(NANOCLR_APPDOMAINS)

if (!pDelegate->m_appDomain->IsLoaded())
Expand Down Expand Up @@ -821,8 +827,8 @@ HRESULT CLR_RT_Thread::ProcessException_Phase1()
us.GetPhase() < UnwindStack::p_1_SearchingForHandler_2_SentUsersChance && stack->m_IP)
{
// We have a debugger attached and we need to send some messages before we start searching.
// These messages should only get sent when the search reaches managed code. Stack::Push sets m_IP to nullptr
// for native code, so therefore we need IP to be non-nullptr
// These messages should only get sent when the search reaches managed code. Stack::Push sets m_IP to
// nullptr for native code, so therefore we need IP to be non-nullptr

us.m_handlerStack = stack;

Expand Down
24 changes: 24 additions & 0 deletions src/CLR/Core/TypeSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5895,6 +5895,30 @@ bool CLR_RT_Assembly::FindNextStaticConstructor(CLR_RT_MethodDef_Index &index)
return false;
}

bool CLR_RT_Assembly::HasStaticConstructor(const CLR_RT_TypeDef_Index &genericTypeDef)
{
NATIVE_PROFILE_CLR_CORE();

CLR_RT_Assembly *ownerAsm = g_CLR_RT_TypeSystem.m_assemblies[genericTypeDef.Assembly() - 1];
const CLR_RECORD_TYPEDEF *ownerTd = ownerAsm->GetTypeDef(genericTypeDef.Type());

// Calculate total method count for this type
int methodCount = ownerTd->virtualMethodCount + ownerTd->instanceMethodCount + ownerTd->staticMethodCount;

// Iterate through all methods of the generic type definition
const CLR_RECORD_METHODDEF *md = ownerAsm->GetMethodDef(ownerTd->firstMethod);
for (int i = 0; i < methodCount; i++, md++)
{
// Check if this is a static constructor
if (md->flags & CLR_RECORD_METHODDEF::MD_StaticConstructor)
{
return true;
}
}

return false;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

HRESULT CLR_RT_Assembly::ResolveComputeHashes()
Expand Down
5 changes: 5 additions & 0 deletions src/CLR/Include/nanoCLR_Runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -1339,6 +1339,7 @@ struct CLR_RT_Assembly : public CLR_RT_HeapBlock_Node // EVENT HEAP - NO RELOCAT
static const CLR_UINT32 Deployed = 0x00000008;
static const CLR_UINT32 PreparingForExecution = 0x00000010;
static const CLR_UINT32 StaticConstructorsExecuted = 0x00000020;
static const CLR_UINT32 StaticGenericConstructorsExecuted = 0x00000040;
// this flag should be set when the m_header was malloc'ed
static const CLR_UINT32 FreeOnDestroy = 0x00000100;

Expand Down Expand Up @@ -1486,6 +1487,7 @@ struct CLR_RT_Assembly : public CLR_RT_HeapBlock_Node // EVENT HEAP - NO RELOCAT
CLR_UINT32 &assmIndex);

bool FindNextStaticConstructor(CLR_RT_MethodDef_Index &index);
bool HasStaticConstructor(const CLR_RT_TypeDef_Index &genericTypeDef);

bool FindMethodBoundaries(CLR_INDEX i, CLR_OFFSET &start, CLR_OFFSET &end);

Expand Down Expand Up @@ -4231,6 +4233,9 @@ struct CLR_RT_ExecutionEngine
const CLR_RT_MethodDef_Index &index);
#else
bool SpawnStaticConstructorHelper(CLR_RT_Assembly *assembly, const CLR_RT_MethodDef_Index &index);
bool SpawnGenericTypeStaticConstructorsHelper(
CLR_RT_Assembly *assembly,
const CLR_RT_TypeSpec_Index &startTypeSpecIndex);
#endif
static void FinalizerTerminationCallback(void *arg);
static void StaticConstructorTerminationCallback(void *arg);
Expand Down
3 changes: 3 additions & 0 deletions src/CLR/Include/nanoCLR_Runtime__HeapBlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -1910,6 +1910,9 @@ struct CLR_RT_HeapBlock_Delegate : public CLR_RT_HeapBlock_Node // OBJECT HEAP -
CLR_RT_AppDomain *m_appDomain;
#endif

// Optional TypeSpec index for generic type static constructors (data == 0 means not set)
CLR_RT_TypeSpec_Index m_genericTypeSpec;

//--//

const CLR_RT_MethodDef_Index &DelegateFtn() const
Expand Down