Skip to content

Generate AOT-compatible C# primitive responses - #11931

Open
Jorge Rangel (jorgerangel-msft) with Copilot wants to merge 49 commits into
mainfrom
copilot/generate-aot-compatible-code
Open

Jorge Rangel (jorgerangel-msft) with Copilot wants to merge 49 commits into
mainfrom
copilot/generate-aot-compatible-code

Conversation

Copilot AI commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

C# convenience methods deserialize primitive responses with BinaryData.ToObjectFromJson<T>(), which relies on reflection and is not Native AOT-compatible.

  • Deserialization: Replace reflection-based conversion with typed JsonElement reads for primitive and enum responses, including sync and async methods.
  • Compatibility: Preserve nullable values, UTF-8 BOM support, and TimeSpan formatting. Leave plain-text, raw binary, model, and collection handling unchanged.

Generated int32 response conversion:

using Stream stream = result.GetRawResponse().Content.ToStream();
using JsonDocument document = JsonDocument.Parse(stream);
return ClientResult.FromValue(document.RootElement.GetInt32(), result.GetRawResponse());

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
1 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI linked an issue Sep 10, 2026 that may be closed by this pull request
@jorgerangel-msft Jorge Rangel (jorgerangel-msft) changed the title [WIP] Generate AOT compatible code for GetSandboxesCount operation [WIP] Generate AOT compatible code for operations that return a primitive as resp body Sep 10, 2026
…ection

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
@microsoft-github-policy-service microsoft-github-policy-service Bot added emitter:client:csharp Issue for the C# client emitter: @typespec/http-client-csharp labels Sep 10, 2026
Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Copilot AI changed the title [WIP] Generate AOT compatible code for operations that return a primitive as resp body Generate AOT-compatible C# primitive responses Sep 10, 2026
@pkg-pr-new

pkg-pr-new Bot commented Sep 10, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@typespec/http-client-csharp@11931

commit: 9a84ed9

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The implementation covers the intended response types and includes focused generation and runtime tests.

Pull request overview

Updates the C# client generator to deserialize primitive and enum responses without reflection, enabling Native AOT compatibility while preserving BOM and nullable handling.

Changes:

  • Uses typed JsonElement reads for primitive and enum responses.
  • Preserves plain-text behavior and constant-format TimeSpan conversion.
  • Adds generation baselines and runtime BOM coverage for sync/async enum responses.
