Skip to content

[Blazor] Generate JS-invokable dispatch metadata - #68296

Open
javiercn wants to merge 12 commits into
javiercn-aot-stack-1-stjfrom
javiercn-aot-stack-2-jsinterop
Open

[Blazor] Generate JS-invokable dispatch metadata#68296
javiercn wants to merge 12 commits into
javiercn-aot-stack-1-stjfrom
javiercn-aot-stack-2-jsinterop

Conversation

@javiercn

@javiercn javiercn commented Aug 8, 2026

Copy link
Copy Markdown
Member

Overview

This is stack layer 2 of 6 for #68332, depends on #68295, and targets javiercn-aot-stack-1-stj; the review range is the stacked PR only (9ca33ec5b0..e76b510be8, 47 files / 12 commits). It makes JS-to-.NET dispatch generated-first and removes runtime reconstruction of outbound result types while preserving reflection as the default compatibility resolver. Component/bindable metadata, framework component providers, strict switches, and E2E proof remain in later stack layers.

Design

The public extension point is an executable descriptor rather than a public resolver abstraction. It contains both protocol keys and the entire typed invocation operation, so generated code—not DotNetDispatcher—owns argument deserialization, method invocation, awaiting, and result serialization.

// src/JSInterop/Microsoft.JSInterop/src/Infrastructure/JSInvokableMethodDescriptor.cs
// Static calls key by (AssemblyName, Identifier); instance calls key by (TargetType, Identifier).
// IsStatic prevents one descriptor from being accidentally indexed under both protocols.
[Experimental("ASPNETCORE9004", UrlFormat = "https://aka.ms/aspnet/analyzer/{0}")]
public sealed class JSInvokableMethodDescriptor
{
    public required string AssemblyName { get; init; }
    public required Type TargetType { get; init; }
    public required string Identifier { get; init; }
    public required bool IsStatic { get; init; }
    public string? MethodKey { get; init; } // stable contribution identity across registered contexts
    public JSInvokableMethodKind Kind { get; init; } // Method / Override / OverrideBlocker
    public required Func<object?, string, JsonSerializerOptions, ValueTask<string?>> Invoke { get; init; }
}

Two existing experimental owners expose flat descriptor lists; all resolver composition remains internal:

// src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs
// null preserves the behavior of every existing runtime: reflection remains available by default.
[Experimental("ASPNETCORE9004", UrlFormat = "https://aka.ms/aspnet/analyzer/{0}")]
protected internal virtual IReadOnlyList<JSInvokableMethodDescriptor>? InvokableMethods => null;
// src/Components/Web/src/Metadata/RazorComponentsMetadataContext.cs
// PR1 already registers/enumerates contexts; this layer adds only the JS descriptor capability.
public abstract IReadOnlyList<JSInvokableMethodDescriptor> JSInvokableMethods { get; }
public abstract IJsonTypeInfoResolver? JsonTypeInfoResolver { get; }

The key design decisions are:

  • One dispatch contract, two implementations. Generated and reflected methods both resolve to JSInvokableMethodDescriptor; DotNetDispatcher never branches on implementation type.
  • Generated first, reflection last. Reflection is independently controlled by Microsoft.JSInterop.JSInvokableMethodResolution.IsReflectionEnabledByDefault, defaults to true, and is the only resolver requiring dynamic-code/trimming annotations.
  • Separate method and JSON resolution. The JS method resolver chooses an executable descriptor; the runtime's existing JsonSerializerOptions supplies contracts. Microsoft.JSInterop therefore gains no Components dependency.
  • Fail closed for inheritance. OverrideBlocker and type-coverage descriptors prevent generated lookup from incorrectly inheriting a base method through an undescribed override/new slot; an uncovered case misses and reaches reflection only when compatibility is enabled.
  • Late-bound framework ownership. Components.Web owns its callback signatures and callback-specific BrowserFile contracts; the application generator reaches that internal provider through an emitted UnsafeAccessor instead of copying framework details.
  • Shared MSTest-on-MTP execution. The generator tests now use the repository's merged MSTest/Microsoft.Testing.Platform infrastructure rather than retaining a PR-local xUnit v3 runner and argument translation.

