Skip to content

Commit 05303c7

Browse files
authored
Stop Serializing Return Values for InvokeVoidAsync (#35807)
* Stop Serializing `InvokeVoidAsync` Return Value * Fix E2E Test * Add JSObjectReferenceTest * Remove `IJSVoidResult` from the public API * release .js files * Revert "Remove `IJSVoidResult` from the public API" This reverts commit e754872. * Add monocrash to gitignore * Fix Tests * Update src/JSInterop/Microsoft.JSInterop/src/PublicAPI.Unshipped.txt * PR Feedback @pranavkm
1 parent 1268b20 commit 05303c7

File tree

17 files changed

+97
-29
lines changed

17 files changed

+97
-29
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,4 @@ StyleCop.Cache
4242
UpgradeLog.htm
4343
.idea
4444
*.svclog
45+
mono_crash.*.json

src/Components/Server/test/ProtectedBrowserStorageTest.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
using Microsoft.AspNetCore.DataProtection;
1313
using Microsoft.AspNetCore.WebUtilities;
1414
using Microsoft.JSInterop;
15+
using Microsoft.JSInterop.Infrastructure;
16+
using Moq;
1517
using Xunit;
1618

1719
namespace Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@@ -265,7 +267,7 @@ public async Task ReusesCachedProtectorsByPurpose()
265267
{
266268
// Arrange
267269
var jsRuntime = new TestJSRuntime();
268-
jsRuntime.NextInvocationResult = new ValueTask<object>((object)null);
270+
jsRuntime.NextInvocationResult = new ValueTask<IJSVoidResult>(Mock.Of<IJSVoidResult>());
269271
var dataProtectionProvider = new TestDataProtectionProvider();
270272
var protectedBrowserStorage = new TestProtectedBrowserStorage("testStore", jsRuntime, dataProtectionProvider);
271273

src/Components/Web.JS/dist/Release/blazor.server.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Components/Web.JS/dist/Release/blazor.webview.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Components/Web.JS/src/Boot.WebAssembly.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ function invokeJSFromDotNet(callInfo: Pointer, arg0: any, arg1: any, arg2: any):
163163
const streamReference = DotNet.createJSStreamReference(result);
164164
const resultJson = JSON.stringify(streamReference);
165165
return BINDING.js_string_to_mono_string(resultJson);
166+
case DotNet.JSCallResultType.JSVoidResult:
167+
return null;
166168
default:
167169
throw new Error(`Invalid JS call result type '${resultType}'.`);
168170
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ internal TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 a
9191
switch (resultType)
9292
{
9393
case JSCallResultType.Default:
94+
case JSCallResultType.JSVoidResult:
9495
var result = InternalCalls.InvokeJS<T0, T1, T2, TResult>(out exception, ref callInfo, arg0, arg1, arg2);
9596
return exception != null
9697
? throw new JSException(exception)

src/Components/test/E2ETest/Tests/InteropTest.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ public void CanInvokeDotNetMethods()
9292
["asyncGenericInstanceMethod"] = @"""Updated value 1""",
9393
["requestDotNetStreamReferenceAsync"] = @"""Success""",
9494
["requestDotNetStreamWrapperReferenceAsync"] = @"""Success""",
95+
["invokeVoidAsyncReturnsWithoutSerializing"] = "Success",
96+
["invokeVoidAsyncReturnsWithoutSerializingInJSObjectReference"] = "Success",
9597
};
9698

9799
var expectedSyncValues = new Dictionary<string, string>

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

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,12 +138,36 @@
138138
ReturnValues["returnPrimitive"] = ((IJSInProcessRuntime)JSRuntime).Invoke<int>("returnPrimitive").ToString();
139139
ReturnValues["returnArray"] = string.Join(",", ((IJSInProcessRuntime)JSRuntime).Invoke<Segment[]>("returnArray").Select(x => x.Source).ToArray());
140140
}
141+
142+
try
143+
{
144+
// Triger a non-serializable WindowProxy (https://developer.mozilla.org/en-US/docs/Web/API/Window)
145+
// InvokeVoidAsync shouldn't serialize return values, and thus there shouldn't be any exception thrown.
146+
await JSRuntime.InvokeVoidAsync("eval", "window");
147+
ReturnValues["invokeVoidAsyncReturnsWithoutSerializing"] = "Success";
148+
}
149+
catch (Exception ex)
150+
{
151+
ReturnValues["invokeVoidAsyncReturnsWithoutSerializing"] = $"Failure: {ex.Message}";
152+
}
141153

142154
var jsObjectReference = await JSRuntime.InvokeAsync<IJSObjectReference>("returnJSObjectReference");
143155
ReturnValues["jsObjectReference.identity"] = await jsObjectReference.InvokeAsync<string>("identity", "Invoked from JSObjectReference");
144156
ReturnValues["jsObjectReference.nested.add"] = (await jsObjectReference.InvokeAsync<int>("nested.add", 2, 3)).ToString();
145157
ReturnValues["addViaJSObjectReference"] = (await JSRuntime.InvokeAsync<int>("addViaJSObjectReference", jsObjectReference, 2, 3)).ToString();
146158

159+
try
160+
{
161+
// Fetch a non-serializable Window (https://developer.mozilla.org/en-US/docs/Web/API/Window)
162+
// InvokeVoidAsync shouldn't serialize return values, and thus there shouldn't be any exception thrown.
163+
await jsObjectReference.InvokeVoidAsync("getWindow");
164+
ReturnValues["invokeVoidAsyncReturnsWithoutSerializingInJSObjectReference"] = "Success";
165+
}
166+
catch (Exception ex)
167+
{
168+
ReturnValues["invokeVoidAsyncReturnsWithoutSerializingInJSObjectReference"] = $"Failure: {ex.Message}";
169+
}
170+
147171
try
148172
{
149173
await jsObjectReference.InvokeAsync<object>("nonFunction");
@@ -201,7 +225,7 @@
201225

202226
var dotNetStreamReferenceWrapper = DotNetStreamReferenceInterop.GetDotNetStreamWrapperReference();
203227
ReturnValues["dotNetToJSReceiveDotNetStreamWrapperReferenceAsync"] = await JSRuntime.InvokeAsync<string>("jsInteropTests.receiveDotNetStreamWrapperReference", dotNetStreamReferenceWrapper);
204-
228+
205229
Invocations = invocations;
206230
DoneWithInterop = true;
207231
}

src/Components/test/testassets/BasicTestApp/wwwroot/js/jsinteroptests.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,9 @@ function returnJSObjectReference() {
324324
identity: function (value) {
325325
return value;
326326
},
327+
getWindow: function() {
328+
return window;
329+
},
327330
nonFunction: 123,
328331
nested: {
329332
add: function (a, b) {

src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ export module DotNet {
280280
Default = 0,
281281
JSObjectReference = 1,
282282
JSStreamReference = 2,
283+
JSVoidResult = 3,
283284
}
284285

285286
/**
@@ -566,6 +567,8 @@ export module DotNet {
566567
return createJSObjectReference(returnValue);
567568
case JSCallResultType.JSStreamReference:
568569
return createJSStreamReference(returnValue);
570+
case JSCallResultType.JSVoidResult:
571+
return null;
569572
default:
570573
throw new Error(`Invalid JS call result type '${resultType}'.`);
571574
}

0 commit comments

Comments
 (0)