Skip to content

Commit 03bdce3

Browse files
committed
Use ad hoc API for wasm
1 parent 35c4f33 commit 03bdce3

File tree

16 files changed

+192
-324
lines changed

16 files changed

+192
-324
lines changed

src/Components/Server/src/Circuits/RemoteJSRuntime.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,10 @@ public RemoteJSRuntime(
4848
DefaultAsyncTimeout = _options.JSInteropDefaultCallTimeout;
4949
ElementReferenceContext = new WebElementReferenceContext(this);
5050
JsonSerializerOptions.Converters.Add(new ElementReferenceJsonConverter(ElementReferenceContext));
51-
JsonSerializerOptions.MakeReadOnly(populateMissingResolver: JsonSerializer.IsReflectionEnabledByDefault);
5251
}
5352

53+
public JsonSerializerOptions ReadJsonSerializerOptions() => JsonSerializerOptions;
54+
5455
internal void Initialize(CircuitClientProxy clientProxy)
5556
{
5657
_clientProxy = clientProxy ?? throw new ArgumentNullException(nameof(clientProxy));

src/Components/Server/src/Circuits/RemoteRenderer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public RemoteRenderer(
4444
ILogger logger,
4545
RemoteJSRuntime jsRuntime,
4646
CircuitJSComponentInterop jsComponentInterop)
47-
: base(serviceProvider, loggerFactory, jsRuntime.JsonSerializerOptions, jsComponentInterop)
47+
: base(serviceProvider, loggerFactory, jsRuntime.ReadJsonSerializerOptions(), jsComponentInterop)
4848
{
4949
_client = client;
5050
_options = options;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.ComponentModel;
5+
using System.Diagnostics.CodeAnalysis;
6+
using Microsoft.JSInterop;
7+
8+
namespace Microsoft.AspNetCore.Components.Web.Internal;
9+
10+
/// <summary>
11+
/// For internal framework use only.
12+
/// </summary>
13+
[EditorBrowsable(EditorBrowsableState.Never)]
14+
public interface IInternalWebJSInProcessRuntime
15+
{
16+
/// <summary>
17+
/// For internal framework use only.
18+
/// </summary>
19+
string InvokeJS(string identifier, [StringSyntax(StringSyntaxAttribute.Json)] string? argsJson, JSCallResultType resultType, long targetInstanceId);
20+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
#nullable enable
2+
Microsoft.AspNetCore.Components.Web.Internal.IInternalWebJSInProcessRuntime
3+
Microsoft.AspNetCore.Components.Web.Internal.IInternalWebJSInProcessRuntime.InvokeJS(string! identifier, string? argsJson, Microsoft.JSInterop.JSCallResultType resultType, long targetInstanceId) -> string!
24
Microsoft.AspNetCore.Components.Web.KeyboardEventArgs.IsComposing.get -> bool
35
Microsoft.AspNetCore.Components.Web.KeyboardEventArgs.IsComposing.set -> void

src/Components/Web/src/WebRenderer.cs

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Text.Json.Serialization;
77
using Microsoft.AspNetCore.Components.Web;
88
using Microsoft.AspNetCore.Components.Web.Infrastructure;
9+
using Microsoft.AspNetCore.Components.Web.Internal;
910
using Microsoft.Extensions.DependencyInjection;
1011
using Microsoft.Extensions.Logging;
1112
using Microsoft.JSInterop;
@@ -43,18 +44,7 @@ public WebRenderer(
4344
jsComponentInterop.AttachToRenderer(this);
4445

4546
var jsRuntime = serviceProvider.GetRequiredService<IJSRuntime>();
46-
var jsRuntimeJsonSerializerOptions = new JsonSerializerOptions(jsRuntime.JsonSerializerOptions);
47-
jsRuntimeJsonSerializerOptions.TypeInfoResolverChain.Clear();
48-
jsRuntimeJsonSerializerOptions.TypeInfoResolverChain.Add(WebRendererSerializerContext.Default);
49-
jsRuntimeJsonSerializerOptions.TypeInfoResolverChain.Add(JsonConverterFactoryTypeInfoResolver<DotNetObjectReference<WebRendererInteropMethods>>.Instance);
50-
51-
jsRuntime.InvokeVoidAsync(
52-
"Blazor._internal.attachWebRendererInterop",
53-
jsRuntimeJsonSerializerOptions,
54-
_rendererId,
55-
_interopMethodsReference,
56-
jsComponentInterop.Configuration.JSComponentParametersByIdentifier,
57-
jsComponentInterop.Configuration.JSComponentIdentifiersByInitializer).Preserve();
47+
AttachWebRendererInterop(jsRuntime, jsonOptions, jsComponentInterop);
5848
}
5949

6050
/// <summary>
@@ -111,6 +101,31 @@ protected override void Dispose(bool disposing)
111101
base.Dispose(disposing);
112102
}
113103

104+
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
105+
private void AttachWebRendererInterop(IJSRuntime jsRuntime, JsonSerializerOptions jsonOptions, JSComponentInterop jsComponentInterop)
106+
{
107+
object[] args = [
108+
_rendererId,
109+
_interopMethodsReference,
110+
jsComponentInterop.Configuration.JSComponentParametersByIdentifier,
111+
jsComponentInterop.Configuration.JSComponentIdentifiersByInitializer,
112+
];
113+
114+
if (jsRuntime is IInternalWebJSInProcessRuntime inProcessRuntime)
115+
{
116+
var newJsonOptions = new JsonSerializerOptions(jsonOptions);
117+
newJsonOptions.TypeInfoResolverChain.Clear();
118+
newJsonOptions.TypeInfoResolverChain.Add(WebRendererSerializerContext.Default);
119+
newJsonOptions.TypeInfoResolverChain.Add(JsonConverterFactoryTypeInfoResolver<DotNetObjectReference<WebRendererInteropMethods>>.Instance);
120+
var argsJson = JsonSerializer.Serialize(args, newJsonOptions);
121+
inProcessRuntime.InvokeJS("Blazor._internal.attachWebRendererInterop", argsJson, JSCallResultType.JSVoidResult, 0);
122+
}
123+
else
124+
{
125+
jsRuntime.InvokeVoidAsync("Blazor._internal.attachWebRendererInterop", args).Preserve();
126+
}
127+
}
128+
114129
/// <summary>
115130
/// A collection of JS invokable methods that the JS-side code can use when it needs to
116131
/// make calls in the context of a particular renderer. This object is never exposed to

src/Components/WebAssembly/WebAssembly/src/Rendering/WebAssemblyRenderer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ internal sealed partial class WebAssemblyRenderer : WebRenderer
2626
private readonly IInternalJSImportMethods _jsMethods;
2727

2828
public WebAssemblyRenderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, JSComponentInterop jsComponentInterop)
29-
: base(serviceProvider, loggerFactory, DefaultWebAssemblyJSRuntime.Instance.JsonSerializerOptions, jsComponentInterop)
29+
: base(serviceProvider, loggerFactory, DefaultWebAssemblyJSRuntime.Instance.ReadJsonSerializerOptions(), jsComponentInterop)
3030
{
3131
_logger = loggerFactory.CreateLogger<WebAssemblyRenderer>();
3232
_jsMethods = serviceProvider.GetRequiredService<IInternalJSImportMethods>();

src/Components/WebAssembly/WebAssembly/src/Services/DefaultWebAssemblyJSRuntime.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Runtime.InteropServices.JavaScript;
77
using System.Runtime.Versioning;
88
using System.Text.Json;
9+
using Microsoft.AspNetCore.Components.Web.Internal;
910
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
1011
using Microsoft.AspNetCore.Components.WebAssembly.Infrastructure;
1112
using Microsoft.JSInterop;
@@ -15,7 +16,7 @@
1516

1617
namespace Microsoft.AspNetCore.Components.WebAssembly.Services;
1718

18-
internal sealed partial class DefaultWebAssemblyJSRuntime : WebAssemblyJSRuntime
19+
internal sealed partial class DefaultWebAssemblyJSRuntime : WebAssemblyJSRuntime, IInternalWebJSInProcessRuntime
1920
{
2021
public static readonly DefaultWebAssemblyJSRuntime Instance = new();
2122

@@ -30,14 +31,14 @@ internal sealed partial class DefaultWebAssemblyJSRuntime : WebAssemblyJSRuntime
3031
[DynamicDependency(nameof(BeginInvokeDotNet))]
3132
[DynamicDependency(nameof(ReceiveByteArrayFromJS))]
3233
[DynamicDependency(nameof(UpdateRootComponentsCore))]
33-
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = $"The call to {nameof(JsonSerializerOptions.MakeReadOnly)} only populates the missing resolver when reflection is enabled")]
3434
private DefaultWebAssemblyJSRuntime()
3535
{
3636
ElementReferenceContext = new WebElementReferenceContext(this);
3737
JsonSerializerOptions.Converters.Add(new ElementReferenceJsonConverter(ElementReferenceContext));
38-
JsonSerializerOptions.MakeReadOnly(populateMissingResolver: JsonSerializer.IsReflectionEnabledByDefault);
3938
}
4039

40+
public JsonSerializerOptions ReadJsonSerializerOptions() => JsonSerializerOptions;
41+
4142
[JSExport]
4243
[SupportedOSPlatform("browser")]
4344
public static string? InvokeDotNet(
@@ -164,4 +165,7 @@ protected override Task TransmitStreamAsync(long streamId, DotNetStreamReference
164165
{
165166
return TransmitDataStreamToJS.TransmitStreamAsync(this, "Blazor._internal.receiveWebAssemblyDotNetDataStream", streamId, dotNetStreamReference);
166167
}
168+
169+
string IInternalWebJSInProcessRuntime.InvokeJS(string identifier, string? argsJson, JSCallResultType resultType, long targetInstanceId)
170+
=> InvokeJS(identifier, argsJson, resultType, targetInstanceId);
167171
}

src/Components/WebView/WebView/src/Services/WebViewJSRuntime.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@ public WebViewJSRuntime()
1919
JsonSerializerOptions.Converters.Add(
2020
new ElementReferenceJsonConverter(
2121
new WebElementReferenceContext(this)));
22-
JsonSerializerOptions.MakeReadOnly(populateMissingResolver: JsonSerializer.IsReflectionEnabledByDefault);
2322
}
2423

2524
public void AttachToWebView(IpcSender ipcSender)
2625
{
2726
_ipcSender = ipcSender;
2827
}
2928

29+
public JsonSerializerOptions ReadJsonSerializerOptions() => JsonSerializerOptions;
30+
3031
protected override void BeginInvokeJS(long taskId, string identifier, string argsJson, JSCallResultType resultType, long targetInstanceId)
3132
{
3233
if (_ipcSender is null)

src/Components/WebView/WebView/src/Services/WebViewRenderer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public WebViewRenderer(
2121
ILoggerFactory loggerFactory,
2222
WebViewJSRuntime jsRuntime,
2323
JSComponentInterop jsComponentInterop) :
24-
base(serviceProvider, loggerFactory, jsRuntime.JsonSerializerOptions, jsComponentInterop)
24+
base(serviceProvider, loggerFactory, jsRuntime.ReadJsonSerializerOptions(), jsComponentInterop)
2525
{
2626
_dispatcher = dispatcher;
2727
_ipcSender = ipcSender;

src/JSInterop/Microsoft.JSInterop/src/IJSInteropTask.cs

Lines changed: 0 additions & 17 deletions
This file was deleted.

src/JSInterop/Microsoft.JSInterop/src/IJSRuntime.cs

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Diagnostics.CodeAnalysis;
5-
using System.Text.Json;
65
using static Microsoft.AspNetCore.Internal.LinkerFlags;
76

87
namespace Microsoft.JSInterop;
@@ -25,21 +24,6 @@ public interface IJSRuntime
2524
/// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
2625
ValueTask<TValue> InvokeAsync<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(string identifier, object?[]? args);
2726

28-
/// <summary>
29-
/// Invokes the specified JavaScript function asynchronously.
30-
/// <para>
31-
/// <see cref="JSRuntime"/> will apply timeouts to this operation based on the value configured in <see cref="JSRuntime.DefaultAsyncTimeout"/>. To dispatch a call with a different timeout, or no timeout,
32-
/// consider using <see cref="InvokeAsync{TValue}(string, CancellationToken, object[])" />.
33-
/// </para>
34-
/// </summary>
35-
/// <typeparam name="TValue">The JSON-serializable return type.</typeparam>
36-
/// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>window.someScope.someFunction</c>.</param>
37-
/// <param name="options">The <see cref="JsonSerializerOptions"/> to use for JSON serialization and deserialization.</param>
38-
/// <param name="args">JSON-serializable arguments.</param>
39-
/// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
40-
ValueTask<TValue> InvokeAsync<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(string identifier, JsonSerializerOptions options, object?[]? args)
41-
=> throw new InvalidOperationException($"Supplying a custom {nameof(JsonSerializerOptions)} is not supported by the current JS runtime");
42-
4327
/// <summary>
4428
/// Invokes the specified JavaScript function asynchronously.
4529
/// </summary>
@@ -52,24 +36,4 @@ public interface IJSRuntime
5236
/// <param name="args">JSON-serializable arguments.</param>
5337
/// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
5438
ValueTask<TValue> InvokeAsync<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(string identifier, CancellationToken cancellationToken, object?[]? args);
55-
56-
/// <summary>
57-
/// Invokes the specified JavaScript function asynchronously.
58-
/// </summary>
59-
/// <typeparam name="TValue">The JSON-serializable return type.</typeparam>
60-
/// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>window.someScope.someFunction</c>.</param>
61-
/// <param name="options">The <see cref="JsonSerializerOptions"/> to use for JSON serialization and deserialization.</param>
62-
/// <param name="cancellationToken">
63-
/// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts
64-
/// (<see cref="JSRuntime.DefaultAsyncTimeout"/>) from being applied.
65-
/// </param>
66-
/// <param name="args">JSON-serializable arguments.</param>
67-
/// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
68-
ValueTask<TValue> InvokeAsync<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(string identifier, JsonSerializerOptions options, CancellationToken cancellationToken, object?[]? args)
69-
=> throw new InvalidOperationException($"Supplying a custom {nameof(JsonSerializerOptions)} is not supported by the current JS runtime");
70-
71-
/// <summary>
72-
/// Gets the <see cref="System.Text.Json.JsonSerializerOptions"/> used to serialize and deserialize interop payloads.
73-
/// </summary>
74-
JsonSerializerOptions JsonSerializerOptions => throw new InvalidOperationException($"The current JS runtime does not support accessing {nameof(JsonSerializerOptions)}");
7539
}

src/JSInterop/Microsoft.JSInterop/src/Infrastructure/TaskGenericsUtil.cs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,26 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Collections.Concurrent;
5+
using System.Globalization;
56

67
namespace Microsoft.JSInterop.Infrastructure;
78

89
internal static class TaskGenericsUtil
910
{
10-
private static readonly ConcurrentDictionary<Type, ITaskResultGetter> _cachedResultGetters = [];
11+
private static readonly ConcurrentDictionary<Type, ITaskResultGetter> _cachedResultGetters
12+
= new ConcurrentDictionary<Type, ITaskResultGetter>();
13+
14+
private static readonly ConcurrentDictionary<Type, ITcsResultSetter> _cachedResultSetters
15+
= new ConcurrentDictionary<Type, ITcsResultSetter>();
16+
17+
public static void SetTaskCompletionSourceResult(object taskCompletionSource, object? result)
18+
=> CreateResultSetter(taskCompletionSource).SetResult(taskCompletionSource, result);
19+
20+
public static void SetTaskCompletionSourceException(object taskCompletionSource, Exception exception)
21+
=> CreateResultSetter(taskCompletionSource).SetException(taskCompletionSource, exception);
22+
23+
public static Type GetTaskCompletionSourceResultType(object taskCompletionSource)
24+
=> CreateResultSetter(taskCompletionSource).ResultType;
1125

1226
public static object? GetTaskResult(Task task)
1327
{
@@ -38,6 +52,13 @@ internal static class TaskGenericsUtil
3852
: null;
3953
}
4054

55+
interface ITcsResultSetter
56+
{
57+
Type ResultType { get; }
58+
void SetResult(object taskCompletionSource, object? result);
59+
void SetException(object taskCompletionSource, Exception exception);
60+
}
61+
4162
private interface ITaskResultGetter
4263
{
4364
object? GetResult(Task task);
@@ -56,4 +77,37 @@ private sealed class VoidTaskResultGetter : ITaskResultGetter
5677
return null;
5778
}
5879
}
80+
81+
private sealed class TcsResultSetter<T> : ITcsResultSetter
82+
{
83+
public Type ResultType => typeof(T);
84+
85+
public void SetResult(object tcs, object? result)
86+
{
87+
var typedTcs = (TaskCompletionSource<T>)tcs;
88+
89+
// If necessary, attempt a cast
90+
var typedResult = result is T resultT
91+
? resultT
92+
: (T)Convert.ChangeType(result, typeof(T), CultureInfo.InvariantCulture)!;
93+
94+
typedTcs.SetResult(typedResult!);
95+
}
96+
97+
public void SetException(object tcs, Exception exception)
98+
{
99+
var typedTcs = (TaskCompletionSource<T>)tcs;
100+
typedTcs.SetException(exception);
101+
}
102+
}
103+
104+
private static ITcsResultSetter CreateResultSetter(object taskCompletionSource)
105+
{
106+
return _cachedResultSetters.GetOrAdd(taskCompletionSource.GetType(), tcsType =>
107+
{
108+
var resultType = tcsType.GetGenericArguments()[0];
109+
return (ITcsResultSetter)Activator.CreateInstance(
110+
typeof(TcsResultSetter<>).MakeGenericType(resultType))!;
111+
});
112+
}
59113
}

src/JSInterop/Microsoft.JSInterop/src/JSInteropTask.cs

Lines changed: 0 additions & 60 deletions
This file was deleted.

0 commit comments

Comments
 (0)