Implementation

Outbound .NET-to-JS calls now retain TValue in the pending-call object. When the wire result returns, virtual dispatch lands in a generic body where TValue is still statically known; the deleted TaskGenericsUtil no longer needs to recover it from Type and construct generic helpers at run time.

// src/JSInterop/Microsoft.JSInterop/src/Infrastructure/PendingAsyncCall.cs
// InvokeAsync<Customer> stores PendingAsyncCall<Customer>; completion therefore requests the exact
// Customer JsonTypeInfo directly, then completes Task<Customer> without MakeGenericType/ChangeType.
internal sealed class PendingAsyncCall<TValue> : IPendingAsyncCall
{
    private readonly TaskCompletionSource<TValue> _completion = new();

    public void Complete(JSRuntime runtime, ref Utf8JsonReader reader)
    {
        var typeInfo = runtime.JsonSerializerOptions.GetTypeInfo(typeof(TValue));
        var value = (TValue?)JsonSerializer.Deserialize(ref reader, typeInfo);
        runtime.ByteArraysToBeRevived.Clear();
        _completion.SetResult(value!);
    }
}

For incoming JS-to-.NET calls, DotNetDispatcher now performs protocol work only: resolve the receiver, keep __Dispose outside method metadata, resolve one descriptor, invoke it, and normalize synchronous/asynchronous completion to ValueTask<string?>.

// src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs
// A static request carries AssemblyName; an instance request carries the runtime receiver type.
if (objectReference is null)
{
    methodInfo = new JSInvokableMethodInfo(callInfo.AssemblyName, null, methodIdentifier);
}
else
{
    if (string.Equals("__Dispose", methodIdentifier, StringComparison.Ordinal))
    {
        objectReference.Dispose(); // protocol pseudo-method cannot be shadowed by a descriptor
        return default;
    }

    methodInfo = new JSInvokableMethodInfo(null, objectReference.Value.GetType(), methodIdentifier);
}

var descriptor = jsRuntime.InvokableMethodResolver.Resolve(methodInfo);
try
{
    return descriptor.Invoke(objectReference?.Value, argsJson ?? "[]", jsRuntime.JsonSerializerOptions);
}
finally
{
    jsRuntime.ByteArraysToBeRevived.Clear();
}

The resolver factory establishes the compatibility boundary. A runtime with descriptors gets generated lookup first; reflection is appended only when its feature guard is enabled. The reflection resolver contains all scans, MethodInfo.Invoke, argument parsing, return adapters, caches, and their exact suppressions, so disabling it gives the linker one removable dynamic-code root.

// src/JSInterop/Microsoft.JSInterop/src/Infrastructure/JSInvokableMethodResolverFactory.cs
var resolvers = new List<IJSInvokableMethodResolver>();
if (runtime.InvokableMethods is { Count: > 0 } descriptors)
{
    resolvers.Add(new SourceGeneratedJSInvokableMethodResolver(descriptors));
}

if (JSInvokableMethodResolutionFeature.IsReflectionEnabledByDefault)
{
    resolvers.Add(new ReflectionJSInvokableMethodResolver());
}

return new CompositeJSInvokableMethodResolver(resolvers);

The application generator walks application/reference symbols, emits one descriptor per usable [JSInvokable] alias, adds blockers/coverage for inheritance, and emits typed delegates. The representative generated shape below is de-templatized from RazorComponentsMetadataGenerator.Emitter.cs; the concrete generic calls are what make the path statically analyzable.

// src/Components/Endpoints/gen/Emitters/RazorComponentsMetadataGenerator.Emitter.cs
// Representative emitted delegate for: [JSInvokable("save")] Task<Result> Save(Request request).
internal static async ValueTask<string?> Invoke_Save(
    object? target,
    string argsJson,
    JsonSerializerOptions options)
{
    using var document = JsonDocument.Parse(argsJson);
    var arguments = document.RootElement;
    ValidateArguments(arguments, "save", expectedCount: 1);

    Request? request = Read<Request>(arguments, "save", index: 0, options);
    Result result = await ((Handler)target!).Save(request!).ConfigureAwait(false);
    return Write<Result>(result, options);
}

