feat: [WIP] make IgniteUI.Blazor.Lite Native-AOT clean - #390
Draft
damyanpetev wants to merge 3 commits into
Draft
Conversation
|
|
||
| // The closed-set typed getters — the exact casts UnmarshalledDataSource.CreateColumn performs. | ||
| Check(((Func<object, int>)Typed("Id"))(item) == 42, "typed int getter"); | ||
| Check(((Func<object, double>)Typed("Ratio"))(item) == 2.5, "typed double getter"); |
| var dictSchema = JSDataSourceSchema.CreateFromDictionary(dict); | ||
| Check(((Func<object, int>)dictSchema.TypedPropertyGetters[IndexOf(dictSchema.PropertyNames, "n")])(dict) == 5, "typed dictionary int getter"); | ||
| Check(((Func<object, string>)dictSchema.TypedPropertyGetters[IndexOf(dictSchema.PropertyNames, "s")])(dict) == "text", "typed dictionary string getter"); | ||
| Check((double)dictSchema.PropertyGetters[IndexOf(dictSchema.PropertyNames, "d")](dict) == 1.25, "untyped dictionary getter"); |
Comment on lines
+117
to
+121
| catch (Exception ex) | ||
| { | ||
| Console.Error.WriteLine("AOT smoke: " + ex); | ||
| return 1; | ||
| } |
Base automatically changed from
dpetev/minor-attr-inspection-refactor
to
master
September 1, 2026 16:32
damyanpetev
force-pushed
the
dpetev/aot
branch
from
September 1, 2026 16:32
eb29dd6 to
5ed4e78
Compare
- IlcTreatWarningsAsErrors instead of ILLinkTreatWarningsAsErrors, which never reaches ILC (Microsoft.NETCore.Native.targets defaults it from TreatWarningsAsErrors) - drop whole-library TrimmerRootAssembly: rooting pulls aspnetcore Components internals (not AOT-clean, dotnet/aspnetcore#51598) into ILC analysis with unfixable IL2072s; Main-reachable analysis covers the library's entire RequiresDynamicCode surface - preserve the smoke POCO via DynamicDependency: the gate's first CI run proved class-level DynamicallyAccessedMembers is honored by wasm ILLink but NOT by ILC for types reached through unannotated Type flows - docs/TRIMMING.md now tells consumers to prefer the DynamicDependency form - fix CS8618/CS8602 nullable warnings in the smoke app Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e Main Constructing any marshal-by-value type makes ILC compile SetParametersAsync and hit aspnetcore's own IL2072 in ComponentProperties.SetProperties (dotnet/aspnetcore#51598) - the same framework wall that forced dropping TrimmerRootAssembly, reached via construction instead of rooting. The factory is a static switch of news with no dynamic-code surface, behaviorally covered by the trimmed browser fixture. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
damyanpetev
force-pushed
the
dpetev/aot
branch
from
September 3, 2026 15:51
688bdc3 to
9180663
Compare
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.
feat: make IgniteUI.Blazor.Lite Native-AOT clean
What & why
Enables the AOT and single-file analyzers (
EnableAotAnalyzer/EnableSingleFileAnalyzer, errors via.editorconfig) and makes the library genuinely AOT-safe. The publicIsAotCompatibleflag is deliberately deferred: Blazor itself is not AOT-compatible yet (Microsoft.AspNetCore.ComponentsshipsIsTrimmableonly; dotnet/aspnetcore#51598 is open/Backlog), so the claim would outrun the platform — a csproj comment marks the flip point. The AOT analyzer surfaced 14 unique IL3050 sites; 11 are fixed for real, 3 carry narrow justified suppressions. Follow-up to the trimming PR, same methodology: empirical inventory → real fixes where possible → suppressions only with verified invariants → publish-time + runtime gates.The design rests on verified RDC semantics:
Expression...Compile()is NOTRequiresDynamicCode— expressions run interpreted under ILC (documented, slower-not-broken). The IL3050s come from delegate-type construction (Expression.GetFuncType, the non-genericLambda(body, params)overloads) and fromMakeGenericType/MakeGenericMethod. The .NET 9+ analyzer intrinsics recognize statically-visible and class-constrained instantiations.Real fixes (11 sites, no suppressions)
JsonDataSourceSchema.cs): untyped builders now useExpression.Lambda<Func<object,object>>(statically-known delegate type — the un-annotated overload); typed builders select their delegate type from a new closedtypeof(Func<object,T>)map covering exactly the shapesUnmarshalledDataSource.CreateColumnhard-casts to (10 primitives + 9 nullables; enums map via their underlying type, as the expression tree already converted). Types outside the set fall back to the untyped getter — their columns (ObjectValue/arrays) only ever consume the untyped one. Every value-typeFunc<object,T>instantiation is now a statically visible literal, so ILC pregenerates them.BaseRendererControl.BuildDynamicContentInfo+DynamicContentInfo<T>/IgbTemplateContent<T>):where T : classconstraint added (the input set is a verified closed set of 5 reference types via internalUpdateTemplatetypeofliterals) — the net9+ analyzer provesMakeGenericTypesafe via the constraint; the factory lambda switched toExpression.Lambda<Func<DynamicContentInfo>>.Lambda<TDelegate>overloads (delegate types were already statically written at the cast sites).Suppressions (3, each with a verified invariant)
BaseRendererControl.BuildDynamicContentInfoRuntimeHelperctorInvokeUnmarshalledprobe; no NativeAOT target exists for net8 wasm, and Mono AOT retains the interpreter.BaseRendererControl.SetPropertyValueArray.CreateInstance)PropertyType, present in metadata whenever the property exists.Structural changes
RuntimeHelpergated to#if NET8_0: the probedInvokeUnmarshalledAPI was removed in net9 (binary-verified), so the probe, itsDynamicDependency, and all its trim/AOT suppressions now compile only on net8; net9/net10 always use the raw-pointerInvokeVoidpath (already the runtime behavior there). This also removes the latent IL2035 exposure found in the suppression audit — the stringDynamicDependencytargetingMicrosoft.AspNetCore.Components.WebAssembly, an assembly absent in MAUI BlazorWebView publishes, no longer exists on the TFMs those apps use. Dead#if NET5_0arms folded away (no net5 TFM).Commit()allocatedTypedFieldGettersasFunc<object,object>[]— storing anyFunc<object,TField>threwArrayTypeMismatchException(typed-field path was broken for every data type with public non-object fields);SendUnmarshalledColumnDataIntentsMessagenull-checked one delegate field but invoked the other.Regression guards
.editorconfig:dotnet_analyzer_diagnostic.category-AOT.severity = errorjoins the Trimming/SingleFile rules — new IL3xxx fails the build.tests/IgniteUI.Blazor.Lite.AotSmoke: console app publishing the library under real ILC (PublishAot,IlcTreatWarningsAsErrors— the propertyILLinkTreatWarningsAsErrorsnever reaches ILC) and running 38 asserted checks over the RDC-adjacent paths (all getter shapes incl. enum/nullable/field, dictionary schema,ExtractSchema,IgbJsonContextround-trip; success = exit 100, aspnetcore trimming-test convention). CI publishes and runs it (linux-x64, ~1-2 min). Two things were tried and dropped for the same reason — aspnetcore Components internals are not AOT-clean ([Blazor] Enable Blazor apps running inside of ASP.NET Core to be Native AoT compiled dotnet/aspnetcore#51598) and emit unfixable IL2072s once reached: whole-libraryTrimmerRootAssemblyrooting, and constructing any ComponentBase-derived type in Main (theMarshalByValueFactorycheck — a static switch ofnews with no dynamic-code surface, behaviorally covered by the trimming browser fixture). Analysis covers what Main reaches, which is the library's entire RDC surface. Locally without the C++ toolchain:dotnet run -p:SimulateNoDynamicCode=trueforces the interpreter paths via the documentedDynamicCodeSupportfeature switch.[DynamicallyAccessedMembers]on an instantiated data-item type is honored by wasm ILLink but not by ILC (the smoke POCO lost its reflected properties through the unannotatedTypeflow). docs/TRIMMING.md consumer guidance now says to prefer the flow-independent[DynamicDependency]form, and the smoke app exercises exactly that pattern.Wasm AOT Smokeworkflow (workflow_dispatch): wasmRunAOTCompilation=truepublish + the existing TrimmedPublish browser fixture against that output — validates the actual Blazor wasm-AOT consumer scenario (Mono AOT emits no IL3xxx; that's the ILC smoke's job). To be dropped if it never catches anything the other gates don't..agentstrimming skill is now trimming & AOT.Verification
dotnet build, all 3 TFMs: 0 IL2xxx + 0 IL3xxx with all category gates as errors.dotnet test: 965 passed / 0 failed on each of net8.0/net9.0/net10.0.IsDynamicCodeSupported=false(interpreter-forced). The full ILC publish+run gate executes in CI (this machine lacks the C++ linker — managed side verified locally, native leg is CI's).Behavioral notes
where T : classonDynamicContentInfo<T>/IgbTemplateContent<T>is technically API-narrowing, but the only instantiation channel (internalUpdateTemplate) has always passed reference types only.RequiresDynamicCodesurface lives; components carry no dynamic code, and no supported scenario runs Blazor components under ILC today. The whole-libraryTrimmerRootAssemblyrooting is what gates component code at ILC-analysis level.Guid, exotic enum underlyings) now return the untyped getter instead of an unconsumed typed delegate — those columns only ever read the untyped getter, so behavior is unchanged.Nullable<enum>members resolve toNullable<int>-style columns whoseCreateColumncast has always thrown; tracked in the followups plan.🤖 Generated with Claude Code