Skip to content

bugfix: fix incorrect dynamic array marshalling#70

Open
ODtian wants to merge 2 commits intoMikhailGorobets:mainfrom
ODtian:main
Open

bugfix: fix incorrect dynamic array marshalling#70
ODtian wants to merge 2 commits intoMikhailGorobets:mainfrom
ODtian:main

Conversation

@ODtian
Copy link

@ODtian ODtian commented Feb 18, 2026

Related: DiligentGraphics/DiligentCore#766

Root Cause & Context:
When a user configures <map override-native-type="true" /> for an array field (e.g., mapping byte[] to a different native type), two cascading issues occur:

  1. Incorrect Marshaller Selection: RemappedTypeMarshaller incorrectly claimed responsibility for the field. Since it lacks array handling capabilities, it generated code that completely missed the array marshalling logic.
  2. Defective Dynamic Array Logic: Even when ValueTypeArrayMarshaller was correctly forced to handle the field, its implementation for Dynamic Arrays was flawed:
    • Memory Corruption: It reused GenerateCopyMemory (designed for Constant Arrays), which unconditionally took the address of the target field (&@ref.Ptr). For dynamic arrays (which are pointers), this caused the marshaller to overwrite the pointer variable itself and adjacent struct fields (e.g., array length), leading to Access Violations.
    • Missing Logic: It failed to generate code to assign the managed array's length to the native struct's length field (defined by LengthRelation), leaving the native API with a zero or uninitialized length.

Fix:

  1. Correct Marshaller Routing:
    • Modified RemappedTypeMarshaller.CanMarshal to explicitly exclude array types, preventing incorrect interception.
    • Modified ValueTypeArrayMarshaller.CanMarshal to allow handling of fields that are mapped to different public types (MappedToDifferentPublicType).
  2. Fix Memory Corruption:
    • Refactored GenerateCopyMemory to accept a bool takeAddress parameter.
    • For Dynamic Arrays, passed takeAddress = false to use the pointer directly as the destination, bypassing the incorrect address-taking.
  3. Complete Length Assignment:
    • Added the missing logic to assign the array length to the corresponding native length field (based on ArraySpecification) during dynamic array marshalling.

@MikhailGorobets
Copy link
Owner

@ODtian Unit tests are failing

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes incorrect codegen for dynamic array marshalling when an array field is remapped via override-native-type="true" (issue #766), ensuring the correct marshaller is selected and preventing pointer/length field corruption during managed→native copies.

Changes:

  • Prevent RemappedTypeMarshaller from intercepting array types; allow ValueTypeArrayMarshaller to handle value-type arrays even when remapped.
  • Refactor ValueTypeArrayMarshaller.GenerateCopyMemory to optionally avoid taking the address of the native destination for dynamic arrays.
  • Add missing dynamic-array length assignment to the related native length field during managed→native marshalling.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
SharpGen/Generator/Marshallers/ValueTypeArrayMarshaller.cs Routes remapped value-type arrays to this marshaller and adds dynamic-array length assignment; uses takeAddress:false for managed→native dynamic copies.
SharpGen/Generator/Marshallers/ValueTypeArrayMarshaller.Copy.cs Refactors copy helper to optionally skip address-of on the native side.
SharpGen/Generator/Marshallers/RemappedTypeMarshaller.cs Stops remapped-type marshaller from claiming array marshalling.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 33 to +36
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName(MarshalParameterRefName), IdentifierName(csElement.Name)),
ParseExpression($"({csElement.PublicType.QualifiedName}*)System.Runtime.InteropServices.NativeMemory.Alloc((nuint) (System.Runtime.CompilerServices.Unsafe.SizeOf<{csElement.PublicType.QualifiedName}>() * {csElement.Name}.Length))"))),
GenerateCopyMemory(csElement, ArrayCopyDirection.ManagedToNative)
GenerateCopyMemory(csElement, ArrayCopyDirection.ManagedToNative, takeAddress: false),
ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName(MarshalParameterRefName), IdentifierName(csElement.ArraySpecification?.SizeIdentifier)), ParseExpression($"({csElement.ArraySpecification?.TypeSizeIdentifier}){csElement.Name}.Length")))
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

