Skip to content

Conversation

@josesimoes
Copy link
Member

@josesimoes josesimoes commented Apr 21, 2025

Description

  • Add links to both main README files.
  • Expand targets tables to include preview versions.
  • Minor formating fixes.

Motivation and Context

How Has This Been Tested?

Screenshots

Types of changes

  • Improvement (non-breaking change that improves a feature, code or algorithm)
  • Bug fix (non-breaking change which fixes an issue with code or algorithm)
  • New feature (non-breaking change which adds functionality to code)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Config and build (change in the configuration and build system, has no impact on code or features)
  • Dev Containers (changes related with Dev Containers, has no impact on code or features)
  • Dependencies/declarations (update dependencies or assembly declarations and changes associated, has no impact on code or features)
  • Documentation (changes or updates in the documentation, has no impact on code or features)

Checklist

  • My code follows the code style of this project (only if there are changes in source code).
  • My changes require an update to the documentation (there are changes that require the docs website to be updated).
  • I have updated the documentation accordingly (the changes require an update on the docs in this repo).
  • I have read the CONTRIBUTING document.
  • I have tested everything locally and all new and existing tests passed (only if there are changes in source code).

Summary by CodeRabbit

  • Documentation

    • Added comprehensive README describing public-preview support for C# generics, upgrade steps, known issues, and feedback channels; README updates added in English and Chinese and firmware tables now show Stable/Preview columns.
  • New Features

    • Preview support for generics on nanoFramework (including generic type reflection and closed-generic handling).
    • Added Span/ReadOnlySpan constructors and String.Format improvements.
    • Exposed a GC Run API to trigger collections from managed code.

✏️ Tip: You can customize this high-level summary in your review settings.

- Fix packaging and versioning of win32 CLR.

***NO_CI***
- Fix condition to publish packages on buils from develop branch.

***NO_CI***
- Remove task publishing to private Azure feed.
- Update condition to publish nanoCLR on develop builds.

***NO_CI***
- Add links to both main README files.
- Expand targets tables to include preview versions.
- Minor formating fixes.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Apr 21, 2025

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

💤 Files selected but had no reviewable changes (4)
  • targets/ChibiOS/_nanoCLR/System.Device.Gpio/cpu_gpio.cpp
  • targets/ESP32/_nanoCLR/System.Device.Adc/sys_dev_adc_native_System_Device_Adc_AdcChannel.cpp
  • targets/ESP32/_nanoCLR/System.Device.Adc/sys_dev_adc_native_System_Device_Adc_AdcController.cpp
  • targets/ESP32/_nanoCLR/System.Device.Gpio/cpu_gpio.cpp

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Adds comprehensive runtime support for generics across the CLR: new native CorLib bindings (Span/ReadOnlySpan/Unsafe/String/Type/GC/etc.), TypeSystem and interpreter/runtime changes to resolve and instantiate generic types, HeapBlock/type-descriptor matching, GC and profiler updates, plus docs, devcontainer image bumps, and CI/pipeline adjustments.

Changes