File summaries
File Description
TestProjects/Local/Sample-TypeSpec/src/Generated/SampleTypeSpecClient.cs Updates generated enum deserialization.
TestProjects/Local.Tests/ExtensibleEnumTests.cs Tests sync/async BOM handling.
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(TimeSpanNullable).cs Adds nullable TimeSpan baseline.
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(TimeSpan).cs Updates TimeSpan baseline.
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(String).cs Updates string baseline.
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Single).cs Updates float baseline.
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Int64).cs Updates long baseline.
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Int32NullableAsync).cs Adds async nullable integer baseline.
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Int32Nullable).cs Adds nullable integer baseline.
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Int32Async).cs Adds async integer baseline.
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Int32).cs Updates integer baseline.
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Double).cs Updates double baseline.
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Decimal).cs Adds decimal baseline.
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(DateTimeOffset).cs Updates date-time baseline.
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(BooleanNullable).cs Adds nullable boolean baseline.
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Boolean).cs Updates boolean baseline.
test/Providers/TestData/ScmMethodProviderCollectionTests/PlainTextReturnTypeMethods.cs Verifies plain-text handling.
test/Providers/TestData/ScmMethodProviderCollectionTests/EnumReturnTypeMethods(True,True,True).cs Covers nullable extensible string enum.
test/Providers/TestData/ScmMethodProviderCollectionTests/EnumReturnTypeMethods(True,True,False).cs Covers extensible string enum.
test/Providers/TestData/ScmMethodProviderCollectionTests/EnumReturnTypeMethods(True,False,False).cs Covers fixed string enum.
test/Providers/TestData/ScmMethodProviderCollectionTests/EnumReturnTypeMethods(False,True,False).cs Covers extensible integer enum.
test/Providers/TestData/ScmMethodProviderCollectionTests/EnumReturnTypeMethods(False,False,True).cs Covers nullable fixed integer enum.
test/Providers/TestData/ScmMethodProviderCollectionTests/EnumReturnTypeMethods(False,False,False).cs Covers fixed integer enum.
test/Providers/ScmMethodProviderCollectionTests.cs Expands scalar and enum generation tests.
test/Providers/ClientProviders/TestData/ClientProviderTests/BackCompatibility_NewRequiredParameterDoesNotAddBackCompatOverload.cs Refreshes generated baseline.
test/Providers/ClientProviders/TestData/ClientProviderTests/BackCompatibility_NewOptionalParameterWithReservedName.cs Refreshes generated baseline.
test/Providers/ClientProviders/TestData/ClientProviderTests/BackCompatibility_NewOptionalNonBodyParameterAddedWithPathAndHeaderParameters.cs Refreshes generated baseline.
test/Providers/ClientProviders/TestData/ClientProviderTests/BackCompatibility_NewOptionalNonBodyParameterAddedWithModelBody.cs Refreshes generated baseline.
test/Providers/ClientProviders/TestData/ClientProviderTests/BackCompatibility_NewOptionalNonBodyParameterAdded.cs Refreshes generated baseline.
test/Providers/ClientProviders/TestData/ClientProviderTests/BackCompatibility_NewOptionalBodyParameterDoesNotAddBackCompatOverload.cs Refreshes generated baseline.
test/Providers/ClientProviders/TestData/ClientProviderTests/BackCompatibility_MultipleNewOptionalNonBodyParametersAdded.cs Refreshes generated baseline.
test/Providers/ClientProviders/TestData/ClientProviderTests/BackCompatibility_ConvenienceMethodParamOrderChanged(Sync).cs Adds sync body baseline.
test/Providers/ClientProviders/TestData/ClientProviderTests/BackCompatibility_ConvenienceMethodParamOrderChanged(Async).cs Adds async body baseline.
test/Providers/ClientProviders/TestData/ClientProviderTests/BackCompatibility_BothMethodsParamOrderChanged(Sync).cs Adds sync compatibility baseline.
test/Providers/ClientProviders/TestData/ClientProviderTests/BackCompatibility_BothMethodsParamOrderChanged(Async).cs Adds async compatibility baseline.
test/Providers/ClientProviders/ClientProviderTests.cs Moves body assertions to baseline files.
src/Providers/ScmMethodProviderCollection.cs Implements typed response deserialization.
Review details
  • Files reviewed: 36/37 changed files
  • Comments generated: 0
  • Review effort level: Balanced

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot there's spelling errors we should address by simply changing the test to avoid the warning / error :

 cspell --no-progress .
packages/http-client-csharp/generator/TestProjects/Local.Tests/ExtensibleEnumTests.cs:25:62 - Unknown word (FEFF)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The implementation is scoped, preserves documented compatibility behavior, and has representative generation and runtime coverage.

Review details
  • Files reviewed: 38/39 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>

Copilot AI commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

Copilot there's spelling errors we should address by simply changing the test to avoid the warning / error :

Fixed in 5c0d5d6 by adding a // cspell:ignore FEFF comment at the top of the file. Verified npx cspell --no-progress now reports 0 issues for this file.

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

BOM-prefixed JSON still fails parsing, and custom duration encodings can now crash generation.

Get a fresh assessment by requesting another Copilot review.

Review details
  • Files reviewed: 85/87 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Copilot AI and others added 2 commits September 14, 2026 16:02
Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

BOM handling misses some primitive types, and several generated and baseline outputs remain stale.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (1)

