Skip to content

Fixes #5259. AOT: use source-generated JSON serializer for dictionary construction in DeepCloner#5243

Merged
tig merged 6 commits intodevelopfrom
copilot/fix-crash-on-dictionary-colorname16
May 7, 2026
Merged

Fixes #5259. AOT: use source-generated JSON serializer for dictionary construction in DeepCloner#5243
tig merged 6 commits intodevelopfrom
copilot/fix-crash-on-dictionary-colorname16

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 7, 2026

Fixes

DeepCloner.CreateDictionaryInstance uses Activator.CreateInstance(dictType) to construct cloned dictionaries. AOT trimming removes default constructors of closed generic Dictionary<,> types not statically reachable, causing MissingMethodException at module-init time. The previous fix (#5240) added a typed clone path for one dictionary; this is whack-a-mole — any new config dictionary type triggers the same crash.

Proposed Changes/Todos

  • CreateDictionaryInstance now tries SourceGenerationContext (source-generated JSON serializer) to create empty dictionary instances before falling back to Activator.CreateInstance. All config dictionary types are already registered there, so this fixes the category.
  • Removed CloneHardCodedPropertyValue and its per-type helpers (CloneKeyBindings, ClonePlatformKeyBinding, CloneKeyArray) from ConfigurationManagerDeepCloner.DeepClone handles all dictionary types generically now.
  • Added test for Dictionary<ColorName16, string> cloning (the specific crash from this issue).

Key change in CreateDictionaryInstance:

// Before falling back to Activator.CreateInstance:
JsonTypeInfo? jsonTypeInfo = ConfigurationManager.SerializerContext.GetTypeInfo (dictType);
if (jsonTypeInfo is not null)
{
    IDictionary? result = JsonSerializer.Deserialize ("{}", jsonTypeInfo) as IDictionary;
    if (result is not null) { return result; }
}

Typed paths remain for ConcurrentDictionary<string, ThemeScope> and Dictionary<string, Scheme?> which require custom comparers.

Pull Request checklist:

  • I've named my PR in the form of "Fixes #issue. Terse description."
  • My code follows the style guidelines of Terminal.Gui - if you use Visual Studio, hit CTRL-K-D to automatically reformat your files before committing.
  • My code follows the Terminal.Gui library design guidelines
  • I ran dotnet test before commit
  • I have made corresponding changes to the API documentation (using /// style comments)
  • My changes generate no new warnings
  • I have checked my code and corrected any poor grammar or misspellings
  • I conducted basic QA to assure all features are working

…serializer in DeepCloner

Instead of Activator.CreateInstance (which fails when AOT trims default ctors),
CreateDictionaryInstance now tries the source-generated JSON serializer context
first. This fixes the category of AOT crashes for all dictionary types registered
in SourceGenerationContext, not just individual instances.

Removes the whack-a-mole CloneHardCodedPropertyValue from ConfigurationManager.

Fixes #5259

Agent-Logs-Url: https://github.com/gui-cs/Terminal.Gui/sessions/9a0247ff-df16-449a-9b6e-4bebc68b2e41

Co-authored-by: tig <585482+tig@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix crash on Dictionary<ColorName16, string> in AOT configuration Fixes #5259. AOT: use source-generated JSON serializer for dictionary construction in DeepCloner May 7, 2026
Copilot finished work on behalf of tig May 7, 2026 20:07
Copilot AI requested a review from tig May 7, 2026 20:07
tig and others added 2 commits May 7, 2026 14:21
… catch

The JSON serializer fallback in CreateDictionaryInstance created dictionaries
with the default comparer, silently dropping any custom comparer passed by the
caller. Now the JSON path is only used when no comparer is needed. Also
narrowed the catch from bare Exception to InvalidOperationException to avoid
masking unexpected failures.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@tig tig marked this pull request as ready for review May 7, 2026 20:21
tig and others added 2 commits May 7, 2026 14:24
- Rewrite NativeAot example to use v2 API (Runnable<T>, Application.Create)
  and exercise AOT-sensitive view types: MenuBar, Menu, FrameView, CheckBox,
  OptionSelector, TextView, StatusBar, Dialog (via MessageBox), in addition
  to the existing Button, Label, TextField, Window.

- Add 'dotnet publish' step to build-validation.yml. The previous 'dotnet
  build' did not trigger AOT compilation, so trimming/reflection errors
  were never caught in CI. Bump timeout to 15 min to accommodate.

- Update README with current instructions and explanation of what the
  example exercises.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update NativeAot sample to explicitly construct and display all IDesignable Terminal.Gui views in a scrollable container, each within a titled FrameView. Integrate ViewPropertiesEditor for property editing, and include status and menu bars. Remove old login/options/notes demo. Revise documentation to explain the new approach, list tested views, and clarify AOT publishing/debugging. Update NativeAot.csproj to link editor files and add global usings.
@tig tig requested a review from BDisp May 7, 2026 21:14
@tig
Copy link
Copy Markdown
Member

tig commented May 7, 2026

@BDisp you'll dig the updates nativeaot example i think. ;-)

@tig tig merged commit 11d6e27 into develop May 7, 2026
11 checks passed
@tig tig deleted the copilot/fix-crash-on-dictionary-colorname16 branch May 7, 2026 21:28
@tig
Copy link
Copy Markdown
Member

tig commented May 7, 2026

See #4367 (comment)

tig added a commit that referenced this pull request May 7, 2026
The fix from #5243 ("Fixes #5259. AOT: use source-generated JSON
serializer for dictionary construction in DeepCloner") was reverted
when release/v2.1.0-rc.1 merged origin/main back at 94dca98. That
merge had conflicts in DeepCloner.cs and ConfigurationManager.cs and
the resolution picked main's pre-#5243 versions, dropping:

  - 27 lines from DeepCloner.cs (the SourceGenerationContext.GetTypeInfo
    fallback in CreateDictionaryInstance, plus its leading comment)

and re-adding 54 lines to ConfigurationManager.cs:

  - CloneHardCodedPropertyValue and the per-type clone helpers
    (CloneKeyBindings, ClonePlatformKeyBinding, CloneKeyArray) that
    #5243 had removed in favor of generic DeepCloner handling
  - the call site change from DeepClone to CloneHardCodedPropertyValue

PRs #5246 (rc.1 → main) and #5247 (rc.1 backmerge → develop) carried
the broken state forward. v2.1.0-beta.1 was tagged before the merge
(no fix); v2.1.0-rc.1 was tagged after but doesn't have it either.

Repro: `dotnet publish Examples/NativeAot -c Release -r win-x64
--self-contained -p:PublishAot=true` against current develop crashes
at module init with `MissingMethodException: No parameterless
constructor defined for type Dictionary<ColorName16, string>`. Same
crash a downstream AOT consumer (clet) hit and tracked as #5239#5240#5242#5243#5248.

This commit checks DeepCloner.cs and ConfigurationManager.cs out of
11d6e27 (the original PR #5243 merge), restoring the source-gen
fallback and removing the per-type helpers. Verified locally:

  - `dotnet build Terminal.Gui/Terminal.Gui.csproj -c Release` → 0 errors
  - clet AOT publish + `clet.exe --version` runs cleanly (was crashing
    against rc.1; now exits 0 with the local TG nupkg pinned)

Refs #5239, #5242, #5243, #5248, #5259.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

AOT: ConfigurationManager.Initialize still crashes on Dictionary<ColorName16, string> after #5240

2 participants