Cohort / File(s) Change Summary
Documentation
README-GENERICS.md, README.md, README.zh-cn.md
New generics preview README; updated main/Chinese READMEs to show Stable/Preview firmware columns and preview badges; guidance for tooling/firmware and known issues.
Devcontainer images
.devcontainer/*/Dockerfile.*
Bumped many devcontainer base image tags from v1.x/v2.x → v3.0 across All, AzureRTOS, ChibiOS, ESP32, FreeRTOS-NXP, TI.
CI / Pipelines / Templates
azure-pipelines.yml, azure-pipelines-nightly.yml, azure-pipelines-templates/*
Added check-mdp-for-build template, expanded publish conditions to include develop, integrated MDP artifact download and preview MSBuild component usage, updated many REVISION counters, and disabled one STM32 matrix entry.
CMake / Binutils options
CMake/Modules/*, CMake/binutils.*.cmake
Removed LTO linker flags in Release/MinSizeRel in multiple platform option files; small include/comment cleanup.
Project files
src/CLR/CorLib/CorLib.vcxproj, src/CLR/Core/Core.vcxproj (+filters)
Added new source files (Span/ReadOnlySpan/Unsafe/native files, GenericInstance, etc.) to projects and filters.
CorLib native additions
src/CLR/CorLib/corlib_native*.cpp, corlib_native.h, corlib_native_System_Span_1.cpp, corlib_native_System_ReadOnlySpan_1.cpp, corlib_native_System_Runtime_CompilerServices_Unsafe.cpp, corlib_native_System_String.cpp, corlib_native_System_Type.cpp, corlib_native_System_GC.cpp, ...
Large additions and wiring of native methods for generics and reflection: Span/ReadOnlySpan constructors, Unsafe.As, String.Format and Trim variants, Type generic introspection APIs, GC Run/AnyPendingFinalizers/Finalize handling changes, many method lookup table entries updated, conditional reflection guards.
Type system & interpreter / execution
src/CLR/Core/*, src/CLR/Core/Interpreter.cpp, Execution.cpp, CLR_RT_StackFrame.cpp, CLR_RT_HeapBlock.cpp, CLR_RT_HeapBlock_Array.cpp, CLR_RT_HeapBlock_GenericInstance.cpp, TypeSystemLookup.cpp, corhdr_private.h
Core runtime work to represent/resolve GENERICINST/VAR/MVAR, add generic-instance constructors and NewGenericInstanceObject, extend InitializeReference/InitializeLocals/Cast/IsInstanceOf to carry generic context, add TypeDescriptor/TypeDescriptorsMatch logic, update enum/element-type table entries.
Garbage collector & memory
src/CLR/Core/GarbageCollector*.cpp, CLR_RT_Memory.cpp, GarbageCollector_Compaction.cpp, GarbageCollector_ComputeReachabilityGraph.cpp
Modernized null use, updated heap field names, added marking for generic static fields, changed GC accounting return, added bounds/assert checks in compaction/relocate.
Diagnostics & Debugger / Profiler
src/CLR/Diagnostics/*, src/CLR/Debugger/*, src/CLR/Diagnostics/Info.cpp, Profile.cpp, Profiler.cpp
Added diagnostic printing overloads aware of generic contexts, updated debugger internals and signatures (nullptr modernization, index type changes), profiler returns/null modernizations.
Interop / Serialization / Streams / Misc
src/CLR/Core/Interop*.cpp, Serialization/BinaryFormatter*.cpp, Streams.cpp, FileStream.cpp, Various.cpp
Null modernization, signature changes to data-type returns (CLR_DataType → NanoCLRDataType), CreateInstance API extensions (array creation with context), and safe dummy returns for unsupported marshalling.
API / header adjustments
src/CLR/CorLib/corlib_native.h, src/CLR/Include/nanoCLR_Checks.h, src/CLR/Core/corhdr_private.h
New/changed native method declarations for generics and reflection, new CorElementType values (VAR/GENERICINST/MVAR), added diagnostics/verify API signatures (VerifyStackOK) and dump METHOD overloads.
Modernization: nullptr & member renames
Many files under src/CLR/CorLib/*, src/CLR/Core/*, src/CLR/Debugger/*
Widespread migration from NULLnullptr and from m_ prefixed members to new names (e.g., m_Assmassembly, m_targettarget, m_datadata) across the codebase to align with updated structs and C++11 style.

Sequence Diagram(s)

sequenceDiagram
    participant Caller as Managed Code
    participant Interpreter
    participant Exec as ExecutionEngine
    participant TypeSys as TypeSystem/SignatureParser
    participant Heap as HeapBlock/Array
    participant GC as GarbageCollector

    Caller->>Interpreter: execute IL (newobj / ldtoken / box / castclass)
    Interpreter->>TypeSys: resolve token (with generic context if present)
    TypeSys-->>Interpreter: type/TypeSpec (GENERICINST/VAR/MVAR)
    Interpreter->>Exec: request object creation with TypeSpec
    Exec->>Heap: New(Generic)InstanceObject / CreateInstanceWithStorage
    Heap-->>Exec: instance reference
    Exec->>GC: register statics / protect temporaries
    GC-->>Exec: confirm protection (may schedule compaction/collection)
    Exec-->>Caller: return new object/reference
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~240 minutes

Areas to focus review on:

  • CLR_RT_HeapBlock type-compatibility logic and TypeDescriptorsMatch (core correctness and edge cases for generics, arrays, pointers).
  • Interpreter/Execution changes that propagate generic context (CALL/NEWOBJ/BOX/CAST paths) and stack-frame generic storage — correctness and GC safety.
  • GarbageCollector changes (generic static fields marking, accounting return change, compaction assertions).
  • CorLib native additions: Span/ReadOnlySpan constructors, Unsafe.As, String.Format, Type reflection APIs and their NANOCLR_REFLECTION guards.
  • ABI/struct renames and wide member-name migrations (ensure no mismatched references across translation units).
  • CI/pipeline template additions that fetch MDP artifacts and preview MSBuild components.

Possibly related PRs

Suggested labels

Type: enhancement, Type: dependencies

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.27% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add README for generics public preview' accurately describes the main change—a new README documentation file introducing generics support in preview form for .NET nanoFramework.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
README.md (2)

18-22: Minor punctuation improvement in new preview section
Add a comma for clarity in the sentence on line 21.
Suggested change:

- If you want to use this feature please refer to the documentation [here](README-GENERICS.md).
+ If you want to use this feature, please refer to the documentation [here](README-GENERICS.md).
🧰 Tools
🪛 LanguageTool

[typographical] ~21-~21: It seems that a comma is missing.
Context: ...enerics. If you want to use this feature please refer to the documentation [here](READM...

(IF_PLEASE_COMMA)


40-40: Remove stray semicolon in separator line
The line -----; appears to include an unintended semicolon and may disrupt Markdown rendering. It should be:

- -----;
+ -----
README.zh-cn.md (1)

18-21: Remove trailing spaces after Chinese sentence
Line 20 ends with two spaces; remove the extra whitespace to clean up formatting.

README-GENERICS.md (2)

42-43: Clarify comparative phrasing
The clause “similarly to how they would on the full .NET runtime” lacks an explicit verb and may read awkwardly.
Suggested revision:

- …write more flexible and reusable code on microcontrollers, similarly to how they would on the full .NET runtime.
+ …write more flexible and reusable code on microcontrollers, similar to how they can on the full .NET runtime.
🧰 Tools
🪛 LanguageTool

[grammar] ~42-~42: It seems that a verb is missing.
Context: ...microcontrollers, similarly to how they would on the full .NET runtime. Keep in mind ...

(MD_DT_JJ)


75-75: Add hyphens for compound adjectives
Use hyphens in “pre- and post-generics projects” for correct compound adjective styling.
Suggested revision:

- …not possible to support pre and post generics projects…
+ …not possible to support pre‑ and post‑generics projects…
🧰 Tools
🪛 LanguageTool

[grammar] ~75-~75: It appears that hyphens are missing.
Context: ...extension, it's not possible to support pre and post generics projects with the same extension. There...

(PRE_AND_POST_NN)


[grammar] ~75-~75: It appears that hyphens are missing.
Context: ...ersions. It is not an option to install pre and post generics extensions in VS2019 and VS2022, for ex...

(PRE_AND_POST_NN)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c2c71d1 and 5adc2fd.

📒 Files selected for processing (3)
  • README-GENERICS.md (1 hunks)
  • README.md (4 hunks)
  • README.zh-cn.md (2 hunks)
🧰 Additional context used
🪛 LanguageTool
README.zh-cn.md

[style] ~119-~119: Using many exclamation marks might seem excessive (in this case: 90 exclamation marks for a text that’s 39429 characters long)
Context: ...-|---|---| | TI_CC1352R1_LAUNCHXL_868 | Latest Version @ Cloudsmith | Latest Version @ Cloudsmith | | TI_CC1352R1_LAUNCHXL_915 | Latest Version @ Cloudsmith | Latest Version @ Cloudsmith | | TI_CC3220SF_LAUNCHXL | [![Latest Version @ Cloudsmith](https://ap...

(EN_EXCESSIVE_EXCLAMATION)

README.md

[typographical] ~21-~21: It seems that a comma is missing.
Context: ...enerics. If you want to use this feature please refer to the documentation [here](READM...

(IF_PLEASE_COMMA)


[style] ~131-~131: Using many exclamation marks might seem excessive (in this case: 91 exclamation marks for a text that’s 43399 characters long)
Context: ...-|---|---| | TI_CC1352R1_LAUNCHXL_868 | Latest Version @ Cloudsmith | Latest Version @ Cloudsmith | | TI_CC1352R1_LAUNCHXL_915 | Latest Version @ Cloudsmith | Latest Version @ Cloudsmith | | TI_CC3220SF_LAUNCHXL | [![Latest Version @ Cloudsmith](https://ap...

(EN_EXCESSIVE_EXCLAMATION)

README-GENERICS.md

[grammar] ~42-~42: It seems that a verb is missing.
Context: ...microcontrollers, similarly to how they would on the full .NET runtime. Keep in mind ...

(MD_DT_JJ)


[grammar] ~75-~75: It appears that hyphens are missing.
Context: ...extension, it's not possible to support pre and post generics projects with the same extension. There...

(PRE_AND_POST_NN)


[grammar] ~75-~75: It appears that hyphens are missing.
Context: ...ersions. It is not an option to install pre and post generics extensions in VS2019 and VS2022, for ex...

(PRE_AND_POST_NN)

🔇 Additional comments (11)
README.md (1)

44-45: Preview column added correctly
The new “Preview” column header and its alignment row are clear and consistent with existing table formatting.

README.zh-cn.md (1)

32-33: 新增“预览”列标题格式正确
表头新增的“预览”列及其对齐符号符合 Markdown 规范,清晰易读。

README-GENERICS.md (9)

1-4: Great new documentation file
The title and overview section clearly introduce the purpose and context of the generics public preview.


5-12: Clear explanation of generics benefits
The bullet points for type safety, reusability, and performance are well‑written and concise.


13-25: Well‑formed generic method example
The sample code for Max<T> demonstrates usage effectively and follows standard C# conventions.


27-38: Effective generic class illustration
The Stack<T> class example is simple, accurate, and easy to follow.


44-58: Firmware flashing instructions are clear
The nanoff --preview example and notes accurately guide users through applying preview firmware.


61-70: VSIX installation steps are well detailed
The instructions for downloading and installing the preview Visual Studio extension are precise and user‑friendly.


77-86: NuGet package update guidance is solid
The steps for enabling prerelease packages and aligning versions are clear and actionable.


88-98: Feedback and known issues section is comprehensive
The caveats around debugging quirks and memory usage set proper expectations for users.


99-100: Strong closing call to action
The thank‑you note and invitation for feedback provide a positive wrap‑up.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (5)
README-GENERICS.md (5)

1-1: Enhance title for clarity and consistency
To align with other nanoFramework documentation, consider renaming the title to something like “Generics Public Preview for .NET nanoFramework CLR” to immediately convey context.


40-42: Clarify wording in paragraph for readability
The phrase “similarly to how they would on the full .NET runtime” is missing a verb and reads awkwardly. For example:

- Developers can now write more flexible and reusable code on microcontrollers, similarly to how they would on the full .NET runtime.
+ Developers can now write more flexible and reusable code on microcontrollers, just as they would on the full .NET runtime.
🧰 Tools
🪛 LanguageTool

[grammar] ~42-~42: It seems that a verb is missing.
Context: ...microcontrollers, similarly to how they would on the full .NET runtime. Keep in mind ...

(MD_DT_JJ)


71-71: Hyphenate “double-click”
Use “double-click” instead of “double click” to match conventional spelling.

- After downloading the file, double click on it and the extension installer will open.
+ After downloading the file, double-click on it and the extension installer will open.
🧰 Tools
🪛 LanguageTool

[grammar] ~71-~71: This expression is usually spelled with a hyphen.
Context: ...l Studio:** After downloading the file, double click on it and the extension installer will ...

(DOUBLE_CLICK_HYPHEN)


75-75: Improve hyphenation and punctuation in warning
This sentence has multiple hyphenation and comma issues. For clarity:

- > **Warning:** Despite we've tried to make that possible using preview features in VS extension, it's not possible to support pre and post generics projects with the same extension.
+ > **Warning:** Despite our efforts to make this possible using the VS extension preview, it’s not possible to support pre- and post-generics projects with the same extension.

Also add a comma after “for example” in the next sentence:

- It is not an option to install pre and post generics extensions in VS2019 and VS2022, for example as both use…
+ It is not an option to install pre- and post-generics extensions in VS2019 and VS2022, for example, as both use…
🧰 Tools
🪛 LanguageTool

[uncategorized] ~75-~75: You might be missing the article “the” here.
Context: ...that possible using preview features in VS extension, it's not possible to support...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[grammar] ~75-~75: It appears that hyphens are missing.
Context: ...extension, it's not possible to support pre and post generics projects with the same extension. There...

(PRE_AND_POST_NN)


[grammar] ~75-~75: It appears that hyphens are missing.
Context: ...ersions. It is not an option to install pre and post generics extensions in VS2019 and VS2022, for ex...

(PRE_AND_POST_NN)


[uncategorized] ~75-~75: A comma might be missing here.
Context: ...cs extensions in VS2019 and VS2022, for example as both use the same msbuild components...

(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)


1-100: Consider adding a Table of Contents
This document is lengthy. A TOC at the top (linking to sections like “Overview,” “Firmware Flashing,” etc.) will improve navigation for readers.

🧰 Tools
🪛 LanguageTool

[grammar] ~42-~42: It seems that a verb is missing.
Context: ...microcontrollers, similarly to how they would on the full .NET runtime. Keep in mind ...

(MD_DT_JJ)


[grammar] ~71-~71: This expression is usually spelled with a hyphen.
Context: ...l Studio:** After downloading the file, double click on it and the extension installer will ...

(DOUBLE_CLICK_HYPHEN)


[uncategorized] ~75-~75: You might be missing the article “the” here.
Context: ...that possible using preview features in VS extension, it's not possible to support...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[grammar] ~75-~75: It appears that hyphens are missing.
Context: ...extension, it's not possible to support pre and post generics projects with the same extension. There...

(PRE_AND_POST_NN)


[grammar] ~75-~75: It appears that hyphens are missing.
Context: ...ersions. It is not an option to install pre and post generics extensions in VS2019 and VS2022, for ex...

(PRE_AND_POST_NN)


[uncategorized] ~75-~75: A comma might be missing here.
Context: ...cs extensions in VS2019 and VS2022, for example as both use the same msbuild components...

(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5adc2fd and 498e471.

📒 Files selected for processing (1)
  • README-GENERICS.md (1 hunks)
🧰 Additional context used
🪛 LanguageTool
README-GENERICS.md

[grammar] ~42-~42: It seems that a verb is missing.
Context: ...microcontrollers, similarly to how they would on the full .NET runtime. Keep in mind ...

(MD_DT_JJ)


[grammar] ~71-~71: This expression is usually spelled with a hyphen.
Context: ...l Studio:** After downloading the file, double click on it and the extension installer will ...

(DOUBLE_CLICK_HYPHEN)


[uncategorized] ~75-~75: You might be missing the article “the” here.
Context: ...that possible using preview features in VS extension, it's not possible to support...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[grammar] ~75-~75: It appears that hyphens are missing.
Context: ...extension, it's not possible to support pre and post generics projects with the same extension. There...

(PRE_AND_POST_NN)


[grammar] ~75-~75: It appears that hyphens are missing.
Context: ...ersions. It is not an option to install pre and post generics extensions in VS2019 and VS2022, for ex...

(PRE_AND_POST_NN)


[uncategorized] ~75-~75: A comma might be missing here.
Context: ...cs extensions in VS2019 and VS2022, for example as both use the same msbuild components...

(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: nf-interpreter (Nightly build) (Build_STM32_targets ST_NUCLEO64_F091RC)
  • GitHub Check: nf-interpreter (Nightly build) (Build_STM32_targets ORGPAL_PALX)
  • GitHub Check: nf-interpreter (Nightly build) (Build_STM32_targets ORGPAL_PALTHREE)
  • GitHub Check: nf-interpreter (Check_Code_Style)
  • GitHub Check: nf-interpreter (Nightly build) (Check_Build_Options)
  • GitHub Check: nf-interpreter (Check_Build_Options)

Copy link
Member

@Ellerbach Ellerbach left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That look super clear! Thanks a lot!

- Pin AZDO vm image to windows-2022. (new windows-2025 is having issues).

***NO_CI***
- Remove pre-check of MDP specific build. Can be handled at step level, allowing re-running a job.

***NO_CI***
@nfbot
Copy link
Member

nfbot commented Dec 9, 2025

@josesimoes there are issues with the code style on the source files.
A PR was submitted with the code style fixes. Please click Eclo#168, review the changes if you want and merge it.

Make sure to follow the project code style. Check the details here on how it works and the tools required to help you with that.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

♻️ Duplicate comments (1)
CMake/Modules/AzureRTOS_EFM32GG11_GCC_options.cmake (1)

65-65: Same LTO removal as TI_SimpleLink target.

This mirrors the change in TI_SimpleLink_CC13X2_GCC_options.cmake, indicating a coordinated decision to disable LTO across multiple targets. The same code size verification considerations apply here.

See the comment on TI_SimpleLink_CC13X2_GCC_options.cmake line 54 for verification steps.

🧹 Nitpick comments (24)
README.md (1)

98-98: Consider adding a hyphen to "chip-based."

Grammar linting suggests: "STM32 boards and chip-based" should be "STM32 boards and chip-based" (with hyphen). The current text reads "STM32 boards and chip based" which would benefit from a hyphen to clarify that "chip" modifies "based" as a compound adjective.

Apply this diff:

-### STM32 boards and chip based
+### STM32 boards and chip-based
src/CLR/Core/Cache.cpp (1)

199-272: Consider extracting diagnostics to a helper function.

The diagnostic block is useful for debugging memory layout issues but spans 74 lines within Initialize(). While the DEBUG-only scope is appropriate, extracting this to a separate function (e.g., VerifyMemoryLayout()) would improve readability and maintainability.

Example refactor:

#ifdef DEBUG
static void VerifyMemoryLayout(Link* entries, Link* entriesMRU, Payload* payloads)
{
    // Move diagnostic code here
}
#endif

void CLR_RT_EventCache::VirtualMethodTable::Initialize()
{
    NATIVE_PROFILE_CLR_CORE();
    CLR_UINT32 index;

    m_entries = (Link *)&g_scratchVirtualMethodTableLink[0];
    m_entriesMRU = (Link *)&g_scratchVirtualMethodTableLinkMRU[0];
    m_payloads = (Payload *)&g_scratchVirtualMethodPayload[0];

#ifdef DEBUG
    VerifyMemoryLayout(m_entries, m_entriesMRU, m_payloads);
#endif

    // Rest of initialization...
}
src/CLR/Debugger/Debugger.cpp (1)

2455-2459: Unreachable code should be removed.

Line 2458 return true; is unreachable because GetValue returns on line 2456. This pattern appears multiple times in the file (lines 2476-2478, 2929-2931, 3041-3044, 3097-3100, 3109-3113, 3134-3137, 3194-3197, 3225-3228, 3254-3257, 3470-3473).

-    // WP_ReplyToCommand(msg, g_CLR_DBG_Debugger->GetValue(msg, pThread, nullptr, nullptr), false, nullptr, 0);
-    return g_CLR_DBG_Debugger->GetValue(msg, pThread, nullptr, nullptr);
-
-    return true;
+    return g_CLR_DBG_Debugger->GetValue(msg, pThread, nullptr, nullptr);
src/CLR/Core/CLR_RT_UnicodeHelper.cpp (1)

807-863: Make m_wCharArray usage consistently use nullptr

The constructor and Release() now use nullptr, but there are still assignments to NULL in the error path of Assign() (Line 845) and in Release() (Line 860). Not a bug, but it’s inconsistent within the same type.

To keep the style uniform and fully modernize the pointer usage, consider:

    if (!m_unicodeHelper.ConvertFromUTF8(-1, false))
    {
        CLR_RT_Memory::Release(m_wCharArray);
-        m_wCharArray = NULL;
+        m_wCharArray = nullptr;
        m_length = 0;
        NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER);
    }
void UnicodeString::Release()
{
-    if (m_wCharArray != nullptr)
-    {
-        CLR_RT_Memory::Release(m_wCharArray);
-        m_wCharArray = NULL;
-    }
+    if (m_wCharArray != nullptr)
+    {
+        CLR_RT_Memory::Release(m_wCharArray);
+        m_wCharArray = nullptr;
+    }
    m_length = 0;
}
src/CLR/Core/NativeEventDispatcher/NativeEventDispatcher.cpp (2)

59-80: Explicit initialization of event and driver fields improves safety

Initializing event to nullptr makes the cleanup path safe if allocation fails, and setting pDrvCustomData/driverMethods to nullptr avoids leaking stale pointers when reusing cached instances. You might optionally move these default assignments into Initialize() (or similar) to centralize the type’s invariants, but the current placement is functionally fine.


160-179: Consider guarding against null Pop/Push results in RemoveFromHALQueue

Now that testRec and newRec are explicitly initialized to nullptr, it’s clearer that Pop()/Push() are assumed never to return nullptr here. If that assumption can be violated under contention or capacity limits, testRec->m_context and newRec->AssignFrom(*testRec) would dereference null. If the queue API can fail, consider adding null checks (or assertions) before dereferencing; if it truly cannot, an assertion documenting that contract would help future readers.

src/CLR/Core/CLR_RT_SystemAssembliesTable.cpp (1)

35-36: Minor: Comment still references NULL.

The comment on line 35 says "return NULL" but the code now returns nullptr. Consider updating for consistency.

-    // This will return NULL if there is no registered interop assembly of that name
+    // This will return nullptr if there is no registered interop assembly of that name
     return LookUpAssemblyNativeDataByName(g_CLR_InteropAssembliesNativeData, lpszAssemblyName);
src/CLR/Core/GarbageCollector_Compaction.cpp (1)

161-165: Fix comment indentation.

The comment block has inconsistent indentation compared to the surrounding code structure.

-                //////////////////////////////////////////////////////
-                //
-                // At this point, we have at least ONE movable block.
-                //
-                //////////////////////////////////////////////////////
+            //////////////////////////////////////////////////////
+            //
+            // At this point, we have at least ONE movable block.
+            //
+            //////////////////////////////////////////////////////
src/CLR/Core/Interpreter.cpp (5)

3019-3205: Generic static field access + .cctor reschedule helper: behavior is reasonable; verify token size assumption

The new handling in LDSFLD, LDSFLDA, and STSFLD:

  • Resolves the field with field.ResolveToken(arg, assm, &stack->m_call) so VAR/MVAR can be interpreted in the current generic context.
  • For generic static fields (field.genericType valid), calls field.assembly->GetStaticFieldByFieldDef(...) with both the TypeSpec context and the current method call context to locate the correct per‑closed‑type slot.
  • If that lookup returns nullptr, uses HandleGenericCctorReschedule to detect a scheduled‑but‑not‑executed generic .cctor and returns CLR_E_RESCHEDULE after rewinding ip by 3 bytes (opcode + compressed field token), causing the interpreter to retry the instruction after the .cctor runs.
  • Falls back to AccessStaticField(field) for non‑generic static fields.

This is a sensible way to gate static field access on generic .cctor completion without introducing runtime races. Two things worth confirming:

  1. The assumption that the compressed field token is always 2 bytes (so rewinding by 3 total bytes reaches the opcode) must match your metadata encoding; if that ever changes, HandleGenericCctorReschedule will need to adjust.
  2. That GetStaticFieldByFieldDef really does return nullptr specifically for “field storage not yet created because cctor hasn’t run”, and not for other error modes that should be treated as hard failures.

If both hold, this logic should behave correctly and deterministically for generic statics.


3545-3821: Array element generics handling (LDELEM/STELEM) is careful; context restore is important

The updated LDELEM/STELEM logic:

  • Uses InitializeArrayReference + FixArrayReferenceForValueTypes to build a by‑ref to the target slot.
  • Temporarily sets stack->m_call.arrayElementType from the runtime element type (ExtractTypeIndexFromObject) before calling ResolveToken(arg, assm, &stack->m_call) so that generic VAR tokens can be closed against the actual array element type.
  • Restores the previous arrayElementType after resolving and/or loading the element, so the generic context doesn’t leak to unrelated instructions.
  • In STELEM, computes the element size from the resolved TypeDef (primitive vs value type) and uses StoreToReference with that size, after promoting the value.

This is a good pattern for reconciling IL’s generic element type with the runtime array type and should avoid mismatched VAR resolutions as long as arrayElementType is never relied on outside this short scope (which you do restore).


4295-4335: localloc implementation: ensure allocated blocks are reliably freed when the frame unwinds

The new CEE_LOCALLOC handling:

  • Validates that sizeRaw >= 0 and enforces a maximum count via m_localAllocCount / c_Max_Localloc_Count.
  • Allocates from the platform heap via platform_malloc(size) and zero‑initializes the memory.
  • Tracks each allocation in stack->m_localAllocs[stack->m_localAllocCount++] and pushes an unmanaged pointer to the eval stack.

This is fine functionally, but it hinges on complementary cleanup:

  • On every path that pops or destroys the CLR_RT_StackFrame (normal ret, exceptions, inline unwind, app‑domain transitions, thread abort, etc.), those m_localAllocs[...] must be freed exactly once, and m_localAllocCount reset.
  • If that cleanup is missing or incomplete in CLR_RT_StackFrame teardown code, locallocs will leak platform heap memory.

Please double‑check that the frame destructor/pop logic frees all entries in m_localAllocs for every exit path.

If helpful, I can sketch a small helper (e.g., CLR_RT_StackFrame::FreeLocallocs()) and show where it should be called from Pop/teardown.


1098-1157: Opcode stack-change debug checks: confirm StackPop/StackPush encoding before relying on them

Under NANOCLR_OPCODE_STACKCHANGES, you add pre‑ and post‑opcode checks that:

  • Use c_CLR_RT_OpcodeLookup[op].StackPop()/StackPush()/StackChanges() to compute expected pops/pushes.
  • Treat any opcode with stackPop == 0 || stackPush == 0 as “variable stack op” and skip validation.
  • For other opcodes, pre‑check for stack underflow and overflow, and post‑check that the actual depth change matches StackChanges().

This is a useful diagnostic, but it depends heavily on how StackPop() / StackPush() encode:

  • If VarPop/VarPush are encoded with the same sentinel value as Pop0/Push0 (e.g., both returning 0), the isVariableStackOp logic will skip validation for all opcodes that legitimately pop or push zero items, which may not be what you intend.
  • Conversely, if Var* use a distinct sentinel (e.g., negative values), the equality‑to‑0 test is wrong and variable‑stack opcodes will be (incorrectly) validated, leading to false stack‑underflow/overflow errors.

Please confirm the actual encoding of these helpers and adjust isVariableStackOp accordingly (e.g., explicitly checking for a known sentinel for VarPop/VarPush) so that the debug checks only skip truly variable‑stack instructions.


2200-2515: Interface/generic call handling: correct but potentially expensive TypeSpec search

For CALL/CALLVIRT you now:

  • Pre-populate calleeInst.arrayElementType from stack->m_call.arrayElementType so that token resolution can use this information.
  • For CALLVIRT on a MethodRef owned by a TypeRef (typical interface calls), with no existing generic context, walk the caller assembly’s TypeSpec table to find a closed generic whose generic type definition matches the runtime this object’s class, and treat that as effectiveCallerGeneric.
  • Pass effectiveCallerGeneric into calleeInst.ResolveToken, then carry the resulting genericType into the dispatched method via virtual dispatch, preserving/overriding arrayElementType as needed (including the SZArrayHelper special case).
  • On push of the new frame, store the final effective generic TypeSpec into m_genericTypeSpecStorage and point m_call.genericType there.

This gives you the correct closed generic context for interface dispatch on generic instances (e.g., IEnumerable<T>/IList<T> on List<int>), which is hard to get right; the logic looks sound.

The only caveat is cost: in the worst case, the for (int i = 0; i < assm->tablesSize[TBL_TypeSpec]; i++) linear scan per CALLVIRT could be noticeable if there are many TypeSpecs and many such calls in hot paths.

If this becomes a hotspot, you might eventually want a small cache or index (e.g., map from (assembly, TypeDef) to TypeSpec index) rather than scanning the whole table each time. For now, the correctness benefit probably outweighs the overhead.

src/CLR/Core/Execution.cpp (1)

996-1119: Review generic .cctor scheduling logic for edge cases.

The function iterates through all TypeSpecs in an assembly to find closed generic types that need .cctor execution. A few observations:

  1. Line 1074: If FindOrCreateGenericCctorRecord returns nullptr (out of memory), the function continues to the next TypeSpec. This is reasonable behavior but could lead to incomplete initialization if memory is critically low.

  2. Lines 1081-1086: The check for already-scheduled/executed .cctors uses bitwise AND. This is correct but consider whether a race condition could occur if multiple threads access the same record.

  3. Lines 1112-1113: If PushThreadProcDelegate fails, the scheduled flag is cleared. Good defensive programming.

Consider adding diagnostic logging for out-of-memory scenarios to aid debugging:

if (record == nullptr)
{
    #if defined(NANOCLR_GC_VERBOSE)
    CLR_Debug::Printf("Failed to allocate .cctor record for generic type at TypeSpec %d\r\n", iTs);
    #endif
    continue;
}
src/CLR/Core/CLR_RT_HeapBlock_GenericInstance.cpp (1)

11-82: Placeholder implementation with unrelated commented-out code.

The CreateInstance stub correctly suppresses unused parameter warnings with (void) casts. However, the extensive commented-out code (lines 21-80) appears to be copy-pasted from delegate creation logic and is unrelated to generic instance creation. This dead code adds confusion for future maintainers.

Consider removing the commented-out code entirely or replacing it with a concise TODO comment describing the intended implementation.

     (void)reference;
     (void)tsIndex;
 
-    //     reference.SetObjectReference( nullptr );
-
-    //     CLR_UINT32 length = 0;
-
-    //   #if defined(NANOCLR_DELEGATE_PRESERVE_STACK)
-    //     if(call)
-    //     {
-    //         NANOCLR_FOREACH_NODE_BACKWARD__DIRECT(CLR_RT_StackFrame,ptr,call)
-    //         {
-    //             length++;
-    //         }
-    //         NANOCLR_FOREACH_NODE_BACKWARD_END();
-    //     }
-
-    //     //
-    //     // Limit depth to three callers.
-    //     //
-    //     if(length > 3) length = 3;
-    //   #else
-    //     (void)call;
-    //   #endif
-
-    //     CLR_UINT32 totLength = (CLR_UINT32)(sizeof(CLR_RT_HeapBlock_Delegate) + length *
-    //     sizeof(CLR_RT_MethodDef_Index));
-
-    //     CLR_RT_HeapBlock_Delegate* dlg =
-    //     (CLR_RT_HeapBlock_Delegate*)g_CLR_RT_ExecutionEngine.ExtractHeapBytesForObjects( DATATYPE_DELEGATE_HEAD, 0,
-    //     totLength ); CHECK_ALLOCATION(dlg);
-
-    //     reference.SetObjectReference( dlg );
-
-    //     dlg->ClearData();
-    //     dlg->m_cls.Clear();
-    //     dlg->m_ftn              = ftn;
-    // #if defined(NANOCLR_DELEGATE_PRESERVE_STACK)
-    //     dlg->m_numOfStackFrames = length;
-    // #endif
-
-    //     dlg->m_object.SetObjectReference( nullptr );
-
-    // #if defined(NANOCLR_APPDOMAINS)
-    //     dlg->m_appDomain = g_CLR_RT_ExecutionEngine.GetCurrentAppDomain();
-    // #endif
-
-    // #if defined(NANOCLR_DELEGATE_PRESERVE_STACK)
-    //     if(call)
-    //     {
-    //         CLR_RT_MethodDef_Index* callStack = dlg->GetStackFrames();
-
-    //         NANOCLR_FOREACH_NODE_BACKWARD__DIRECT(CLR_RT_StackFrame,ptr,call)
-    //         {
-    //             if(length-- == 0) break;
-
-    //             *callStack++ = ptr->m_call;
-    //         }
-    //         NANOCLR_FOREACH_NODE_BACKWARD_END();
-    //     }
-    // #endif
-
-    // NANOCLR_NOCLEANUP();
+    // TODO: Implement generic instance creation
+
     NANOCLR_NOCLEANUP_NOLABEL();
src/CLR/CorLib/corlib_native_System_String.cpp (1)

1469-1572: Consider extracting numeric formatting to reduce goto usage.

The goto process_numeric pattern at line 1571 works correctly but reduces readability. Since the numeric formatting logic (lines 1470-1563) is substantial, consider extracting it to a helper function that can be called from both the DATATYPE_VALUETYPE branch and the direct numeric type branch.

This would eliminate the goto and improve maintainability:

// Helper function signature
static const char* FormatNumericArg(
    CLR_RT_HeapBlock *deref,
    NanoCLRDataType dt,
    const char *formatSpec,
    char *argBuffer,
    char *negSign,
    char *decSep);

Then both branches could simply call:

argStr = FormatNumericArg(deref, dt, formatSpec, argBuffer, negSign, decSep);
src/CLR/CorLib/corlib_native_System_Span_1.cpp (1)

117-121: Inconsistent signed/unsigned comparisons.

Line 118 uses .u4 (unsigned) for length comparison, but line 129 uses .s4 (signed) for the empty check. While functionally correct (since lengths should be non-negative), this inconsistency could be confusing.

Consider using consistent type access:

-    if (sourceSpan[FIELD___length].NumericByRefConst().u4 > destinationSpan[FIELD___length].NumericByRefConst().u4)
+    if (sourceSpan[FIELD___length].NumericByRefConst().s4 > destinationSpan[FIELD___length].NumericByRefConst().s4)

Also applies to: 129-132

src/CLR/CorLib/corlib_native_System_Runtime_CompilerServices_RuntimeHelpers.cpp (3)

272-276: Minor: Redundant |= false operation.

The expression isRefContainsRefs |= false has no effect - OR-ing with false never changes the value. While harmless, it could be simplified.

     if (dt <= DATATYPE_LAST_NONPOINTER)
     {
         // primitive types aren't and don't contain references
-        isRefContainsRefs |= false;
+        // no action needed - primitive types don't contain references
     }

292-296: Minor: Another redundant |= false operation.

Same as above - this has no effect on the result.

         if (totFields == 0)
         {
             // this class has no fields, therefore doesn't contain references
-            isRefContainsRefs |= false;
+            // no action needed - no fields means no references
         }

322-325: Unused variables when NANOCLR_INSTANCE_NAMES is not defined.

The typeName and fieldName variables are only used for debugging purposes when NANOCLR_INSTANCE_NAMES is defined, which may cause compiler warnings in release builds.

This is acceptable if the compiler optimizes out unused variables, but you could consider marking them with (void)typeName; (void)fieldName; or wrapping the entire block more tightly if warnings are a concern.

src/CLR/CorLib/corlib_native_System_Reflection_Assembly.cpp (1)

30-37: Consider using snprintf for defense in depth.

While the buffer sizing appears safe (2x assembly name max), using snprintf would provide additional protection against buffer overflows.

-    sprintf(
+    snprintf(
         buffer,
+        sizeof(buffer),
         "%s, Version=%d.%d.%d.%d",
         assm->name,
         header->version.majorVersion,
src/CLR/Core/CLR_RT_HeapBlock_Array.cpp (1)

390-392: Consider making the storage pointer check a runtime error instead of assert.

The ASSERT only triggers in debug builds. If this condition could occur in production due to a bug, consider returning an error or handling it gracefully instead of silently allowing the memmove in release builds.

             if (!arraySrc->m_fReference)
             {
-                // check that array is not stored in stack
-                ASSERT(arraySrc->m_StoragePointer == 0);
+                // check that array is not stored externally
+                if (arraySrc->m_StoragePointer != 0)
+                {
+                    NANOCLR_SET_AND_LEAVE(CLR_E_NOT_SUPPORTED);
+                }

                 memmove(dataDst, dataSrc, length * sizeElem);
             }
azure-pipelines.yml (1)

76-97: Inconsistent authentication header formats

The $auth variable is set on line 76 with format ":$(GitHubToken)" (empty username), then redefined on line 97 with "nfbot:$(GitHubToken)" (with username). While line 97 properly overrides for PR builds, the inconsistency may cause confusion.

Consider using the same format consistently, or remove the redundant assignment on line 76 if it's not used before being overwritten.

src/CLR/CorLib/corlib_native.h (1)

203-303: Significant code duplication in System_String conditional branches.

The #if (NANOCLR_REFLECTION == TRUE) and #else branches contain nearly identical method declarations with minor differences. Key differences:

  • TrimStart___STRING and TrimEnd___STRING (parameterless) only exist in the NANOCLR_REFLECTION path
  • Both branches define Trim___STRING (parameterless)

Consider extracting common declarations to reduce duplication and maintenance burden.

One approach is to declare common methods outside the conditional and only wrap the differing methods:

 struct Library_corlib_native_System_String
 {
     static const int FIELD_STATIC__Empty = 2;
 
+    // Common methods for both paths
+    NANOCLR_NATIVE_DECLARE(CompareTo___I4__OBJECT);
+    NANOCLR_NATIVE_DECLARE(get_Chars___CHAR__I4);
+    // ... other common methods ...
+
 #if (NANOCLR_REFLECTION == TRUE)
-    NANOCLR_NATIVE_DECLARE(CompareTo___I4__OBJECT);
-    // ... all methods duplicated ...
+    // Additional methods only for reflection path
+    NANOCLR_NATIVE_DECLARE(TrimStart___STRING);
+    NANOCLR_NATIVE_DECLARE(TrimEnd___STRING);
 #else
-    NANOCLR_NATIVE_DECLARE(CompareTo___I4__OBJECT);
-    // ... all methods duplicated again ...
+    // Non-reflection specific declarations if any
 #endif
 };

Comment on lines +10 to +27
HRESULT CLR_Checks::VerifyStackOK(const CLR_RT_StackFrame &stack, const CLR_RT_HeapBlock *top, const int num)
{
NANOCLR_HEADER();

// Check if stack has enough space for num elements
if (top + num > stack.m_evalStackEnd)
{
NANOCLR_SET_AND_LEAVE(CLR_E_STACK_OVERFLOW);
}

// Check if top is within stack bounds
if (top < stack.m_evalStack || top >= stack.m_evalStackEnd)
{
NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER);
}

NANOCLR_NOCLEANUP();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Harden VerifyStackOK against negative counts and tighten check ordering

As written, two edge cases are worth addressing:

  • If num is negative, the “has enough space” check becomes ineffective (top + num moves backwards), and the function will return success even though the call is logically invalid.
  • You perform top + num before validating that top is at least within the eval stack range, which makes debugging bad callers harder and does unnecessary pointer arithmetic on an obviously-invalid argument.

A minimal tightening that keeps the same behavior for valid callers:

 HRESULT CLR_Checks::VerifyStackOK(const CLR_RT_StackFrame &stack, const CLR_RT_HeapBlock *top, const int num)
 {
     NANOCLR_HEADER();
 
-    // Check if stack has enough space for num elements
-    if (top + num > stack.m_evalStackEnd)
-    {
-        NANOCLR_SET_AND_LEAVE(CLR_E_STACK_OVERFLOW);
-    }
-
-    // Check if top is within stack bounds
-    if (top < stack.m_evalStack || top >= stack.m_evalStackEnd)
-    {
-        NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER);
-    }
+    // Sanity‑check count
+    if (num < 0)
+    {
+        NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER);
+    }
+
+    // Check that top is within stack bounds before doing pointer arithmetic
+    if (top < stack.m_evalStack || top >= stack.m_evalStackEnd)
+    {
+        NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER);
+    }
+
+    // Check if stack has enough space for num elements
+    if (top + num > stack.m_evalStackEnd)
+    {
+        NANOCLR_SET_AND_LEAVE(CLR_E_STACK_OVERFLOW);
+    }
 
     NANOCLR_NOCLEANUP();
 }

This makes the contract around num explicit and ensures you only do the “capacity” computation after confirming top is within the stack interval.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
HRESULT CLR_Checks::VerifyStackOK(const CLR_RT_StackFrame &stack, const CLR_RT_HeapBlock *top, const int num)
{
NANOCLR_HEADER();
// Check if stack has enough space for num elements
if (top + num > stack.m_evalStackEnd)
{
NANOCLR_SET_AND_LEAVE(CLR_E_STACK_OVERFLOW);
}
// Check if top is within stack bounds
if (top < stack.m_evalStack || top >= stack.m_evalStackEnd)
{
NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER);
}
NANOCLR_NOCLEANUP();
}
HRESULT CLR_Checks::VerifyStackOK(const CLR_RT_StackFrame &stack, const CLR_RT_HeapBlock *top, const int num)
{
NANOCLR_HEADER();
// Sanity‑check count
if (num < 0)
{
NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER);
}
// Check that top is within stack bounds before doing pointer arithmetic
if (top < stack.m_evalStack || top >= stack.m_evalStackEnd)
{
NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER);
}
// Check if stack has enough space for num elements
if (top + num > stack.m_evalStackEnd)
{
NANOCLR_SET_AND_LEAVE(CLR_E_STACK_OVERFLOW);
}
NANOCLR_NOCLEANUP();
}
🧰 Tools
🪛 Cppcheck (2.18.0)

[information] 10-10: Include file

(missingIncludeSystem)


[information] 12-12: Include file

(missingIncludeSystem)


[information] 13-13: Include file

(missingIncludeSystem)


[information] 15-15: Include file

(missingIncludeSystem)


[information] 19-19: Include file

(missingIncludeSystem)


[information] 22-22: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)

🤖 Prompt for AI Agents
In src/CLR/Core/Checks.cpp around lines 10 to 27, the function VerifyStackOK
should first validate that the incoming top pointer is within
[stack.m_evalStack, stack.m_evalStackEnd) and then validate that num is
non-negative before performing top + num arithmetic; update the checks to (1) if
top < stack.m_evalStack || top >= stack.m_evalStackEnd return
CLR_E_INVALID_PARAMETER, (2) if num < 0 return CLR_E_INVALID_PARAMETER, and (3)
if top + num > stack.m_evalStackEnd return CLR_E_STACK_OVERFLOW, ensuring the
pointer arithmetic is only done after the arguments are known-valid.

Comment on lines 30 to +36
_ASSERTE(sizeof(CLR_RT_HeapBlock_WaitForObject) % 4 == 0);


CLR_UINT32 totLength =
(CLR_UINT32)(sizeof(CLR_RT_HeapBlock_WaitForObject) + cObjects * sizeof(struct CLR_RT_HeapBlock));


_ASSERTE(sizeof(CLR_RT_HeapBlock_WaitForObject) % 4 == 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Duplicate assertion detected.

The assertion _ASSERTE(sizeof(CLR_RT_HeapBlock_WaitForObject) % 4 == 0) appears twice: on line 30 and again on line 36. The second instance appears to be unintentional duplication.

 
-    _ASSERTE(sizeof(CLR_RT_HeapBlock_WaitForObject) % 4 == 0);
-    
     CLR_RT_HeapBlock_WaitForObject *wait = EVENTCACHE_EXTRACT_NODE_AS_BYTES(

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/CLR/Core/CLR_RT_HeapBlock_WaitForObject.cpp around lines 30 to 36, there
is a duplicated assertion `_ASSERTE(sizeof(CLR_RT_HeapBlock_WaitForObject) % 4
== 0)` appearing twice; remove the redundant second assertion (the one at line
36) so the size alignment check appears only once, ensuring no change to
surrounding logic or formatting.

Comment on lines +780 to +787

default:
{
// value-type, primitive, struct, etc.
// this->CopyFrom(rhs);
break;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Incomplete implementation: value-types and primitives are not copied.

The default case at lines 781-786 has the copy operation commented out (// this->CopyFrom(rhs);), meaning value-types, primitives, and structs won't be copied after type validation passes.

This appears to be incomplete. Either implement the copy logic or delegate to the existing Reassign:

         default:
         {
             // value-type, primitive, struct, etc.
-            // this->CopyFrom(rhs);
-            break;
+            NANOCLR_SET_AND_LEAVE(this->Reassign(rhs));
         }

Alternatively, if intentional, add a comment explaining why no copy is needed.

🧰 Tools
🪛 Cppcheck (2.18.0)

[information] Limiting analysis of branches. Use --check-level=exhaustive to analyze all branches.

(normalCheckLevelMaxBranches)

🤖 Prompt for AI Agents
In src/CLR/Core/CLR_RT_HeapBlock.cpp around lines 780 to 787, the default switch
case leaves value-types/primitives/structs unhandled with this->CopyFrom(rhs)
commented out; restore proper copy semantics by either calling
this->CopyFrom(rhs) (or this->Reassign(rhs) if that matches surrounding
semantics) so primitives and structs are copied after type validation, and if
omission was intentional replace the commented code with a clear explanatory
comment describing why no copy is required.

Comment on lines +96 to +98
CLR_RT_HeapCluster *freeRegion_hc = nullptr;
;
CLR_RT_HeapBlock_Node *freeRegion = nullptr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove stray semicolon.

Line 97 has an extra standalone semicolon that appears to be a merge artifact.

     CLR_RT_HeapCluster *freeRegion_hc = nullptr;
-    ;
     CLR_RT_HeapBlock_Node *freeRegion = nullptr;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
CLR_RT_HeapCluster *freeRegion_hc = nullptr;
;
CLR_RT_HeapBlock_Node *freeRegion = nullptr;
CLR_RT_HeapCluster *freeRegion_hc = nullptr;
CLR_RT_HeapBlock_Node *freeRegion = nullptr;
🧰 Tools
🪛 Cppcheck (2.18.0)

[information] Limiting analysis of branches. Use --check-level=exhaustive to analyze all branches.

(normalCheckLevelMaxBranches)

🤖 Prompt for AI Agents
In src/CLR/Core/GarbageCollector_Compaction.cpp around lines 96 to 98, there is
a stray standalone semicolon on line 97 (merge artifact) between the variable
declarations; remove that extra semicolon so the declarations read cleanly
without an empty statement.

NANOCLR_FOREACH_NODE_END();

return NULL;
return nullptr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Critical inconsistency: Code changes in a documentation-only PR.

The PR objectives state this is a documentation-only change ("Type: documentation" label, "Change type: documentation only"), but this file contains actual code changes—specifically, NULL-to-nullptr conversions. While these modernization changes are technically correct and safe, they should not be included in a documentation-focused PR.

Recommendation:
Either update the PR description to reflect that it includes code modernization changes, or move these code changes to a separate PR and keep this one strictly for documentation updates.

Also applies to: 56-56, 137-137

🤖 Prompt for AI Agents
In src/CLR/Core/RPC/CLR_RT_HeapBlock_EndPoint.cpp around lines 39, 56, and 137,
this PR includes actual code changes (NULL-to-nullptr conversions) despite being
labeled and described as documentation-only; restore the original code in this
PR and move the nullptr modernizations to a separate code-modernization PR, or
alternatively update the PR title/description and labels to accurately indicate
it contains code changes; pick one: either revert the three nullptr edits back
to NULL here and open a new PR with the modernization, or adjust PR metadata and
description to reflect that code changes are included.

Comment on lines +366 to +376
CLR_RT_SignatureParser parser{};
parser.Initialize_TypeSpec(genericInstance);

CLR_RT_SignatureParser::Element element;

// only need to consume the 1st element which has the level data we're looking for
parser.Advance(element);

stack.SetResult_Boolean(td.m_data == g_CLR_RT_WellKnownTypes.m_Array.m_data);
// check if element has levels
stack.SetResult_Boolean(element.Levels > 0);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Unchecked parser.Advance() return value.

Similar to GetGenericTypeDefinition, the parser.Advance(element) call at line 372 doesn't check the return value.

Consider adding error handling for consistency:

         // only need to consume the 1st element which has the level data we're looking for
-        parser.Advance(element);
+        if (parser.Advance(element) != S_OK)
+        {
+            NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE);
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
CLR_RT_SignatureParser parser{};
parser.Initialize_TypeSpec(genericInstance);
CLR_RT_SignatureParser::Element element;
// only need to consume the 1st element which has the level data we're looking for
parser.Advance(element);
stack.SetResult_Boolean(td.m_data == g_CLR_RT_WellKnownTypes.m_Array.m_data);
// check if element has levels
stack.SetResult_Boolean(element.Levels > 0);
}
CLR_RT_SignatureParser parser{};
parser.Initialize_TypeSpec(genericInstance);
CLR_RT_SignatureParser::Element element;
// only need to consume the 1st element which has the level data we're looking for
if (parser.Advance(element) != S_OK)
{
NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE);
}
// check if element has levels
stack.SetResult_Boolean(element.Levels > 0);
}
🧰 Tools
🪛 Cppcheck (2.18.0)

[information] Limiting analysis of branches. Use --check-level=exhaustive to analyze all branches.

(normalCheckLevelMaxBranches)

🤖 Prompt for AI Agents
In src/CLR/CorLib/corlib_native_System_Type.cpp around lines 366-376,
parser.Advance(element) is called without checking its boolean return; update
the code to check the return value and handle failure consistently with
GetGenericTypeDefinition: if Advance returns false, set
stack.SetResult_Boolean(false) (or the appropriate error/return path used in the
surrounding function) and return immediately to avoid using an invalid element;
ensure any necessary cleanup or error logging mirrors existing patterns in the
file.

Comment on lines +428 to +460
// consume GENERICINST
parser.Advance(element);

// consume class | value type
parser.Advance(element);

// check if it has generic parameters
if (element.GenParamCount == 0)
{
isTypeDefinition = false;
}
else
{
// keep reading the generic parameters
for (int i = 0; i < element.GenParamCount; i++)
{
if (SUCCEEDED(parser.Advance(element)))
{
if (element.DataType == DATATYPE_VAR || element.DataType == DATATYPE_MVAR)
{
// if we find a VAR or MVAR, then this is a generic type definition
isTypeDefinition = true;
break;
}
else
{
// anything else, this is not a generic type definition
isTypeDefinition = false;
break;
}
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Unchecked parser.Advance() return values and logic concern.

  1. Lines 429 and 432 don't check the return value of parser.Advance().

  2. The logic breaks on the first parameter - if a generic type has <T, int>, it would correctly identify as a definition, but if it has <int, T>, the current logic would incorrectly return false on the first int parameter.

Apply this diff to fix both issues:

         // consume GENERICINST
-        parser.Advance(element);
+        if (parser.Advance(element) != S_OK)
+        {
+            NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE);
+        }

         // consume class | value type
-        parser.Advance(element);
+        if (parser.Advance(element) != S_OK)
+        {
+            NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE);
+        }

         // check if it has generic parameters
         if (element.GenParamCount == 0)
         {
             isTypeDefinition = false;
         }
         else
         {
             // keep reading the generic parameters
             for (int i = 0; i < element.GenParamCount; i++)
             {
                 if (SUCCEEDED(parser.Advance(element)))
                 {
                     if (element.DataType == DATATYPE_VAR || element.DataType == DATATYPE_MVAR)
                     {
                         // if we find a VAR or MVAR, then this is a generic type definition
                         isTypeDefinition = true;
                         break;
                     }
-                    else
-                    {
-                        // anything else, this is not a generic type definition
-                        isTypeDefinition = false;
-                        break;
-                    }
+                    // continue checking other parameters
                 }
             }
         }

The else branch with break causes early exit on the first non-VAR/MVAR parameter, which is incorrect for partially-closed generics.

🧰 Tools
🪛 Cppcheck (2.18.0)

[information] Limiting analysis of branches. Use --check-level=exhaustive to analyze all branches.

(normalCheckLevelMaxBranches)

🤖 Prompt for AI Agents
In src/CLR/CorLib/corlib_native_System_Type.cpp around lines 428-460, the code
calls parser.Advance(element) at lines ~429 and ~432 without checking return
values and incorrectly breaks out of the generic-parameter loop on the first
non-VAR/MVAR parameter (making <int,T> mis-classified). Fix by: 1) Check the
HRESULT/SUCCEEDED result of the two initial parser.Advance calls and handle
failures (set isTypeDefinition = false and exit or propagate error) before
proceeding; 2) In the GenParamCount loop, on each iteration verify
parser.Advance succeeded and if it fails handle as above; 3) Only set
isTypeDefinition = true and break when you encounter a DATATYPE_VAR or
DATATYPE_MVAR; otherwise continue scanning remaining parameters and only set
isTypeDefinition = false after the loop completes without finding any VAR/MVAR
(do not break on the first non-VAR/MVAR). Ensure no early exit on non-generic
parameters and consistent error handling for Advance failures.

Comment on lines +2862 to +2866
for (int i = 0; i < res.GenericParamPosition; i++)
{
parser.Advance(elem);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Bug: Wrong parser used in generic parameter loop.

Line 2864 uses parser.Advance(elem) but should use typeParser.Advance(elem). The variable parser is the outer signature parser that has already been advanced to the original element position, while typeParser is the newly initialized parser intended for walking to the Nth generic-type parameter.

             // now we've landed on a DATATYPE_VAR; walk to the Nth generic‐type parameter
             typeParser.Advance(elem);
             for (int i = 0; i < res.GenericParamPosition; i++)
             {
-                parser.Advance(elem);
+                typeParser.Advance(elem);
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (int i = 0; i < res.GenericParamPosition; i++)
{
parser.Advance(elem);
}
for (int i = 0; i < res.GenericParamPosition; i++)
{
typeParser.Advance(elem);
}
🧰 Tools
🪛 Cppcheck (2.18.0)

[information] Limiting analysis of branches. Use --check-level=exhaustive to analyze all branches.

(normalCheckLevelMaxBranches)


[information] Too many #ifdef configurations - cppcheck only checks 12 configurations. Use --force to check all configurations. For more details, use --enable=information.

(toomanyconfigs)

🤖 Prompt for AI Agents
In src/CLR/Debugger/Debugger.cpp around lines 2862 to 2866, the loop advancing
through generic parameters incorrectly calls parser.Advance(elem) (using the
outer signature parser) instead of advancing the newly created typeParser;
change the call to typeParser.Advance(elem) so the loop walks the Nth
generic-type parameter using the correct parser instance, keeping elem and loop
bounds the same.

Comment on lines +3069 to +3073
else if (array->m_StoragePointer != 0)
{
// TODO need to handle this case properly
ASSERT(FALSE);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Incomplete implementation will cause assertion failure.

This new branch for m_StoragePointer != 0 contains only ASSERT(FALSE) and a TODO comment. If this code path is ever reached:

  • In debug builds: assertion failure/crash
  • In release builds: undefined behavior as blk remains uninitialized from this branch

Either implement the proper handling or document when this condition can occur and whether it's truly unreachable.

Would you like me to help research the proper handling for arrays with m_StoragePointer set, or should this branch return an error instead of asserting?

🧰 Tools
🪛 Cppcheck (2.18.0)

[information] Limiting analysis of branches. Use --check-level=exhaustive to analyze all branches.

(normalCheckLevelMaxBranches)


[information] Too many #ifdef configurations - cppcheck only checks 12 configurations. Use --force to check all configurations. For more details, use --enable=information.

(toomanyconfigs)

🤖 Prompt for AI Agents
In src/CLR/Debugger/Debugger.cpp around lines 3069-3073, the branch for
array->m_StoragePointer != 0 currently only ASSERTs and leaves blk
uninitialized; replace that with real handling: read the storage pointer value,
validate it, initialize blk and any related size/length variables from the
pointed storage (or convert it to the same representation used in the other
branch), and handle failures by returning an appropriate error code instead of
asserting; if this condition is truly impossible, add a clear comment explaining
why and replace the ASSERT with a defensive return/error to avoid uninitialized
use in release builds.

Comment on lines +460 to +461
static CLR_RT_TypeSpec_Index s_ownerTypeSpec;
s_ownerTypeSpec.Set(assemblyIndex, fr->OwnerIndex());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Static variable in DumpToken may cause issues in concurrent scenarios.

s_ownerTypeSpec is declared static inside a conditional block, meaning it persists across calls. While diagnostics code is typically single-threaded, this could lead to unexpected behavior if DumpToken is called concurrently or re-entrantly.

Consider making this a local variable if the static lifetime is not intentionally required.

-                if (fr->Owner() == TBL_TypeSpec)
-                {
-                    static CLR_RT_TypeSpec_Index s_ownerTypeSpec;
-                    s_ownerTypeSpec.Set(assemblyIndex, fr->OwnerIndex());
+                if (fr->Owner() == TBL_TypeSpec)
+                {
+                    CLR_RT_TypeSpec_Index ownerTypeSpec;
+                    ownerTypeSpec.Set(assemblyIndex, fr->OwnerIndex());
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
static CLR_RT_TypeSpec_Index s_ownerTypeSpec;
s_ownerTypeSpec.Set(assemblyIndex, fr->OwnerIndex());
if (fr->Owner() == TBL_TypeSpec)
{
CLR_RT_TypeSpec_Index ownerTypeSpec;
ownerTypeSpec.Set(assemblyIndex, fr->OwnerIndex());
🧰 Tools
🪛 Cppcheck (2.18.0)

[information] Limiting analysis of branches. Use --check-level=exhaustive to analyze all branches.

(normalCheckLevelMaxBranches)

🤖 Prompt for AI Agents
In src/CLR/Diagnostics/Info.cpp around lines 460-461, the variable
s_ownerTypeSpec is declared static inside DumpToken which can persist across
calls and cause concurrency/re-entrancy issues; change it to a regular local
CLR_RT_TypeSpec_Index variable (remove the static keyword), initialize it as you
currently do with Set(assemblyIndex, fr->OwnerIndex()), and use this local
instance so each call to DumpToken has its own independent owner type spec.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants