Reactor's core framework targets .NET native AOT. The repo sets IsAotCompatible=true and treats the IL2*/IL3* analyzer warnings as errors on the core library, so trimming/AOT regressions can't merge silently. The tests/stress_perf/StressPerf.Reactor benchmark harness publishes Reactor with PublishAot=true on every CI build that runs perf — that's the canary for the framework's AOT viability.
But not every subsystem is AOT-clean. Some features rely on reflection in ways that can't yet be expressed without a source generator, and they sit behind unconditional trim suppressions. The suppressions silence the analyzer; at runtime, the affected code paths will throw under AOT if they're invoked.
This page enumerates what works, what doesn't, and what's planned. If you publish your app with PublishAot=true, you're responsible for staying inside the green column.
- Core reconciler — virtual element tree, diffing, mount/update, keyed reconciliation, element pooling.
- DSL & elements — factory methods, fluent modifiers.
- Hooks —
UseState,UseReducer,UseEffect,UseMemo,UseRef,UseCallback. (See note onUseObservablebelow.) - Flex layout — Yoga port is pure C#, no reflection.
- Charting (D3) — algorithm port is pure C#.
- Markdown — md4c-backed parser and renderer.
- Commanding — command records, focus-scoped accelerators.
- Animation — compositor-layer transitions, keyframes.
- Theming —
ThemeReftokens, style caching. - Markdown / Localization (read path) —
IStringLoc/Loc.X.Ylookups generated byReactor.Localization.Generatorare AOT-clean. Source-generated, no runtime reflection.
These subsystems compile cleanly with IsAotCompatible=true (the warnings are suppressed), but the suppressions cover real reflection that will throw at runtime under PublishAot=true. Don't rely on them in an AOT-published app until they're rebuilt on source generators.
| Subsystem | What breaks | Tracking |
|---|---|---|
| Devtools / MCP server | Assembly.GetTypes for component discovery, reflection-based hook inspection in DevtoolsStateTool, reflection-based method invocation in DevtoolsFireTool, DependencyProperty enumeration in DevtoolsPropertyTools, JsonSerializer for MCP request/response. The whole devtools surface (/--devtools, /--mcp-stdio) is reflection-driven. |
Issue #70 |
| PropertyGrid auto-discovery | TypeRegistry.Resolve, ReflectionTypeMetadataProvider walk public properties, build init-only setters, and instantiate editors via Activator.CreateInstance. Manually-registered metadata is fine; auto-discovery from a runtime type is not. |
Issue #70 |
| PropertyGrid array editor | Array.CreateInstance is annotated RequiresDynamicCode. Array-typed property editors won't work AOT. |
Issue #70 |
DataGrid AutoColumns<T> |
Reflects over T's public properties via TypeRegistry. The T parameter is annotated [DynamicallyAccessedMembers(PublicProperties)] so the trimmer keeps the members, but AutoColumns ultimately funnels into TypeRegistry.Resolve, which is RequiresUnreferencedCode. Use explicit Column<T,V>(…) definitions instead. |
Issue #70 |
UseObservable on POCOs |
ObservableTreeTracker walks public properties via reflection to subscribe to INPC. Observables built explicitly (Observable<T>, IObservableCollection) are fine; the implicit-INPC path is not. |
Issue #70 |
| Form validation | FormField's default editor resolution goes through TypeRegistry. Same caveat as PropertyGrid. |
Issue #70 |
| Navigation state JSON | NavigationHandle serializes deep-link state via JsonSerializer without a source-generated context. Custom types that ride through navigation state will fail to serialize under AOT. |
Issue #70 |
Component discovery (ReactorApp.Run<TApp> reflection paths) |
The instantiation of TApp itself is annotated and works. The devtools-only --list-components enumeration scans Assembly.GetTypes; that path is gated to non-AOT builds. |
Issue #70 |
Theme resource lookup (Theme.X, ThemeRef.Resolve) |
ThemeRef.Resolve walks Application.Current.Resources + its merged/theme dictionaries. The token records (Theme.Accent, Theme.PrimaryText, …) construct fine, but at runtime the XamlControlsResources entries that ReactorApplication.xaml brings in aren't fully populated under AOT — Resolve returns null for keys that exist under the JIT. Brushes applied via .Foreground(Theme.X) will fall back to control defaults. |
Issue #70 |
XAML-metadata-dependent controls (NavigationView, TabView, TemplateBinding) |
A subset of WinUI controls and the XAML-template parser need richer IXamlMetadataProvider data than what's reachable through trimmed AOT publish. NavigationView / TabView won't even reach Mount in this state, and {TemplateBinding} against custom DPs can't resolve the DP descriptor (see issue #142 reproductions). Use simpler containers under AOT. |
Issue #70 |
- The library compiles AOT-clean. Builds of
Reactor.csprojproduce zero IL2*/IL3* warnings. New code that reaches for reflection must either be source-generated, annotated withDynamicallyAccessedMembers, or — as a last resort — gated behind[RequiresUnreferencedCode]/[RequiresDynamicCode]so consumers see the warning at the call site. - Suppressions are temporary. Every
[UnconditionalSuppressMessage("Trimming", ...)]or("AOT", ...)in this repo is a TODO. The justification field names the reflection use; tracking is folded into issue #70. - The benchmark canary.
tests/stress_perf/StressPerf.Reactor(and theStressPerf.Direct/ReactorGridsiblings) setPublishAot=true. If they stop publishing, an AOT regression has landed in the framework.
If you need a feature listed in the "does not work" table and you're publishing AOT, file an issue against #70 with your scenario. The fix in most cases is a source generator pass; what gets prioritized is driven by who's hitting the wall.