RemoteJSRuntime snapshots registered contexts in registration order and exposes the flattened list through the runtime hook. Generated duplicate contributions with the same MethodKey keep the first registration; conflicting lookup keys still fail deterministically.

// src/Components/Server/src/Circuits/RemoteJSRuntime.cs
_invokableMethods = metadataContexts?
    .SelectMany(static context => context.JSInvokableMethods)
    .ToArray() ?? [];

protected override IReadOnlyList<JSInvokableMethodDescriptor>? InvokableMethods
    => _invokableMethods.Length == 0 ? null : _invokableMethods;

Framework-owned callbacks are the same descriptor equivalence class with one ownership delta: Components.Web supplies seven typed instance descriptors (four WebRenderer operations, two virtualization spacers, and InputFile.NotifyChange). BrowserFile metadata is emitted only by the owning Web build because the shared serializer source also compiles into WebAssembly, where the internal type is inaccessible.

// src/Components/Web/src/Internal/WebJSInteropSerializerContext.cs
#if !COMPONENTS_WEBASSEMBLY
[JsonSerializable(typeof(BrowserFile))]
[JsonSerializable(typeof(BrowserFile[]))]
#endif
internal sealed partial class WebJSInteropSerializerContext : JsonSerializerContext;

The generator test project follows the current Components testing stack: MSTest v4 on Microsoft.Testing.Platform, the repository Test target, and the standard non-quarantined category filter. The former custom xUnit v3 RunTests target and legacy-argument workaround are intentionally absent.

<!-- src/Components/Endpoints/test/Microsoft.AspNetCore.Components.Endpoints.Generators.Tests/Microsoft.AspNetCore.Components.Endpoints.Generators.Tests.csproj -->
<PropertyGroup>
  <EnableMSTestRunner>true</EnableMSTestRunner>
  <TestRunnerName>Microsoft.Testing.Platform</TestRunnerName>
  <TestRunnerAdditionalArguments>--filter &quot;TestCategory!=Quarantined&quot;</TestRunnerAdditionalArguments>
</PropertyGroup>
<ItemGroup>
  <Reference Include="MSTest.TestFramework" />
  <Reference Include="Microsoft.Testing.Extensions.TrxReport" />
  <PackageReference Include="MSTest.TestAdapter" Version="$(MSTestVersion)" />
</ItemGroup>

Coverage is organized by behavioral equivalence class rather than by file:

Equivalence class Representative behavior Covered deltas
Lookup keys Static (assembly, alias) Instance (type, alias), base-type walk, constructed generics, duplicate aliases/contributions
Inheritance Annotated override wins Unannotated override blocker, new-slot conflict, covered/uncovered derived type, reflection fallback
Completion Synchronous value void/null, pending sync rejection, Task, Task<T>, derived task, ValueTask, ValueTask<T>, failures
Wire validation Exact typed argument array invalid root, too few/many values, malformed JSON, incorrect DotNetObjectReference<T> shape
Resolution source Application-generated descriptor reflection-only runtime, partial generated miss, multiple contexts, framework callback provider
Object lifetime Normal instance invocation __Dispose bypass, pending byte-array cleanup, cancellation/failure of outbound calls
Framework JSON Renderer primitives/JsonElement BrowserFile and BrowserFile[] in Web; inaccessible shared WebAssembly build excluded
Test execution Repository Test target direct MSTest/MTP discovery and category filtering

Outcome

Validation class Result
Full Microsoft.JSInterop.Tests runtime matrix 211 passed, 0 failed/skipped
Generator tests through the repository Test target succeeded with 0 warnings/errors
Direct MSTest/MTP generator execution 10 passed, 0 failed/skipped
RemoteJSRuntimeMetadataTest context ordering/fallback 5 passed, 0 failed/skipped
WebRendererJsonResolverTest resolver order + BrowserFile contracts 4 passed, 0 failed/skipped
Linker gates WasmLinkerTest and LinkabilityChecker succeeded with 0 warnings/errors before this testing-only restack; all 11 product/test patches retain identical stable patch IDs

Default/JIT compatibility remains unchanged because method reflection is enabled unless explicitly disabled. Strict reflection-off Native AOT and end-to-end feature proof are intentionally deferred to the final stack layer.