packages/http-client-csharp/generator/TestProjects/Local.Tests/ExtensibleEnumTests.cs:48

  • This BOM test currently calls the checked-in SampleTypeSpecClient, whose changed GetInt32Value methods still use JsonDocument.Parse(stream) (SampleTypeSpecClient.cs:1147-1160) and therefore reject this BOM-prefixed payload. Regenerate the Local sample after the new TrimStart('\uFEFF') generator path so the test exercises—and passes with—the implementation under review.
  • Files reviewed: 86/89 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Valid custom duration encodings still cause a generation-time exception instead of a diagnostic and fallback.

Review details

Suppressed comments (1)

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs:988

  • Custom DurationKnownEncoding values are valid inputs (Microsoft.TypeSpec.Generator.Input/test/DurationKnownEncodingTests.cs:34-39), so this still crashes generation for a supported code model. Follow the existing unsupported-serialization warning-and-fallback pattern in MrwSerializationTypeDefinition.cs:2064-2071 and update the test that currently expects InvalidOperationException.
                _ => content.As<string>().ParseTimeSpan(Literal(format.ToFormatSpecifier() ?? throw new InvalidOperationException($"Unsupported duration serialization format: {format}")))
  • Files reviewed: 86/89 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

…g on unsupported duration encodings

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>

Copilot AI commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

Jorge Rangel (@jorgerangel-msft) Unfortunately I hit an unexpected error while processing your comment. I've automatically reported this to GitHub.

You can ask me to try again later by mentioning me in a new comment.

If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: 047a492b-da0e-4683-9a49-834efaf99444

Sorry for the inconvenience!

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Some JSON primitive types still bypass BOM-safe parsing, and the new diagnostic is not fully asserted.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (1)

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs:305

  • JSON Uri, byte, and sbyte responses still take this fallback because TypeFactory.cs:157-167 produces those CLR types but IsConvertibleFromBinaryData excludes them. The fallback at lines 310-311 parses BinaryData without the BOM stripping used at lines 650-657, so a BOM-prefixed response still throws; route these primitive types through the same BOM-safe document setup and cover one in the runtime matrix.
                    IsConvertibleFromBinaryData(responseBodyType)
                        || (IsPlainTextParsableType(responseBodyType) && HasOnlyPlainTextContentType())
  • Files reviewed: 86/89 changed files
  • Comments generated: 1
  • Review effort level: Balanced

…n-BinaryData-convertible types

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

The unsupported-duration fallback test does not assert the newly required diagnostic code or severity.

Review details

Suppressed comments (1)

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmMethodProviderCollectionTests.cs:1793

  • This fallback test only compares generated code, so it still passes if the newly required unsupported-serialization diagnostic is removed or its code/severity regresses. Capture the emitter diagnostic and assert it alongside the fallback baseline so the new diagnostic path is actually covered.
            // An unsupported/custom duration encoding has no known format specifier, so generation reports a
            // diagnostic and falls back to the constant ("c") format instead of throwing.
            Assert.AreEqual(Helpers.GetExpectedFromFile("Constant", method: "PlainTextDurationReturnTypeMethods"), writer.ToString(false));
  • Files reviewed: 86/89 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The implementation covers the affected conversion paths with broad baselines and targeted runtime BOM tests.

Review details
  • Files reviewed: 89/92 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Supported numeric duration encodings currently emit a spurious unsupported-serialization diagnostic.

Get a fresh assessment by requesting another Copilot review.

Review details
  • Files reviewed: 89/92 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Plain-text media types are matched too strictly, and the primary JSON primitive path lacks runtime coverage.

Get a fresh assessment by requesting another Copilot review.

Review details
  • Files reviewed: 87/88 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Copilot AI and others added 2 commits September 14, 2026 17:55
Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Mixed JSON/plain-text responses can select JSON parsing even when the server legally returns raw text.

Review details

