From c630b82ae533c16fcc87a30e536f0ecf3234b4ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Tue, 25 Nov 2025 03:51:18 +0000 Subject: [PATCH 01/44] Implement localloc IL instruction - Add handler for instruction. Add storage pointers to stack frame to allow dealocation on moethod return. --- src/CLR/Core/CLR_RT_StackFrame.cpp | 42 ++++++++++++++++++++++++++++++ src/CLR/Core/Interpreter.cpp | 36 ++++++++++++++++++++++++- src/CLR/Include/nanoCLR_Runtime.h | 11 ++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/CLR/Core/CLR_RT_StackFrame.cpp b/src/CLR/Core/CLR_RT_StackFrame.cpp index 3a6678dd2e..ac69469536 100644 --- a/src/CLR/Core/CLR_RT_StackFrame.cpp +++ b/src/CLR/Core/CLR_RT_StackFrame.cpp @@ -120,6 +120,14 @@ HRESULT CLR_RT_StackFrame::Push(CLR_RT_Thread *th, const CLR_RT_MethodDef_Instan // // Initialize generic type context storage to invalid stack->m_genericTypeSpecStorage.Clear(); + + // initialize localloc elements + stack->m_localAllocCount = 0; + for (CLR_INT32 i = 0; i < CLR_RT_StackFrame::c_Max_Localloc_Count; i++) + { + stack->m_localAllocs[i] = nullptr; + } + // #ifndef NANOCLR_NO_IL_INLINE stack->m_inlineFrame = nullptr; @@ -323,6 +331,11 @@ bool CLR_RT_StackFrame::PushInline( m_inlineFrame->m_frame.m_call = m_call; m_inlineFrame->m_frame.m_evalStack = m_evalStack; m_inlineFrame->m_frame.m_evalPos = pThis; + m_inlineFrame->m_frame.m_localAllocCount = m_localAllocCount; + for (CLR_INT32 i = 0; i < c_Max_Localloc_Count; i++) + { + m_inlineFrame->m_frame.m_localAllocs[i] = m_localAllocs[i]; + } // increment the evalPos pointer so that we don't corrupt the real stack evalPos++; @@ -338,6 +351,13 @@ bool CLR_RT_StackFrame::PushInline( m_IPstart = ip; m_IP = ip; + // initialize localloc for the inline frame + m_localAllocCount = 0; + for (CLR_INT32 i = 0; i < c_Max_Localloc_Count; i++) + { + m_localAllocs[i] = nullptr; + } + if (md->localsCount) { g_CLR_RT_ExecutionEngine.InitializeLocals(m_locals, calleeInst); @@ -412,6 +432,11 @@ void CLR_RT_StackFrame::RestoreFromInlineStack() m_IPstart = m_inlineFrame->m_frame.m_IPStart; m_evalStack = m_inlineFrame->m_frame.m_evalStack; m_evalStackPos = m_inlineFrame->m_frame.m_evalPos; + m_localAllocCount = m_inlineFrame->m_frame.m_localAllocCount; + for (CLR_INT32 i = 0; i < c_Max_Localloc_Count; i++) + { + m_localAllocs[i] = m_inlineFrame->m_frame.m_localAllocs[i]; + } } void CLR_RT_StackFrame::RestoreStack(CLR_RT_InlineFrame &frame) @@ -424,6 +449,11 @@ void CLR_RT_StackFrame::RestoreStack(CLR_RT_InlineFrame &frame) m_evalStack = frame.m_evalStack; m_evalStackPos = frame.m_evalPos; m_evalStackEnd -= m_call.target->localsCount; + m_localAllocCount = frame.m_localAllocCount; + for (CLR_INT32 i = 0; i < c_Max_Localloc_Count; i++) + { + m_localAllocs[i] = frame.m_localAllocs[i]; + } } void CLR_RT_StackFrame::SaveStack(CLR_RT_InlineFrame &frame) @@ -435,6 +465,11 @@ void CLR_RT_StackFrame::SaveStack(CLR_RT_InlineFrame &frame) frame.m_IPStart = m_IPstart; frame.m_evalPos = m_evalStackPos; frame.m_evalStack = m_evalStack; + frame.m_localAllocCount = m_localAllocCount; + for (CLR_INT32 i = 0; i < c_Max_Localloc_Count; i++) + { + frame.m_localAllocs[i] = m_localAllocs[i]; + } } #endif @@ -822,6 +857,13 @@ void CLR_RT_StackFrame::Pop() { NATIVE_PROFILE_CLR_CORE(); + // Clear localloc references before popping + m_localAllocCount = 0; + for (CLR_INT32 i = 0; i < c_Max_Localloc_Count; i++) + { + m_localAllocs[i] = nullptr; + } + #if defined(NANOCLR_PROFILE_NEW_CALLS) { // diff --git a/src/CLR/Core/Interpreter.cpp b/src/CLR/Core/Interpreter.cpp index 9fab21ea7d..3382b278b4 100644 --- a/src/CLR/Core/Interpreter.cpp +++ b/src/CLR/Core/Interpreter.cpp @@ -4204,6 +4204,41 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) //----------------------------------------------------------------------------------------------------------// + OPDEF(CEE_LOCALLOC, "localloc", PopI, PushI, InlineNone, IPrimitive, 2, 0xFE, 0x0F, NEXT) + { + CLR_UINT32 size = evalPos[0].NumericByRef().u4; + + evalPos--; + CHECKSTACK(stack, evalPos); + + // Create the byte array + CLR_RT_HeapBlock tempArray; + CLR_RT_TypeDef_Index byteType = g_CLR_RT_WellKnownTypes.UInt8; + + NANOCLR_CHECK_HRESULT(CLR_RT_HeapBlock_Array::CreateInstance(tempArray, size, byteType)); + + CLR_RT_ProtectFromGC gc(tempArray); + CLR_RT_HeapBlock_Array *array = tempArray.DereferenceArray(); + + // store pointer for allocated array, if enough room + // In localloc: + if (stack->m_localAllocCount >= CLR_RT_StackFrame::c_Max_Localloc_Count) + { + NANOCLR_SET_AND_LEAVE(CLR_E_STACK_OVERFLOW); + } + + stack->m_localAllocs[stack->m_localAllocCount++] = array; + + evalPos++; + CHECKSTACK(stack, evalPos); + + // deviating from ECMA-335: we return the array reference instead of a raw pointer + evalPos[0].SetObjectReference(array); + break; + } + + //----------------------------------------------------------------------------------------------------------// + ////////////////////////////////////////////////////////////////////////////////////////// // // These opcodes do nothing... @@ -4224,7 +4259,6 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) OPDEF(CEE_INITBLK, "initblk", PopI + PopI + PopI, Push0, InlineNone, IPrimitive, 2, 0xFE, 0x18, NEXT) OPDEF(CEE_CALLI, "calli", VarPop, VarPush, InlineSig, IPrimitive, 1, 0xFF, 0x29, CALL) OPDEF(CEE_CKFINITE, "ckfinite", Pop1, PushR8, InlineNone, IPrimitive, 1, 0xFF, 0xC3, NEXT) - OPDEF(CEE_LOCALLOC, "localloc", PopI, PushI, InlineNone, IPrimitive, 2, 0xFE, 0x0F, NEXT) OPDEF(CEE_MKREFANY, "mkrefany", PopI, Push1, InlineType, IPrimitive, 1, 0xFF, 0xC6, NEXT) OPDEF(CEE_REFANYTYPE, "refanytype", Pop1, PushI, InlineNone, IPrimitive, 2, 0xFE, 0x1D, NEXT) OPDEF(CEE_REFANYVAL, "refanyval", Pop1, PushI, InlineType, IPrimitive, 1, 0xFF, 0xC2, NEXT) diff --git a/src/CLR/Include/nanoCLR_Runtime.h b/src/CLR/Include/nanoCLR_Runtime.h index bcda8ff63b..456f750c23 100644 --- a/src/CLR/Include/nanoCLR_Runtime.h +++ b/src/CLR/Include/nanoCLR_Runtime.h @@ -2529,6 +2529,9 @@ struct CLR_RT_HeapCluster : public CLR_RT_HeapBlock_Node // EVENT HEAP - NO RELO //--// +// maximum number of local allocations per stack frame +#define MAX_LOCALALLOC_COUNT 4 + #ifndef NANOCLR_NO_IL_INLINE struct CLR_RT_InlineFrame { @@ -2539,6 +2542,8 @@ struct CLR_RT_InlineFrame CLR_RT_MethodDef_Instance m_call; CLR_PMETADATA m_IP; CLR_PMETADATA m_IPStart; + CLR_UINT8 m_localAllocCount; + CLR_RT_HeapBlock_Array *m_localAllocs[MAX_LOCALALLOC_COUNT]; }; struct CLR_RT_InlineBuffer @@ -2562,6 +2567,9 @@ struct CLR_RT_StackFrame : public CLR_RT_HeapBlock_Node // EVENT HEAP - NO RELOC static const int c_OverheadForNewObjOrInteropMethod = 2; static const int c_MinimumStack = 10; + // max mumber of local allocations per stack frame + static const int c_Max_Localloc_Count = MAX_LOCALALLOC_COUNT; + static const CLR_UINT32 c_MethodKind_Native = 0x00000000; static const CLR_UINT32 c_MethodKind_Interpreted = 0x00000001; static const CLR_UINT32 c_UNUSED_00000002 = 0x00000002; // c_MethodKind_Jitted @@ -2663,6 +2671,9 @@ struct CLR_RT_StackFrame : public CLR_RT_HeapBlock_Node // EVENT HEAP - NO RELOC bool m_fNativeProfiled; #endif + CLR_UINT8 m_localAllocCount; + CLR_RT_HeapBlock_Array *m_localAllocs[c_Max_Localloc_Count]; + CLR_RT_HeapBlock m_extension[1]; //////////////////////////////////////// From 51a2269eb4c38b7115553bc8faf6bf6d246ba2c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Tue, 25 Nov 2025 03:52:35 +0000 Subject: [PATCH 02/44] Implement ctor from pointer for Span and ReadOnlySpan - Update mscorlib declaration. --- src/CLR/CorLib/corlib_native.cpp | 19 ++- src/CLR/CorLib/corlib_native.h | 2 + .../corlib_native_System_ReadOnlySpan_1.cpp | 143 ++++++++++++++++++ .../CorLib/corlib_native_System_Span_1.cpp | 143 ++++++++++++++++++ 4 files changed, 306 insertions(+), 1 deletion(-) diff --git a/src/CLR/CorLib/corlib_native.cpp b/src/CLR/CorLib/corlib_native.cpp index 34d3d4dd95..0c8b5532cb 100644 --- a/src/CLR/CorLib/corlib_native.cpp +++ b/src/CLR/CorLib/corlib_native.cpp @@ -696,6 +696,10 @@ static const CLR_RT_MethodHandler method_lookup[] = Library_corlib_native_System_Random::_ctor___VOID, Library_corlib_native_System_Random::_ctor___VOID__I4, nullptr, + Library_corlib_native_System_ReadOnlySpan_1::_ctor___VOID__VOIDptr__I4, + nullptr, + nullptr, + nullptr, nullptr, nullptr, nullptr, @@ -711,6 +715,14 @@ static const CLR_RT_MethodHandler method_lookup[] = nullptr, nullptr, nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, Library_corlib_native_System_Reflection_ConstructorInfo::GetCustomAttributes___SZARRAY_OBJECT__BOOLEAN, Library_corlib_native_System_Reflection_ConstructorInfo::Invoke___OBJECT__SZARRAY_OBJECT, nullptr, @@ -798,6 +810,7 @@ static const CLR_RT_MethodHandler method_lookup[] = nullptr, nullptr, nullptr, + Library_corlib_native_System_Span_1::_ctor___VOID__VOIDptr__I4, nullptr, nullptr, nullptr, @@ -827,6 +840,10 @@ static const CLR_RT_MethodHandler method_lookup[] = nullptr, nullptr, nullptr, + nullptr, + nullptr, + nullptr, + nullptr, Library_corlib_native_System_Threading_AutoResetEvent::_ctor___VOID__BOOLEAN, Library_corlib_native_System_Threading_AutoResetEvent::Reset___BOOLEAN, Library_corlib_native_System_Threading_AutoResetEvent::Set___BOOLEAN, @@ -1596,7 +1613,7 @@ const CLR_RT_NativeAssemblyData g_CLR_AssemblyNative_mscorlib = #if (NANOCLR_REFLECTION == TRUE) - 0x3C34952A, + 0xCECAB752, #elif (NANOCLR_REFLECTION == FALSE) diff --git a/src/CLR/CorLib/corlib_native.h b/src/CLR/CorLib/corlib_native.h index 7305b59f69..bbeea04d69 100644 --- a/src/CLR/CorLib/corlib_native.h +++ b/src/CLR/CorLib/corlib_native.h @@ -783,6 +783,7 @@ struct Library_corlib_native_System_ReadOnlySpan_1 static const int FIELD___array = 1; static const int FIELD___length = 2; + NANOCLR_NATIVE_DECLARE(_ctor___VOID__VOIDptr__I4); NANOCLR_NATIVE_DECLARE(NativeReadOnlySpanConstructor___VOID__SZARRAY_GENERICTYPE__I4__I4); //--// @@ -962,6 +963,7 @@ struct Library_corlib_native_System_Span_1 static const int FIELD___array = 1; static const int FIELD___length = 2; + NANOCLR_NATIVE_DECLARE(_ctor___VOID__VOIDptr__I4); NANOCLR_NATIVE_DECLARE(CopyTo___VOID__SystemSpan_1); NANOCLR_NATIVE_DECLARE(NativeSpanConstructor___VOID__SZARRAY_GENERICTYPE__I4__I4); diff --git a/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp b/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp index b3dcd53119..dc56b0cdc6 100644 --- a/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp +++ b/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp @@ -6,6 +6,149 @@ #include "CorLib.h" +typedef Library_corlib_native_System_Runtime_CompilerServices_RuntimeHelpers RuntimeHelpers; + +HRESULT Library_corlib_native_System_ReadOnlySpan_1::_ctor___VOID__VOIDptr__I4(CLR_RT_StackFrame &stack) +{ + NANOCLR_HEADER(); + + int32_t length; + bool isRefContainsRefs = false; + + CLR_RT_HeapBlock_Array *sourceArray; + + CLR_RT_HeapBlock *thisSpan = stack.This(); + + // grab caller to get the generic type + CLR_RT_MethodDef_Instance &caller = stack.MethodCall(); + + if (caller.genericType == nullptr || !NANOCLR_INDEX_IS_VALID(*caller.genericType)) + { + NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); + } + + CLR_RT_TypeSpec_Instance typeSpec; + if (!typeSpec.InitializeFromIndex(*caller.genericType)) + { + NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); + } + + CLR_RT_SignatureParser parser; + parser.Initialize_TypeSpec(typeSpec); + + CLR_RT_SignatureParser::Element element; + + if (FAILED(parser.Advance(element))) + { + NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); + } + + // The first element should be the generic type instantiation + if (element.DataType != DATATYPE_GENERICINST) + { + NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); + } + + // Advance to get the generic argument (T) + if (FAILED(parser.Advance(element))) + { + NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); + } + + if (element.DataType == DATATYPE_VALUETYPE) + { + // For value types we need to advance again + if (FAILED(parser.Advance(element))) + { + NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); + } + } + + // check if T is a reference type or contains references + NANOCLR_CHECK_HRESULT(RuntimeHelpers::CheckReferenceOrContainsReferences( + element.Class, + element.DataType, + &parser, + isRefContainsRefs)); + + if (isRefContainsRefs) + { + NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER); + } + + // validate length parameter + length = stack.Arg2().NumericByRefConst().s4; + + if (length < 0) + { + NANOCLR_SET_AND_LEAVE(CLR_E_OUT_OF_RANGE); + } + + // get the pointer to the array + // assuming the pointer its an array allocated by a previous call to localloc + sourceArray = stack.Arg1().DereferenceArray(); + + // check the element being UInt8 + if (sourceArray->m_typeOfElement != DATATYPE_U1) + { + NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER); + } + + { + // set reference to the pointer to the array + thisSpan[FIELD___array].SetObjectReference(sourceArray); + + // adjust the element type and size to match the generic type T + // The sourceArray was allocated as byte[], but we're going to re-shaping it as T[] + + // Get the TypeDef instance for the element type + CLR_RT_TypeDef_Instance inst{}; + if (!inst.InitializeFromIndex(element.Class)) + { + NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); + } + + // Get the element data type and lookup table + NanoCLRDataType dt = (NanoCLRDataType)inst.target->dataType; + const CLR_RT_DataTypeLookup &dtl = c_CLR_RT_DataTypeLookup[dt]; + + CLR_UINT32 elementSize = dtl.m_sizeInBytes; + + if (elementSize == CLR_RT_DataTypeLookup::c_NA) + { + NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); + } + + CLR_UINT32 newNumElements = sourceArray->m_numOfElements / elementSize; + + // Validate that length doesn't exceed available space + if ((CLR_UINT32)length > newNumElements) + { + NANOCLR_SET_AND_LEAVE(CLR_E_OUT_OF_RANGE); + } + + // Update the reflection data type to the new element type + sourceArray->ReflectionData().data.type = element.Class; + + // Now re-shape the array to make it T[] + sourceArray->m_typeOfElement = dt; + sourceArray->m_sizeOfElement = (CLR_UINT8)elementSize; + sourceArray->m_numOfElements = (CLR_UINT32)length; + + // Set fReference flag based on whether element type is numeric + // (same logic as ExtractHeapBlocksForArray) + sourceArray->m_fReference = (dtl.m_flags & CLR_RT_DataTypeLookup::c_Numeric) == 0; + + // need to call this in order to have the individual elements cleared + sourceArray->ClearElements(0, length); + } + + // set length + thisSpan[FIELD___length].NumericByRef().s4 = length; + + NANOCLR_NOCLEANUP(); +} + HRESULT Library_corlib_native_System_ReadOnlySpan_1::NativeReadOnlySpanConstructor___VOID__SZARRAY_GENERICTYPE__I4__I4( CLR_RT_StackFrame &stack) { diff --git a/src/CLR/CorLib/corlib_native_System_Span_1.cpp b/src/CLR/CorLib/corlib_native_System_Span_1.cpp index e9cebde926..e8a2fd3238 100644 --- a/src/CLR/CorLib/corlib_native_System_Span_1.cpp +++ b/src/CLR/CorLib/corlib_native_System_Span_1.cpp @@ -6,6 +6,149 @@ #include "CorLib.h" +typedef Library_corlib_native_System_Runtime_CompilerServices_RuntimeHelpers RuntimeHelpers; + +HRESULT Library_corlib_native_System_Span_1::_ctor___VOID__VOIDptr__I4(CLR_RT_StackFrame &stack) +{ + NANOCLR_HEADER(); + + int32_t length; + bool isRefContainsRefs = false; + + CLR_RT_HeapBlock_Array *sourceArray; + + CLR_RT_HeapBlock *thisSpan = stack.This(); + + // grab caller to get the generic type + CLR_RT_MethodDef_Instance &caller = stack.MethodCall(); + + if (caller.genericType == nullptr || !NANOCLR_INDEX_IS_VALID(*caller.genericType)) + { + NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); + } + + CLR_RT_TypeSpec_Instance typeSpec; + if (!typeSpec.InitializeFromIndex(*caller.genericType)) + { + NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); + } + + CLR_RT_SignatureParser parser; + parser.Initialize_TypeSpec(typeSpec); + + CLR_RT_SignatureParser::Element element; + + if (FAILED(parser.Advance(element))) + { + NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); + } + + // The first element should be the generic type instantiation + if (element.DataType != DATATYPE_GENERICINST) + { + NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); + } + + // Advance to get the generic argument (T) + if (FAILED(parser.Advance(element))) + { + NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); + } + + if (element.DataType == DATATYPE_VALUETYPE) + { + // For value types we need to advance again + if (FAILED(parser.Advance(element))) + { + NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); + } + } + + // check if T is a reference type or contains references + NANOCLR_CHECK_HRESULT(RuntimeHelpers::CheckReferenceOrContainsReferences( + element.Class, + element.DataType, + &parser, + isRefContainsRefs)); + + if (isRefContainsRefs) + { + NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER); + } + + // validate length parameter + length = stack.Arg2().NumericByRefConst().s4; + + if (length < 0) + { + NANOCLR_SET_AND_LEAVE(CLR_E_OUT_OF_RANGE); + } + + // get the pointer to the array + // assuming the pointer its an array allocated by a previous call to localloc + sourceArray = stack.Arg1().DereferenceArray(); + + // check the element being UInt8 + if (sourceArray->m_typeOfElement != DATATYPE_U1) + { + NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER); + } + + { + // set reference to the pointer to the array + thisSpan[FIELD___array].SetObjectReference(sourceArray); + + // adjust the element type and size to match the generic type T + // The sourceArray was allocated as byte[], but we're going to re-shaping it as T[] + + // Get the TypeDef instance for the element type + CLR_RT_TypeDef_Instance inst{}; + if (!inst.InitializeFromIndex(element.Class)) + { + NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); + } + + // Get the element data type and lookup table + NanoCLRDataType dt = (NanoCLRDataType)inst.target->dataType; + const CLR_RT_DataTypeLookup &dtl = c_CLR_RT_DataTypeLookup[dt]; + + CLR_UINT32 elementSize = dtl.m_sizeInBytes; + + if (elementSize == CLR_RT_DataTypeLookup::c_NA) + { + NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); + } + + CLR_UINT32 newNumElements = sourceArray->m_numOfElements / elementSize; + + // Validate that length doesn't exceed available space + if ((CLR_UINT32)length > newNumElements) + { + NANOCLR_SET_AND_LEAVE(CLR_E_OUT_OF_RANGE); + } + + // Update the reflection data type to the new element type + sourceArray->ReflectionData().data.type = element.Class; + + // Now re-shape the array to make it T[] + sourceArray->m_typeOfElement = dt; + sourceArray->m_sizeOfElement = (CLR_UINT8)elementSize; + sourceArray->m_numOfElements = (CLR_UINT32)length; + + // Set fReference flag based on whether element type is numeric + // (same logic as ExtractHeapBlocksForArray) + sourceArray->m_fReference = (dtl.m_flags & CLR_RT_DataTypeLookup::c_Numeric) == 0; + + // need to call this in order to have the individual elements cleared + sourceArray->ClearElements(0, length); + } + + // set length + thisSpan[FIELD___length].NumericByRef().s4 = length; + + NANOCLR_NOCLEANUP(); +} + HRESULT Library_corlib_native_System_Span_1::CopyTo___VOID__SystemSpan_1(CLR_RT_StackFrame &stack) { NANOCLR_HEADER(); From 2dde11e8624dde557e5cc38043a4d80bbf4af14a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Thu, 27 Nov 2025 00:41:21 +0000 Subject: [PATCH 03/44] Add implementation of cpblk IL isntruction --- src/CLR/Core/Interpreter.cpp | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/CLR/Core/Interpreter.cpp b/src/CLR/Core/Interpreter.cpp index 3382b278b4..7fc4588534 100644 --- a/src/CLR/Core/Interpreter.cpp +++ b/src/CLR/Core/Interpreter.cpp @@ -4234,6 +4234,38 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) // deviating from ECMA-335: we return the array reference instead of a raw pointer evalPos[0].SetObjectReference(array); + + //----------------------------------------------------------------------------------------------------------// + + OPDEF(CEE_CPBLK, "cpblk", PopI + PopI + PopI, Push0, InlineNone, IPrimitive, 2, 0xFE, 0x17, NEXT) + { + // Stack: ... ... -> ... + + // get size + CLR_UINT32 size = evalPos[0].NumericByRef().u4; + evalPos--; + + // get source address +#ifdef _WIN64 + uintptr_t sourceAddress = evalPos[0].NumericByRef().s8; +#else + uintptr_t sourceAddress = evalPos[0].NumericByRef().s4; +#endif + evalPos--; + + // get destination address +#ifdef _WIN64 + uintptr_t destinationAddress = evalPos[0].NumericByRef().s8; +#else + uintptr_t destinationAddress = evalPos[0].NumericByRef().s4; +#endif + evalPos--; + + CHECKSTACK(stack, evalPos); + + // perform memory copy + memmove((void *)destinationAddress, (const void *)sourceAddress, size); + break; } @@ -4254,7 +4286,6 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) // Unsupported opcodes... // OPDEF(CEE_ARGLIST, "arglist", Pop0, PushI, InlineNone, IPrimitive, 2, 0xFE, 0x00, NEXT) - OPDEF(CEE_CPBLK, "cpblk", PopI + PopI + PopI, Push0, InlineNone, IPrimitive, 2, 0xFE, 0x17, NEXT) OPDEF(CEE_JMP, "jmp", Pop0, Push0, InlineMethod, IPrimitive, 1, 0xFF, 0x27, CALL) OPDEF(CEE_INITBLK, "initblk", PopI + PopI + PopI, Push0, InlineNone, IPrimitive, 2, 0xFE, 0x18, NEXT) OPDEF(CEE_CALLI, "calli", VarPop, VarPush, InlineSig, IPrimitive, 1, 0xFF, 0x29, CALL) From 42764cb5332a6bfb61d4541db4a47610fb76020f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Thu, 27 Nov 2025 00:44:02 +0000 Subject: [PATCH 04/44] Add new data type DATATYPE_PTR --- src/CLR/Include/nanoCLR_Runtime__HeapBlock.h | 6 ++++++ src/CLR/Include/nanoCLR_Types.h | 3 +++ 2 files changed, 9 insertions(+) diff --git a/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h b/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h index d9a22795b4..f1dac775e8 100644 --- a/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h +++ b/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h @@ -1012,6 +1012,12 @@ struct CLR_RT_HeapBlock m_data.objectReference.ptr = (CLR_RT_HeapBlock *)ptr; } + void SetUnmangedPointer(const uintptr_t ptr) + { + m_id.raw = CLR_RT_HEAPBLOCK_RAW_ID(DATATYPE_PTR, 0, 1); + m_data.objectReference.ptr = (CLR_RT_HeapBlock *)ptr; + } + #if defined(NANOCLR_APPDOMAINS) CLR_RT_AppDomain *TransparentProxyAppDomain() const { diff --git a/src/CLR/Include/nanoCLR_Types.h b/src/CLR/Include/nanoCLR_Types.h index 666106a2cc..0d69281e78 100644 --- a/src/CLR/Include/nanoCLR_Types.h +++ b/src/CLR/Include/nanoCLR_Types.h @@ -518,6 +518,9 @@ enum NanoCLRDataType // KEEP IN SYNC WITH nanoCLR_DataType enum in nanoFramework DATATYPE_TYPE_PINNED = 0x05 | DATATYPE_TYPE_MODIFIER, DATATYPE_TYPE_R4_HFA = 0x06 | DATATYPE_TYPE_MODIFIER, // used only internally for R4 HFA types DATATYPE_TYPE_R8_HFA = 0x07 | DATATYPE_TYPE_MODIFIER, // used only internally for R8 HFA types + + // unamaged pointer + DATATYPE_PTR }; enum CLR_ReflectionType From 3dcc6a46c5b1657cdc97e1056c903a182515aa12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Thu, 27 Nov 2025 00:45:11 +0000 Subject: [PATCH 05/44] Local allocation array is not of uintptr_t type - Update code accordingly. --- src/CLR/Core/CLR_RT_StackFrame.cpp | 11 ++++++----- src/CLR/Include/nanoCLR_Runtime.h | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/CLR/Core/CLR_RT_StackFrame.cpp b/src/CLR/Core/CLR_RT_StackFrame.cpp index ac69469536..e11e5be5ee 100644 --- a/src/CLR/Core/CLR_RT_StackFrame.cpp +++ b/src/CLR/Core/CLR_RT_StackFrame.cpp @@ -125,7 +125,7 @@ HRESULT CLR_RT_StackFrame::Push(CLR_RT_Thread *th, const CLR_RT_MethodDef_Instan stack->m_localAllocCount = 0; for (CLR_INT32 i = 0; i < CLR_RT_StackFrame::c_Max_Localloc_Count; i++) { - stack->m_localAllocs[i] = nullptr; + stack->m_localAllocs[i] = 0; } // @@ -355,7 +355,7 @@ bool CLR_RT_StackFrame::PushInline( m_localAllocCount = 0; for (CLR_INT32 i = 0; i < c_Max_Localloc_Count; i++) { - m_localAllocs[i] = nullptr; + m_localAllocs[i] = 0; } if (md->localsCount) @@ -858,11 +858,12 @@ void CLR_RT_StackFrame::Pop() NATIVE_PROFILE_CLR_CORE(); // Clear localloc references before popping - m_localAllocCount = 0; - for (CLR_INT32 i = 0; i < c_Max_Localloc_Count; i++) + for (CLR_INT32 i = 0; i < m_localAllocCount; i++) { - m_localAllocs[i] = nullptr; + platform_free((void *)m_localAllocs[i]); + m_localAllocs[i] = 0; } + m_localAllocCount = 0; #if defined(NANOCLR_PROFILE_NEW_CALLS) { diff --git a/src/CLR/Include/nanoCLR_Runtime.h b/src/CLR/Include/nanoCLR_Runtime.h index 456f750c23..b12da25076 100644 --- a/src/CLR/Include/nanoCLR_Runtime.h +++ b/src/CLR/Include/nanoCLR_Runtime.h @@ -2543,7 +2543,7 @@ struct CLR_RT_InlineFrame CLR_PMETADATA m_IP; CLR_PMETADATA m_IPStart; CLR_UINT8 m_localAllocCount; - CLR_RT_HeapBlock_Array *m_localAllocs[MAX_LOCALALLOC_COUNT]; + uintptr_t m_localAllocs[MAX_LOCALALLOC_COUNT]; }; struct CLR_RT_InlineBuffer @@ -2672,7 +2672,7 @@ struct CLR_RT_StackFrame : public CLR_RT_HeapBlock_Node // EVENT HEAP - NO RELOC #endif CLR_UINT8 m_localAllocCount; - CLR_RT_HeapBlock_Array *m_localAllocs[c_Max_Localloc_Count]; + uintptr_t m_localAllocs[c_Max_Localloc_Count]; CLR_RT_HeapBlock m_extension[1]; From e016185969fd237d69a1dbfec9929f5b9bf9fe28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Thu, 27 Nov 2025 00:47:06 +0000 Subject: [PATCH 06/44] Work in local alloc code - localloc now allocates memory from platform heap. - Stores it as unmaged pointer. --- src/CLR/Core/CLR_RT_HeapBlock.cpp | 6 ++++++ src/CLR/Core/Interpreter.cpp | 34 ++++++++++++++++++------------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/CLR/Core/CLR_RT_HeapBlock.cpp b/src/CLR/Core/CLR_RT_HeapBlock.cpp index ae1877f779..141ee3d43e 100644 --- a/src/CLR/Core/CLR_RT_HeapBlock.cpp +++ b/src/CLR/Core/CLR_RT_HeapBlock.cpp @@ -641,6 +641,12 @@ HRESULT CLR_RT_HeapBlock::StoreToReference(CLR_RT_HeapBlock &ref, int size) { obj = &ref; } + else if (dt == DATATYPE_PTR) + { + // unmanaged pointer, perform a direct memory copy + obj = ref.Dereference(); + memcpy((void *)obj, (void *)&NumericByRef(), size); + } else { NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); diff --git a/src/CLR/Core/Interpreter.cpp b/src/CLR/Core/Interpreter.cpp index 7fc4588534..07b33a4712 100644 --- a/src/CLR/Core/Interpreter.cpp +++ b/src/CLR/Core/Interpreter.cpp @@ -4211,29 +4211,35 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) evalPos--; CHECKSTACK(stack, evalPos); - // Create the byte array - CLR_RT_HeapBlock tempArray; - CLR_RT_TypeDef_Index byteType = g_CLR_RT_WellKnownTypes.UInt8; - - NANOCLR_CHECK_HRESULT(CLR_RT_HeapBlock_Array::CreateInstance(tempArray, size, byteType)); - - CLR_RT_ProtectFromGC gc(tempArray); - CLR_RT_HeapBlock_Array *array = tempArray.DereferenceArray(); - - // store pointer for allocated array, if enough room - // In localloc: + // check if we have room for another localloc if (stack->m_localAllocCount >= CLR_RT_StackFrame::c_Max_Localloc_Count) { NANOCLR_SET_AND_LEAVE(CLR_E_STACK_OVERFLOW); } - stack->m_localAllocs[stack->m_localAllocCount++] = array; + // allocate from platform heap + uintptr_t allocPointer = (uintptr_t)platform_malloc(size); + + // sanity check + if (allocPointer == 0) + { + NANOCLR_SET_AND_LEAVE(CLR_E_OUT_OF_MEMORY); + } + + // per ECMA-335 zero-initialize the memory + memset((void *)allocPointer, 0, size); + + // store the pointer to the local allocated memory + stack->m_localAllocs[stack->m_localAllocCount++] = allocPointer; evalPos++; CHECKSTACK(stack, evalPos); - // deviating from ECMA-335: we return the array reference instead of a raw pointer - evalPos[0].SetObjectReference(array); + // store the pointer of the local allocated memory + evalPos[0].SetUnmangedPointer(allocPointer); + + break; + } //----------------------------------------------------------------------------------------------------------// From d94514754d9923d456593ea45975e2fd3a68cf3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Thu, 27 Nov 2025 00:47:49 +0000 Subject: [PATCH 07/44] Numeric add and substract can now handle operations with unmaged pointers --- src/CLR/Core/CLR_RT_HeapBlock.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/CLR/Core/CLR_RT_HeapBlock.cpp b/src/CLR/Core/CLR_RT_HeapBlock.cpp index 141ee3d43e..6c93b6066e 100644 --- a/src/CLR/Core/CLR_RT_HeapBlock.cpp +++ b/src/CLR/Core/CLR_RT_HeapBlock.cpp @@ -2079,6 +2079,20 @@ HRESULT CLR_RT_HeapBlock::NumericAdd(const CLR_RT_HeapBlock &right) } break; + case DATATYPE_PTR: + if (right.DataType() == DATATYPE_I4) + { + // binary numeric add (byte wise) (ECMA-335 Table III.2) + uint8_t *unmanagedPtr = (uint8_t *)m_data.objectReference.ptr; + unmanagedPtr += right.NumericByRefConst().s4; + + m_data.objectReference.ptr = (CLR_RT_HeapBlock *)unmanagedPtr; + + break; + } + // fall through, can't add other types to a PTR + [[fallthrough]]; + default: NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); } @@ -2165,6 +2179,21 @@ HRESULT CLR_RT_HeapBlock::NumericSub(const CLR_RT_HeapBlock &right) m_data.arrayReference.index -= right.m_data.numeric.s4 / array->m_sizeOfElement; } break; + + case DATATYPE_PTR: + if (right.DataType() == DATATYPE_I4) + { + // binary numeric sub (byte wise) (ECMA-335 Table III.2) + uint8_t *unmanagedPtr = (uint8_t *)m_data.objectReference.ptr; + unmanagedPtr -= right.NumericByRefConst().s4; + + m_data.objectReference.ptr = (CLR_RT_HeapBlock *)unmanagedPtr; + + break; + } + // fall through, can't subtract other types to a PTR + [[fallthrough]]; + default: NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); } From c02975475478e4b85fba20d67069e3d34dc58d6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Thu, 27 Nov 2025 00:48:57 +0000 Subject: [PATCH 08/44] ldsflda now properly handles fields with RVA - Now gets pointer to metadata storage. --- src/CLR/Core/Interpreter.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/CLR/Core/Interpreter.cpp b/src/CLR/Core/Interpreter.cpp index 07b33a4712..239edb7e8e 100644 --- a/src/CLR/Core/Interpreter.cpp +++ b/src/CLR/Core/Interpreter.cpp @@ -1813,6 +1813,7 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) evalPos[2].Promote(); NANOCLR_CHECK_HRESULT(evalPos[2].StoreToReference(evalPos[1], size)); + break; } @@ -3065,6 +3066,22 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); } + // check if field has RVA + CLR_RT_FieldDef_Instance inst; + if (inst.InitializeFromIndex(field) && (inst.target->flags & CLR_RECORD_FIELDDEF::FD_HasFieldRVA) && + inst.target->defaultValue != CLR_EmptyIndex) + { + CLR_PMETADATA ptrSrc; + + // Get the data from the Signatures table (this contains the raw byte array) + ptrSrc = inst.assembly->GetSignature(inst.target->defaultValue); + CLR_UINT32 elementCount; + NANOCLR_READ_UNALIGNED_UINT16(elementCount, ptrSrc); + + // ptrSrc is now pointing to the raw byte data + ptr = (CLR_RT_HeapBlock *)ptrSrc; + } + evalPos++; CHECKSTACK(stack, evalPos); From 750b1ef36e650603a1a9ae81f2fcfbe5b378a3dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Thu, 27 Nov 2025 00:51:55 +0000 Subject: [PATCH 09/44] Fix Span .ctor from unmaged pointer --- .../corlib_native_System_ReadOnlySpan_1.cpp | 64 ++++--------------- .../CorLib/corlib_native_System_Span_1.cpp | 64 ++++--------------- 2 files changed, 26 insertions(+), 102 deletions(-) diff --git a/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp b/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp index dc56b0cdc6..a1d2f54484 100644 --- a/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp +++ b/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp @@ -14,9 +14,9 @@ HRESULT Library_corlib_native_System_ReadOnlySpan_1::_ctor___VOID__VOIDptr__I4(C int32_t length; bool isRefContainsRefs = false; + uintptr_t objectRawPointer; - CLR_RT_HeapBlock_Array *sourceArray; - + CLR_RT_HeapBlock_Array *destinationArray; CLR_RT_HeapBlock *thisSpan = stack.This(); // grab caller to get the generic type @@ -85,62 +85,24 @@ HRESULT Library_corlib_native_System_ReadOnlySpan_1::_ctor___VOID__VOIDptr__I4(C } // get the pointer to the array - // assuming the pointer its an array allocated by a previous call to localloc - sourceArray = stack.Arg1().DereferenceArray(); - - // check the element being UInt8 - if (sourceArray->m_typeOfElement != DATATYPE_U1) + // validate data type as being an unmanaged pointer + if (stack.Arg1().DataType() != DATATYPE_PTR) { NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER); } - { - // set reference to the pointer to the array - thisSpan[FIELD___array].SetObjectReference(sourceArray); - - // adjust the element type and size to match the generic type T - // The sourceArray was allocated as byte[], but we're going to re-shaping it as T[] - - // Get the TypeDef instance for the element type - CLR_RT_TypeDef_Instance inst{}; - if (!inst.InitializeFromIndex(element.Class)) - { - NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); - } + objectRawPointer = (uintptr_t)stack.Arg1().Dereference(); - // Get the element data type and lookup table - NanoCLRDataType dt = (NanoCLRDataType)inst.target->dataType; - const CLR_RT_DataTypeLookup &dtl = c_CLR_RT_DataTypeLookup[dt]; - - CLR_UINT32 elementSize = dtl.m_sizeInBytes; - - if (elementSize == CLR_RT_DataTypeLookup::c_NA) - { - NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); - } - - CLR_UINT32 newNumElements = sourceArray->m_numOfElements / elementSize; - - // Validate that length doesn't exceed available space - if ((CLR_UINT32)length > newNumElements) - { - NANOCLR_SET_AND_LEAVE(CLR_E_OUT_OF_RANGE); - } - - // Update the reflection data type to the new element type - sourceArray->ReflectionData().data.type = element.Class; - - // Now re-shape the array to make it T[] - sourceArray->m_typeOfElement = dt; - sourceArray->m_sizeOfElement = (CLR_UINT8)elementSize; - sourceArray->m_numOfElements = (CLR_UINT32)length; + { + CLR_RT_HeapBlock &refArray = thisSpan[FIELD___array]; + CLR_RT_HeapBlock_Array::CreateInstance(refArray, length, element.Class); - // Set fReference flag based on whether element type is numeric - // (same logic as ExtractHeapBlocksForArray) - sourceArray->m_fReference = (dtl.m_flags & CLR_RT_DataTypeLookup::c_Numeric) == 0; + destinationArray = thisSpan[FIELD___array].DereferenceArray(); + CLR_UINT32 elementSize = destinationArray->m_sizeOfElement; + CLR_UINT8 *elementPtr = destinationArray->GetFirstElement(); - // need to call this in order to have the individual elements cleared - sourceArray->ClearElements(0, length); + // copy data from the raw pointer to the newly created array + memcpy(elementPtr, (void *)objectRawPointer, elementSize * length); } // set length diff --git a/src/CLR/CorLib/corlib_native_System_Span_1.cpp b/src/CLR/CorLib/corlib_native_System_Span_1.cpp index e8a2fd3238..5f7388e71d 100644 --- a/src/CLR/CorLib/corlib_native_System_Span_1.cpp +++ b/src/CLR/CorLib/corlib_native_System_Span_1.cpp @@ -14,9 +14,9 @@ HRESULT Library_corlib_native_System_Span_1::_ctor___VOID__VOIDptr__I4(CLR_RT_St int32_t length; bool isRefContainsRefs = false; + uintptr_t objectRawPointer; - CLR_RT_HeapBlock_Array *sourceArray; - + CLR_RT_HeapBlock_Array *destinationArray; CLR_RT_HeapBlock *thisSpan = stack.This(); // grab caller to get the generic type @@ -85,62 +85,24 @@ HRESULT Library_corlib_native_System_Span_1::_ctor___VOID__VOIDptr__I4(CLR_RT_St } // get the pointer to the array - // assuming the pointer its an array allocated by a previous call to localloc - sourceArray = stack.Arg1().DereferenceArray(); - - // check the element being UInt8 - if (sourceArray->m_typeOfElement != DATATYPE_U1) + // validate data type as being an unmanaged pointer + if (stack.Arg1().DataType() != DATATYPE_PTR) { NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER); } - { - // set reference to the pointer to the array - thisSpan[FIELD___array].SetObjectReference(sourceArray); - - // adjust the element type and size to match the generic type T - // The sourceArray was allocated as byte[], but we're going to re-shaping it as T[] - - // Get the TypeDef instance for the element type - CLR_RT_TypeDef_Instance inst{}; - if (!inst.InitializeFromIndex(element.Class)) - { - NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); - } - - // Get the element data type and lookup table - NanoCLRDataType dt = (NanoCLRDataType)inst.target->dataType; - const CLR_RT_DataTypeLookup &dtl = c_CLR_RT_DataTypeLookup[dt]; - - CLR_UINT32 elementSize = dtl.m_sizeInBytes; + objectRawPointer = (uintptr_t)stack.Arg1().Dereference(); - if (elementSize == CLR_RT_DataTypeLookup::c_NA) - { - NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); - } - - CLR_UINT32 newNumElements = sourceArray->m_numOfElements / elementSize; - - // Validate that length doesn't exceed available space - if ((CLR_UINT32)length > newNumElements) - { - NANOCLR_SET_AND_LEAVE(CLR_E_OUT_OF_RANGE); - } - - // Update the reflection data type to the new element type - sourceArray->ReflectionData().data.type = element.Class; - - // Now re-shape the array to make it T[] - sourceArray->m_typeOfElement = dt; - sourceArray->m_sizeOfElement = (CLR_UINT8)elementSize; - sourceArray->m_numOfElements = (CLR_UINT32)length; + { + CLR_RT_HeapBlock &refArray = thisSpan[FIELD___array]; + CLR_RT_HeapBlock_Array::CreateInstance(refArray, length, element.Class); - // Set fReference flag based on whether element type is numeric - // (same logic as ExtractHeapBlocksForArray) - sourceArray->m_fReference = (dtl.m_flags & CLR_RT_DataTypeLookup::c_Numeric) == 0; + destinationArray = thisSpan[FIELD___array].DereferenceArray(); + CLR_UINT32 elementSize = destinationArray->m_sizeOfElement; + CLR_UINT8 *elementPtr = destinationArray->GetFirstElement(); - // need to call this in order to have the individual elements cleared - sourceArray->ClearElements(0, length); + // copy data from the raw pointer to the newly created array + memcpy(elementPtr, (void *)objectRawPointer, elementSize * length); } // set length From 93c5e7613ebe7ac567eeee3411498f1dede1b2ff Mon Sep 17 00:00:00 2001 From: nfbot Date: Thu, 27 Nov 2025 01:07:38 +0000 Subject: [PATCH 10/44] Code style fixes Automated fixes for code style. --- .../CorLib/corlib_native_System_ReadOnlySpan_1.cpp | 11 ++++++----- src/CLR/CorLib/corlib_native_System_Span_1.cpp | 11 ++++++----- src/CLR/Core/Interpreter.cpp | 4 ++-- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp b/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp index a1d2f54484..c26dc08f3c 100644 --- a/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp +++ b/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp @@ -65,11 +65,12 @@ HRESULT Library_corlib_native_System_ReadOnlySpan_1::_ctor___VOID__VOIDptr__I4(C } // check if T is a reference type or contains references - NANOCLR_CHECK_HRESULT(RuntimeHelpers::CheckReferenceOrContainsReferences( - element.Class, - element.DataType, - &parser, - isRefContainsRefs)); + NANOCLR_CHECK_HRESULT( + RuntimeHelpers::CheckReferenceOrContainsReferences( + element.Class, + element.DataType, + &parser, + isRefContainsRefs)); if (isRefContainsRefs) { diff --git a/src/CLR/CorLib/corlib_native_System_Span_1.cpp b/src/CLR/CorLib/corlib_native_System_Span_1.cpp index 5f7388e71d..f0297c44f8 100644 --- a/src/CLR/CorLib/corlib_native_System_Span_1.cpp +++ b/src/CLR/CorLib/corlib_native_System_Span_1.cpp @@ -65,11 +65,12 @@ HRESULT Library_corlib_native_System_Span_1::_ctor___VOID__VOIDptr__I4(CLR_RT_St } // check if T is a reference type or contains references - NANOCLR_CHECK_HRESULT(RuntimeHelpers::CheckReferenceOrContainsReferences( - element.Class, - element.DataType, - &parser, - isRefContainsRefs)); + NANOCLR_CHECK_HRESULT( + RuntimeHelpers::CheckReferenceOrContainsReferences( + element.Class, + element.DataType, + &parser, + isRefContainsRefs)); if (isRefContainsRefs) { diff --git a/src/CLR/Core/Interpreter.cpp b/src/CLR/Core/Interpreter.cpp index 239edb7e8e..d80332318b 100644 --- a/src/CLR/Core/Interpreter.cpp +++ b/src/CLR/Core/Interpreter.cpp @@ -4272,7 +4272,7 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) #ifdef _WIN64 uintptr_t sourceAddress = evalPos[0].NumericByRef().s8; #else - uintptr_t sourceAddress = evalPos[0].NumericByRef().s4; + uintptr_t sourceAddress = evalPos[0].NumericByRef().s4; #endif evalPos--; @@ -4280,7 +4280,7 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) #ifdef _WIN64 uintptr_t destinationAddress = evalPos[0].NumericByRef().s8; #else - uintptr_t destinationAddress = evalPos[0].NumericByRef().s4; + uintptr_t destinationAddress = evalPos[0].NumericByRef().s4; #endif evalPos--; From 81c52b2a2852403d77a1473b3693cf39922679c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Thu, 27 Nov 2025 01:40:18 +0000 Subject: [PATCH 11/44] Add check for array creation --- src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp | 4 +++- src/CLR/CorLib/corlib_native_System_Span_1.cpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp b/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp index c26dc08f3c..586326694b 100644 --- a/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp +++ b/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp @@ -96,7 +96,9 @@ HRESULT Library_corlib_native_System_ReadOnlySpan_1::_ctor___VOID__VOIDptr__I4(C { CLR_RT_HeapBlock &refArray = thisSpan[FIELD___array]; - CLR_RT_HeapBlock_Array::CreateInstance(refArray, length, element.Class); + NANOCLR_CHECK_HRESULT(CLR_RT_HeapBlock_Array::CreateInstance(refArray, length, element.Class)); + + CLR_RT_ProtectFromGC gc(refArray); destinationArray = thisSpan[FIELD___array].DereferenceArray(); CLR_UINT32 elementSize = destinationArray->m_sizeOfElement; diff --git a/src/CLR/CorLib/corlib_native_System_Span_1.cpp b/src/CLR/CorLib/corlib_native_System_Span_1.cpp index f0297c44f8..5f65fa42fd 100644 --- a/src/CLR/CorLib/corlib_native_System_Span_1.cpp +++ b/src/CLR/CorLib/corlib_native_System_Span_1.cpp @@ -96,7 +96,9 @@ HRESULT Library_corlib_native_System_Span_1::_ctor___VOID__VOIDptr__I4(CLR_RT_St { CLR_RT_HeapBlock &refArray = thisSpan[FIELD___array]; - CLR_RT_HeapBlock_Array::CreateInstance(refArray, length, element.Class); + NANOCLR_CHECK_HRESULT(CLR_RT_HeapBlock_Array::CreateInstance(refArray, length, element.Class)); + + CLR_RT_ProtectFromGC gc(refArray); destinationArray = thisSpan[FIELD___array].DereferenceArray(); CLR_UINT32 elementSize = destinationArray->m_sizeOfElement; From c661286762c23fc21f334e6c7a80bb848abfe780 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Thu, 27 Nov 2025 01:41:55 +0000 Subject: [PATCH 12/44] Rename unnused var for clarity --- src/CLR/Core/Interpreter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CLR/Core/Interpreter.cpp b/src/CLR/Core/Interpreter.cpp index d80332318b..0c4fdad536 100644 --- a/src/CLR/Core/Interpreter.cpp +++ b/src/CLR/Core/Interpreter.cpp @@ -3075,8 +3075,8 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) // Get the data from the Signatures table (this contains the raw byte array) ptrSrc = inst.assembly->GetSignature(inst.target->defaultValue); - CLR_UINT32 elementCount; - NANOCLR_READ_UNALIGNED_UINT16(elementCount, ptrSrc); + CLR_UINT32 dummyVar; + NANOCLR_READ_UNALIGNED_UINT16(dummyVar, ptrSrc); // ptrSrc is now pointing to the raw byte data ptr = (CLR_RT_HeapBlock *)ptrSrc; From df4fb02c17cce0b01c38b65d1a1f5292780b1e86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Thu, 27 Nov 2025 01:42:07 +0000 Subject: [PATCH 13/44] Fix type --- src/CLR/Core/Interpreter.cpp | 2 +- src/CLR/Include/nanoCLR_Runtime__HeapBlock.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CLR/Core/Interpreter.cpp b/src/CLR/Core/Interpreter.cpp index 0c4fdad536..11e91a3c05 100644 --- a/src/CLR/Core/Interpreter.cpp +++ b/src/CLR/Core/Interpreter.cpp @@ -4253,7 +4253,7 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) CHECKSTACK(stack, evalPos); // store the pointer of the local allocated memory - evalPos[0].SetUnmangedPointer(allocPointer); + evalPos[0].SetUnmanagedPointer(allocPointer); break; } diff --git a/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h b/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h index f1dac775e8..26cea51c32 100644 --- a/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h +++ b/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h @@ -1012,7 +1012,7 @@ struct CLR_RT_HeapBlock m_data.objectReference.ptr = (CLR_RT_HeapBlock *)ptr; } - void SetUnmangedPointer(const uintptr_t ptr) + void SetUnmanagedPointer(const uintptr_t ptr) { m_id.raw = CLR_RT_HEAPBLOCK_RAW_ID(DATATYPE_PTR, 0, 1); m_data.objectReference.ptr = (CLR_RT_HeapBlock *)ptr; From 6bb60cc6b3f137597a31879e58e20396bc7daa1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Thu, 27 Nov 2025 04:26:38 +0000 Subject: [PATCH 14/44] Add new APIs to heap block to deal with unmanaged pointers --- .../corlib_native_System_ReadOnlySpan_1.cpp | 2 +- .../CorLib/corlib_native_System_Span_1.cpp | 2 +- src/CLR/Core/CLR_RT_HeapBlock.cpp | 11 ++++---- src/CLR/Core/Interpreter.cpp | 2 +- src/CLR/Include/nanoCLR_Runtime__HeapBlock.h | 26 ++++++++++++++----- 5 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp b/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp index 586326694b..5fad33f016 100644 --- a/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp +++ b/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp @@ -92,7 +92,7 @@ HRESULT Library_corlib_native_System_ReadOnlySpan_1::_ctor___VOID__VOIDptr__I4(C NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER); } - objectRawPointer = (uintptr_t)stack.Arg1().Dereference(); + objectRawPointer = (uintptr_t)stack.Arg1().UnmanagedPointer(); { CLR_RT_HeapBlock &refArray = thisSpan[FIELD___array]; diff --git a/src/CLR/CorLib/corlib_native_System_Span_1.cpp b/src/CLR/CorLib/corlib_native_System_Span_1.cpp index 5f65fa42fd..426c3b0442 100644 --- a/src/CLR/CorLib/corlib_native_System_Span_1.cpp +++ b/src/CLR/CorLib/corlib_native_System_Span_1.cpp @@ -92,7 +92,7 @@ HRESULT Library_corlib_native_System_Span_1::_ctor___VOID__VOIDptr__I4(CLR_RT_St NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER); } - objectRawPointer = (uintptr_t)stack.Arg1().Dereference(); + objectRawPointer = (uintptr_t)stack.Arg1().UnmanagedPointer(); { CLR_RT_HeapBlock &refArray = thisSpan[FIELD___array]; diff --git a/src/CLR/Core/CLR_RT_HeapBlock.cpp b/src/CLR/Core/CLR_RT_HeapBlock.cpp index 6c93b6066e..d0fee97ec0 100644 --- a/src/CLR/Core/CLR_RT_HeapBlock.cpp +++ b/src/CLR/Core/CLR_RT_HeapBlock.cpp @@ -644,8 +644,7 @@ HRESULT CLR_RT_HeapBlock::StoreToReference(CLR_RT_HeapBlock &ref, int size) else if (dt == DATATYPE_PTR) { // unmanaged pointer, perform a direct memory copy - obj = ref.Dereference(); - memcpy((void *)obj, (void *)&NumericByRef(), size); + memcpy((void *)ref.UnmanagedPointer(), (void *)&NumericByRef(), size); } else { @@ -2083,10 +2082,10 @@ HRESULT CLR_RT_HeapBlock::NumericAdd(const CLR_RT_HeapBlock &right) if (right.DataType() == DATATYPE_I4) { // binary numeric add (byte wise) (ECMA-335 Table III.2) - uint8_t *unmanagedPtr = (uint8_t *)m_data.objectReference.ptr; + uint8_t *unmanagedPtr = (uint8_t *)UnmanagedPointer(); unmanagedPtr += right.NumericByRefConst().s4; - m_data.objectReference.ptr = (CLR_RT_HeapBlock *)unmanagedPtr; + SetUnmanagedPointer((uintptr_t)unmanagedPtr); break; } @@ -2184,10 +2183,10 @@ HRESULT CLR_RT_HeapBlock::NumericSub(const CLR_RT_HeapBlock &right) if (right.DataType() == DATATYPE_I4) { // binary numeric sub (byte wise) (ECMA-335 Table III.2) - uint8_t *unmanagedPtr = (uint8_t *)m_data.objectReference.ptr; + uint8_t *unmanagedPtr = (uint8_t *)UnmanagedPointer(); unmanagedPtr -= right.NumericByRefConst().s4; - m_data.objectReference.ptr = (CLR_RT_HeapBlock *)unmanagedPtr; + SetUnmanagedPointer((uintptr_t)unmanagedPtr); break; } diff --git a/src/CLR/Core/Interpreter.cpp b/src/CLR/Core/Interpreter.cpp index 11e91a3c05..cd837f76a0 100644 --- a/src/CLR/Core/Interpreter.cpp +++ b/src/CLR/Core/Interpreter.cpp @@ -4286,7 +4286,7 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) CHECKSTACK(stack, evalPos); - // perform memory copy + // perform memory move as addresses may overlap memmove((void *)destinationAddress, (const void *)sourceAddress, size); break; diff --git a/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h b/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h index 26cea51c32..d4da099d45 100644 --- a/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h +++ b/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h @@ -760,6 +760,13 @@ struct CLR_RT_HeapBlock //--// + struct UnmanagedPointer + { + uintptr_t ptr; + } unmanagedPointer; + + //--// + } m_data; public: @@ -1012,12 +1019,6 @@ struct CLR_RT_HeapBlock m_data.objectReference.ptr = (CLR_RT_HeapBlock *)ptr; } - void SetUnmanagedPointer(const uintptr_t ptr) - { - m_id.raw = CLR_RT_HEAPBLOCK_RAW_ID(DATATYPE_PTR, 0, 1); - m_data.objectReference.ptr = (CLR_RT_HeapBlock *)ptr; - } - #if defined(NANOCLR_APPDOMAINS) CLR_RT_AppDomain *TransparentProxyAppDomain() const { @@ -1100,6 +1101,19 @@ struct CLR_RT_HeapBlock return this->m_data.numeric.u8 == right.m_data.numeric.u8; } + //--// + + void SetUnmanagedPointer(const uintptr_t ptr) + { + m_id.raw = CLR_RT_HEAPBLOCK_RAW_ID(DATATYPE_PTR, 0, 1); + m_data.unmanagedPointer.ptr = ptr; + } + + uintptr_t UnmanagedPointer() const + { + return (DataType() == DATATYPE_PTR) ? m_data.unmanagedPointer.ptr : 0; + } + //--// CLR_RT_HeapBlock_Array *RecoverArrayHeader() const From 8bac1455c31aa7048860927a77389ce2a5961dce Mon Sep 17 00:00:00 2001 From: nfbot Date: Thu, 27 Nov 2025 04:28:48 +0000 Subject: [PATCH 15/44] Code style fixes Automated fixes for code style. --- src/CLR/Include/nanoCLR_Runtime__HeapBlock.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h b/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h index d4da099d45..119d68dea1 100644 --- a/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h +++ b/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h @@ -764,7 +764,7 @@ struct CLR_RT_HeapBlock { uintptr_t ptr; } unmanagedPointer; - + //--// } m_data; @@ -1102,7 +1102,7 @@ struct CLR_RT_HeapBlock } //--// - + void SetUnmanagedPointer(const uintptr_t ptr) { m_id.raw = CLR_RT_HEAPBLOCK_RAW_ID(DATATYPE_PTR, 0, 1); From 9aaaff5ed721c7dc35e2ce4a4bf193cb99da145a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Tue, 2 Dec 2025 10:04:38 +0000 Subject: [PATCH 16/44] Fix call to BuildTypeName for TypeSpec in DumpToken - Now passing context TypeSpec for correct type resolution. --- src/CLR/Diagnostics/Info.cpp | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/CLR/Diagnostics/Info.cpp b/src/CLR/Diagnostics/Info.cpp index d48d0bc71e..762038c088 100644 --- a/src/CLR/Diagnostics/Info.cpp +++ b/src/CLR/Diagnostics/Info.cpp @@ -578,7 +578,13 @@ void CLR_RT_Assembly::DumpToken(CLR_UINT32 token, const CLR_RT_MethodDef_Instanc char bufCorrupt[256]; char *pCorrupt = bufCorrupt; size_t cbCorrupt = sizeof(bufCorrupt); - g_CLR_RT_TypeSystem.BuildTypeName(tsIdx, pCorrupt, cbCorrupt, elem.Levels); + g_CLR_RT_TypeSystem.BuildTypeName( + tsIdx, + pCorrupt, + cbCorrupt, + elem.Levels, + methodDefInstance.genericType, + &methodDefInstance); CLR_Debug::Printf("%s", bufCorrupt); break; } @@ -722,7 +728,13 @@ void CLR_RT_Assembly::DumpToken(CLR_UINT32 token, const CLR_RT_MethodDef_Instanc char bufArr[256]; char *pArr = bufArr; size_t cbArr = sizeof(bufArr); - g_CLR_RT_TypeSystem.BuildTypeName(tsIdx, pArr, cbArr, elem.Levels); + g_CLR_RT_TypeSystem.BuildTypeName( + tsIdx, + pArr, + cbArr, + elem.Levels, + methodDefInstance.genericType, + &methodDefInstance); CLR_Debug::Printf("%s", bufArr); break; } @@ -732,7 +744,13 @@ void CLR_RT_Assembly::DumpToken(CLR_UINT32 token, const CLR_RT_MethodDef_Instanc char bufTypeName[256]; char *pTypeName = bufTypeName; size_t cbType = sizeof(bufTypeName); - g_CLR_RT_TypeSystem.BuildTypeName(tsIdx, pTypeName, cbType, elem.Levels); + g_CLR_RT_TypeSystem.BuildTypeName( + tsIdx, + pTypeName, + cbType, + elem.Levels, + methodDefInstance.genericType, + &methodDefInstance); CLR_Debug::Printf("%s", bufTypeName); break; } From ea7ecadfaf5de2baa98b85d3a11b023fbd922e25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Tue, 2 Dec 2025 11:39:58 +0000 Subject: [PATCH 17/44] Add new reflection type for storage pointer --- src/CLR/Include/nanoCLR_Types.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CLR/Include/nanoCLR_Types.h b/src/CLR/Include/nanoCLR_Types.h index 0d69281e78..d6e52a01af 100644 --- a/src/CLR/Include/nanoCLR_Types.h +++ b/src/CLR/Include/nanoCLR_Types.h @@ -534,6 +534,7 @@ enum CLR_ReflectionType REFLECTION_FIELD = 0x06, REFLECTION_GENERICTYPE = 0x07, REFLECTION_TYPESPEC = 0x08, + REFLECTION_STORAGE_PTR = 0x09, }; //////////////////////////////////////////////////////////////////////////////////////////////////// From 946af0644a571e00484d633b4a5f6306b597cf7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Tue, 2 Dec 2025 11:42:50 +0000 Subject: [PATCH 18/44] Add support for storage pointers in heap block array - Adjust get element API to return pointer to storage, when appropriate. - Add API to create array from storage. --- src/CLR/Core/CLR_RT_HeapBlock.cpp | 3 ++ src/CLR/Core/CLR_RT_HeapBlock_Array.cpp | 49 +++++++++++++++++++- src/CLR/Core/Execution.cpp | 1 + src/CLR/Debugger/Debugger.cpp | 5 ++ src/CLR/Include/nanoCLR_Runtime__HeapBlock.h | 31 ++++++++++++- 5 files changed, 85 insertions(+), 4 deletions(-) diff --git a/src/CLR/Core/CLR_RT_HeapBlock.cpp b/src/CLR/Core/CLR_RT_HeapBlock.cpp index d0fee97ec0..338010f018 100644 --- a/src/CLR/Core/CLR_RT_HeapBlock.cpp +++ b/src/CLR/Core/CLR_RT_HeapBlock.cpp @@ -1471,6 +1471,9 @@ bool CLR_RT_HeapBlock::ObjectsEqual( objLeft->m_sizeOfElement == objRight->m_sizeOfElement && objLeft->m_typeOfElement == objRight->m_typeOfElement) { + // check that array is not stored in stack + ASSERT(objLeft->m_StoragePointer == 0); + if (!objLeft->m_fReference) { if (memcmp( diff --git a/src/CLR/Core/CLR_RT_HeapBlock_Array.cpp b/src/CLR/Core/CLR_RT_HeapBlock_Array.cpp index c655986912..811cc268e3 100644 --- a/src/CLR/Core/CLR_RT_HeapBlock_Array.cpp +++ b/src/CLR/Core/CLR_RT_HeapBlock_Array.cpp @@ -24,7 +24,7 @@ HRESULT CLR_RT_HeapBlock_Array::CreateInstance( if ((CLR_INT32)length < 0) NANOCLR_SET_AND_LEAVE(CLR_E_OUT_OF_RANGE); - if (reflex.kind != REFLECTION_TYPE) + if (reflex.kind != REFLECTION_TYPE && reflex.kind != REFLECTION_STORAGE_PTR) { NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); } @@ -57,7 +57,12 @@ HRESULT CLR_RT_HeapBlock_Array::CreateInstance( reference.SetObjectReference(pArray); - NANOCLR_SET_AND_LEAVE(pArray->ClearElements(0, length)); + // only clear elements if they belong to the array + // don't do it when they are stored elsewhere + if (reflex.kind != REFLECTION_STORAGE_PTR) + { + NANOCLR_SET_AND_LEAVE(pArray->ClearElements(0, length)); + } } NANOCLR_NOCLEANUP(); @@ -82,6 +87,37 @@ HRESULT CLR_RT_HeapBlock_Array::CreateInstance( NANOCLR_NOCLEANUP(); } + +HRESULT CLR_RT_HeapBlock_Array::CreateInstanceWithStorage( + CLR_RT_HeapBlock &reference, + CLR_UINT32 length, + const uintptr_t storageAddress, + const CLR_RT_TypeDef_Index &cls) +{ + NATIVE_PROFILE_CLR_CORE(); + NANOCLR_HEADER(); + + CLR_RT_HeapBlock_Array *thisArray; + CLR_RT_ReflectionDef_Index reflex; + + reflex.kind = REFLECTION_STORAGE_PTR; + reflex.levels = 1; + reflex.data.type = cls; + + // create an instance with ZERO length because there is no need to allocate storage + NANOCLR_CHECK_HRESULT(CreateInstance(reference, 0, reflex)); + + thisArray = reference.DereferenceArray(); + + // set the storage + thisArray->m_StoragePointer = storageAddress; + + // adjust the number of elements with the provided length + thisArray->m_numOfElements = length; + + NANOCLR_NOCLEANUP(); +} + HRESULT CLR_RT_HeapBlock_Array::CreateInstance( CLR_RT_HeapBlock &reference, CLR_UINT32 length, @@ -132,6 +168,12 @@ HRESULT CLR_RT_HeapBlock_Array::ClearElements(int index, int length) const CLR_RT_ReflectionDef_Index &reflex = ReflectionDataConst(); CLR_UINT8 *data = GetElement(index); + // sanity check + if (IsStoragePointer()) + { + NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); + } + CLR_RT_Memory::ZeroFill(data, length * m_sizeOfElement); if (m_fReference) @@ -345,6 +387,9 @@ HRESULT CLR_RT_HeapBlock_Array::Copy( if (!arraySrc->m_fReference) { + // check that array is not stored in stack + ASSERT(arraySrc->m_StoragePointer == 0); + memmove(dataDst, dataSrc, length * sizeElem); } else diff --git a/src/CLR/Core/Execution.cpp b/src/CLR/Core/Execution.cpp index 9d5d87f078..0fe0a2bfce 100644 --- a/src/CLR/Core/Execution.cpp +++ b/src/CLR/Core/Execution.cpp @@ -1716,6 +1716,7 @@ CLR_RT_HeapBlock *CLR_RT_ExecutionEngine::ExtractHeapBlocksForArray( pArray->m_typeOfElement = dt; pArray->m_sizeOfElement = dtl.m_sizeInBytes; pArray->m_fReference = (dtl.m_flags & CLR_RT_DataTypeLookup::c_Numeric) == 0; + pArray->m_StoragePointer = 0; #if defined(NANOCLR_PROFILE_NEW_ALLOCATIONS) g_CLR_PRF_Profiler.TrackObjectCreation(pArray); diff --git a/src/CLR/Debugger/Debugger.cpp b/src/CLR/Debugger/Debugger.cpp index 9265464b88..d1f522becd 100644 --- a/src/CLR/Debugger/Debugger.cpp +++ b/src/CLR/Debugger/Debugger.cpp @@ -3065,6 +3065,11 @@ bool CLR_DBG_Debugger::Debugging_Value_GetArray(WP_Message *msg) { blk = (CLR_RT_HeapBlock *)array->GetElement(cmd->m_index); } + else if (array->m_StoragePointer != 0) + { + // TODO need to handle this case properly + ASSERT(FALSE); + } else { if (FAILED(tmp.LoadFromReference(ref))) diff --git a/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h b/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h index 119d68dea1..e4710bbe4d 100644 --- a/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h +++ b/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h @@ -1085,7 +1085,9 @@ struct CLR_RT_HeapBlock CLR_RT_HeapBlock *obj = Dereference(); if (obj && obj->DataType() == DATATYPE_VALUETYPE && obj->IsBoxed() == false) + { return true; + } } return false; @@ -1857,6 +1859,7 @@ struct CLR_RT_HeapBlock_Array : public CLR_RT_HeapBlock CLR_UINT8 m_sizeOfElement; CLR_UINT8 m_fReference; CLR_UINT8 m_pad; + uintptr_t m_StoragePointer; //--// @@ -1871,10 +1874,22 @@ struct CLR_RT_HeapBlock_Array : public CLR_RT_HeapBlock CLR_RT_Assembly *assm, CLR_UINT32 tk, const CLR_RT_MethodDef_Instance *caller); + static HRESULT CreateInstanceWithStorage( + CLR_RT_HeapBlock &reference, + CLR_UINT32 length, + const uintptr_t storageAddress, + const CLR_RT_TypeDef_Index &cls); CLR_UINT8 *GetFirstElement() { - return ((CLR_UINT8 *)&this[1]); + if (ReflectionData().kind == REFLECTION_STORAGE_PTR) + { + return ((CLR_UINT8 *)this->m_StoragePointer); + } + else + { + return ((CLR_UINT8 *)&this[1]); + } } CLR_UINT8 *GetElement(CLR_UINT32 index) @@ -1884,7 +1899,14 @@ struct CLR_RT_HeapBlock_Array : public CLR_RT_HeapBlock CLR_UINT16 *GetFirstElementUInt16() { - return ((CLR_UINT16 *)&this[1]); + if (ReflectionData().kind == REFLECTION_STORAGE_PTR) + { + return ((CLR_UINT16 *)this->m_StoragePointer); + } + else + { + return ((CLR_UINT16 *)&this[1]); + } } CLR_UINT16 *GetElementUInt16(CLR_UINT32 index) @@ -1892,6 +1914,11 @@ struct CLR_RT_HeapBlock_Array : public CLR_RT_HeapBlock return GetFirstElementUInt16() + m_sizeOfElement * index; } + bool IsStoragePointer() + { + return (ReflectionData().kind == REFLECTION_STORAGE_PTR); + } + HRESULT ClearElements(int index, int length); //--// From 70edd2f40c4d6966328a489e41fd067c8a4749b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Tue, 2 Dec 2025 11:44:26 +0000 Subject: [PATCH 19/44] Fix handler for ldsflda to deal correctly with fields with RVA - Now using pointer to storage when appropriate. --- src/CLR/Core/Interpreter.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/CLR/Core/Interpreter.cpp b/src/CLR/Core/Interpreter.cpp index cd837f76a0..4d474001a1 100644 --- a/src/CLR/Core/Interpreter.cpp +++ b/src/CLR/Core/Interpreter.cpp @@ -3066,6 +3066,9 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); } + evalPos++; + CHECKSTACK(stack, evalPos); + // check if field has RVA CLR_RT_FieldDef_Instance inst; if (inst.InitializeFromIndex(field) && (inst.target->flags & CLR_RECORD_FIELDDEF::FD_HasFieldRVA) && @@ -3078,14 +3081,13 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) CLR_UINT32 dummyVar; NANOCLR_READ_UNALIGNED_UINT16(dummyVar, ptrSrc); - // ptrSrc is now pointing to the raw byte data - ptr = (CLR_RT_HeapBlock *)ptrSrc; + evalPos[0].SetUnmanagedPointer((uintptr_t)ptrSrc); + } + else + { + evalPos[0].SetReference(*ptr); } - evalPos++; - CHECKSTACK(stack, evalPos); - - evalPos[0].SetReference(*ptr); break; } From fd9b575df9997021dee96184b775a47623507c16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Tue, 2 Dec 2025 11:45:05 +0000 Subject: [PATCH 20/44] Fix Span constructors and copy operations to use storage pointers --- .../CorLib/corlib_native_System_Span_1.cpp | 73 +++++++------------ 1 file changed, 26 insertions(+), 47 deletions(-) diff --git a/src/CLR/CorLib/corlib_native_System_Span_1.cpp b/src/CLR/CorLib/corlib_native_System_Span_1.cpp index 426c3b0442..7144add7ad 100644 --- a/src/CLR/CorLib/corlib_native_System_Span_1.cpp +++ b/src/CLR/CorLib/corlib_native_System_Span_1.cpp @@ -96,16 +96,8 @@ HRESULT Library_corlib_native_System_Span_1::_ctor___VOID__VOIDptr__I4(CLR_RT_St { CLR_RT_HeapBlock &refArray = thisSpan[FIELD___array]; - NANOCLR_CHECK_HRESULT(CLR_RT_HeapBlock_Array::CreateInstance(refArray, length, element.Class)); - - CLR_RT_ProtectFromGC gc(refArray); - - destinationArray = thisSpan[FIELD___array].DereferenceArray(); - CLR_UINT32 elementSize = destinationArray->m_sizeOfElement; - CLR_UINT8 *elementPtr = destinationArray->GetFirstElement(); - - // copy data from the raw pointer to the newly created array - memcpy(elementPtr, (void *)objectRawPointer, elementSize * length); + NANOCLR_CHECK_HRESULT( + CLR_RT_HeapBlock_Array::CreateInstanceWithStorage(refArray, length, objectRawPointer, element.Class)); } // set length @@ -134,27 +126,20 @@ HRESULT Library_corlib_native_System_Span_1::CopyTo___VOID__SystemSpan_1(CLR_RT_ destinationArray = destinationSpan[FIELD___array].DereferenceArray(); { - // prevent GC from moving the arrays while we copy the data - if (sourceArray) - { - CLR_RT_ProtectFromGC gc1(*sourceArray); - } - if (destinationArray) + // sanity check for empty source array + if (sourceSpan[FIELD___length].NumericByRefConst().s4 == 0) { - CLR_RT_ProtectFromGC gc2(*destinationArray); + NANOCLR_SET_AND_LEAVE(S_OK); } + + // prevent GC from moving the arrays while we copy the data + CLR_RT_ProtectFromGC gc1(*sourceArray); + CLR_RT_ProtectFromGC gc2(*destinationArray); - // does the source array has a reference? - if (sourceArray && destinationArray) - { - // copy array - CLR_RT_HeapBlock_Array::Copy( - sourceArray, - 0, - destinationArray, - 0, - sourceSpan[FIELD___length].NumericByRefConst().s4); - } + memmove( + destinationArray->GetElement(0), + sourceArray->GetElement(0), + sourceSpan[FIELD___length].NumericByRefConst().s4 * sourceArray->m_sizeOfElement); } NANOCLR_NOCLEANUP(); @@ -178,29 +163,23 @@ HRESULT Library_corlib_native_System_Span_1::NativeSpanConstructor___VOID__SZARR start = stack.Arg2().NumericByRefConst().s4; length = stack.Arg3().NumericByRefConst().s4; - { - // get type of the source array - NANOCLR_CHECK_HRESULT(descDst.InitializeFromObject(*sourceArray)); - descDst.GetElementType(descDst); + // get type of the source array + NANOCLR_CHECK_HRESULT(descDst.InitializeFromObject(*sourceArray)); + descDst.GetElementType(descDst); - sourceType.data = descDst.m_handlerCls.data; + sourceType.data = descDst.m_handlerCls.data; - CLR_RT_HeapBlock &refArray = thisSpan[FIELD___array]; - CLR_RT_HeapBlock_Array::CreateInstance(refArray, length, sourceType); - - // get pointer to the array - destinationArray = thisSpan[FIELD___array].DereferenceArray(); - - // protect from GC - CLR_RT_ProtectFromGC gc1(*sourceArray); - CLR_RT_ProtectFromGC gc2(refArray); - - // copy array - CLR_RT_HeapBlock_Array::Copy(sourceArray, start, destinationArray, 0, length); + { + // get the pointer to the element at start address + uintptr_t ptrToStartElement = (uintptr_t)sourceArray->GetElement(start); - // set length - thisSpan[FIELD___length].NumericByRef().s4 = length; + CLR_RT_HeapBlock &refArray = thisSpan[FIELD___array]; + NANOCLR_CHECK_HRESULT( + CLR_RT_HeapBlock_Array::CreateInstanceWithStorage(refArray, length, ptrToStartElement, sourceType)); } + // set length + thisSpan[FIELD___length].NumericByRef().s4 = length; + NANOCLR_NOCLEANUP(); } From cfa0be9f92116e3ea9b3b4474d341fcae55fc61c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Tue, 2 Dec 2025 16:30:52 +0000 Subject: [PATCH 21/44] GetGenericArgument at MethodSpec instance now returns a signature element - Required to deal with VAR params. - Update callers accordingly. --- src/CLR/Core/TypeSystem.cpp | 34 +++++++++++++++---------------- src/CLR/Include/nanoCLR_Runtime.h | 2 +- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/CLR/Core/TypeSystem.cpp b/src/CLR/Core/TypeSystem.cpp index 7f5db4e53e..bcc266c7de 100644 --- a/src/CLR/Core/TypeSystem.cpp +++ b/src/CLR/Core/TypeSystem.cpp @@ -1306,16 +1306,18 @@ bool CLR_RT_TypeDef_Instance::ResolveToken( CLR_RT_MethodSpec_Instance methodSpecInstance; if (methodSpecInstance.InitializeFromIndex(caller->methodSpec)) { - NanoCLRDataType dataType; - CLR_RT_TypeDef_Index typeDef; - methodSpecInstance.GetGenericArgument( - paramElement.GenericParamPosition, - typeDef, - dataType); + CLR_RT_SignatureParser::Element element; - data = typeDef.data; - assembly = g_CLR_RT_TypeSystem.m_assemblies[typeDef.Assembly() - 1]; - target = assembly->GetTypeDef(typeDef.Type()); + if (!methodSpecInstance.GetGenericArgument( + paramElement.GenericParamPosition, + element)) + { + return false; + } + + data = element.Class.data; + assembly = g_CLR_RT_TypeSystem.m_assemblies[element.Class.Assembly() - 1]; + target = assembly->GetTypeDef(element.Class.Type()); } else { @@ -2230,8 +2232,7 @@ void CLR_RT_MethodSpec_Instance::ClearInstance() bool CLR_RT_MethodSpec_Instance::GetGenericArgument( CLR_INT32 argumentPosition, - CLR_RT_TypeDef_Index &typeDef, - NanoCLRDataType &dataType) + CLR_RT_SignatureParser::Element &element) { CLR_RT_SignatureParser parser; parser.Initialize_MethodSignature(this); @@ -2242,20 +2243,15 @@ bool CLR_RT_MethodSpec_Instance::GetGenericArgument( return false; } - CLR_RT_SignatureParser::Element elem; - // loop through parameters to find the desired one for (CLR_INT32 i = 0; i <= argumentPosition; i++) { - if (FAILED(parser.Advance(elem))) + if (FAILED(parser.Advance(element))) { return false; } } - typeDef = elem.Class; - dataType = elem.DataType; - return true; } @@ -7583,9 +7579,11 @@ HRESULT CLR_RT_TypeSystem::BuildTypeName( if (NANOCLR_INDEX_IS_VALID(contextMethodDef->methodSpec)) { CLR_RT_MethodSpec_Instance methodSpec{}; + CLR_RT_SignatureParser::Element paramElement; + CLR_RT_TypeDef_Index paramTypeDef; methodSpec.InitializeFromIndex(contextMethodDef->methodSpec); - if (!methodSpec.GetGenericArgument(element.GenericParamPosition, typeDef, element.DataType)) + if (!methodSpec.GetGenericArgument(element.GenericParamPosition, paramElement)) { NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); } diff --git a/src/CLR/Include/nanoCLR_Runtime.h b/src/CLR/Include/nanoCLR_Runtime.h index b12da25076..72223380a7 100644 --- a/src/CLR/Include/nanoCLR_Runtime.h +++ b/src/CLR/Include/nanoCLR_Runtime.h @@ -2349,7 +2349,7 @@ struct CLR_RT_MethodSpec_Instance : public CLR_RT_MethodSpec_Index CLR_EncodedMethodDefOrRef InstanceOfMethod; - bool GetGenericArgument(CLR_INT32 argumentPosition, CLR_RT_TypeDef_Index &typeDef, NanoCLRDataType &dataType); + bool GetGenericArgument(CLR_INT32 argumentPosition, CLR_RT_SignatureParser::Element &element); }; //////////////////////////////////////////////////////////////////////////////////////////////////// From 3d910ff949902f3ef2021c1d3d6b13a3d9e02028 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Tue, 2 Dec 2025 16:31:57 +0000 Subject: [PATCH 22/44] Fix ComputeHashForClosedGenericType - Noe correctly resolves cross refs from VAR and MVAR. --- src/CLR/Core/TypeSystem.cpp | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/CLR/Core/TypeSystem.cpp b/src/CLR/Core/TypeSystem.cpp index bcc266c7de..c239c34f00 100644 --- a/src/CLR/Core/TypeSystem.cpp +++ b/src/CLR/Core/TypeSystem.cpp @@ -8476,6 +8476,7 @@ CLR_UINT32 CLR_RT_TypeSystem::ComputeHashForClosedGenericType( { CLR_UINT32 hash = 0; int argCount; + CLR_INDEX genericPosition; // Start with the generic type definition hash = SUPPORT_ComputeCRC(&typeInstance.genericTypeDef.data, sizeof(CLR_UINT32), hash); @@ -8509,9 +8510,14 @@ CLR_UINT32 CLR_RT_TypeSystem::ComputeHashForClosedGenericType( break; } + // copy generic position + genericPosition = elem.GenericParamPosition; + // Check if this is an unresolved generic parameter (VAR or MVAR) if (elem.DataType == DATATYPE_VAR && contextTypeSpec && NANOCLR_INDEX_IS_VALID(*contextTypeSpec)) { +resolve_type_param: + // Resolve VAR (type parameter) from context TypeSpec CLR_RT_TypeSpec_Instance contextTs; if (!contextTs.InitializeFromIndex(*contextTypeSpec)) @@ -8521,7 +8527,7 @@ CLR_UINT32 CLR_RT_TypeSystem::ComputeHashForClosedGenericType( } CLR_RT_SignatureParser::Element paramElement; - if (contextTs.GetGenericParam(elem.GenericParamPosition, paramElement)) + if (contextTs.GetGenericParam(genericPosition, paramElement)) { // Use the resolved type from context hash = SUPPORT_ComputeCRC(¶mElement.DataType, sizeof(paramElement.DataType), hash); @@ -8545,13 +8551,22 @@ CLR_UINT32 CLR_RT_TypeSystem::ComputeHashForClosedGenericType( CLR_RT_MethodSpec_Instance methodSpecInst{}; if (methodSpecInst.InitializeFromIndex(contextMethod->methodSpec)) { - CLR_RT_TypeDef_Index resolvedTypeDef; - NanoCLRDataType resolvedDataType; + CLR_RT_SignatureParser::Element argElement; - if (methodSpecInst.GetGenericArgument(elem.GenericParamPosition, resolvedTypeDef, resolvedDataType)) + if (methodSpecInst.GetGenericArgument(genericPosition, argElement)) { + if (argElement.DataType == DATATYPE_VAR) + { + // need another pass to resolve a generic type parameter + + // copy generic parameter position + genericPosition = argElement.GenericParamPosition; + + goto resolve_type_param; + } + // Use the resolved type from MethodSpec - hash = SUPPORT_ComputeCRC(&resolvedDataType, sizeof(resolvedDataType), hash); + hash = SUPPORT_ComputeCRC(&argElement.DataType, sizeof(argElement.DataType), hash); } else { From ffb1e521b24212bf665b4616f00c2295228eb2a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Tue, 2 Dec 2025 17:41:56 +0000 Subject: [PATCH 23/44] Resolving token in TypeDef instance now take optional parameter typeSpec for context - Rework code to resolve nested VAR and MVAR situations. - General improvement to use recently added helper funcions. --- src/CLR/Core/TypeSystem.cpp | 98 +++++++++++++++++++------------ src/CLR/Include/nanoCLR_Runtime.h | 6 +- 2 files changed, 64 insertions(+), 40 deletions(-) diff --git a/src/CLR/Core/TypeSystem.cpp b/src/CLR/Core/TypeSystem.cpp index c239c34f00..d73ac4d214 100644 --- a/src/CLR/Core/TypeSystem.cpp +++ b/src/CLR/Core/TypeSystem.cpp @@ -1157,7 +1157,8 @@ void CLR_RT_TypeDef_Instance::ClearInstance() bool CLR_RT_TypeDef_Instance::ResolveToken( CLR_UINT32 tk, CLR_RT_Assembly *assm, - const CLR_RT_MethodDef_Instance *caller) + const CLR_RT_MethodDef_Instance *caller, + const CLR_RT_TypeSpec_Index *contextTypeSpec) { NATIVE_PROFILE_CLR_CORE(); @@ -1226,6 +1227,8 @@ bool CLR_RT_TypeDef_Instance::ResolveToken( CLR_RT_SignatureParser parser; parser.Initialize_TypeSpec(assm, ts); + CLR_INDEX genericPosition; + CLR_RT_SignatureParser::Element elem; if (FAILED(parser.Advance(elem))) { @@ -1275,11 +1278,11 @@ bool CLR_RT_TypeDef_Instance::ResolveToken( } } + genericPosition = elem.GenericParamPosition; + // If it's a type‐generic slot (!T), resolve against the caller's closed generic if (elem.DataType == DATATYPE_VAR) { - int pos = elem.GenericParamPosition; - CLR_RT_TypeSpec_Instance callerTypeSpec; if (!callerTypeSpec.InitializeFromIndex(*caller->genericType)) { @@ -1289,7 +1292,7 @@ bool CLR_RT_TypeDef_Instance::ResolveToken( CLR_RT_SignatureParser::Element paramElement; // Try to map using the generic context (e.g. !T→Int32) - if (callerTypeSpec.GetGenericParam((CLR_UINT32)pos, paramElement)) + if (callerTypeSpec.GetGenericParam(genericPosition, paramElement)) { // Successfully resolved from generic context if (NANOCLR_INDEX_IS_VALID(paramElement.Class)) @@ -1300,34 +1303,10 @@ bool CLR_RT_TypeDef_Instance::ResolveToken( } else if (paramElement.DataType == DATATYPE_MVAR) { - // resolve from methodspec context - if (NANOCLR_INDEX_IS_VALID(caller->methodSpec)) - { - CLR_RT_MethodSpec_Instance methodSpecInstance; - if (methodSpecInstance.InitializeFromIndex(caller->methodSpec)) - { - CLR_RT_SignatureParser::Element element; - - if (!methodSpecInstance.GetGenericArgument( - paramElement.GenericParamPosition, - element)) - { - return false; - } - - data = element.Class.data; - assembly = g_CLR_RT_TypeSystem.m_assemblies[element.Class.Assembly() - 1]; - target = assembly->GetTypeDef(element.Class.Type()); - } - else - { - return false; - } - } - else - { - return false; - } + // need to defer to generic method argument + genericPosition = paramElement.GenericParamPosition; + + goto resolve_generic_argument; } else if (paramElement.DataType == DATATYPE_VAR) { @@ -1340,7 +1319,7 @@ bool CLR_RT_TypeDef_Instance::ResolveToken( return false; } } - else if (NANOCLR_INDEX_IS_VALID(caller->arrayElementType) && pos == 0) + else if (NANOCLR_INDEX_IS_VALID(caller->arrayElementType) && genericPosition == 0) { // Fallback to arrayElementType for SZArrayHelper scenarios data = caller->arrayElementType.data; @@ -1354,20 +1333,61 @@ bool CLR_RT_TypeDef_Instance::ResolveToken( } else if (elem.DataType == DATATYPE_MVAR) { + resolve_generic_argument: + // Use the caller bound genericType (Stack, etc.) if (caller == nullptr || caller->genericType == nullptr) { return false; } - CLR_RT_GenericParam_Index gpIdx; - caller->assembly->FindGenericParamAtMethodDef(*caller, elem.GenericParamPosition, gpIdx); + // resolve from methodspec context + if (NANOCLR_INDEX_IS_VALID(caller->methodSpec)) + { + CLR_RT_MethodSpec_Instance methodSpecInstance; + if (methodSpecInstance.InitializeFromIndex(caller->methodSpec)) + { + CLR_RT_SignatureParser::Element element; - auto &gp = caller->assembly->crossReferenceGenericParam[gpIdx.GenericParam()]; + if (!methodSpecInstance.GetGenericArgument(genericPosition, element)) + { + return false; + } - data = gp.classTypeDef.data; - assembly = g_CLR_RT_TypeSystem.m_assemblies[gp.classTypeDef.Assembly() - 1]; - target = assembly->GetTypeDef(gp.classTypeDef.Type()); + if (element.DataType == DATATYPE_VAR) + { + // need to defer to generic type parameter + CLR_RT_TypeSpec_Instance contextTs; + if (!contextTypeSpec || !contextTs.InitializeFromIndex(*contextTypeSpec)) + { + return false; + } + + if (!contextTs.GetGenericParam(element.GenericParamPosition, element)) + { + return false; + } + } + else if (element.DataType == DATATYPE_MVAR) + { + // nested MVAR not implemented + ASSERT(false); + return false; + } + + data = element.Class.data; + assembly = g_CLR_RT_TypeSystem.m_assemblies[element.Class.Assembly() - 1]; + target = assembly->GetTypeDef(element.Class.Type()); + } + else + { + return false; + } + } + else + { + return false; + } } } } diff --git a/src/CLR/Include/nanoCLR_Runtime.h b/src/CLR/Include/nanoCLR_Runtime.h index 72223380a7..2e510056df 100644 --- a/src/CLR/Include/nanoCLR_Runtime.h +++ b/src/CLR/Include/nanoCLR_Runtime.h @@ -2216,7 +2216,11 @@ struct CLR_RT_TypeDef_Instance : public CLR_RT_TypeDef_Index void ClearInstance(); - bool ResolveToken(CLR_UINT32 tk, CLR_RT_Assembly *assm, const CLR_RT_MethodDef_Instance *caller = nullptr); + bool ResolveToken( + CLR_UINT32 tk, + CLR_RT_Assembly *assm, + const CLR_RT_MethodDef_Instance *caller = nullptr, + const CLR_RT_TypeSpec_Index *contextTypeSpec = nullptr); bool ResolveNullableType(CLR_UINT32 tk, CLR_RT_Assembly *assm, const CLR_RT_MethodDef_Instance *caller = nullptr); //--// From e06ea501f2fb6a0c80f97bb62fcefdee733e69a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Tue, 2 Dec 2025 17:47:04 +0000 Subject: [PATCH 24/44] BuildTypeName now is able to resolve VAR elements --- src/CLR/Core/TypeSystem.cpp | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/CLR/Core/TypeSystem.cpp b/src/CLR/Core/TypeSystem.cpp index d73ac4d214..423797d622 100644 --- a/src/CLR/Core/TypeSystem.cpp +++ b/src/CLR/Core/TypeSystem.cpp @@ -7608,7 +7608,41 @@ HRESULT CLR_RT_TypeSystem::BuildTypeName( NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); } - NANOCLR_CHECK_HRESULT(BuildTypeName(typeDef, szBuffer, iBuffer)); + paramTypeDef = paramElement.Class; + + if (paramElement.DataType == DATATYPE_VAR) + { + // Build the type name for this generic argument + // Use the method's declaring type as context for VAR resolution + + CLR_RT_TypeSpec_Instance contextTs; + CLR_RT_SignatureParser::Element argElement; + + // try to resolve from method context + if (!contextTs.InitializeFromIndex(*contextMethodDef->genericType)) + { + NANOCLR_SET_AND_LEAVE(CLR_E_FAIL); + } + + if (contextTs.GetGenericParam(paramElement.GenericParamPosition, argElement)) + { + paramTypeDef = argElement.Class; + + goto output_type; + } + else + { + // Couldn't resolve + char encodedParam[7]; + snprintf(encodedParam, ARRAYSIZE(encodedParam), "!%d", argElement.GenericParamPosition); + NANOCLR_CHECK_HRESULT(QueueStringToBuffer(szBuffer, iBuffer, encodedParam)); + } + } + else + { + output_type: + NANOCLR_CHECK_HRESULT(BuildTypeName(paramTypeDef, szBuffer, iBuffer)); + } } } else From 7b825a4cefa6b09e566e46f4fe88689205091b0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Tue, 2 Dec 2025 17:48:03 +0000 Subject: [PATCH 25/44] Creating HeapBlock array now accepts typeSpec parameter for type resolution - Update callers and callees accordingly. --- src/CLR/Core/CLR_RT_HeapBlock_Array.cpp | 5 +++-- src/CLR/Core/Interpreter.cpp | 2 +- src/CLR/Include/nanoCLR_Runtime__HeapBlock.h | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/CLR/Core/CLR_RT_HeapBlock_Array.cpp b/src/CLR/Core/CLR_RT_HeapBlock_Array.cpp index 811cc268e3..b6cc02decf 100644 --- a/src/CLR/Core/CLR_RT_HeapBlock_Array.cpp +++ b/src/CLR/Core/CLR_RT_HeapBlock_Array.cpp @@ -123,7 +123,8 @@ HRESULT CLR_RT_HeapBlock_Array::CreateInstance( CLR_UINT32 length, CLR_RT_Assembly *assm, CLR_UINT32 tk, - const CLR_RT_MethodDef_Instance *caller) + const CLR_RT_MethodDef_Instance *caller, + const CLR_RT_TypeSpec_Index *contextTypeSpec) { NATIVE_PROFILE_CLR_CORE(); NANOCLR_HEADER(); @@ -134,7 +135,7 @@ HRESULT CLR_RT_HeapBlock_Array::CreateInstance( memset(&ref, 0, sizeof(struct CLR_RT_HeapBlock)); - if (cls.ResolveToken(tk, assm, caller)) + if (cls.ResolveToken(tk, assm, caller, contextTypeSpec)) { NANOCLR_CHECK_HRESULT(ref.SetReflection(cls)); } diff --git a/src/CLR/Core/Interpreter.cpp b/src/CLR/Core/Interpreter.cpp index 4d474001a1..96c4f95f32 100644 --- a/src/CLR/Core/Interpreter.cpp +++ b/src/CLR/Core/Interpreter.cpp @@ -3402,7 +3402,7 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) for (int pass = 0; pass < 2; pass++) { - hr = CLR_RT_HeapBlock_Array::CreateInstance(evalPos[0], size, assm, arg, &stack->m_call); + hr = CLR_RT_HeapBlock_Array::CreateInstance(evalPos[0], size, assm, arg, &stack->m_call, &stack->m_genericTypeSpecStorage); if (SUCCEEDED(hr)) { break; diff --git a/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h b/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h index e4710bbe4d..14719fe3c2 100644 --- a/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h +++ b/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h @@ -1873,7 +1873,8 @@ struct CLR_RT_HeapBlock_Array : public CLR_RT_HeapBlock CLR_UINT32 length, CLR_RT_Assembly *assm, CLR_UINT32 tk, - const CLR_RT_MethodDef_Instance *caller); + const CLR_RT_MethodDef_Instance *caller, + const CLR_RT_TypeSpec_Index *contextTypeSpec); static HRESULT CreateInstanceWithStorage( CLR_RT_HeapBlock &reference, CLR_UINT32 length, From 9582948db61496f5ab1388903fce3351a712de5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Tue, 2 Dec 2025 17:48:34 +0000 Subject: [PATCH 26/44] Missed commit ffb1e521b24212bf665b4616f00c2295228eb2a0 --- src/CLR/Core/Execution.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/CLR/Core/Execution.cpp b/src/CLR/Core/Execution.cpp index 0fe0a2bfce..456f5d99d1 100644 --- a/src/CLR/Core/Execution.cpp +++ b/src/CLR/Core/Execution.cpp @@ -2361,13 +2361,20 @@ HRESULT CLR_RT_ExecutionEngine::InitializeLocals( if (NANOCLR_INDEX_IS_VALID(methodDefInstance.methodSpec)) { CLR_RT_MethodSpec_Instance methodSpec; + CLR_RT_SignatureParser::Element element; + if (!methodSpec.InitializeFromIndex(methodDefInstance.methodSpec)) { NANOCLR_SET_AND_LEAVE(CLR_E_FAIL); } // Use GetGenericArgument to get the concrete type from MethodSpec's signature - if (!methodSpec.GetGenericArgument(genericParamPosition, cls, dt)) + if (methodSpec.GetGenericArgument(genericParamPosition, element)) + { + cls = element.Class; + dt = element.DataType; + } + else { NANOCLR_SET_AND_LEAVE(CLR_E_FAIL); } From faa5fb2577b53f5ee3b19e8694c936a107d03323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Tue, 2 Dec 2025 17:49:12 +0000 Subject: [PATCH 27/44] AllocateGenericStaticFieldsOnDemand now correctly stores TypeSpec context --- src/CLR/Core/TypeSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CLR/Core/TypeSystem.cpp b/src/CLR/Core/TypeSystem.cpp index 423797d622..5c171af749 100644 --- a/src/CLR/Core/TypeSystem.cpp +++ b/src/CLR/Core/TypeSystem.cpp @@ -5677,7 +5677,7 @@ HRESULT CLR_RT_Assembly::AllocateGenericStaticFieldsOnDemand( CLR_RT_HeapBlock_Delegate *dlg = refDlg.DereferenceDelegate(); // Store the TypeSpec index so the .cctor can resolve type generic parameters - dlg->m_genericTypeSpec = typeSpecIndex; + dlg->m_genericTypeSpec = *contextTypeSpec; // Store the caller's MethodSpec (if any) to enable reolution of method generic parameters if (contextMethod != nullptr) From 65b7476aa2238610a2d538688fbe703e303fc313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Tue, 2 Dec 2025 17:52:17 +0000 Subject: [PATCH 28/44] Improve BuildMethodName to build type name for generic instances --- src/CLR/Core/TypeSystem.cpp | 80 ++++++++++--------------------------- 1 file changed, 22 insertions(+), 58 deletions(-) diff --git a/src/CLR/Core/TypeSystem.cpp b/src/CLR/Core/TypeSystem.cpp index 5c171af749..1d7aab5293 100644 --- a/src/CLR/Core/TypeSystem.cpp +++ b/src/CLR/Core/TypeSystem.cpp @@ -7866,28 +7866,14 @@ HRESULT CLR_RT_TypeSystem::BuildMethodName( NANOCLR_CHECK_HRESULT(BuildTypeName(instOwner, szBuffer, iBuffer)); CLR_SafeSprintf(szBuffer, iBuffer, "::%s", mdInst.assembly->GetString(mdInst.target->name)); - } else { // First, build the type name (either from genericType or from the method's declaring type) - if (genericType != nullptr && NANOCLR_INDEX_IS_VALID(*genericType) && genericType->data != CLR_EmptyToken) + if (mdInst.genericType != nullptr && NANOCLR_INDEX_IS_VALID(*mdInst.genericType)) { // Use the provided generic type context - if (!SUCCEEDED(BuildTypeName(*genericType, szBuffer, iBuffer, 0, nullptr, &mdInst))) - { - // Fall back to the declaring type - if (instOwner.InitializeFromMethod(mdInst) == false) - { - NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); - } - NANOCLR_CHECK_HRESULT(BuildTypeName(instOwner, szBuffer, iBuffer)); - } - } - else if (mdInst.genericType != nullptr && NANOCLR_INDEX_IS_VALID(*mdInst.genericType)) - { - // Use the method instance's generic type - if (!SUCCEEDED(BuildTypeName(*mdInst.genericType, szBuffer, iBuffer, 0))) + if (FAILED(BuildTypeName(*mdInst.genericType, szBuffer, iBuffer, 0, genericType))) { // Fall back to the declaring type if (instOwner.InitializeFromMethod(mdInst) == false) @@ -7935,53 +7921,31 @@ HRESULT CLR_RT_TypeSystem::BuildMethodName( CLR_SafeSprintf(szBuffer, iBuffer, ", "); } - // Build the type name for this generic argument - // Use the method's declaring type as context for VAR resolution - const CLR_RT_TypeSpec_Index *context = - (mdInst.genericType && NANOCLR_INDEX_IS_VALID(*mdInst.genericType)) ? mdInst.genericType - : nullptr; - - if (elem.DataType == DATATYPE_VAR || elem.DataType == DATATYPE_MVAR) + if (elem.DataType == DATATYPE_VAR) { - // Generic parameter - try to resolve it - if (context != nullptr) - { - CLR_RT_TypeSpec_Instance contextTs; - if (!contextTs.InitializeFromIndex(*context)) - { - NANOCLR_SET_AND_LEAVE(CLR_E_FAIL); - } + // Build the type name for this generic argument + // Use the method's declaring type as context for VAR resolution - CLR_RT_SignatureParser::Element paramElement; - if (contextTs.GetGenericParam(elem.GenericParamPosition, paramElement)) - { - NANOCLR_CHECK_HRESULT(BuildTypeName(paramElement.Class, szBuffer, iBuffer)); - } - else - { - // Couldn't resolve - show as !n or !!n - if (elem.DataType == DATATYPE_VAR) - { - CLR_SafeSprintf(szBuffer, iBuffer, "!%d", elem.GenericParamPosition); - } - else - { - CLR_SafeSprintf(szBuffer, iBuffer, "!!%d", elem.GenericParamPosition); - } - } + CLR_RT_TypeSpec_Instance contextTs; + CLR_RT_SignatureParser::Element paramElement; + + // try to resolve from method context + if (!contextTs.InitializeFromIndex(*genericType)) + { + NANOCLR_SET_AND_LEAVE(CLR_E_FAIL); } - else + + if (!contextTs.GetGenericParam(elem.GenericParamPosition, paramElement)) { - // No context - show as !n or !!n - if (elem.DataType == DATATYPE_VAR) - { - CLR_SafeSprintf(szBuffer, iBuffer, "!%d", elem.GenericParamPosition); - } - else - { - CLR_SafeSprintf(szBuffer, iBuffer, "!!%d", elem.GenericParamPosition); - } + // Couldn't resolve + CLR_SafeSprintf(szBuffer, iBuffer, "!%d", elem.GenericParamPosition); } + + NANOCLR_CHECK_HRESULT(BuildTypeName(paramElement.Class, szBuffer, iBuffer)); + } + else if (elem.DataType == DATATYPE_MVAR) + { + CLR_SafeSprintf(szBuffer, iBuffer, "!!%d", elem.GenericParamPosition); } else if (NANOCLR_INDEX_IS_VALID(elem.Class)) { From aac7793a5edd90597b703c8964f05858a42adb3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Tue, 2 Dec 2025 17:52:26 +0000 Subject: [PATCH 29/44] Code style fixes --- src/CLR/Core/TypeSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CLR/Core/TypeSystem.cpp b/src/CLR/Core/TypeSystem.cpp index 1d7aab5293..fdf0ed3360 100644 --- a/src/CLR/Core/TypeSystem.cpp +++ b/src/CLR/Core/TypeSystem.cpp @@ -8534,7 +8534,7 @@ CLR_UINT32 CLR_RT_TypeSystem::ComputeHashForClosedGenericType( // Check if this is an unresolved generic parameter (VAR or MVAR) if (elem.DataType == DATATYPE_VAR && contextTypeSpec && NANOCLR_INDEX_IS_VALID(*contextTypeSpec)) { -resolve_type_param: + resolve_type_param: // Resolve VAR (type parameter) from context TypeSpec CLR_RT_TypeSpec_Instance contextTs; From 8fbe83387b97ecd298cede96fee4dc81b21c2af7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Tue, 2 Dec 2025 19:51:57 +0000 Subject: [PATCH 30/44] Fix constructors of ReadOnlySpan --- .../corlib_native_System_ReadOnlySpan_1.cpp | 55 +++++++------------ 1 file changed, 20 insertions(+), 35 deletions(-) diff --git a/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp b/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp index 5fad33f016..ff66dde505 100644 --- a/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp +++ b/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp @@ -65,12 +65,11 @@ HRESULT Library_corlib_native_System_ReadOnlySpan_1::_ctor___VOID__VOIDptr__I4(C } // check if T is a reference type or contains references - NANOCLR_CHECK_HRESULT( - RuntimeHelpers::CheckReferenceOrContainsReferences( - element.Class, - element.DataType, - &parser, - isRefContainsRefs)); + NANOCLR_CHECK_HRESULT(RuntimeHelpers::CheckReferenceOrContainsReferences( + element.Class, + element.DataType, + &parser, + isRefContainsRefs)); if (isRefContainsRefs) { @@ -96,16 +95,8 @@ HRESULT Library_corlib_native_System_ReadOnlySpan_1::_ctor___VOID__VOIDptr__I4(C { CLR_RT_HeapBlock &refArray = thisSpan[FIELD___array]; - NANOCLR_CHECK_HRESULT(CLR_RT_HeapBlock_Array::CreateInstance(refArray, length, element.Class)); - - CLR_RT_ProtectFromGC gc(refArray); - - destinationArray = thisSpan[FIELD___array].DereferenceArray(); - CLR_UINT32 elementSize = destinationArray->m_sizeOfElement; - CLR_UINT8 *elementPtr = destinationArray->GetFirstElement(); - - // copy data from the raw pointer to the newly created array - memcpy(elementPtr, (void *)objectRawPointer, elementSize * length); + NANOCLR_CHECK_HRESULT( + CLR_RT_HeapBlock_Array::CreateInstanceWithStorage(refArray, length, objectRawPointer, element.Class)); } // set length @@ -132,29 +123,23 @@ HRESULT Library_corlib_native_System_ReadOnlySpan_1::NativeReadOnlySpanConstruct start = stack.Arg2().NumericByRefConst().s4; length = stack.Arg3().NumericByRefConst().s4; - { - // get type of the source array - NANOCLR_CHECK_HRESULT(descDst.InitializeFromObject(*sourceArray)); - descDst.GetElementType(descDst); - - sourceType.data = descDst.m_handlerCls.data; - - CLR_RT_HeapBlock &refArray = thisSpan[FIELD___array]; - CLR_RT_HeapBlock_Array::CreateInstance(refArray, length, sourceType); + // get type of the source array + NANOCLR_CHECK_HRESULT(descDst.InitializeFromObject(*sourceArray)); + descDst.GetElementType(descDst); - // get pointer to the array - destinationArray = thisSpan[FIELD___array].DereferenceArray(); + sourceType.data = descDst.m_handlerCls.data; - // protect from GC - CLR_RT_ProtectFromGC gc1(*sourceArray); - CLR_RT_ProtectFromGC gc2(refArray); - - // copy array - CLR_RT_HeapBlock_Array::Copy(sourceArray, start, destinationArray, 0, length); + { + // get the pointer to the element at start address + uintptr_t ptrToStartElement = (uintptr_t)sourceArray->GetElement(start); - // set length - thisSpan[FIELD___length].NumericByRef().s4 = length; + CLR_RT_HeapBlock &refArray = thisSpan[FIELD___array]; + NANOCLR_CHECK_HRESULT( + CLR_RT_HeapBlock_Array::CreateInstanceWithStorage(refArray, length, ptrToStartElement, sourceType)); } + // set length + thisSpan[FIELD___length].NumericByRef().s4 = length; + NANOCLR_NOCLEANUP(); } From c894eefae63dad30d38470bb4b98a7eda7d22edb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Wed, 3 Dec 2025 11:41:34 +0000 Subject: [PATCH 31/44] Code style fixes --- src/CLR/Core/Interpreter.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/CLR/Core/Interpreter.cpp b/src/CLR/Core/Interpreter.cpp index 96c4f95f32..26f931eddb 100644 --- a/src/CLR/Core/Interpreter.cpp +++ b/src/CLR/Core/Interpreter.cpp @@ -3402,7 +3402,13 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) for (int pass = 0; pass < 2; pass++) { - hr = CLR_RT_HeapBlock_Array::CreateInstance(evalPos[0], size, assm, arg, &stack->m_call, &stack->m_genericTypeSpecStorage); + hr = CLR_RT_HeapBlock_Array::CreateInstance( + evalPos[0], + size, + assm, + arg, + &stack->m_call, + &stack->m_genericTypeSpecStorage); if (SUCCEEDED(hr)) { break; From 0bc02e01564732b4c46695821618d808024b99fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Wed, 3 Dec 2025 12:00:04 +0000 Subject: [PATCH 32/44] Add robust stack validation - Pre-Execution Validation (before each opcode executes): + Stack Underflow Check: Verifies that the stack has enough items for the opcode to pop + Stack Overflow Check: Ensures the opcode won't exceed maximum stack depth - Post-Execution Validation (after each opcode completes): + Stack Depth Consistency Check: Verifies the stack changed by the expected amount - Enhanced Error Reporting: + All validation errors include: Opcode name and hex value, expected vs. actual values, current stack state Motivation: - Catches underflow before it happens - validates stack has required items - Detects implementation bugs - verifies opcodes modify stack correctly - Pinpoints exact failure - shows which opcode caused the problem - Works with existing infrastructure - uses NANOCLR_OPCODE_STACKCHANGES flag - No performance impact when disabled - all checks are conditional --- src/CLR/Core/Interpreter.cpp | 96 +++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 2 deletions(-) diff --git a/src/CLR/Core/Interpreter.cpp b/src/CLR/Core/Interpreter.cpp index 26f931eddb..1ba6feda3b 100644 --- a/src/CLR/Core/Interpreter.cpp +++ b/src/CLR/Core/Interpreter.cpp @@ -1047,6 +1047,11 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) bool fCondition; bool fDirty = false; +#if defined(NANOCLR_OPCODE_STACKCHANGES) + // Track stack depth for validation + CLR_RT_HeapBlock *evalPosBeforeOp = nullptr; +#endif + READCACHE(stack, evalPos, ip, fDirty); while (true) @@ -1093,8 +1098,61 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) #if defined(NANOCLR_OPCODE_STACKCHANGES) if (op != CEE_PREFIX1) { - NANOCLR_CHECK_HRESULT( - CLR_Checks::VerifyStackOK(*stack, &evalPos[1], c_CLR_RT_OpcodeLookup[op].StackChanges())); + // Capture stack position before opcode execution + evalPosBeforeOp = evalPos; + + const CLR_RT_OpcodeLookup &opcodeInfo = c_CLR_RT_OpcodeLookup[op]; + CLR_INT32 stackPop = opcodeInfo.StackPop(); + CLR_INT32 stackPush = opcodeInfo.StackPush(); + CLR_INT32 stackChange = opcodeInfo.StackChanges(); + + // Skip validation for opcodes with variable stack behavior (VarPop/VarPush) + // These include CEE_CALL, CEE_CALLVIRT, CEE_RET, CEE_NEWOBJ which have stack + // changes that depend on method signatures and cannot be validated statically + bool isVariableStackOp = (stackPop == 0 && stackPush == 0); + + if (!isVariableStackOp) + { + // Validate we have enough items on stack for this opcode to pop + // evalPos points to the slot AFTER the top item, so &evalPos[0] is top item + // stack->m_evalStack is the base, so (evalPos - stack->m_evalStack) is current depth + CLR_INT32 currentDepth = (CLR_INT32)(evalPos - stack->m_evalStack) + 1; + + if (stackPop > 0 && currentDepth < (CLR_INT32)stackPop) + { + // Stack underflow: trying to pop more items than available + CLR_Debug::Printf( + "STACK UNDERFLOW PRE-CHECK: Opcode %s (0x%X) needs %d items but only %d available\r\n", + opcodeInfo.Name(), + (int)op, + (int)stackPop, + (int)currentDepth); + NANOCLR_SET_AND_LEAVE(CLR_E_STACK_UNDERFLOW); + } + + // Validate we have enough space for items this opcode will push + // After popping stackPop items and pushing stackPush items, new depth will be: + // currentDepth - stackPop + stackPush = currentDepth + stackChange + CLR_INT32 projectedDepth = currentDepth + stackChange; + CLR_INT32 maxDepth = (CLR_INT32)(stack->m_evalStackEnd - stack->m_evalStack); + + if (projectedDepth > maxDepth) + { + // Stack overflow: would exceed maximum stack depth + CLR_Debug::Printf( + "STACK OVERFLOW PRE-CHECK: Opcode %s (0x%X) would result in depth %d, max is %d\r\n", + opcodeInfo.Name(), + (int)op, + (int)projectedDepth, + (int)maxDepth); + NANOCLR_SET_AND_LEAVE(CLR_E_STACK_OVERFLOW); + } + } + else + { + // For variable stack opcodes, set evalPosBeforeOp to nullptr to skip post-validation + evalPosBeforeOp = nullptr; + } } #endif @@ -2464,6 +2522,10 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) #ifndef NANOCLR_NO_IL_INLINE if (stack->m_inlineFrame) { + // Inline return - skip stack validation as the context is restored by PopInline() +#if defined(NANOCLR_OPCODE_STACKCHANGES) + evalPosBeforeOp = nullptr; // Disable post-validation for inline return +#endif stack->m_evalStackPos = evalPos; stack->PopInline(); @@ -4337,6 +4399,36 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) #undef OPDEF } +#if defined(NANOCLR_OPCODE_STACKCHANGES) + // Post-execution validation: verify stack depth changed by expected amount + if (op != CEE_PREFIX1 && evalPosBeforeOp != nullptr) + { + const CLR_RT_OpcodeLookup &opcodeInfo = c_CLR_RT_OpcodeLookup[op]; + CLR_INT32 expectedChange = opcodeInfo.StackChanges(); + CLR_INT32 actualChange = (CLR_INT32)(evalPos - evalPosBeforeOp); + + if (actualChange != expectedChange) + { + // Stack depth mismatch: opcode didn't change stack as expected + CLR_Debug::Printf( + "STACK DEPTH MISMATCH POST-CHECK: Opcode %s (0x%X) expected change %d but actual change was %d\r\n", + opcodeInfo.Name(), + (int)op, + (int)expectedChange, + (int)actualChange); + CLR_Debug::Printf( + " Stack before: %d items, after: %d items\r\n", + (int)(evalPosBeforeOp - stack->m_evalStack) + 1, + (int)(evalPos - stack->m_evalStack) + 1); + + // This is a critical error indicating a bug in the opcode implementation + NANOCLR_SET_AND_LEAVE(CLR_E_STACK_UNDERFLOW); + } + + evalPosBeforeOp = nullptr; + } +#endif + #if defined(NANOCLR_ENABLE_SOURCELEVELDEBUGGING) if (stack->m_flags & CLR_RT_StackFrame::c_HasBreakpoint) { From 79314741367bdb8c985d2d9f681a99c90c516cf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Wed, 3 Dec 2025 18:36:58 +0000 Subject: [PATCH 33/44] Signature parser now can consume modifiers in types --- src/CLR/Core/TypeSystem.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/CLR/Core/TypeSystem.cpp b/src/CLR/Core/TypeSystem.cpp index fdf0ed3360..f06561db98 100644 --- a/src/CLR/Core/TypeSystem.cpp +++ b/src/CLR/Core/TypeSystem.cpp @@ -540,6 +540,16 @@ HRESULT CLR_RT_SignatureParser::Advance(Element &res) switch (res.DataType) { + case ELEMENT_TYPE_CMOD_REQD: + case ELEMENT_TYPE_CMOD_OPT: + // Consume the modifier type token (TypeDefOrRef coded index) + // Format: CMOD_REQD/OPT + token + actual_type + // We don't need to do anything with the modifier itself, + // just skip it to get to the actual type + CLR_TkFromStream(Signature); + // Continue loop to read the actual element type + break; + case DATATYPE_BYREF: if (res.IsByRef) { From b8492c3d04baf559dba6531f760d42fd0bcaffe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Wed, 3 Dec 2025 18:43:36 +0000 Subject: [PATCH 34/44] Fix arithmetic of stack validation --- src/CLR/Core/Interpreter.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/CLR/Core/Interpreter.cpp b/src/CLR/Core/Interpreter.cpp index 1ba6feda3b..9c77b35df8 100644 --- a/src/CLR/Core/Interpreter.cpp +++ b/src/CLR/Core/Interpreter.cpp @@ -1109,7 +1109,8 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) // Skip validation for opcodes with variable stack behavior (VarPop/VarPush) // These include CEE_CALL, CEE_CALLVIRT, CEE_RET, CEE_NEWOBJ which have stack // changes that depend on method signatures and cannot be validated statically - bool isVariableStackOp = (stackPop == 0 && stackPush == 0); + // Note: VarPop and VarPush are both encoded as 0, so check if EITHER is 0 + bool isVariableStackOp = (stackPop == 0 || stackPush == 0); if (!isVariableStackOp) { From e04dec8083bd40ec959bafc8fafbd9a6f53c099d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Wed, 3 Dec 2025 18:55:34 +0000 Subject: [PATCH 35/44] Moved readonly. prefix to group of "do nothing" op codes --- src/CLR/Core/Interpreter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CLR/Core/Interpreter.cpp b/src/CLR/Core/Interpreter.cpp index 9c77b35df8..16d1ece34c 100644 --- a/src/CLR/Core/Interpreter.cpp +++ b/src/CLR/Core/Interpreter.cpp @@ -4373,6 +4373,7 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) OPDEF(CEE_UNALIGNED, "unaligned.", Pop0, Push0, ShortInlineI, IPrefix, 2, 0xFE, 0x12, META) OPDEF(CEE_VOLATILE, "volatile.", Pop0, Push0, InlineNone, IPrefix, 2, 0xFE, 0x13, META) OPDEF(CEE_TAILCALL, "tail.", Pop0, Push0, InlineNone, IPrefix, 2, 0xFE, 0x14, META) + OPDEF(CEE_READONLY, "readonly.", Pop0, Push0, InlineNone, IPrefix, 2, 0xFE, 0x1E, META) break; ////////////////////////////////////////////////////////////////////////////////////////// @@ -4387,7 +4388,6 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) OPDEF(CEE_MKREFANY, "mkrefany", PopI, Push1, InlineType, IPrimitive, 1, 0xFF, 0xC6, NEXT) OPDEF(CEE_REFANYTYPE, "refanytype", Pop1, PushI, InlineNone, IPrimitive, 2, 0xFE, 0x1D, NEXT) OPDEF(CEE_REFANYVAL, "refanyval", Pop1, PushI, InlineType, IPrimitive, 1, 0xFF, 0xC2, NEXT) - OPDEF(CEE_READONLY, "readonly.", Pop0, Push0, InlineNone, IPrefix, 2, 0xFE, 0x1E, META) NANOCLR_CHECK_HRESULT(CLR_Checks::VerifyUnsupportedInstruction(op)); break; From 63052c8f9670df4d5b007bcbd92c4bf1b8716bfa Mon Sep 17 00:00:00 2001 From: nfbot Date: Wed, 3 Dec 2025 19:29:10 +0000 Subject: [PATCH 36/44] Code style fixes Automated fixes for code style. --- .../corlib_native_System_ReadOnlySpan_1.cpp | 11 ++-- .../CorLib/corlib_native_System_Span_1.cpp | 2 +- src/CLR/Core/CLR_RT_HeapBlock_Array.cpp | 1 - src/CLR/Core/Interpreter.cpp | 65 ++++++++++--------- src/CLR/Debugger/Debugger.cpp | 9 +-- 5 files changed, 45 insertions(+), 43 deletions(-) diff --git a/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp b/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp index ff66dde505..ba23888878 100644 --- a/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp +++ b/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp @@ -65,11 +65,12 @@ HRESULT Library_corlib_native_System_ReadOnlySpan_1::_ctor___VOID__VOIDptr__I4(C } // check if T is a reference type or contains references - NANOCLR_CHECK_HRESULT(RuntimeHelpers::CheckReferenceOrContainsReferences( - element.Class, - element.DataType, - &parser, - isRefContainsRefs)); + NANOCLR_CHECK_HRESULT( + RuntimeHelpers::CheckReferenceOrContainsReferences( + element.Class, + element.DataType, + &parser, + isRefContainsRefs)); if (isRefContainsRefs) { diff --git a/src/CLR/CorLib/corlib_native_System_Span_1.cpp b/src/CLR/CorLib/corlib_native_System_Span_1.cpp index 7144add7ad..b02fedb8d8 100644 --- a/src/CLR/CorLib/corlib_native_System_Span_1.cpp +++ b/src/CLR/CorLib/corlib_native_System_Span_1.cpp @@ -131,7 +131,7 @@ HRESULT Library_corlib_native_System_Span_1::CopyTo___VOID__SystemSpan_1(CLR_RT_ { NANOCLR_SET_AND_LEAVE(S_OK); } - + // prevent GC from moving the arrays while we copy the data CLR_RT_ProtectFromGC gc1(*sourceArray); CLR_RT_ProtectFromGC gc2(*destinationArray); diff --git a/src/CLR/Core/CLR_RT_HeapBlock_Array.cpp b/src/CLR/Core/CLR_RT_HeapBlock_Array.cpp index b6cc02decf..59514fa169 100644 --- a/src/CLR/Core/CLR_RT_HeapBlock_Array.cpp +++ b/src/CLR/Core/CLR_RT_HeapBlock_Array.cpp @@ -87,7 +87,6 @@ HRESULT CLR_RT_HeapBlock_Array::CreateInstance( NANOCLR_NOCLEANUP(); } - HRESULT CLR_RT_HeapBlock_Array::CreateInstanceWithStorage( CLR_RT_HeapBlock &reference, CLR_UINT32 length, diff --git a/src/CLR/Core/Interpreter.cpp b/src/CLR/Core/Interpreter.cpp index 16d1ece34c..11ecf5bd54 100644 --- a/src/CLR/Core/Interpreter.cpp +++ b/src/CLR/Core/Interpreter.cpp @@ -1100,25 +1100,25 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) { // Capture stack position before opcode execution evalPosBeforeOp = evalPos; - + const CLR_RT_OpcodeLookup &opcodeInfo = c_CLR_RT_OpcodeLookup[op]; CLR_INT32 stackPop = opcodeInfo.StackPop(); CLR_INT32 stackPush = opcodeInfo.StackPush(); CLR_INT32 stackChange = opcodeInfo.StackChanges(); - + // Skip validation for opcodes with variable stack behavior (VarPop/VarPush) // These include CEE_CALL, CEE_CALLVIRT, CEE_RET, CEE_NEWOBJ which have stack // changes that depend on method signatures and cannot be validated statically // Note: VarPop and VarPush are both encoded as 0, so check if EITHER is 0 bool isVariableStackOp = (stackPop == 0 || stackPush == 0); - + if (!isVariableStackOp) { // Validate we have enough items on stack for this opcode to pop // evalPos points to the slot AFTER the top item, so &evalPos[0] is top item // stack->m_evalStack is the base, so (evalPos - stack->m_evalStack) is current depth CLR_INT32 currentDepth = (CLR_INT32)(evalPos - stack->m_evalStack) + 1; - + if (stackPop > 0 && currentDepth < (CLR_INT32)stackPop) { // Stack underflow: trying to pop more items than available @@ -1130,13 +1130,13 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) (int)currentDepth); NANOCLR_SET_AND_LEAVE(CLR_E_STACK_UNDERFLOW); } - + // Validate we have enough space for items this opcode will push // After popping stackPop items and pushing stackPush items, new depth will be: // currentDepth - stackPop + stackPush = currentDepth + stackChange CLR_INT32 projectedDepth = currentDepth + stackChange; CLR_INT32 maxDepth = (CLR_INT32)(stack->m_evalStackEnd - stack->m_evalStack); - + if (projectedDepth > maxDepth) { // Stack overflow: would exceed maximum stack depth @@ -2525,7 +2525,7 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) { // Inline return - skip stack validation as the context is restored by PopInline() #if defined(NANOCLR_OPCODE_STACKCHANGES) - evalPosBeforeOp = nullptr; // Disable post-validation for inline return + evalPosBeforeOp = nullptr; // Disable post-validation for inline return #endif stack->m_evalStackPos = evalPos; @@ -4401,33 +4401,34 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) } #if defined(NANOCLR_OPCODE_STACKCHANGES) - // Post-execution validation: verify stack depth changed by expected amount - if (op != CEE_PREFIX1 && evalPosBeforeOp != nullptr) - { - const CLR_RT_OpcodeLookup &opcodeInfo = c_CLR_RT_OpcodeLookup[op]; - CLR_INT32 expectedChange = opcodeInfo.StackChanges(); - CLR_INT32 actualChange = (CLR_INT32)(evalPos - evalPosBeforeOp); - - if (actualChange != expectedChange) + // Post-execution validation: verify stack depth changed by expected amount + if (op != CEE_PREFIX1 && evalPosBeforeOp != nullptr) { - // Stack depth mismatch: opcode didn't change stack as expected - CLR_Debug::Printf( - "STACK DEPTH MISMATCH POST-CHECK: Opcode %s (0x%X) expected change %d but actual change was %d\r\n", - opcodeInfo.Name(), - (int)op, - (int)expectedChange, - (int)actualChange); - CLR_Debug::Printf( - " Stack before: %d items, after: %d items\r\n", - (int)(evalPosBeforeOp - stack->m_evalStack) + 1, - (int)(evalPos - stack->m_evalStack) + 1); - - // This is a critical error indicating a bug in the opcode implementation - NANOCLR_SET_AND_LEAVE(CLR_E_STACK_UNDERFLOW); + const CLR_RT_OpcodeLookup &opcodeInfo = c_CLR_RT_OpcodeLookup[op]; + CLR_INT32 expectedChange = opcodeInfo.StackChanges(); + CLR_INT32 actualChange = (CLR_INT32)(evalPos - evalPosBeforeOp); + + if (actualChange != expectedChange) + { + // Stack depth mismatch: opcode didn't change stack as expected + CLR_Debug::Printf( + "STACK DEPTH MISMATCH POST-CHECK: Opcode %s (0x%X) expected change %d but actual change was " + "%d\r\n", + opcodeInfo.Name(), + (int)op, + (int)expectedChange, + (int)actualChange); + CLR_Debug::Printf( + " Stack before: %d items, after: %d items\r\n", + (int)(evalPosBeforeOp - stack->m_evalStack) + 1, + (int)(evalPos - stack->m_evalStack) + 1); + + // This is a critical error indicating a bug in the opcode implementation + NANOCLR_SET_AND_LEAVE(CLR_E_STACK_UNDERFLOW); + } + + evalPosBeforeOp = nullptr; } - - evalPosBeforeOp = nullptr; - } #endif #if defined(NANOCLR_ENABLE_SOURCELEVELDEBUGGING) diff --git a/src/CLR/Debugger/Debugger.cpp b/src/CLR/Debugger/Debugger.cpp index d1f522becd..98e081ed8c 100644 --- a/src/CLR/Debugger/Debugger.cpp +++ b/src/CLR/Debugger/Debugger.cpp @@ -2429,10 +2429,11 @@ bool CLR_DBG_Debugger::Debugging_Thread_Get(WP_Message *msg) pRes[Library_corlib_native_System_Threading_Thread::FIELD___priority].NumericByRef().s4 = pri; - if (SUCCEEDED(CLR_RT_ObjectToEvent_Source::CreateInstance( - th, - *pRes, - pRes[Library_corlib_native_System_Threading_Thread::FIELD___thread]))) + if (SUCCEEDED( + CLR_RT_ObjectToEvent_Source::CreateInstance( + th, + *pRes, + pRes[Library_corlib_native_System_Threading_Thread::FIELD___thread]))) { #if defined(NANOCLR_APPDOMAINS) CLR_RT_ObjectToEvent_Source::CreateInstance( From 76dd1b8b589ab4726db1dff3aba18e5baf832f0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Wed, 3 Dec 2025 19:57:57 +0000 Subject: [PATCH 37/44] Remove unused var --- src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp | 1 - src/CLR/CorLib/corlib_native_System_Span_1.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp b/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp index ba23888878..5c3d9c667b 100644 --- a/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp +++ b/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp @@ -16,7 +16,6 @@ HRESULT Library_corlib_native_System_ReadOnlySpan_1::_ctor___VOID__VOIDptr__I4(C bool isRefContainsRefs = false; uintptr_t objectRawPointer; - CLR_RT_HeapBlock_Array *destinationArray; CLR_RT_HeapBlock *thisSpan = stack.This(); // grab caller to get the generic type diff --git a/src/CLR/CorLib/corlib_native_System_Span_1.cpp b/src/CLR/CorLib/corlib_native_System_Span_1.cpp index b02fedb8d8..1c5c5c628c 100644 --- a/src/CLR/CorLib/corlib_native_System_Span_1.cpp +++ b/src/CLR/CorLib/corlib_native_System_Span_1.cpp @@ -16,7 +16,6 @@ HRESULT Library_corlib_native_System_Span_1::_ctor___VOID__VOIDptr__I4(CLR_RT_St bool isRefContainsRefs = false; uintptr_t objectRawPointer; - CLR_RT_HeapBlock_Array *destinationArray; CLR_RT_HeapBlock *thisSpan = stack.This(); // grab caller to get the generic type From 8e6374202645036ab70b0668f90be991c2464583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Wed, 3 Dec 2025 20:05:16 +0000 Subject: [PATCH 38/44] Addressing code reviews --- src/CLR/Core/CLR_RT_HeapBlock.cpp | 7 +++++-- src/CLR/Core/Interpreter.cpp | 9 ++++++++- src/CLR/Core/TypeSystem.cpp | 18 ++++++++++++++++-- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/CLR/Core/CLR_RT_HeapBlock.cpp b/src/CLR/Core/CLR_RT_HeapBlock.cpp index 338010f018..23d900075f 100644 --- a/src/CLR/Core/CLR_RT_HeapBlock.cpp +++ b/src/CLR/Core/CLR_RT_HeapBlock.cpp @@ -643,8 +643,11 @@ HRESULT CLR_RT_HeapBlock::StoreToReference(CLR_RT_HeapBlock &ref, int size) } else if (dt == DATATYPE_PTR) { - // unmanaged pointer, perform a direct memory copy - memcpy((void *)ref.UnmanagedPointer(), (void *)&NumericByRef(), size); + // unmanaged pointer, perform a direct memory move as the addresses can overlap + memmove((void *)ref.UnmanagedPointer(), (void *)&NumericByRef(), size); + + // Nothing to assign back to a HeapBlock in this case + NANOCLR_SET_AND_LEAVE(S_OK); } else { diff --git a/src/CLR/Core/Interpreter.cpp b/src/CLR/Core/Interpreter.cpp index 11ecf5bd54..87d1b4abc3 100644 --- a/src/CLR/Core/Interpreter.cpp +++ b/src/CLR/Core/Interpreter.cpp @@ -4294,7 +4294,14 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg) OPDEF(CEE_LOCALLOC, "localloc", PopI, PushI, InlineNone, IPrimitive, 2, 0xFE, 0x0F, NEXT) { - CLR_UINT32 size = evalPos[0].NumericByRef().u4; + CLR_INT32 sizeRaw = evalPos[0].NumericByRef().s4; + + if (sizeRaw < 0) + { + NANOCLR_SET_AND_LEAVE(CLR_E_OUT_OF_RANGE); + } + + CLR_UINT32 size = (CLR_UINT32)sizeRaw; evalPos--; CHECKSTACK(stack, evalPos); diff --git a/src/CLR/Core/TypeSystem.cpp b/src/CLR/Core/TypeSystem.cpp index f06561db98..0d3fc3cb17 100644 --- a/src/CLR/Core/TypeSystem.cpp +++ b/src/CLR/Core/TypeSystem.cpp @@ -1293,6 +1293,12 @@ bool CLR_RT_TypeDef_Instance::ResolveToken( // If it's a type‐generic slot (!T), resolve against the caller's closed generic if (elem.DataType == DATATYPE_VAR) { + // sanity check + if (caller == nullptr || NANOCLR_INDEX_IS_INVALID(*caller->genericType)) + { + return false; + } + CLR_RT_TypeSpec_Instance callerTypeSpec; if (!callerTypeSpec.InitializeFromIndex(*caller->genericType)) { @@ -1346,7 +1352,7 @@ bool CLR_RT_TypeDef_Instance::ResolveToken( resolve_generic_argument: // Use the caller bound genericType (Stack, etc.) - if (caller == nullptr || caller->genericType == nullptr) + if (caller == nullptr || NANOCLR_INDEX_IS_INVALID(*caller->genericType)) { return false; } @@ -5687,7 +5693,14 @@ HRESULT CLR_RT_Assembly::AllocateGenericStaticFieldsOnDemand( CLR_RT_HeapBlock_Delegate *dlg = refDlg.DereferenceDelegate(); // Store the TypeSpec index so the .cctor can resolve type generic parameters - dlg->m_genericTypeSpec = *contextTypeSpec; + if (contextTypeSpec != nullptr && NANOCLR_INDEX_IS_VALID(*contextTypeSpec)) + { + dlg->m_genericTypeSpec = *contextTypeSpec; + } + else + { + dlg->m_genericTypeSpec = typeSpecIndex; + } // Store the caller's MethodSpec (if any) to enable reolution of method generic parameters if (contextMethod != nullptr) @@ -7949,6 +7962,7 @@ HRESULT CLR_RT_TypeSystem::BuildMethodName( { // Couldn't resolve CLR_SafeSprintf(szBuffer, iBuffer, "!%d", elem.GenericParamPosition); + continue; } NANOCLR_CHECK_HRESULT(BuildTypeName(paramElement.Class, szBuffer, iBuffer)); From efe42d903f6f4d297d84cc0726ca09ab4123e2ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Wed, 3 Dec 2025 20:10:58 +0000 Subject: [PATCH 39/44] Remove unused vars --- src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp | 1 - src/CLR/CorLib/corlib_native_System_Span_1.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp b/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp index 5c3d9c667b..a87a3d6ebb 100644 --- a/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp +++ b/src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp @@ -115,7 +115,6 @@ HRESULT Library_corlib_native_System_ReadOnlySpan_1::NativeReadOnlySpanConstruct CLR_RT_TypeDescriptor descDst{}; CLR_RT_TypeDef_Index sourceType{}; - CLR_RT_HeapBlock_Array *destinationArray; CLR_RT_HeapBlock_Array *sourceArray = stack.Arg1().DereferenceArray(); CLR_RT_HeapBlock *thisSpan = stack.This(); diff --git a/src/CLR/CorLib/corlib_native_System_Span_1.cpp b/src/CLR/CorLib/corlib_native_System_Span_1.cpp index 1c5c5c628c..be28aca598 100644 --- a/src/CLR/CorLib/corlib_native_System_Span_1.cpp +++ b/src/CLR/CorLib/corlib_native_System_Span_1.cpp @@ -154,7 +154,6 @@ HRESULT Library_corlib_native_System_Span_1::NativeSpanConstructor___VOID__SZARR CLR_RT_TypeDescriptor descDst{}; CLR_RT_TypeDef_Index sourceType{}; - CLR_RT_HeapBlock_Array *destinationArray; CLR_RT_HeapBlock_Array *sourceArray = stack.Arg1().DereferenceArray(); CLR_RT_HeapBlock *thisSpan = stack.This(); From 769059b9bcd0b42d9f94c6160797c3e2ba7de907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Thu, 4 Dec 2025 15:01:43 +0000 Subject: [PATCH 40/44] Fix the context being stored in the delegate to generic static .cctor --- src/CLR/Core/TypeSystem.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/CLR/Core/TypeSystem.cpp b/src/CLR/Core/TypeSystem.cpp index 0d3fc3cb17..1446ade0b5 100644 --- a/src/CLR/Core/TypeSystem.cpp +++ b/src/CLR/Core/TypeSystem.cpp @@ -5692,15 +5692,11 @@ HRESULT CLR_RT_Assembly::AllocateGenericStaticFieldsOnDemand( { CLR_RT_HeapBlock_Delegate *dlg = refDlg.DereferenceDelegate(); - // Store the TypeSpec index so the .cctor can resolve type generic parameters - if (contextTypeSpec != nullptr && NANOCLR_INDEX_IS_VALID(*contextTypeSpec)) - { - dlg->m_genericTypeSpec = *contextTypeSpec; - } - else - { - dlg->m_genericTypeSpec = typeSpecIndex; - } + // Developer notes: + // - Store the TypeSpec index so the .cctor can resolve type generic parameters + // - Use the typeSpecIndex which represents the closed generic type being initialized + // NOT the caller's context which may be non-generic + dlg->m_genericTypeSpec = typeSpecIndex; // Store the caller's MethodSpec (if any) to enable reolution of method generic parameters if (contextMethod != nullptr) From 6392da00e3c83ab9c66d2b6ef1fd87f6ca5310cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Thu, 4 Dec 2025 15:02:20 +0000 Subject: [PATCH 41/44] Fix resolving token of TypeDef for a VAR in a TypeSpec --- src/CLR/Core/TypeSystem.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/CLR/Core/TypeSystem.cpp b/src/CLR/Core/TypeSystem.cpp index 1446ade0b5..9ba0d63b8b 100644 --- a/src/CLR/Core/TypeSystem.cpp +++ b/src/CLR/Core/TypeSystem.cpp @@ -1293,14 +1293,25 @@ bool CLR_RT_TypeDef_Instance::ResolveToken( // If it's a type‐generic slot (!T), resolve against the caller's closed generic if (elem.DataType == DATATYPE_VAR) { - // sanity check - if (caller == nullptr || NANOCLR_INDEX_IS_INVALID(*caller->genericType)) + // Determine the effective generic type context + // Prefer explicit contextTypeSpec over caller->genericType + const CLR_RT_TypeSpec_Index *effectiveContext = nullptr; + + if (contextTypeSpec != nullptr && NANOCLR_INDEX_IS_VALID(*contextTypeSpec)) + { + effectiveContext = contextTypeSpec; + } + else if (caller != nullptr && NANOCLR_INDEX_IS_VALID(*caller->genericType)) + { + effectiveContext = caller->genericType; + } + else { return false; } CLR_RT_TypeSpec_Instance callerTypeSpec; - if (!callerTypeSpec.InitializeFromIndex(*caller->genericType)) + if (!callerTypeSpec.InitializeFromIndex(*effectiveContext)) { return false; } From ff0f76bfe7788d510d9431c334ef83982e0a1870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Thu, 4 Dec 2025 15:49:40 +0000 Subject: [PATCH 42/44] Dump assembly token now takes TypeSpec parameter for context - Update declaration and callers. - Rework code to resolve VAR and MVAR of TypeSpec using it. --- src/CLR/Core/TypeSystem.cpp | 4 +- src/CLR/Diagnostics/Diagnostics_stub.cpp | 2 +- src/CLR/Diagnostics/Info.cpp | 137 +++++++++++++++-------- src/CLR/Include/nanoCLR_Runtime.h | 2 +- 4 files changed, 95 insertions(+), 50 deletions(-) diff --git a/src/CLR/Core/TypeSystem.cpp b/src/CLR/Core/TypeSystem.cpp index 9ba0d63b8b..b9d81189e1 100644 --- a/src/CLR/Core/TypeSystem.cpp +++ b/src/CLR/Core/TypeSystem.cpp @@ -7902,8 +7902,8 @@ HRESULT CLR_RT_TypeSystem::BuildMethodName( // First, build the type name (either from genericType or from the method's declaring type) if (mdInst.genericType != nullptr && NANOCLR_INDEX_IS_VALID(*mdInst.genericType)) { - // Use the provided generic type context - if (FAILED(BuildTypeName(*mdInst.genericType, szBuffer, iBuffer, 0, genericType))) + // Use the method's genericType and pass itself as context to resolve VAR parameters + if (FAILED(BuildTypeName(*mdInst.genericType, szBuffer, iBuffer, 0, mdInst.genericType, &mdInst))) { // Fall back to the declaring type if (instOwner.InitializeFromMethod(mdInst) == false) diff --git a/src/CLR/Diagnostics/Diagnostics_stub.cpp b/src/CLR/Diagnostics/Diagnostics_stub.cpp index 24a4ace5e7..00cc93bc51 100644 --- a/src/CLR/Diagnostics/Diagnostics_stub.cpp +++ b/src/CLR/Diagnostics/Diagnostics_stub.cpp @@ -112,7 +112,7 @@ __nfweak const CLR_UINT8 *CLR_SkipBodyOfOpcodeCompressed(const CLR_UINT8 *ip, CL #if defined(NANOCLR_TRACE_INSTRUCTIONS) -__nfweak void CLR_RT_Assembly::DumpToken(CLR_UINT32 tk, const CLR_RT_MethodDef_Instance &methodDefInstance) +__nfweak void CLR_RT_Assembly::DumpToken(CLR_UINT32 tk, const CLR_RT_MethodDef_Instance &methodDefInstance, const CLR_RT_TypeSpec_Index *contextTypeSpec) { NATIVE_PROFILE_CLR_DIAGNOSTICS(); } diff --git a/src/CLR/Diagnostics/Info.cpp b/src/CLR/Diagnostics/Info.cpp index 762038c088..d01eb47734 100644 --- a/src/CLR/Diagnostics/Info.cpp +++ b/src/CLR/Diagnostics/Info.cpp @@ -413,7 +413,10 @@ const CLR_UINT8 *CLR_SkipBodyOfOpcodeCompressed(const CLR_UINT8 *ip, CLR_OPCODE #if defined(NANOCLR_TRACE_INSTRUCTIONS) -void CLR_RT_Assembly::DumpToken(CLR_UINT32 token, const CLR_RT_MethodDef_Instance &methodDefInstance) +void CLR_RT_Assembly::DumpToken( + CLR_UINT32 token, + const CLR_RT_MethodDef_Instance &methodDefInstance, + const CLR_RT_TypeSpec_Index *contextTypeSpec) { NATIVE_PROFILE_CLR_DIAGNOSTICS(); CLR_UINT32 index = CLR_DataFromTk(token); @@ -547,6 +550,7 @@ void CLR_RT_Assembly::DumpToken(CLR_UINT32 token, const CLR_RT_MethodDef_Instanc // Only if it is not a plain VAR/MVAR do we then check for arrays or else fall // back to BuildTypeName for the full concrete name. // + CLR_INDEX genericPosition; CLR_UINT32 ownerAsm = assemblyIndex; if (methodDefInstance.genericType != nullptr && NANOCLR_INDEX_IS_VALID(*methodDefInstance.genericType)) @@ -599,80 +603,116 @@ void CLR_RT_Assembly::DumpToken(CLR_UINT32 token, const CLR_RT_MethodDef_Instanc } } + genericPosition = elem.GenericParamPosition; + if (elem.DataType == DATATYPE_VAR) { - int gpPosition = elem.GenericParamPosition; + // Use contextTypeSpec if provided, otherwise fall back to methodDefInstance.genericType + const CLR_RT_TypeSpec_Index *effectiveContext = + (contextTypeSpec && NANOCLR_INDEX_IS_VALID(*contextTypeSpec)) ? contextTypeSpec + : methodDefInstance.genericType; - // if the caller's genericType is non‐null, ask the CLR to map !n→actual argument: - if (methodDefInstance.genericType != nullptr && NANOCLR_INDEX_IS_VALID(*methodDefInstance.genericType)) + if (effectiveContext != nullptr && NANOCLR_INDEX_IS_VALID(*effectiveContext)) { CLR_RT_TypeSpec_Instance typeSpec; - if (!typeSpec.InitializeFromIndex(*methodDefInstance.genericType)) + if (!typeSpec.InitializeFromIndex(*effectiveContext)) { - CLR_Debug::Printf("!%d", gpPosition); + CLR_Debug::Printf("!%d", genericPosition); break; } CLR_RT_SignatureParser::Element paramElement; - if (typeSpec.GetGenericParam(gpPosition, paramElement)) + if (typeSpec.GetGenericParam(genericPosition, paramElement)) { - char bufArg[256]{}; - char *pArg = bufArg; - size_t cbArg = sizeof(bufArg); - - g_CLR_RT_TypeSystem.BuildTypeName( - paramElement.Class, - pArg, - cbArg, - CLR_RT_TypeSystem::TYPENAME_FLAGS_FULL, - elem.Levels); - - CLR_Debug::Printf("%s", bufArg); + // Successfully resolved from generic context + if (NANOCLR_INDEX_IS_VALID(paramElement.Class)) + { + char bufArg[256]{}; + char *pArg = bufArg; + size_t cbArg = sizeof(bufArg); + + g_CLR_RT_TypeSystem.BuildTypeName( + paramElement.Class, + pArg, + cbArg, + CLR_RT_TypeSystem::TYPENAME_FLAGS_FULL, + elem.Levels); + + CLR_Debug::Printf("%s", bufArg); + } + else if (paramElement.DataType == DATATYPE_MVAR) + { + // need to defer to generic method argument + genericPosition = paramElement.GenericParamPosition; - break; + goto resolve_generic_argument; + } + else if (paramElement.DataType == DATATYPE_VAR) + { + // nested VAR not implemented + ASSERT(false); + } } } // Couldn't resolve or caller was not generic: print "!n" literally - CLR_Debug::Printf("!%d", gpPosition); + CLR_Debug::Printf("!%d", genericPosition); break; } else if (elem.DataType == DATATYPE_MVAR) { - int gpPosition = elem.GenericParamPosition; + resolve_generic_argument: - // if the caller's genericType is non‐null, ask the CLR to map !n→actual argument: - if (methodDefInstance.genericType != nullptr && NANOCLR_INDEX_IS_VALID(*methodDefInstance.genericType)) + if (NANOCLR_INDEX_IS_VALID(methodDefInstance.methodSpec)) { - CLR_RT_GenericParam_Index gpIndex; - - bool ok = g_CLR_RT_TypeSystem.m_assemblies[methodDefInstance.genericType->Assembly() - 1] - ->FindGenericParamAtMethodDef(methodDefInstance, gpPosition, gpIndex); - if (ok) + CLR_RT_MethodSpec_Instance methodSpecInstance; + if (methodSpecInstance.InitializeFromIndex(methodDefInstance.methodSpec)) { - CLR_RT_GenericParam_CrossReference gp = - g_CLR_RT_TypeSystem.m_assemblies[methodDefInstance.genericType->Assembly() - 1] - ->crossReferenceGenericParam[gpIndex.GenericParam()]; + CLR_RT_SignatureParser::Element element; + + if (methodSpecInstance.GetGenericArgument(genericPosition, element)) + { + if (element.DataType == DATATYPE_VAR) + { + // need to defer to generic type parameter + CLR_RT_TypeSpec_Instance contextTs; + if (contextTypeSpec && contextTs.InitializeFromIndex(*contextTypeSpec)) + { + if (!contextTs.GetGenericParam(element.GenericParamPosition, element)) + { + CLR_Debug::Printf("!!%d", genericPosition); + + break; + } + } + } + else if (element.DataType == DATATYPE_MVAR) + { + // nested MVAR not implemented + ASSERT(false); + } - char bufArg[256]{}; - char *pArg = bufArg; - size_t cbArg = sizeof(bufArg); + char bufArg[256]{}; + char *pArg = bufArg; + size_t cbArg = sizeof(bufArg); - g_CLR_RT_TypeSystem.BuildTypeName( - gp.classTypeDef, - pArg, - cbArg, - CLR_RT_TypeSystem::TYPENAME_FLAGS_FULL, - elem.Levels); + g_CLR_RT_TypeSystem.BuildTypeName( + element.Class, + pArg, + cbArg, + CLR_RT_TypeSystem::TYPENAME_FLAGS_FULL, + elem.Levels); - CLR_Debug::Printf("%s", bufArg); + CLR_Debug::Printf("%s", bufArg); - break; + break; + } } } + // Couldn't resolve or caller was not generic: print "!!n" literally - CLR_Debug::Printf("!!%d", gpPosition); + CLR_Debug::Printf("!!%d", genericPosition); break; } @@ -980,12 +1020,17 @@ void CLR_RT_Assembly::DumpOpcodeDirect( else { // In the unlikely case ResolveToken fails, fall back to raw DumpToken: - DumpToken(token, call); + DumpToken(token, call, nullptr); } } else { - DumpToken(token, call); + // Pass the stack's generic type storage as context for VAR resolution + const CLR_RT_TypeSpec_Index *context = + (NANOCLR_INDEX_IS_VALID(call) && call.genericType && NANOCLR_INDEX_IS_VALID(*call.genericType)) + ? call.genericType + : nullptr; + DumpToken(token, call, context); } } else diff --git a/src/CLR/Include/nanoCLR_Runtime.h b/src/CLR/Include/nanoCLR_Runtime.h index 2e510056df..85a75274fe 100644 --- a/src/CLR/Include/nanoCLR_Runtime.h +++ b/src/CLR/Include/nanoCLR_Runtime.h @@ -1593,7 +1593,7 @@ struct CLR_RT_Assembly : public CLR_RT_HeapBlock_Node // EVENT HEAP - NO RELOCAT DECL_POSTFIX; private: - void DumpToken(CLR_UINT32 tk, const CLR_RT_MethodDef_Instance &methodDefInstance) DECL_POSTFIX; + void DumpToken(CLR_UINT32 tk, const CLR_RT_MethodDef_Instance &methodDefInstance, const CLR_RT_TypeSpec_Index *contextTypeSpec) DECL_POSTFIX; void DumpSignature(CLR_SIG sig) DECL_POSTFIX; void DumpSignature(CLR_PMETADATA &p) DECL_POSTFIX; void DumpSignatureToken(CLR_PMETADATA &p) DECL_POSTFIX; From 4218d94d5e5d0b4d2dd14e63a1f1703590763c93 Mon Sep 17 00:00:00 2001 From: nfbot Date: Thu, 4 Dec 2025 15:55:41 +0000 Subject: [PATCH 43/44] Code style fixes Automated fixes for code style. --- src/CLR/Diagnostics/Diagnostics_stub.cpp | 5 ++++- src/CLR/Include/nanoCLR_Runtime.h | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/CLR/Diagnostics/Diagnostics_stub.cpp b/src/CLR/Diagnostics/Diagnostics_stub.cpp index 00cc93bc51..1cd6046570 100644 --- a/src/CLR/Diagnostics/Diagnostics_stub.cpp +++ b/src/CLR/Diagnostics/Diagnostics_stub.cpp @@ -112,7 +112,10 @@ __nfweak const CLR_UINT8 *CLR_SkipBodyOfOpcodeCompressed(const CLR_UINT8 *ip, CL #if defined(NANOCLR_TRACE_INSTRUCTIONS) -__nfweak void CLR_RT_Assembly::DumpToken(CLR_UINT32 tk, const CLR_RT_MethodDef_Instance &methodDefInstance, const CLR_RT_TypeSpec_Index *contextTypeSpec) +__nfweak void CLR_RT_Assembly::DumpToken( + CLR_UINT32 tk, + const CLR_RT_MethodDef_Instance &methodDefInstance, + const CLR_RT_TypeSpec_Index *contextTypeSpec) { NATIVE_PROFILE_CLR_DIAGNOSTICS(); } diff --git a/src/CLR/Include/nanoCLR_Runtime.h b/src/CLR/Include/nanoCLR_Runtime.h index 85a75274fe..55c995fcd6 100644 --- a/src/CLR/Include/nanoCLR_Runtime.h +++ b/src/CLR/Include/nanoCLR_Runtime.h @@ -1593,7 +1593,10 @@ struct CLR_RT_Assembly : public CLR_RT_HeapBlock_Node // EVENT HEAP - NO RELOCAT DECL_POSTFIX; private: - void DumpToken(CLR_UINT32 tk, const CLR_RT_MethodDef_Instance &methodDefInstance, const CLR_RT_TypeSpec_Index *contextTypeSpec) DECL_POSTFIX; + void DumpToken( + CLR_UINT32 tk, + const CLR_RT_MethodDef_Instance &methodDefInstance, + const CLR_RT_TypeSpec_Index *contextTypeSpec) DECL_POSTFIX; void DumpSignature(CLR_SIG sig) DECL_POSTFIX; void DumpSignature(CLR_PMETADATA &p) DECL_POSTFIX; void DumpSignatureToken(CLR_PMETADATA &p) DECL_POSTFIX; From a98bfe09dc9598bfb58267246fecce50e23ce1fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Fri, 5 Dec 2025 09:23:41 +0000 Subject: [PATCH 44/44] Fix corruption in virtual methods cache table *** PR separately *** --- src/CLR/Core/Cache.cpp | 86 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/src/CLR/Core/Cache.cpp b/src/CLR/Core/Cache.cpp index 1b3bdf5ad6..9ecb636d30 100644 --- a/src/CLR/Core/Cache.cpp +++ b/src/CLR/Core/Cache.cpp @@ -196,6 +196,59 @@ void CLR_RT_EventCache::VirtualMethodTable::Initialize() m_entriesMRU = (Link *)&g_scratchVirtualMethodTableLinkMRU[0]; m_payloads = (Payload *)&g_scratchVirtualMethodPayload[0]; +#ifdef DEBUG + + // DIAGNOSTIC: Verify structure sizes and alignment + CLR_Debug::Printf("\r\n========== VirtualMethodTable::Initialize DIAGNOSTICS ==========\r\n"); + CLR_Debug::Printf("sizeof(Link) = %u, alignof(Link) = %u\r\n", sizeof(Link), alignof(Link)); + CLR_Debug::Printf("sizeof(Payload) = %u, alignof(Payload) = %u\r\n", sizeof(Payload), alignof(Payload)); + CLR_Debug::Printf("sizeof(Payload::Key) = %u\r\n", sizeof(Payload::Key)); + CLR_Debug::Printf("LinkArraySize() = %u (expected: 641)\r\n", LinkArraySize()); + CLR_Debug::Printf("LinkMRUArraySize() = %u (expected: 513)\r\n", LinkMRUArraySize()); + CLR_Debug::Printf("PayloadArraySize() = %u (expected: 512)\r\n", PayloadArraySize()); + + // Verify array base addresses don't overlap + uintptr_t entries_start = (uintptr_t)m_entries; + uintptr_t entries_end = entries_start + (LinkArraySize() * sizeof(Link)); + uintptr_t entriesMRU_start = (uintptr_t)m_entriesMRU; + uintptr_t entriesMRU_end = entriesMRU_start + (LinkMRUArraySize() * sizeof(Link)); + uintptr_t payloads_start = (uintptr_t)m_payloads; + uintptr_t payloads_end = payloads_start + (PayloadArraySize() * sizeof(Payload)); + + CLR_Debug::Printf( + "m_entries: 0x%08X - 0x%08X (%u bytes)\r\n", + (unsigned int)entries_start, + (unsigned int)entries_end, + (unsigned int)(entries_end - entries_start)); + CLR_Debug::Printf( + "m_entriesMRU: 0x%08X - 0x%08X (%u bytes)\r\n", + (unsigned int)entriesMRU_start, + (unsigned int)entriesMRU_end, + (unsigned int)(entriesMRU_end - entriesMRU_start)); + CLR_Debug::Printf( + "m_payloads: 0x%08X - 0x%08X (%u bytes)\r\n", + (unsigned int)payloads_start, + (unsigned int)payloads_end, + (unsigned int)(payloads_end - payloads_start)); + + // Check for overlaps + if (entries_end > entriesMRU_start && entries_start < entriesMRU_end) + { + CLR_Debug::Printf("*** WARNING: m_entries and m_entriesMRU OVERLAP! ***\r\n"); + } + if (entries_end > payloads_start && entries_start < payloads_end) + { + CLR_Debug::Printf("*** WARNING: m_entries and m_payloads OVERLAP! ***\r\n"); + } + if (entriesMRU_end > payloads_start && entriesMRU_start < payloads_end) + { + CLR_Debug::Printf("*** WARNING: m_entriesMRU and m_payloads OVERLAP! ***\r\n"); + } + + CLR_Debug::Printf("================================================================\r\n\r\n"); + +#endif + // // Link all the entries to themselves => no elements in the lists. // @@ -211,6 +264,7 @@ void CLR_RT_EventCache::VirtualMethodTable::Initialize() // Link all the entries to the following one => all the elements are in the MRU list. // _ASSERTE(LinkMRUArraySize() < 0xFFFF); + for (index = 0; index < LinkMRUArraySize(); index++) { Link &lnk = m_entriesMRU[index]; @@ -301,14 +355,36 @@ bool CLR_RT_EventCache::VirtualMethodTable::FindVirtualMethod( for (index = m_entries[indexHead].m_next;; index = m_entries[index].m_next) { +#ifdef DEBUG &&_WIN64 + CLR_Debug::Printf(" Loop: index=%u, indexHead=%u\r\n", index, indexHead); +#endif + + // validate index before using it to prevent crashes from corrupted data + if (index >= LinkArraySize()) + { + // !! corrupted index detected !! + // // repair the hash chain and treat as cache miss + m_entries[indexHead].m_next = indexHead; + m_entries[indexHead].m_prev = indexHead; + + index = indexHead; + } + if (index != indexHead) { + _ASSERTE(index < PayloadArraySize()); + Payload &res = m_payloads[index]; if (res.m_key.m_mdVirtual.data != mdVirtualData) + { continue; + } + if (res.m_key.m_cls.data != clsData) + { continue; + } md = res.m_md; @@ -317,10 +393,20 @@ bool CLR_RT_EventCache::VirtualMethodTable::FindVirtualMethod( else { if (g_CLR_RT_TypeSystem.FindVirtualMethodDef(cls, mdVirtual, md) == false) + { return false; + } index = GetNewEntry(); +#ifdef DEBUG &&_WIN64 + CLR_Debug::Printf(" GetNewEntry returned: %u\r\n", index); +#endif + + // initialize the entry's links before use to prevent corruption + m_entries[index].m_next = index; + m_entries[index].m_prev = index; + Payload &res = m_payloads[index]; res.m_md = md;