javiercn and others added 12 commits August 11, 2026 11:58
Keep each outbound call's generic result type alive until completion so result deserialization no longer reconstructs it from runtime Type values.

Behavior: preserved
State: builds; parity coverage follows in the next commit
Review hint: the pending-call abstraction replaces TaskCompletionSource reflection without changing completion, cancellation, or failure flow.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Exercise typed completion for nullable outbound JS results in addition to the existing object, array, failure, and cancellation coverage.

Behavior: preserved
State: complete for typed pending-call completion

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Move method scanning, argument parsing, invocation, async adaptation, and caches behind an internal reflection resolver so DotNetDispatcher only coordinates wire dispatch.

Behavior: preserved
State: builds; generated resolution follows in the next commit
Review hint: the resolver contains the legacy reflection implementation and its suppressions; DotNetDispatcher now handles only protocol flow.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Expose executable JS-invokable descriptors and compose generated lookup ahead of an independently switchable reflection compatibility resolver.

Behavior: changed: runtimes can supply generated JS-invokable descriptors that take precedence over reflection
State: builds; dispatch matrix coverage follows in the next commit
Review hint: SourceGeneratedJSInvokableMethodResolver defines precedence and inheritance; the factory keeps reflection enabled by default behind its own linker-recognized switch.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Cover manual generated descriptors, generated-first precedence, inheritance and alias rules, misses, reflection parity, async completion, and object-reference disposal.

Behavior: preserved
State: complete for runtime descriptor resolution

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Add the JS-only metadata generator foundation, extend the experimental application context with JSInvokableMethods, and let RemoteJSRuntime flatten registered contexts into generated-first dispatch.

Behavior: changed: registered application metadata contexts now contribute generated JS-invokable methods
State: builds; generator and runtime integration tests follow in the next commit
Review hint: this layer emits only JsonTypeInfoResolver and JSInvokableMethods; component and bindable collection/emission are intentionally absent.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Exercise generated JS descriptors across method shapes, aliases, inheritance, and serialization, and verify registered contexts reach RemoteJSRuntime in registration order.

Behavior: preserved
State: complete for application-generated JS metadata
Review hint: the generator test project contains only the JS slice; component and bindable generator suites remain deferred.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Discover the Web assembly's built-in JS callback provider, spread its descriptors through generated contexts via UnsafeAccessor, and add callback-specific BrowserFile contracts only to the owning Web build.

Behavior: changed: generated application contexts now include framework-owned Web callbacks
State: builds; provider coverage follows in the next commit
Review hint: the generator only knows the provider assembly and factory shape; callback signatures and JSON contracts remain owned by Components.Web.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Verify generated contexts reach the Components.Web callback provider through the emitted UnsafeAccessor and include the complete built-in callback descriptor set.

Behavior: preserved
State: complete for framework callback metadata

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Delete the task-result generic reconstruction helper and broad linker warning XML now that pending calls retain result types and reflection suppressions sit beside the exact legacy operations.

Behavior: preserved
State: complete
Review hint: both deleted artifacts have no remaining callers or live suppression targets.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Prove the owning Components.Web serializer context emits BrowserFile and BrowserFile[] metadata and that the built-in NotifyChange descriptor consumes the array contract.

Behavior: preserved
State: complete
Review hint: this test guards the Web-only side of the existing COMPONENTS_WEBASSEMBLY conditional.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Migrate the generator tests from the PR-local xUnit v3 executable to the repository's current MSTest-on-MTP pattern, so the standard Test target owns discovery, filtering, and exit handling.

Behavior: preserved
State: complete
Review hint: test scenarios and generator harness behavior are unchanged; only framework attributes, assertions, and project runner wiring move to MSTest.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@javiercn
javiercn force-pushed the javiercn-aot-stack-2-jsinterop branch from f292100 to e76b510 Compare August 11, 2026 10:15
@javiercn
javiercn marked this pull request as ready for review August 11, 2026 14:07
@javiercn
javiercn requested a review from a team as a code owner August 11, 2026 14:07
Copilot AI lite review requested due to automatic review settings August 11, 2026 14:07

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.

