Skip to content

Commit bbc7fd8

Browse files
IJSUnmarshalledObjectReference for unmarshalled JS interop calls on JavaScript objects. (#25548)
* Added IJSUnmarshalledObjectReference * Working support for IJSUnmarshalledObjectReference * CR feedback * Removed IVT and made JSObjectReference public * Updated JSON converter. * Update JSObjectReferenceJsonConverterTest.cs * Removed whitespace 😓
1 parent c61bdfd commit bbc7fd8

23 files changed

+337
-71
lines changed

src/Components/Web.JS/dist/Release/blazor.webassembly.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: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,16 @@ function invokeJSFromDotNet(callInfo: Pointer, arg0: any, arg1: any, arg2: any):
130130
}
131131
} else {
132132
const func = DotNet.jsCallDispatcher.findJSFunction(functionIdentifier, targetInstanceId);
133-
return func.call(null, arg0, arg1, arg2);
133+
const result = func.call(null, arg0, arg1, arg2);
134+
135+
switch (resultType) {
136+
case DotNet.JSCallResultType.Default:
137+
return result;
138+
case DotNet.JSCallResultType.JSObjectReference:
139+
return DotNet.createJSObjectReference(result).__jsObjectId;
140+
default:
141+
throw new Error(`Invalid JS call result type '${resultType}'.`);
142+
}
134143
}
135144
}
136145

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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 Microsoft.JSInterop.Implementation;
5+
6+
namespace Microsoft.JSInterop.WebAssembly
7+
{
8+
internal class WebAssemblyJSObjectReference : JSInProcessObjectReference, IJSUnmarshalledObjectReference
9+
{
10+
private readonly WebAssemblyJSRuntime _jsRuntime;
11+
12+
public WebAssemblyJSObjectReference(WebAssemblyJSRuntime jsRuntime, long id) : base(jsRuntime, id)
13+
{
14+
_jsRuntime = jsRuntime;
15+
}
16+
17+
public TResult InvokeUnmarshalled<TResult>(string identifier)
18+
{
19+
ThrowIfDisposed();
20+
21+
return _jsRuntime.InvokeUnmarshalled<object, object, object, TResult>(identifier, null, null, null, Id);
22+
}
23+
24+
public TResult InvokeUnmarshalled<T0, TResult>(string identifier, T0 arg0)
25+
{
26+
ThrowIfDisposed();
27+
28+
return _jsRuntime.InvokeUnmarshalled<T0, object, object, TResult>(identifier, arg0, null, null, Id);
29+
}
30+
31+
public TResult InvokeUnmarshalled<T0, T1, TResult>(string identifier, T0 arg0, T1 arg1)
32+
{
33+
ThrowIfDisposed();
34+
35+
return _jsRuntime.InvokeUnmarshalled<T0, T1, object, TResult>(identifier, arg0, arg1, null, Id);
36+
}
37+
38+
public TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2)
39+
{
40+
ThrowIfDisposed();
41+
42+
return _jsRuntime.InvokeUnmarshalled<T0, T1, T2, TResult>(identifier, arg0, arg1, arg2, Id);
43+
}
44+
}
45+
}

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

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System;
45
using System.Text.Json;
56
using Microsoft.JSInterop.Infrastructure;
67
using WebAssembly.JSInterop;
@@ -60,32 +61,50 @@ protected override void EndInvokeDotNet(DotNetInvocationInfo callInfo, in DotNet
6061
BeginInvokeJS(0, "DotNet.jsCallDispatcher.endInvokeDotNetFromJS", args, JSCallResultType.Default, 0);
6162
}
6263

