Add test coverage for marshalling dictionary Keys/Values collections - #2539
Open
Sergio Pedri (Sergio0694) wants to merge 1 commit into
Open
Conversation
Binding a XAML control to 'someDictionary.Values' (issue #2537) marshals the 'Dictionary<TKey, TValue>.KeyCollection'/'ValueCollection' nested types across the ABI, not the dictionary itself, so each needs a CCW exposing 'IIterable<T>' and 'IBindableIterable'. Neither type is ever named in user code: 'Keys'/'Values' are declared as 'IEnumerable<T>' on 'IReadOnlyDictionary<TKey, TValue>' and as 'ICollection<T>' on 'IDictionary<TKey, TValue>'. The interop generator already handles this, off the dictionary instantiation itself: for every type specification it sees, it resolves the definition and instantiates the signatures of its members with that generic context, so 'Dictionary<int, string>' yields the constructed nested collection types returned by 'get_Keys'/'get_Values'. Verified by adding a dictionary instantiation over an otherwise unused pair of type arguments and observing the matching CCW entries appear in the generated 'WinRT.Interop.dll'. That was untested, though, so this pins it down: the test identifies the two types by name rather than naming them in code (which would put them in the test assembly's metadata and make the coverage vacuous), checks the CCW answers 'QueryInterface' for both interfaces, and then enumerates the keys from native code through both 'IIterable<int>' and the bindable iterable that XAML uses for an 'ItemsSource'. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sergio Pedri (Sergio0694)
requested a review
from Manodasan Wignarajah (manodasanW)
August 27, 2026 12:57
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a unit test covering the scenario from #2537: marshalling a dictionary's
Keys/Valuescollections across the Windows Runtime ABI. Investigation confirmed the interop generator already handles this correctly in CsWinRT 3.0, so this PR adds no generator changes — only the missing test coverage that pins the behavior down.Motivation
Issue #2537 reports a Native AOT crash under CsWinRT 2.x when binding a XAML control to
someDictionary.Values(e.g.{x:Bind vm.Dict.Values}). The interesting part of that scenario is that the object crossing the ABI is not the dictionary at all: it is the nestedDictionary<TKey, TValue>.KeyCollection/ValueCollectiontype, which needs a CCW exposingIIterable<T>andIBindableIterable.Those types are never named in user code, and cannot be:
Keys/Valuesare declared asIEnumerable<T>onIReadOnlyDictionary<TKey, TValue>and asICollection<T>onIDictionary<TKey, TValue>, so the concrete nested types appear nowhere in the consuming assembly's metadata. They only get marshalling code if the interop generator derives them from the dictionary instantiation itself.It does.
ModuleDefinitionExtensions.EnumerateTypeSignaturesdoes not just read theTypeSpectable: for each type specification it resolves the type definition and instantiates its members' signatures with that generic context. ADictionary<int, string>type spec therefore yields the constructed return types ofget_Keys/get_Values, and both nested collections flow through the normal user-defined-type discovery path and get CCW entries.This was verified empirically rather than by reading alone: adding a
Dictionary<char, char>instantiation over an otherwise unused pair of type arguments and rebuilding made the matchingKeyCollection/ValueCollectionCCW entries appear in the generatedWinRT.Interop.dll, and removing it made them disappear again. That also rules out the new test passing vacuously against entries some other assembly happened to contribute.The behavior was untested, though, which is what this PR fixes. It is a scenario that is easy to regress precisely because nothing in user code references the types involved.
Changes
src/Tests/UnitTest/TestComponentCSharp_Tests.cs: addsTestDictionaryKeyAndValueCollectionInterfaceMarshalling, modelled on the existingTestCollectionChangedListInterfaceMarshalling. It identifies the two nested collection types by name rather than naming them in code (naming them would put them in the test assembly's metadata, which is exactly what the scenario cannot rely on, making the coverage vacuous), asserts the CCW answersQueryInterfacefor bothIIterable<T>andIBindableIterable, and then enumerates the keys from native code throughGetIteratorForCollectionand throughBindableIterableProperty, which is the path XAML uses to bind anItemsSource.Validation
The full unit test suite passes (559/560). The single failure,
TestDataTransferManager, is pre-existing and environment-dependent, in a file untouched by this PR.