Pull request overview

This PR introduces a source-generated-first dispatch path for JS-to-.NET [JSInvokable] calls by adding a JSInvokableMethodDescriptor contract and resolver chain, while keeping reflection as the default compatibility fallback. It also removes runtime reconstruction of outbound .NET -> JS result types by keeping the generic result type alive in pending-call objects.

Changes:

  • Add JSInvokableMethodDescriptor (+ resolver infrastructure) and refactor DotNetDispatcher to resolve + execute descriptors, falling back to reflection when enabled.
  • Replace runtime generic result recovery for .NET -> JS pending calls with PendingAsyncCall<TValue> and an IPendingAsyncCall table.
  • Add Components-side metadata context surface for JS descriptors, framework-provided built-in descriptors, and generator + tests for descriptor emission.

Reviewed changes

Copilot reviewed 47 out of 47 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Tests.csproj Suppress experimental warning in tests.
src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetDispatcherTest.cs Update argument parsing tests + add nullable result test.
src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetDispatcherDescriptorTest.cs New tests for descriptor resolution, ordering, fallback, and switches.
src/JSInterop/Microsoft.JSInterop/src/PublicAPI.Unshipped.txt Record new public API surface for descriptors + JSRuntime.InvokableMethods.
src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.WarningSuppressions.xml Remove linker suppression file tied to old reflection implementation.
src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj Suppress experimental warning in product project.
src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs Switch pending-call storage to typed pending calls + add invokable resolver hook.
src/JSInterop/Microsoft.JSInterop/src/Infrastructure/TaskGenericsUtil.cs Remove runtime generic task/TCS helper.
src/JSInterop/Microsoft.JSInterop/src/Infrastructure/SourceGeneratedJSInvokableMethodResolver.cs New resolver for source-generated descriptor lookup + inheritance coverage logic.
src/JSInterop/Microsoft.JSInterop/src/Infrastructure/ReflectionJSInvokableMethodResolver.cs New reflection-based compatibility resolver returning executable descriptors.
src/JSInterop/Microsoft.JSInterop/src/Infrastructure/PendingAsyncCall.cs New PendingAsyncCall<TValue> implementing IPendingAsyncCall.
src/JSInterop/Microsoft.JSInterop/src/Infrastructure/JSInvokableMethodResolverFactory.cs New resolver factory + reflection-enable feature switch.
src/JSInterop/Microsoft.JSInterop/src/Infrastructure/JSInvokableMethodInfo.cs New method identity record used for resolution keys.
src/JSInterop/Microsoft.JSInterop/src/Infrastructure/JSInvokableMethodDescriptor.cs New public experimental dispatch descriptor contract + method kind enum.
src/JSInterop/Microsoft.JSInterop/src/Infrastructure/IPendingAsyncCall.cs New interface for completing pending .NET -> JS calls without reflection.
src/JSInterop/Microsoft.JSInterop/src/Infrastructure/IJSInvokableMethodResolver.cs New internal resolver abstraction used by the composite chain.
src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs Refactor dispatcher to resolve + invoke descriptors; unify sync/async completion.
src/JSInterop/Microsoft.JSInterop/src/Infrastructure/CompositeJSInvokableMethodResolver.cs New composite resolver that tries generated then reflection.
src/Components/WebAssembly/WebAssembly/test/Services/WebAssemblyHostSerializationContextTest.cs Update test contexts for new metadata context abstract member.
src/Components/Web/test/Rendering/WebRendererJsonResolverTest.cs Add coverage for framework callback contracts including BrowserFile shapes.
src/Components/Web/test/Metadata/ComponentMetadataServiceCollectionExtensionsTest.cs Update test contexts for new metadata context abstract member.
src/Components/Web/src/PublicAPI.Unshipped.txt Record new RazorComponentsMetadataContext.JSInvokableMethods API.
src/Components/Web/src/Metadata/RazorComponentsMetadataContext.cs Add abstract JSInvokableMethods to metadata contexts (experimental surface).
src/Components/Web/src/Metadata/BuiltInJSInvokableMethodDescriptors.cs Add built-in framework callback descriptors (WebRenderer, Virtualize, InputFile).
src/Components/Web/src/Internal/WebJSInteropSerializerContext.cs Add BrowserFile JSON contracts for non-WASM build.
src/Components/Server/test/ProtectedBrowserStorageSerializerOptionsTest.cs Update test context to satisfy new abstract metadata member.
src/Components/Server/test/Microsoft.AspNetCore.Components.Server.Tests.csproj Add RemoteExecutor reference for new tests.
src/Components/Server/test/Circuits/RemoteJSRuntimeMetadataTest.cs New tests for context ordering, reflection fallback, and switch behavior.
src/Components/Server/src/Circuits/RemoteJSRuntime.cs Snapshot metadata contexts into runtime InvokableMethods list for dispatch.
src/Components/Endpoints/test/Microsoft.AspNetCore.Components.Endpoints.Tests.csproj Suppress experimental warning + exclude generator tests from this project.
src/Components/Endpoints/test/Microsoft.AspNetCore.Components.Endpoints.Generators.Tests/RazorComponentsMetadataGeneratorTestBase.cs New Roslyn harness for generator tests + compilation/load helpers.
src/Components/Endpoints/test/Microsoft.AspNetCore.Components.Endpoints.Generators.Tests/RazorComponentsMetadataGeneratorJSInteropTests.cs New generator tests covering descriptor emission, overrides, ordering, built-ins.
src/Components/Endpoints/test/Microsoft.AspNetCore.Components.Endpoints.Generators.Tests/Microsoft.AspNetCore.Components.Endpoints.Generators.Tests.csproj New MSTest/MTP-based generator test project.
src/Components/Endpoints/test/DependencyInjection/ComponentJsonMetadataIsolationTest.cs Update test contexts for new metadata context abstract member.
src/Components/Endpoints/gen/WellKnownTypes.cs Add well-known symbols for generator analysis (JSInvokable, Task/ValueTask, etc.).
src/Components/Endpoints/gen/TypeAccessibility.cs Add type/nameability checks for generator emission boundaries.
src/Components/Endpoints/gen/SymbolHelpers.cs Add symbol helpers (qualified names, type enumeration, partial checks, etc.).
src/Components/Endpoints/gen/RazorComponentsMetadataGenerator.JSInterop.cs Add generator collection logic for JS invokables and inheritance metadata.
src/Components/Endpoints/gen/RazorComponentsMetadataGenerator.cs Add incremental generator entry + model building and context resolution.
src/Components/Endpoints/gen/Models/MetadataContextModel.cs Add generator models + comparers for incremental caching.
src/Components/Endpoints/gen/Microsoft.AspNetCore.Components.Endpoints.Generators.csproj New analyzer project for the metadata generator.
src/Components/Endpoints/gen/Emitters/RazorComponentsMetadataGenerator.Emitter.cs Emit metadata context partial implementation + descriptors + invocation bodies.
src/Components/Endpoints/gen/DiagnosticDescriptors.cs Add generator diagnostic for non-partial context declarations.
src/Components/Endpoints/gen/CodeWriter.cs Add minimal writer used for readable generated output.
src/Components/ComponentsNoDeps.slnf Include generator project + tests in solution filter.
src/Components/Components.slnf Include generator project + tests in solution filter.
AspNetCore.slnx Include generator project + tests in main solution map.
Suppressed comments (1)

src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs:338

  • When a JS->.NET completion arrives for an unknown taskId (e.g., the .NET side timed out/canceled and removed it), any received byte-array payloads remain in ByteArraysToBeRevived until another transfer starts. This can retain large buffers unnecessarily and can also interfere with subsequent calls if no new byte-array transfer begins. Clearing ByteArraysToBeRevived before returning avoids leaking/bleeding data from late completions.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +99 to +102
if (invoked && !invocationResult.IsCompletedSuccessfully)
{
_ = invocationResult.AsTask();
}
Comment on lines +71 to +74
if (actualCount > parameterTypes.Length)
{
throw new JsonException($"Unexpected JSON token {GetTokenName(argumentsElement[parameterTypes.Length])}. Ensure that the call to `{methodIdentifier}' is supplied with exactly '{parameterTypes.Length}' parameters.");
}
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