63-
/// <inheritdoc />
64-
TResult IJSUnmarshalledRuntime.InvokeUnmarshalled<TResult>(string identifier)
65-
=> ((IJSUnmarshalledRuntime)this).InvokeUnmarshalled<object, object, object, TResult>(identifier, null, null, null);
66-
67-
/// <inheritdoc />
68-
TResult IJSUnmarshalledRuntime.InvokeUnmarshalled<T0, TResult>(string identifier, T0 arg0)
69-
=> ((IJSUnmarshalledRuntime)this).InvokeUnmarshalled<T0, object, object, TResult>(identifier, arg0, null, null);
70-
71-
/// <inheritdoc />
72-
TResult IJSUnmarshalledRuntime.InvokeUnmarshalled<T0, T1, TResult>(string identifier, T0 arg0, T1 arg1)
73-
=> ((IJSUnmarshalledRuntime)this).InvokeUnmarshalled<T0, T1, object, TResult>(identifier, arg0, arg1, null);
74-
75-
/// <inheritdoc />
76-
TResult IJSUnmarshalledRuntime.InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2)
64+
internal TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2, long targetInstanceId)
7765
{
66+
var resultType = JSCallResultTypeHelper.FromGeneric<TResult>();
67+
7868
var callInfo = new JSCallInfo
7969
{
8070
FunctionIdentifier = identifier,
81-
ResultType = JSCallResultTypeHelper.FromGeneric<TResult>()
71+
TargetInstanceId = targetInstanceId,
72+
ResultType = resultType,
8273
};
8374

84-
var result = InternalCalls.InvokeJS<T0, T1, T2, TResult>(out var exception, ref callInfo, arg0, arg1, arg2);
75+
string exception;
8576

86-
return exception != null
87-
? throw new JSException(exception)
88-
: result;
77+
switch (resultType)
78+
{
79+
case JSCallResultType.Default:
80+
var result = InternalCalls.InvokeJS<T0, T1, T2, TResult>(out exception, ref callInfo, arg0, arg1, arg2);
81+
return exception != null
82+
? throw new JSException(exception)
83+
: result;
84+
case JSCallResultType.JSObjectReference:
85+
var id = InternalCalls.InvokeJS<T0, T1, T2, int>(out exception, ref callInfo, arg0, arg1, arg2);
86+
return exception != null
87+
? throw new JSException(exception)
88+
: (TResult)(object)new WebAssemblyJSObjectReference(this, id);
89+
default:
90+
throw new InvalidOperationException($"Invalid result type '{resultType}'.");
91+
}
8992
}
93+
94+
/// <inheritdoc />
95+
public TResult InvokeUnmarshalled<TResult>(string identifier)
96+
=> InvokeUnmarshalled<object, object, object, TResult>(identifier, null, null, null, 0);
97+
98+
/// <inheritdoc />
99+
public TResult InvokeUnmarshalled<T0, TResult>(string identifier, T0 arg0)
100+
=> InvokeUnmarshalled<T0, object, object, TResult>(identifier, arg0, null, null, 0);
101+
102+
/// <inheritdoc />
103+
public TResult InvokeUnmarshalled<T0, T1, TResult>(string identifier, T0 arg0, T1 arg1)
104+
=> InvokeUnmarshalled<T0, T1, object, TResult>(identifier, arg0, arg1, null, 0);
105+
106+
/// <inheritdoc />
107+
public TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2)
108+
=> InvokeUnmarshalled<T0, T1, T2, TResult>(identifier, arg0, arg1, arg2, 0);
90109
}
91110
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ protected override void Dispose(bool disposing)
9898
/// <inheritdoc />
9999
protected override Task UpdateDisplayAsync(in RenderBatch batch)
100100
{
101-
((IJSUnmarshalledRuntime)DefaultWebAssemblyJSRuntime.Instance).InvokeUnmarshalled<int, RenderBatch, object>(
101+
DefaultWebAssemblyJSRuntime.Instance.InvokeUnmarshalled<int, RenderBatch, object>(
102102
"Blazor._internal.renderBatch",
103103
_webAssemblyRendererId,
104104
batch);

src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyConsoleLogger.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ private void WriteMessage(LogLevel logLevel, string logName, int eventId, string
9999
_jsRuntime.InvokeVoid("console.error", formattedMessage);
100100
break;
101101
case LogLevel.Critical:
102-
((IJSUnmarshalledRuntime)_jsRuntime).InvokeUnmarshalled<string, object>("Blazor._internal.dotNetCriticalError", formattedMessage);
102+
_jsRuntime.InvokeUnmarshalled<string, object>("Blazor._internal.dotNetCriticalError", formattedMessage);
103103
break;
104104
default: // LogLevel.None or invalid enum values
105105
Console.WriteLine(formattedMessage);

src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyJSRuntimeInvoker.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@ internal class WebAssemblyJSRuntimeInvoker
2222
public static WebAssemblyJSRuntimeInvoker Instance = new WebAssemblyJSRuntimeInvoker();
2323

2424
public virtual TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2)
25-
=> ((IJSUnmarshalledRuntime)DefaultWebAssemblyJSRuntime.Instance).InvokeUnmarshalled<T0, T1, T2, TResult>(identifier, arg0, arg1, arg2);
25+
=> DefaultWebAssemblyJSRuntime.Instance.InvokeUnmarshalled<T0, T1, T2, TResult>(identifier, arg0, arg1, arg2);
2626
}
2727
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ public void CanInvokeDotNetMethods()
110110
["instanceMethodIncomingByRef"] = "123",
111111
["instanceMethodOutgoingByRef"] = "1234",
112112
["jsInProcessObjectReference.identity"] = "Invoked from JSInProcessObjectReference",
113+
["jsUnmarshalledObjectReference.unmarshalledFunction"] = "True",
113114
["stringValueUpperSync"] = "MY STRING",
114115
["testDtoNonSerializedValueSync"] = "99999",
115116
["testDtoSync"] = "Same",

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,14 @@
194194

195195
var jsInProcObjectReference = inProcRuntime.Invoke<IJSInProcessObjectReference>("returnJSObjectReference");
196196
ReturnValues["jsInProcessObjectReference.identity"] = jsInProcObjectReference.Invoke<string>("identity", "Invoked from JSInProcessObjectReference");
197+
198+
var unmarshalledRuntime = (IJSUnmarshalledRuntime)JSRuntime;
199+
var jsUnmarshalledReference = unmarshalledRuntime.InvokeUnmarshalled<IJSUnmarshalledObjectReference>("returnJSObjectReference");
200+
ReturnValues["jsUnmarshalledObjectReference.unmarshalledFunction"] = jsUnmarshalledReference.InvokeUnmarshalled<InteropStruct, bool>("unmarshalledFunction", new InteropStruct
201+
{
202+
Message = "Sent from .NET",
203+
NumberField = 42,
204+
}).ToString();
197205
}
198206

199207
public class PassDotNetObjectByRefArgs
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System.Runtime.InteropServices;
2+
3+
namespace BasicTestApp.InteropTest
4+
{
5+
[StructLayout(LayoutKind.Explicit)]
6+
public struct InteropStruct
7+
{
8+
[FieldOffset(0)]
9+
public string Message;
10+
11+
[FieldOffset(8)]
12+
public int NumberField;
13+
}
14+
}

0 commit comments

Comments
 (0)