Skip to content

Commit 2a0b7dc

Browse files
JSObjectReference API review follow-up (#25476)
* API review changes. * Fixed JSObjectReferenceJsonConverter * CR feedback * Update JSObjectReferenceExtensions.cs
1 parent 6ec1b48 commit 2a0b7dc

16 files changed

+286
-99
lines changed

src/Components/WebAssembly/JSInterop/src/Microsoft.JSInterop.WebAssembly.csproj

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
@@ -12,4 +12,8 @@
1212
<Reference Include="Microsoft.JSInterop" />
1313
</ItemGroup>
1414

15+
<ItemGroup>
16+
<Compile Include="$(SharedSourceRoot)JSInterop\*.cs" />
17+
</ItemGroup>
18+
1519
</Project>

src/Components/WebAssembly/JSInterop/src/WebAssemblyJSRuntime.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ TResult IJSUnmarshalledRuntime.InvokeUnmarshalled<T0, T1, T2, TResult>(string id
7878
var callInfo = new JSCallInfo
7979
{
8080
FunctionIdentifier = identifier,
81-
ResultType = ResultTypeFromGeneric<TResult>()
81+
ResultType = JSCallResultTypeHelper.FromGeneric<TResult>()
8282
};
8383

8484
var result = InternalCalls.InvokeJS<T0, T1, T2, TResult>(out var exception, ref callInfo, arg0, arg1, arg2);

src/Components/test/testassets/BasicTestApp/InteropComponent.razor

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@
137137
ReturnValues["returnArray"] = string.Join(",", ((IJSInProcessRuntime)JSRuntime).Invoke<Segment[]>("returnArray").Select(x => x.Source).ToArray());
138138
}
139139

140-
var jsObjectReference = await JSRuntime.InvokeAsync<JSObjectReference>("returnJSObjectReference");
140+
var jsObjectReference = await JSRuntime.InvokeAsync<IJSObjectReference>("returnJSObjectReference");
141141
ReturnValues["jsObjectReference.identity"] = await jsObjectReference.InvokeAsync<string>("identity", "Invoked from JSObjectReference");
142142
ReturnValues["jsObjectReference.nested.add"] = (await jsObjectReference.InvokeAsync<int>("nested.add", 2, 3)).ToString();
143143
ReturnValues["addViaJSObjectReference"] = (await JSRuntime.InvokeAsync<int>("addViaJSObjectReference", jsObjectReference, 2, 3)).ToString();
@@ -151,7 +151,7 @@
151151
JSObjectReferenceInvokeNonFunctionException = e;
152152
}
153153

154-
var module = await JSRuntime.InvokeAsync<JSObjectReference>("import", "./js/testmodule.js");
154+
var module = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./js/testmodule.js");
155155
ReturnValues["jsObjectReferenceModule"] = await module.InvokeAsync<string>("identity", "Returned from module!");
156156

157157
if (shouldSupportSyncInterop)
@@ -192,7 +192,7 @@
192192
{
193193
var inProcRuntime = ((IJSInProcessRuntime)JSRuntime);
194194

195-
var jsInProcObjectReference = inProcRuntime.Invoke<JSInProcessObjectReference>("returnJSObjectReference");
195+
var jsInProcObjectReference = inProcRuntime.Invoke<IJSInProcessObjectReference>("returnJSObjectReference");
196196
ReturnValues["jsInProcessObjectReference.identity"] = jsInProcObjectReference.Invoke<string>("identity", "Invoked from JSInProcessObjectReference");
197197
}
198198

src/Components/test/testassets/BasicTestApp/InteropTest/JavaScriptInterop.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -415,20 +415,20 @@ public static int ExtractNonSerializedValue(DotNetObjectReference<TestDTO> objec
415415
}
416416

417417
[JSInvokable]
418-
public static JSObjectReference RoundTripJSObjectReference(JSObjectReference jsObjectReference)
418+
public static IJSObjectReference RoundTripJSObjectReference(IJSObjectReference jsObjectReference)
419419
{
420420
return jsObjectReference;
421421
}
422422