GenerateCopyMemory(..., takeAddress: false) is applied for the dynamic-array Managed→Native path, but the dynamic-array Native→Managed path still calls GenerateCopyMemory(csElement, ArrayCopyDirection.NativeToManaged) with the default takeAddress=true. Since GetMarshalStorageLocation(csElement) is a pointer field for dynamic arrays, taking its address yields a T** and will copy from/to the pointer variable itself rather than the pointed-to array. Pass takeAddress: false for the dynamic Native→Managed case as well (and consider restricting takeAddress based on ArraySpecificationType.Dynamic inside GenerateCopyMemory to avoid future call-site mistakes).

Copilot uses AI. Check for mistakes.
Comment on lines 28 to +36
CsField { ArraySpecification.Type: ArraySpecificationType.Dynamic } =>
IfStatement(BinaryExpression(SyntaxKind.GreaterThanExpression, GeneratorHelpers.NullableLengthExpression(IdentifierName(csElement.Name)), ZeroLiteral),
Block(
ExpressionStatement(
AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName(MarshalParameterRefName), IdentifierName(csElement.Name)),
ParseExpression($"({csElement.PublicType.QualifiedName}*)System.Runtime.InteropServices.NativeMemory.Alloc((nuint) (System.Runtime.CompilerServices.Unsafe.SizeOf<{csElement.PublicType.QualifiedName}>() * {csElement.Name}.Length))"))),
GenerateCopyMemory(csElement, ArrayCopyDirection.ManagedToNative)
GenerateCopyMemory(csElement, ArrayCopyDirection.ManagedToNative, takeAddress: false),
ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName(MarshalParameterRefName), IdentifierName(csElement.ArraySpecification?.SizeIdentifier)), ParseExpression($"({csElement.ArraySpecification?.TypeSizeIdentifier}){csElement.Name}.Length")))
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

This change introduces new dynamic-array behavior for value-type arrays when the element type is remapped (MappedToDifferentPublicType) and adds length-field assignment during marshalling. There doesn't appear to be an integration test covering a struct field dynamic array with override-native-type="true" + length(...) (e.g., PointerSizeMemberExtended::byteCode in SdkTests/Struct/Mapping.xml). Adding an SdkTests/Struct test that exercises round-tripping (pointer + length) would help prevent regressions like the reported memory corruption and missing length assignment.

Copilot uses AI. Check for mistakes.
@ODtian
Copy link
Author

ODtian commented Feb 18, 2026

@ODtian Unit tests are failing

The tests pass successfully on my local machine. I suspect this is caused by a misconfiguration in the GitHub Actions workflow, as the output seems to indicate that the .NET 6.0 environment is missing. 🥲

Testhost process for source(s) 'D:\a\Diligent-SharpGenTools\Diligent-SharpGenTools\SharpGen.UnitTests\bin\Release\net6.0\SharpGen.UnitTests.dll' exited with error: You must install or update .NET to run this application.
App: D:\a\Diligent-SharpGenTools\Diligent-SharpGenTools\SharpGen.UnitTests\bin\Release\net6.0\testhost.exe
Architecture: x64
Framework: 'Microsoft.NETCore.App', version '6.0.0' (x64)
.NET location: C:\Program Files\dotnet
The following frameworks were found:
  8.0.6 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  8.0.22 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  8.0.23 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  9.0.6 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  9.0.12 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  10.0.2 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
The following frameworks for other architectures were found:
  x86
    8.0.23 at [C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App]
    9.0.12 at [C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App]
Learn more:
https://aka.ms/dotnet/app-launch-failed
To install missing framework, download:
https://aka.ms/dotnet-core-applaunch?framework=Microsoft.NETCore.App&framework_version=6.0.0&arch=x64&rid=win-x64&os=win10
. Please check the diagnostic logs for more information.
Results File: D:\a\Diligent-SharpGenTools\Diligent-SharpGenTools/artifacts/test-results/UnitTests.trx

Test Run Aborted.
Write-Error: D:\a\_temp\36b38049-476c-453f-8eee-1d7380766f74.ps1:2
Line |
   2 |  ./build.ps1 -Configuration Release
     |  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Unit Tests Failed
Error: Process completed with exit code 1.

@MikhailGorobets
Copy link
Owner

@ODtian Unit tests are failing

The tests pass successfully on my local machine. I suspect this is caused by a misconfiguration in the GitHub Actions workflow, as the output seems to indicate that the .NET 6.0 environment is missing. 🥲