Suppressed comments (1)

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs:1061

  • Mixed application/json/text/plain responses are always sent down the JSON path, but RestClientProvider.cs:1794-1808 advertises every declared media type, so a server may legally return raw text; a string or enum body such as Monday will then reach JsonDocument.Parse and throw. Choose the conversion from the actual response Content-Type (or request only one representation), and cover the mixed case with a non-JSON-compatible string/enum value.
        private bool HasOnlyPlainTextContentType()
        {
            var contentTypes = ServiceMethod.Operation.Responses
                .Where(r => r.IsErrorResponse is false)
                .SelectMany(r => r.ContentTypes);
            return contentTypes.Any() && contentTypes.All(contentType => contentType.Contains("text/plain", StringComparison.OrdinalIgnoreCase));
  • Files reviewed: 87/88 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@JoshLove-msft JoshLove-msft left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Re-reviewed the updates through 9b2f4e2. The earlier duration wire-encoding finding is addressed, and nullable plain-text null handling now accepts surrounding whitespace. No new actionable findings; existing review threads and explicitly deferred items were not duplicated.

--generated by Copilot

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Media-type detection can incorrectly route JSON responses through raw-text parsing.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs:654

  • The PR’s documented generated shape parses Content.ToStream(), but this instead transcodes every JSON response to a UTF-16 string before parsing. JsonDocument.Parse(Stream) accepts a leading UTF-8 BOM directly, so generate and dispose a content stream here (and in the fallback path) to avoid the extra full-body allocation.
  • Files reviewed: 87/88 changed files
  • Comments generated: 1
  • Review effort level: Balanced

@JoshLove-msft

Copy link
Copy Markdown
Contributor

Copilot Please remove the BOM-specific handling (TrimStart('\uFEFF')) throughout this PR. We do not expect BOM-prefixed HTTP response bodies and do not want to preserve that compatibility behavior. Parse JSON directly from BinaryData without the intermediate string conversion, and leave plain-text response content unchanged.

Remove the BOM-specific test cases and comments while retaining the normal primitive, enum, nullable, and sync/async coverage. Update the affected baselines and regenerate the test libraries. Please do not reintroduce BOM handling in response to earlier review comments; this is an intentional decision.

--generated by Copilot

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

String transcoding weakens malformed UTF-8 validation in both new JSON parsing paths.

Review details

Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs:310

  • This fallback also transcodes the raw bytes through a replacement-fallback UTF-8 string before JSON parsing, which can silently accept malformed response bytes. Use the stream overload so BOM handling does not weaken JSON's UTF-8 validation.

This issue also appears on line 650 of the same file.

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs:654

  • Decoding the response through BinaryData.ToString() replaces malformed UTF-8 with U+FFFD, so an invalid JSON string can now deserialize successfully instead of raising a JSON error. Parse Content.ToStream() directly instead; JsonDocument.Parse(Stream) accepts a leading UTF-8 BOM while preserving strict UTF-8 validation.
                var contentExpression = data.InvokeToString().Invoke(nameof(string.TrimStart), Literal('\uFEFF')).As<string>();
                var statements = new MethodBodyStatement[]
                {
                    Declare("responseContent", typeof(string), contentExpression, out var responseContent),
                    UsingDeclare("document", JsonDocumentSnippets.Parse(responseContent), out var document)
  • Files reviewed: 87/88 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>

Copilot AI commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

Copilot Please remove the BOM-specific handling (TrimStart('\uFEFF')) throughout this PR. We do not expect BOM-prefixed HTTP response bodies and do not want to preserve that compatibility behavior....

Addressed in 9a84ed9. JSON response conversion now parses BinaryData directly, plain-text content is unchanged, BOM-specific handling and cases are removed, and generated libraries/baselines were regenerated.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Both JSON and plain-text conversion paths currently break the stated UTF-8 BOM compatibility requirement.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (1)

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs:647

  • Parsing BinaryData directly selects the ReadOnlyMemory<byte> JSON overload, which explicitly rejects a UTF-8 BOM, so BOM-prefixed primitive responses fail despite the PR's compatibility requirement. Generate a stream and parse that overload instead, and cover this with a BOM regression test.
                    UsingDeclare("document", data.Parse(), out var document)
  • Files reviewed: 87/88 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

emitter:client:csharp Issue for the C# client emitter: @typespec/http-client-csharp

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Generate AOT compatible code when an operation returns a primitive

4 participants