423423
[JSInvokable]
424-
public static async Task<JSObjectReference> RoundTripJSObjectReferenceAsync(JSObjectReference jSObjectReference)
424+
public static async Task<IJSObjectReference> RoundTripJSObjectReferenceAsync(IJSObjectReference jSObjectReference)
425425
{
426426
await Task.Yield();
427427
return jSObjectReference;
428428
}
429429

430430
[JSInvokable]
431-
public static string InvokeDisposedJSObjectReferenceException(JSInProcessObjectReference jsObjectReference)
431+
public static string InvokeDisposedJSObjectReferenceException(IJSInProcessObjectReference jsObjectReference)
432432
{
433433
try
434434
{
@@ -442,7 +442,7 @@ public static string InvokeDisposedJSObjectReferenceException(JSInProcessObjectR
442442
}
443443

444444
[JSInvokable]
445-
public static async Task<string> InvokeDisposedJSObjectReferenceExceptionAsync(JSObjectReference jsObjectReference)
445+
public static async Task<string> InvokeDisposedJSObjectReferenceExceptionAsync(IJSObjectReference jsObjectReference)
446446
{
447447
try
448448
{
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
namespace Microsoft.JSInterop
5+
{
6+
/// <summary>
7+
/// Represents a reference to a JavaScript object whose functions can be invoked synchronously.
8+
/// </summary>
9+
public interface IJSInProcessObjectReference : IJSObjectReference
10+
{
11+
/// <summary>
12+
/// Invokes the specified JavaScript function synchronously.
13+
/// </summary>
14+
/// <typeparam name="TValue">The JSON-serializable return type.</typeparam>
15+
/// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
16+
/// <param name="args">JSON-serializable arguments.</param>
17+
/// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
18+
TValue Invoke<TValue>(string identifier, params object?[]? args);
19+
}
20+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
8+
namespace Microsoft.JSInterop
9+
{
10+
/// <summary>
11+
/// Represents a reference to a JavaScript object.
12+
/// </summary>
13+
public interface IJSObjectReference : IAsyncDisposable
14+
{
15+
/// <summary>
16+
/// Invokes the specified JavaScript function asynchronously.
17+
/// <para>
18+
/// <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, or no timeout,
19+
/// consider using <see cref="InvokeAsync{TValue}(string, CancellationToken, object[])" />.
20+
/// </para>
21+
/// </summary>
22+
/// <typeparam name="TValue">The JSON-serializable return type.</typeparam>
23+
/// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
24+
/// <param name="args">JSON-serializable arguments.</param>
25+
/// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
26+
ValueTask<TValue> InvokeAsync<TValue>(string identifier, object?[]? args);
27+
28+
/// <summary>
29+
/// Invokes the specified JavaScript function asynchronously.
30+
/// </summary>
31+
/// <typeparam name="TValue">The JSON-serializable return type.</typeparam>
32+
/// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
33+
/// <param name="cancellationToken">
34+
/// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts
35+
/// (<see cref="JSRuntime.DefaultAsyncTimeout"/>) from being applied.
36+
/// </param>
37+
/// <param name="args">JSON-serializable arguments.</param>
38+
/// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
39+
ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToken cancellationToken, object?[]? args);
40+
}
41+
}

src/JSInterop/Microsoft.JSInterop/src/Infrastructure/JSObjectReferenceJsonConverter.cs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,21 @@
77

88
namespace Microsoft.JSInterop.Infrastructure
99
{
10-
internal sealed class JSObjectReferenceJsonConverter<TJSObjectReference>
11-
: JsonConverter<TJSObjectReference> where TJSObjectReference : JSObjectReference
10+
internal sealed class JSObjectReferenceJsonConverter<TInterface, TImplementation> : JsonConverter<TInterface>
11+
where TInterface : class, IJSObjectReference
12+
where TImplementation : JSObjectReference, TInterface
1213
{
13-
private readonly Func<long, TJSObjectReference> _jsObjectReferenceFactory;
14+
private readonly Func<long, TImplementation> _jsObjectReferenceFactory;
1415

15-
public JSObjectReferenceJsonConverter(Func<long, TJSObjectReference> jsObjectReferenceFactory)
16+
public JSObjectReferenceJsonConverter(Func<long, TImplementation> jsObjectReferenceFactory)
1617
{
1718
_jsObjectReferenceFactory = jsObjectReferenceFactory;
1819
}
1920

20-
public override TJSObjectReference? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
21+
public override bool CanConvert(Type typeToConvert)
22+
=> typeToConvert == typeof(TInterface) || typeToConvert == typeof(TImplementation);
23+
24+
public override TInterface? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
2125
{
2226
long id = -1;
2327

@@ -49,10 +53,10 @@ public JSObjectReferenceJsonConverter(Func<long, TJSObjectReference> jsObjectRef
4953
return _jsObjectReferenceFactory(id);
5054
}
5155

52-
public override void Write(Utf8JsonWriter writer, TJSObjectReference value, JsonSerializerOptions options)
56+
public override void Write(Utf8JsonWriter writer, TInterface value, JsonSerializerOptions options)
5357
{
5458
writer.WriteStartObject();
55-
writer.WriteNumber(JSObjectReference.IdKey, value.Id);
59+
writer.WriteNumber(JSObjectReference.IdKey, ((TImplementation)value).Id);
5660
writer.WriteEndObject();
5761
}
5862
}

src/JSInterop/Microsoft.JSInterop/src/JSInProcessObjectReference.cs

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@
55

66
namespace Microsoft.JSInterop
77
{
8-
/// <summary>
9-
/// Represents a reference to a JavaScript object whose functions can be invoked synchronously.
10-
/// </summary>
11-
public class JSInProcessObjectReference : JSObjectReference
8+
internal class JSInProcessObjectReference : JSObjectReference, IJSInProcessObjectReference
129
{
1310
private readonly JSInProcessRuntime _jsRuntime;
1411

@@ -17,15 +14,8 @@ internal JSInProcessObjectReference(JSInProcessRuntime jsRuntime, long id) : bas
1714
_jsRuntime = jsRuntime;
1815
}
1916

20-
/// <summary>
21-
/// Invokes the specified JavaScript function synchronously.
22-
/// </summary>
23-
/// <typeparam name="TValue">The JSON-serializable return type.</typeparam>
24-
/// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
25-
/// <param name="args">JSON-serializable arguments.</param>
26-
/// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
2717
[return: MaybeNull]
28-
public TValue Invoke<TValue>(string identifier, params object[] args)
18+
public TValue Invoke<TValue>(string identifier, params object?[]? args)
2919
{
3020
ThrowIfDisposed();
3121

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
6+
namespace Microsoft.JSInterop
7+
{
8+
public static class JSInProcessObjectReferenceExtensions
9+
{
10+
/// <summary>
11+
/// Invokes the specified JavaScript function synchronously.
12+
/// </summary>
13+
/// <param name="jsObjectReference">The <see cref="IJSInProcessObjectReference"/>.</param>
14+
/// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
15+
/// <param name="args">JSON-serializable arguments.</param>
16+
public static void InvokeVoid(this IJSInProcessObjectReference jsObjectReference, string identifier, params object?[] args)
17+
{
18+
if (jsObjectReference == null)
19+
{
20+
throw new ArgumentNullException(nameof(jsObjectReference));
21+
}
22+
23+
jsObjectReference.Invoke<object>(identifier, args);
24+
}
25+
}
26+
}

src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public abstract class JSInProcessRuntime : JSRuntime, IJSInProcessRuntime
1717
/// </summary>
1818
protected JSInProcessRuntime()
1919
{
20-
JsonSerializerOptions.Converters.Add(new JSObjectReferenceJsonConverter<JSInProcessObjectReference>(
20+
JsonSerializerOptions.Converters.Add(new JSObjectReferenceJsonConverter<IJSInProcessObjectReference, JSInProcessObjectReference>(
2121
id => new JSInProcessObjectReference(this, id)));
2222
}
2323

@@ -27,7 +27,7 @@ internal TValue Invoke<TValue>(string identifier, long targetInstanceId, params
2727
var resultJson = InvokeJS(
2828
identifier,
2929
JsonSerializer.Serialize(args, JsonSerializerOptions),
30-
ResultTypeFromGeneric<TValue>(),
30+
JSCallResultTypeHelper.FromGeneric<TValue>(),
3131
targetInstanceId);
3232

3333
if (resultJson is null)

0 commit comments

Comments
 (0)