Skip to content

Fix #1411 - NativeAOT proxy accessibility and interceptor fallback#1472

Open
AArnott wants to merge 6 commits into
mainfrom
aarnott-nativeaot-proxies
Open

Fix #1411 - NativeAOT proxy accessibility and interceptor fallback#1472
AArnott wants to merge 6 commits into
mainfrom
aarnott-nativeaot-proxies

Conversation

@AArnott

@AArnott AArnott commented Jun 23, 2026

Copy link
Copy Markdown
Member

Problem

When interceptors are enabled for RPC contracts, proxy types must be accessible from the calling assembly. Issue #1411 identified that the documentation was unclear about this requirement, and the interceptor source generator was emitting code that would not compile for inaccessible proxies.

Solution

This PR addresses the issue with two changes:

  1. Documentation Update - Updated docfx/docs/nativeAOT.md to explicitly call out that when interceptors are enabled, external assemblies that declare RPC contracts may export their proxy types via [assembly: ExportRpcContractProxies] to make them more performant.

  2. Reflection-Based Fallback - Updated the interceptor source generator to handle inaccessible proxy types gracefully:

    • When a proxy type is internal to another assembly and cannot be directly referenced, the generated interceptor now uses ProxyBase.TryCreateProxy for reflection-based activation.
    • This approach is NativeAOT-safe and allows the interceptor to work even with internal proxy classes.
    • The fallback only activates when direct access is not possible, minimizing runtime overhead for the common case.

Design Notes

The generator now:

  • Analyzes proxy accessibility for each Attach call at code generation time
  • Groups attach calls by accessibility (direct vs. requires reflection)
  • For directly accessible proxies: generates straightforward interceptor code
  • For inaccessible proxies: generates an interceptor that uses ProxyBase.TryCreateProxy to activate the proxy via reflection, with proper disposal of the temporary JsonRpc instance

This design maintains NativeAOT compatibility while supporting cross-assembly RPC contract scenarios without requiring all proxy classes to be public.

Testing

Added/updated regression tests verifying:

  • Interceptor generation with external, inaccessible proxy types
  • Proper reflection activation path and error handling
  • Both net472 and net8.0 frameworks

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 23, 2026 16:00

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves the NativeAOT interceptor pipeline for cross-assembly RPC contracts by explicitly requiring that any reused proxy type be accessible from the calling assembly. Instead of emitting interceptor code that can’t compile (due to an inaccessible proxy), the generator now fails fast with a new diagnostic and the documentation/tests are updated to reflect the required opt-in.

Changes:

  • Documented that contract assemblies referenced by NativeAOT interceptor-enabled apps must expose proxies via [assembly: ExportRpcContractProxies].
  • Added StreamJsonRpc0040 (error) and updated interceptor generation to report it when an external proxy mapping points to an inaccessible type.
  • Updated analyzer suppressor behavior and added/updated regression tests + expected generated outputs for the new error path.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
test/StreamJsonRpc.Analyzer.Tests/Resources/Interceptor_ExternalProxyInaccessible_DifferentProject/ContractsLib.IMyService.g.cs Adds expected generated output used by the new regression test scenario.
test/StreamJsonRpc.Analyzer.Tests/ProxyGeneratorTests.cs Adds a regression test asserting StreamJsonRpc0040 is produced for inaccessible external proxies in a referenced project.
src/StreamJsonRpc.Analyzers/Strings.resx Adds user-facing strings for the new StreamJsonRpc0040 diagnostic.
src/StreamJsonRpc.Analyzers/ProxyGenerator.cs Threads through invocation location + accessibility info and detects whether mapped proxy types are accessible from the calling assembly.
src/StreamJsonRpc.Analyzers/GeneratorModels/FullModel.cs Filters out inaccessible external proxies from interceptor/proxy generation and reports StreamJsonRpc0040 when interceptors are enabled.
src/StreamJsonRpc.Analyzers/GeneratorModels/AttachUse.cs Extends the attach-use model to carry invocation location + external proxy accessibility status.
src/StreamJsonRpc.Analyzers/AttachAOTDiagnosticSuppressor.cs Avoids suppressing AOT diagnostics when the attach call won’t be intercepted due to an inaccessible external proxy.
src/StreamJsonRpc.Analyzers/AnalyzerReleases.Unshipped.md Documents the new analyzer/generator diagnostic StreamJsonRpc0040.
docfx/docs/nativeAOT.md Documents the new requirement for proxy accessibility via [assembly: ExportRpcContractProxies] for cross-assembly interceptor generation.

Comment thread src/StreamJsonRpc.Analyzers/Strings.resx Outdated
AArnott and others added 2 commits June 23, 2026 10:12
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 23, 2026 16:21

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Comment thread src/StreamJsonRpc.Analyzers/Strings.resx Outdated
@AArnott AArnott added this to the v2.26 milestone Jun 23, 2026
@AArnott AArnott changed the title Clarify NativeAOT proxy requirements Fix #1411 - NativeAOT proxy accessibility and interceptor fallback Jun 23, 2026
With the reflection-based fallback, ExportRpcContractProxies is no longer required.
It is now presented as an optional optimization to avoid reflection overhead when
constructing proxies from separate assemblies.
Copilot AI review requested due to automatic review settings June 23, 2026 17:38

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Comment thread src/StreamJsonRpc.Analyzers/GeneratorModels/InterceptionModel.cs Outdated
Comment thread src/StreamJsonRpc.Analyzers/GeneratorModels/AttachUse.cs Outdated
Comment thread docfx/docs/nativeAOT.md
Improve the interceptor reflection fallback exception text with NativeAOT documentation guidance, remove unused InvocationLocation from attach analysis state, and clarify docs that inaccessible external proxies use reflection fallback (with optional export for faster direct construction).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@AArnott AArnott enabled auto-merge (squash) June 23, 2026 18:46
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.

2 participants