[Blazor] Generate component metadata for Native AOT - #68299
Draft
javiercn wants to merge 17 commits into
Draft
Conversation
javiercn
force-pushed
the
javiercn-component-metadata-aot
branch
from
August 9, 2026 11:47
a0b2f1b to
73ba69c
Compare
javiercn
force-pushed
the
javiercn-component-metadata-aot
branch
from
August 9, 2026 12:36
73ba69c to
cdc58e7
Compare
javiercn
force-pushed
the
javiercn-component-metadata-aot
branch
from
August 9, 2026 19:26
cdc58e7 to
1afdfb0
Compare
…pe info Route component activation, injection, parameters, cascades, and persistent members through a shared reflection-backed type-info model. Keep the descriptor implementation internal until generated metadata is introduced. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Verify reflection-backed activation, injection, ordinary parameters, cascading parameters, and persistent members before generated metadata is introduced. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Carry component type info through route discovery, route cache identity, endpoint metadata, and page descriptors. Preserve dynamically accessed type flow and include the donor's final route-table annotation fix. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Delete the assembly-scan builder graph now that route and endpoint discovery resolve component type info directly. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Cover route-table metadata, endpoint discovery, hot reload, and page descriptor propagation using normalized component type info with reflection fallback. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Carry resolved component type info through initial markers, dynamic root operations, resumed circuits, renderer attachment, and parameter deserialization. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Cover metadata-aware root component deserialization, renderer attachment, dynamic operations, circuit persistence, and Server service registration. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Expose component, parameter, and injectable descriptors when the existing metadata context first carries Components. Generate application component factories, attributes, accessors, and diagnostics, then resolve generated type info before the default reflection fallback without adding framework provider machinery. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Cover component activation metadata, parameters, cascading and persistent members, injection, inherited metadata, accessibility diagnostics, unique contexts, and runtime registration while preserving lower-layer JSON, JS, and binding contexts. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Wire the E2E manifest task, add the native-capable source-generated harness and targets, and introduce runnable JIT feature applications with metadata registration and browser fixtures. Native testing remains available to later layers but is not enabled here. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Drive activation, injection, parameters, cascades, binding graphs, JS dispatch, JSON composition, custom events, persistence, Session, protected storage, dynamic roots, and endpoint metadata through the JIT feature application. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Keep a dedicated JIT witness for undescribed components, JS-invokable methods, and JSON payloads using the default reflection fallbacks. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Route ComponentBase parameter assignment through renderer-scoped type info and add the compatibility-preserving reflection switch infrastructure and fail-fast branches required when a later layer disables reflection. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Resolve Session and TempData component getters from component parameter descriptors first, preserving reflection fallback compatibility and eliminating their independent reflection caches. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Cover cascading state, activation, injection, resolver ordering and caches, ParameterView, persistent members, generated render modes, activator substitution, and reflection-disabled fail-fast behavior. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Verify descriptor precedence, reflection compatibility, reflection-disabled failure, and Session and TempData subscription parity. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Verify persistent-state serializer closure, JSON resolver composition, typed JS serialization contracts, and generated parameter bridges for DynamicallyAccessedMembers annotations. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
javiercn
force-pushed
the
javiercn-component-metadata-aot
branch
from
August 9, 2026 21:47
c98e962 to
eba8437
Compare
Member
Author
|
/azp run |
|
Azure Pipelines: Successfully started running 2 pipeline(s). 3 pipeline(s) were filtered out due to trigger conditions. |
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.
Overview
This is layer 4 of 6 for #68332, stacked on #68297. Across 17 commits, it gives component activation, member access, routing/discovery, and Server root operations one shared
ComponentTypeInfomodel, then teaches the existing application metadata generator to produce that model at compile time. The cross-cutting constraint is generated first, reflection last: applications that register metadata avoid the reflection paths, while existing applications with no metadata context continue to work unchanged; framework-owned descriptor providers and strict Native AOT enablement remain in layers 5 and 6 respectively.Design
The new experimental contract describes a component as executable metadata rather than as reflected members. The descriptor carries construction, parameters, injection, and attribute-shaped facts in the same forms the runtime already consumes:
ComponentParameterDescriptoradds typed getter/setter delegates plus an optional closed persistent-state serializer factory.ComponentInjectableDescriptoris intentionally separate because injection has different semantics: service type, keyed[Inject]metadata, and a setter, but no getter or unmatched-value behavior. Both shapes let generated code use direct assignments orUnsafeAccessorbridges without runtime generic construction or reflection.The existing application context gains one new capability, and the existing registration call composes it with the JSON, JS-invokable, and bindable metadata introduced by lower layers:
Two design decisions keep this backward-compatible and reviewable:
true. This layer defines generated-only failure semantics; layer 6 is responsible for actually setting the switch tofalsein the strict Native AOT lane.Framework-owned descriptor lists/factories are deliberately absent here. The application generator skips framework assemblies; layer 5 supplies owner-authored framework metadata that can later merge with application descriptors through the resolver model established here.
Implementation
A renderer creates one resolver chain and passes it to activation, property injection, parameter binding, cascading state, persistence, discovery, and roots. The source-generated resolver is consulted first; the composite caches positive type/name/assembly lookups, fills a missing generated factory from a later resolver, and invalidates caches for hot reload. When both generated and reflection resolvers exist, hot reload disables stale generated descriptors and continues through reflection.
The generator walks referenced application types with private metadata imported, because Razor-generated
@injectproperties and render-mode attributes are not visible under Roslyn's default import mode. It refuses partial descriptions, preserves most-derived member semantics, reconstructs component attributes, and emits executable delegates. The representative parameter emission below covers ordinary, cascading/query/session/TempData, and persistent parameters; their only deltas are the reconstructed attribute and optional serializer/accessor bridge.At runtime, component entry points carry renderer-scoped type info instead of rediscovering members independently.
ComponentBasebinds through the renderer's resolver;ComponentFactoryreads render mode metadata, uses generated activation and injection delegates, and only permits legacy type-based custom activators when reflection is enabled. Session and TempData use the same descriptor getter rather than maintaining separate reflection caches:Discovery is the assembly-enumeration equivalence class. Routing and endpoint discovery now enumerate
ComponentTypeInforather than scanning exported types independently; route cache identity includes the resolver, and endpoint metadata is the descriptor metadata minusRouteAttribute. Layout and render-mode consumers use the same metadata bag. The obsolete assembly/component/page builder graph is removed.Server roots are the lifetime-equivalence class. Initial marker deserialization resolves and stores
ComponentTypeInfo; dynamic add/update operations pass that same object into the renderer; resumed circuits enumerate retained(typeInfo, parameters)pairs. This prevents a generated descriptor from being discarded and re-resolved by type midway through the operation.The feature harness is native-capable but this layer runs it under JIT. An E2E project can opt into
NativeTesting; the testing targets then add a build-only source generator to the entry-point application's own compilation, avoiding startup hooks that a native image cannot load. The permanent scenarios cover application-generated component metadata alongside the lower-layer binding, JS, and JSON capabilities; a separateJitDefaulttest proves undescribed component, JS method, and JSON payload fallbacks still work.Outcome
ComponentTypeInfoDynamicallyAccessedMembersbridgesStack note: this PR depends on #68297. Framework-owned component descriptor providers are added by the next stack layer; the final strict reflection-disabled Native AOT proof follows after that.