Skip to content

Generate CCW metadata for IReadOnlyDictionary and dictionary key/value collections - #2538

Open
Sergio Pedri (Sergio0694) wants to merge 1 commit into
masterfrom
user/sergiopedri/fix-readonly-dictionary-ccw-metadata
Open

Generate CCW metadata for IReadOnlyDictionary and dictionary key/value collections#2538
Sergio Pedri (Sergio0694) wants to merge 1 commit into
masterfrom
user/sergiopedri/fix-readonly-dictionary-ccw-metadata

Conversation

@Sergio0694

Copy link
Copy Markdown
Member

Fixes #2537

Problem

Binding to the values of a dictionary crashes under NativeAOT:

class ViewModel
{
    public IReadOnlyDictionary<int, string> Dict { get; } = new Dictionary<int, string> { [42] = "hello" };
}
<ComboBox ItemsSource="{x:Bind vm.Dict.Values}" />

There were two gaps in AddVtableAdapterTypeForKnownInterface in the AOT source generator:

  1. IReadOnlyDictionary<K, V> was never handled. The method special cased IDictionary<K, V> (registering ReadOnlyDictionary<K, V>, KeyValuePair<K, V> and ConstantSplittableMap<K, V>), but had no branch for IReadOnlyDictionary<K, V>. That interface is projected as IMapView<K, V>, whose Split method hands out ConstantSplittableMap<K, V> instances and whose iteration hands out KeyValuePair<K, V> values, so a type implementing only the read only interface was missing both CCW entries.

  2. Keys and Values were never discovered. A dictionary hands those out through concrete collection types (e.g. Dictionary<K, V>.ValueCollection) which never appear by name in user code, so nothing in the generator ever gathered them. Marshaling one to XAML then found no vtable in the CCW lookup table, so the resulting CCW had no IBindableIterable and the bind crashed. This is what the repro in the issue hits: Dictionary<int, string> itself was registered, but Dictionary<int, string>.ValueCollection was not.

Fix

  • Added an IReadOnlyDictionary<K, V> branch registering KeyValuePair<K, V> and ConstantSplittableMap<K, V>.
  • For any dictionary type, walk its public Keys/Values properties and register a vtable for the concrete collection type behind them, along with the ToAbiEnumeratorAdapter<T> needed to enumerate it. Interface typed Keys/Values are skipped, since there is no concrete type to discover in that case.

The lookup table is keyed by string, so this adds table entries only — no new type references in generated code.

Testing

  • 4 new tests in AotOptimizerTests.cs, each verified to fail without the generator change and pass with it. Full suite: 74/74 passing.
  • Added an end to end regression case to the Collections AOT functional test that assigns dictionary.Keys/dictionary.Values to Class.BindableIterableProperty (typed Microsoft.UI.Xaml.Interop.IBindableIterable), which fails to marshal unless the collection CCW exposes IBindableIterable.

…e collections

The AOT source generator special cased IDictionary<K, V> when gathering the
adapter types a dictionary CCW hands out, but had no branch for
IReadOnlyDictionary<K, V>. Since it is projected as IMapView<K, V>, whose Split
method hands out ConstantSplittableMap<K, V> instances and whose iteration hands
out KeyValuePair<K, V> values, a type implementing only the read only interface
was missing both entries.

Additionally, a dictionary hands out its Keys and Values through concrete
collection types (eg. Dictionary<K, V>.ValueCollection) that never appear by
name in user code, so nothing discovered them. Binding to them (eg.
"{x:Bind ViewModel.Dictionary.Values}") then crashed under NativeAOT because the
marshaled collection had no CCW vtable. Those collection types and their
enumerator adapters are now registered through the dictionary itself.

Fixes #2537

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: aa4f63a9-8c06-4dbb-9eae-a788a38c6262
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

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.

Bug: WinRT source generator doesn't generate member metadata for IReadOnlyDictionary

1 participant