Testhost process for source(s) 'D:\a\Diligent-SharpGenTools\Diligent-SharpGenTools\SharpGen.UnitTests\bin\Release\net6.0\SharpGen.UnitTests.dll' exited with error: You must install or update .NET to run this application.
App: D:\a\Diligent-SharpGenTools\Diligent-SharpGenTools\SharpGen.UnitTests\bin\Release\net6.0\testhost.exe
Architecture: x64
Framework: 'Microsoft.NETCore.App', version '6.0.0' (x64)
.NET location: C:\Program Files\dotnet
The following frameworks were found:
  8.0.6 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  8.0.22 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  8.0.23 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  9.0.6 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  9.0.12 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  10.0.2 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
The following frameworks for other architectures were found:
  x86
    8.0.23 at [C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App]
    9.0.12 at [C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App]
Learn more:
https://aka.ms/dotnet/app-launch-failed
To install missing framework, download:
https://aka.ms/dotnet-core-applaunch?framework=Microsoft.NETCore.App&framework_version=6.0.0&arch=x64&rid=win-x64&os=win10
. Please check the diagnostic logs for more information.
Results File: D:\a\Diligent-SharpGenTools\Diligent-SharpGenTools/artifacts/test-results/UnitTests.trx

Test Run Aborted.
Write-Error: D:\a\_temp\36b38049-476c-453f-8eee-1d7380766f74.ps1:2
Line |
   2 |  ./build.ps1 -Configuration Release
     |  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Unit Tests Failed
Error: Process completed with exit code 1.

@ODtian Unit tests are failing

The tests pass successfully on my local machine. I suspect this is caused by a misconfiguration in the GitHub Actions workflow, as the output seems to indicate that the .NET 6.0 environment is missing. 🥲

Testhost process for source(s) 'D:\a\Diligent-SharpGenTools\Diligent-SharpGenTools\SharpGen.UnitTests\bin\Release\net6.0\SharpGen.UnitTests.dll' exited with error: You must install or update .NET to run this application.
App: D:\a\Diligent-SharpGenTools\Diligent-SharpGenTools\SharpGen.UnitTests\bin\Release\net6.0\testhost.exe
Architecture: x64
Framework: 'Microsoft.NETCore.App', version '6.0.0' (x64)
.NET location: C:\Program Files\dotnet
The following frameworks were found:
  8.0.6 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  8.0.22 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  8.0.23 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  9.0.6 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  9.0.12 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  10.0.2 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
The following frameworks for other architectures were found:
  x86
    8.0.23 at [C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App]
    9.0.12 at [C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App]
Learn more:
https://aka.ms/dotnet/app-launch-failed
To install missing framework, download:
https://aka.ms/dotnet-core-applaunch?framework=Microsoft.NETCore.App&framework_version=6.0.0&arch=x64&rid=win-x64&os=win10
. Please check the diagnostic logs for more information.
Results File: D:\a\Diligent-SharpGenTools\Diligent-SharpGenTools/artifacts/test-results/UnitTests.trx

Test Run Aborted.
Write-Error: D:\a\_temp\36b38049-476c-453f-8eee-1d7380766f74.ps1:2
Line |
   2 |  ./build.ps1 -Configuration Release
     |  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Unit Tests Failed
Error: Process completed with exit code 1.

Try adding a .NET 6.0 setup step to the CI (it’s also possible to update the configuration files to the latest .NET version supported by GitHub Actions, but I think that would take a lot of time).

@MikhailGorobets
Copy link
Owner

There’s also another version of these bindings, but I haven’t tried it
https://github.com/rbnpontes/DiligentEngineNET

@ODtian
Copy link
Author

ODtian commented Feb 18, 2026

There’s also another version of these bindings, but I haven’t tried it https://github.com/rbnpontes/DiligentEngineNET

Thanks for mentioning it, but the project doesn't seem to be officially supported or actively maintained. 😭

BTW, can you approve the awaiting action request? Thanks!

@MikhailGorobets
Copy link
Owner

There’s also another version of these bindings, but I haven’t tried it https://github.com/rbnpontes/DiligentEngineNET

Thanks for mentioning it, but the project doesn't seem to be officially supported or actively maintained. 😭

BTW, can you approve the awaiting action request? Thanks!

Could you also add a unit test for the case where the error occurred? (Also, please make the commit titles consistent with this repository’s commit history.)

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants