Skip to content

Optimize generated activation factories for authored components#2471

Draft
Sergio0694 wants to merge 5 commits into
staging/3.0from
user/sergiopedri/optimize-activation-factory-ctor
Draft

Optimize generated activation factories for authored components#2471
Sergio0694 wants to merge 5 commits into
staging/3.0from
user/sergiopedri/optimize-activation-factory-ctor

Conversation

@Sergio0694

Copy link
Copy Markdown
Member

Summary

Optimize the *ServerActivationFactory classes generated for authored Windows Runtime components: the static constructor that forces the authored type's class constructor to run before activation is now emitted only when the type actually needs it (i.e. when it, or an authored base type, registers XAML dependency properties). For all other authored types it is pure overhead and is now omitted.

Motivation

Every generated activation factory used to emit a static constructor calling RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle). That is only required when the authored type registers XAML dependency properties (declared as static DependencyProperty fields), so they are registered before the type is first activated. For every other type the static constructor is unnecessary work at activation time, so making it pay-for-play removes that overhead.

Whether a type registers dependency properties cannot be answered from the .winmd metadata (it does not carry static fields), so the change also threads the component's compiled implementation assemblies into the projection writer and adds the infrastructure to answer that question on demand while emitting each factory. Along the way it consolidates a small shared helper (SignatureComparer.IgnoreVersion) into the shared generator core so both the projection writer and the interop generator use a single copy.

Changes

The PR is organised into focused commits:

  • Plumb the managed implementation assemblies into the projection writerProjectionWriterOptions.ComponentImplementationAssemblyPaths (carried through Settings) exposes the authored component's compiled .dll(s) to the writer, and the projection generator host collects, in component mode, the reference assemblies marked [WindowsRuntimeComponentAssembly].
  • Skip the activation factory static constructor when not neededComponentFactory.WriteFactoryClass emits the static constructor only when required, via a callback so the factory body stays a single interpolated template; adds DependencyProperty and the Microsoft.UI.Xaml / Windows.UI.Xaml namespaces to the well-known name references.
  • Move SignatureComparer.IgnoreVersion to the shared generator core — promotes the version-agnostic comparer to WinRT.Generator.Core and migrates both the projection writer and the interop generator to consume the single shared copy, removing the interop generator's duplicate.
  • Make the dependency-property analysis lazyComponentImplementationMetadata is a thin resolver that indexes the managed assemblies' types by name, and a new ComponentStaticConstructorAnalyzer computes the requirement on demand, walking base types lazily and memoizing per visited type so shared hierarchies are traversed once.
  • Compute the requirement on the fly via the emit contextProjectionEmitContext carries the shared analyzer, so WriteFactoryClass derives the answer from the type and context instead of taking a pre-computed flag; the analyzer's cache is concurrent since it is queried from the parallel emission work items.

Backward compatible: when the implementation assemblies are not available (e.g. reference-projection generation), the analysis conservatively keeps the static constructor, matching the previous always-emit behavior.

Sergio0694 and others added 5 commits June 22, 2026 23:43
… writer

Expose the authored component's compiled implementation assembly path(s) to the
projection writer via ProjectionWriterOptions.ComponentImplementationAssemblyPaths
(carried through Settings.ComponentImplementationAssemblies). The component .winmd
does not carry implementation details such as the static fields backing XAML
dependency properties, so the writer will consult these managed assemblies to make
per-type activation-factory decisions.

The projection generator host collects, in component mode, the paths of reference
assemblies marked [WindowsRuntimeComponentAssembly] and forwards them through.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The generated '<Type>ServerActivationFactory' classes always emitted a static
constructor calling RuntimeHelpers.RunClassConstructor(typeof(Type).TypeHandle).
That is only required when the authored type registers XAML dependency properties
(declared as static DependencyProperty fields), so that they are registered before
the type is first activated. For every other authored type it is pure overhead.

The factory now emits the static constructor only when the authored type, or an
authored base type, declares a static field of type DependencyProperty
(Windows.UI.Xaml.DependencyProperty for UWP XAML, otherwise
Microsoft.UI.Xaml.DependencyProperty). This is determined by scanning the managed
component implementation assemblies (the .winmd does not carry these fields) via the
new ComponentImplementationMetadata helper; types not found there conservatively keep
the constructor. The conditional emission goes through a WriteStaticConstructor
callback so the factory body remains a single interpolated template either way.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The version-agnostic SignatureComparer used as the IEqualityComparer for AsmResolver
entity collections lived only in the interop generator. The projection writer now needs
it too (for its component activatable-type sets), so promote it to the shared
WinRT.Generator.Core library as a single SignatureComparerExtensions.IgnoreVersion and
have both tools consume it:

- Add SignatureComparerExtensions (IgnoreVersion) to WinRT.Generator.Core and make Core's
  internals visible to WinRT.Projection.Writer.
- Reference Core from the projection writer and drop the writer's local copy.
- Remove the interop generator's duplicate IgnoreVersion (keeping its tuple comparers) and
  resolve it from Core instead.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ComponentImplementationMetadata.Load previously computed, for every type in the
component's managed assemblies, whether it declared a dependency property. That work is
only needed for the handful of activatable types actually projected, so make it lazy:

- ComponentImplementationMetadata is now a thin resolver that just indexes the managed
  assembly types by full name and resolves them on demand.
- A new ComponentStaticConstructorAnalyzer computes whether a type (or an authored base
  type) registers a dependency property, resolving and walking base types only when
  queried and memoizing the result per visited type so shared hierarchies are traversed once.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
WriteFactoryClass took an 'emitStaticConstructor' bool that the caller pre-computed into
a separate set during discovery. Since the method already has the type and the emit
context, let the context carry the component's loaded implementation assemblies and have
the factory query them directly:

- ProjectionEmitContext now exposes the shared ComponentStaticConstructorAnalyzer, created
  once on the ProjectionGenerator and reused by every emit context (so its memoization is
  shared). The analyzer's cache is now a ConcurrentDictionary since it is queried from the
  parallel emission work items (AsmResolver reads are thread-safe).
- WriteFactoryClass drops the bool parameter and computes the requirement from the context.
- DiscoverComponentActivatableTypes no longer builds the requiring-static-constructor set,
  and ProjectionGeneratorRunState no longer carries it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Sergio0694 Sergio0694 added performance Related to performance work authoring Related to authoring feature work CsWinRT 3.0 labels Jun 23, 2026
@Sergio0694 Sergio0694 requested a review from manodasanW June 23, 2026 18:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

authoring Related to authoring feature work CsWinRT 3.0 performance Related to performance work

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant