Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Components/Web.JS/dist/Release/blazor.webassembly.js

Large diffs are not rendered by default.

11 changes: 10 additions & 1 deletion src/Components/Web.JS/src/Boot.WebAssembly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,16 @@ function invokeJSFromDotNet(callInfo: Pointer, arg0: any, arg1: any, arg2: any):
}
} else {
const func = DotNet.jsCallDispatcher.findJSFunction(functionIdentifier, targetInstanceId);
return func.call(null, arg0, arg1, arg2);
const result = func.call(null, arg0, arg1, arg2);

switch (resultType) {
case DotNet.JSCallResultType.Default:
return result;
case DotNet.JSCallResultType.JSObjectReference:
return DotNet.createJSObjectReference(result).__jsObjectId;
default:
throw new Error(`Invalid JS call result type '${resultType}'.`);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using Microsoft.JSInterop.Implementation;

namespace Microsoft.JSInterop.WebAssembly
{
internal class WebAssemblyJSObjectReference : JSInProcessObjectReference, IJSUnmarshalledObjectReference
{
private readonly WebAssemblyJSRuntime _jsRuntime;

public WebAssemblyJSObjectReference(WebAssemblyJSRuntime jsRuntime, long id) : base(jsRuntime, id)
{
_jsRuntime = jsRuntime;
}

public TResult InvokeUnmarshalled<TResult>(string identifier)
{
ThrowIfDisposed();

return _jsRuntime.InvokeUnmarshalled<object, object, object, TResult>(identifier, null, null, null, Id);
}

public TResult InvokeUnmarshalled<T0, TResult>(string identifier, T0 arg0)
{
ThrowIfDisposed();

return _jsRuntime.InvokeUnmarshalled<T0, object, object, TResult>(identifier, arg0, null, null, Id);
}

public TResult InvokeUnmarshalled<T0, T1, TResult>(string identifier, T0 arg0, T1 arg1)
{
ThrowIfDisposed();

return _jsRuntime.InvokeUnmarshalled<T0, T1, object, TResult>(identifier, arg0, arg1, null, Id);
}

public TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2)
{
ThrowIfDisposed();

return _jsRuntime.InvokeUnmarshalled<T0, T1, T2, TResult>(identifier, arg0, arg1, arg2, Id);
}
}
}
57 changes: 38 additions & 19 deletions src/Components/WebAssembly/JSInterop/src/WebAssemblyJSRuntime.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Text.Json;
using Microsoft.JSInterop.Infrastructure;
using WebAssembly.JSInterop;
Expand Down Expand Up @@ -60,32 +61,50 @@ protected override void EndInvokeDotNet(DotNetInvocationInfo callInfo, in DotNet
BeginInvokeJS(0, "DotNet.jsCallDispatcher.endInvokeDotNetFromJS", args, JSCallResultType.Default, 0);
}

/// <inheritdoc />
TResult IJSUnmarshalledRuntime.InvokeUnmarshalled<TResult>(string identifier)
=> ((IJSUnmarshalledRuntime)this).InvokeUnmarshalled<object, object, object, TResult>(identifier, null, null, null);

/// <inheritdoc />
TResult IJSUnmarshalledRuntime.InvokeUnmarshalled<T0, TResult>(string identifier, T0 arg0)
=> ((IJSUnmarshalledRuntime)this).InvokeUnmarshalled<T0, object, object, TResult>(identifier, arg0, null, null);

/// <inheritdoc />
TResult IJSUnmarshalledRuntime.InvokeUnmarshalled<T0, T1, TResult>(string identifier, T0 arg0, T1 arg1)
=> ((IJSUnmarshalledRuntime)this).InvokeUnmarshalled<T0, T1, object, TResult>(identifier, arg0, arg1, null);

/// <inheritdoc />
TResult IJSUnmarshalledRuntime.InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2)
internal TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2, long targetInstanceId)
{
var resultType = JSCallResultTypeHelper.FromGeneric<TResult>();

var callInfo = new JSCallInfo
{
FunctionIdentifier = identifier,
ResultType = JSCallResultTypeHelper.FromGeneric<TResult>()
TargetInstanceId = targetInstanceId,
ResultType = resultType,
};

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

return exception != null
? throw new JSException(exception)
: result;
switch (resultType)
{
case JSCallResultType.Default:
var result = InternalCalls.InvokeJS<T0, T1, T2, TResult>(out exception, ref callInfo, arg0, arg1, arg2);
return exception != null
? throw new JSException(exception)
: result;
case JSCallResultType.JSObjectReference:
var id = InternalCalls.InvokeJS<T0, T1, T2, int>(out exception, ref callInfo, arg0, arg1, arg2);
return exception != null
? throw new JSException(exception)
: (TResult)(object)new WebAssemblyJSObjectReference(this, id);
default:
throw new InvalidOperationException($"Invalid result type '{resultType}'.");
}
}

/// <inheritdoc />
public TResult InvokeUnmarshalled<TResult>(string identifier)
=> InvokeUnmarshalled<object, object, object, TResult>(identifier, null, null, null, 0);

/// <inheritdoc />
public TResult InvokeUnmarshalled<T0, TResult>(string identifier, T0 arg0)
=> InvokeUnmarshalled<T0, object, object, TResult>(identifier, arg0, null, null, 0);

/// <inheritdoc />
public TResult InvokeUnmarshalled<T0, T1, TResult>(string identifier, T0 arg0, T1 arg1)
=> InvokeUnmarshalled<T0, T1, object, TResult>(identifier, arg0, arg1, null, 0);

/// <inheritdoc />
public TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2)
=> InvokeUnmarshalled<T0, T1, T2, TResult>(identifier, arg0, arg1, arg2, 0);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ protected override void Dispose(bool disposing)
/// <inheritdoc />
protected override Task UpdateDisplayAsync(in RenderBatch batch)
{
((IJSUnmarshalledRuntime)DefaultWebAssemblyJSRuntime.Instance).InvokeUnmarshalled<int, RenderBatch, object>(
DefaultWebAssemblyJSRuntime.Instance.InvokeUnmarshalled<int, RenderBatch, object>(
"Blazor._internal.renderBatch",
_webAssemblyRendererId,
batch);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ private void WriteMessage(LogLevel logLevel, string logName, int eventId, string
_jsRuntime.InvokeVoid("console.error", formattedMessage);
break;
case LogLevel.Critical:
((IJSUnmarshalledRuntime)_jsRuntime).InvokeUnmarshalled<string, object>("Blazor._internal.dotNetCriticalError", formattedMessage);
_jsRuntime.InvokeUnmarshalled<string, object>("Blazor._internal.dotNetCriticalError", formattedMessage);
break;
default: // LogLevel.None or invalid enum values
Console.WriteLine(formattedMessage);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ internal class WebAssemblyJSRuntimeInvoker
public static WebAssemblyJSRuntimeInvoker Instance = new WebAssemblyJSRuntimeInvoker();

public virtual TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2)
=> ((IJSUnmarshalledRuntime)DefaultWebAssemblyJSRuntime.Instance).InvokeUnmarshalled<T0, T1, T2, TResult>(identifier, arg0, arg1, arg2);
=> DefaultWebAssemblyJSRuntime.Instance.InvokeUnmarshalled<T0, T1, T2, TResult>(identifier, arg0, arg1, arg2);
}
}
1 change: 1 addition & 0 deletions src/Components/test/E2ETest/Tests/InteropTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ public void CanInvokeDotNetMethods()
["instanceMethodIncomingByRef"] = "123",
["instanceMethodOutgoingByRef"] = "1234",
["jsInProcessObjectReference.identity"] = "Invoked from JSInProcessObjectReference",
["jsUnmarshalledObjectReference.unmarshalledFunction"] = "True",
["stringValueUpperSync"] = "MY STRING",
["testDtoNonSerializedValueSync"] = "99999",
["testDtoSync"] = "Same",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,14 @@

var jsInProcObjectReference = inProcRuntime.Invoke<IJSInProcessObjectReference>("returnJSObjectReference");
ReturnValues["jsInProcessObjectReference.identity"] = jsInProcObjectReference.Invoke<string>("identity", "Invoked from JSInProcessObjectReference");

var unmarshalledRuntime = (IJSUnmarshalledRuntime)JSRuntime;
var jsUnmarshalledReference = unmarshalledRuntime.InvokeUnmarshalled<IJSUnmarshalledObjectReference>("returnJSObjectReference");
ReturnValues["jsUnmarshalledObjectReference.unmarshalledFunction"] = jsUnmarshalledReference.InvokeUnmarshalled<InteropStruct, bool>("unmarshalledFunction", new InteropStruct
{
Message = "Sent from .NET",
NumberField = 42,
}).ToString();
}

public class PassDotNetObjectByRefArgs
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System.Runtime.InteropServices;

namespace BasicTestApp.InteropTest
{
[StructLayout(LayoutKind.Explicit)]
public struct InteropStruct
{
[FieldOffset(0)]
public string Message;

[FieldOffset(8)]
public int NumberField;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,11 @@ function returnJSObjectReference() {
dispose: function () {
DotNet.disposeJSObjectReference(this);
},
unmarshalledFunction: function (fields) {
const message = Blazor.platform.readStringField(fields, 0);
const numberField = Blazor.platform.readInt32Field(fields, 8);
return message === "Sent from .NET" && numberField === 42;
}
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;

namespace Microsoft.JSInterop
{
/// <summary>
/// Represents a reference to a JavaScript object whose functions can be invoked synchronously.
/// </summary>
public interface IJSInProcessObjectReference : IJSObjectReference
public interface IJSInProcessObjectReference : IJSObjectReference, IDisposable
{
/// <summary>
/// Invokes the specified JavaScript function synchronously.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace Microsoft.JSInterop
{
/// <summary>
/// Represents a reference to a JavaScript object whose functions can be invoked synchronously without JSON marshalling.
/// </summary>
public interface IJSUnmarshalledObjectReference : IJSInProcessObjectReference
{
/// <summary>
/// Invokes the JavaScript function registered with the specified identifier.
/// </summary>
/// <typeparam name="TResult">The .NET type corresponding to the function's return value type.</typeparam>
/// <param name="identifier">The identifier used when registering the target function.</param>
/// <returns>The result of the function invocation.</returns>
TResult InvokeUnmarshalled<TResult>(string identifier);

/// <summary>
/// Invokes the JavaScript function registered with the specified identifier.
/// </summary>
/// <typeparam name="T0">The type of the first argument.</typeparam>
/// <typeparam name="TResult">The .NET type corresponding to the function's return value type.</typeparam>
/// <param name="identifier">The identifier used when registering the target function.</param>
/// <param name="arg0">The first argument.</param>
/// <returns>The result of the function invocation.</returns>
TResult InvokeUnmarshalled<T0, TResult>(string identifier, T0 arg0);

/// <summary>
/// Invokes the JavaScript function registered with the specified identifier.
/// </summary>
/// <typeparam name="T0">The type of the first argument.</typeparam>
/// <typeparam name="T1">The type of the second argument.</typeparam>
/// <typeparam name="TResult">The .NET type corresponding to the function's return value type.</typeparam>
/// <param name="identifier">The identifier used when registering the target function.</param>
/// <param name="arg0">The first argument.</param>
/// <param name="arg1">The second argument.</param>
/// <returns>The result of the function invocation.</returns>
TResult InvokeUnmarshalled<T0, T1, TResult>(string identifier, T0 arg0, T1 arg1);

/// <summary>
/// Invokes the JavaScript function registered with the specified identifier.
/// </summary>
/// <typeparam name="T0">The type of the first argument.</typeparam>
/// <typeparam name="T1">The type of the second argument.</typeparam>
/// <typeparam name="T2">The type of the third argument.</typeparam>
/// <typeparam name="TResult">The .NET type corresponding to the function's return value type.</typeparam>
/// <param name="identifier">The identifier used when registering the target function.</param>
/// <param name="arg0">The first argument.</param>
/// <param name="arg1">The second argument.</param>
/// <param name="arg2">The third argument.</param>
/// <returns>The result of the function invocation.</returns>
TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Diagnostics.CodeAnalysis;

namespace Microsoft.JSInterop.Implementation
{
/// <summary>
/// Implements functionality for <see cref="IJSInProcessObjectReference"/>.
/// </summary>
public class JSInProcessObjectReference : JSObjectReference, IJSInProcessObjectReference
{
private readonly JSInProcessRuntime _jsRuntime;

/// <summary>
/// Inititializes a new <see cref="JSInProcessObjectReference"/> instance.
/// </summary>
/// <param name="jsRuntime">The <see cref="JSInProcessRuntime"/> used for invoking JS interop calls.</param>
/// <param name="id">The unique identifier.</param>
protected internal JSInProcessObjectReference(JSInProcessRuntime jsRuntime, long id) : base(jsRuntime, id)
{
_jsRuntime = jsRuntime;
}

/// <inheritdoc />
[return: MaybeNull]
public TValue Invoke<TValue>(string identifier, params object?[]? args)
{
ThrowIfDisposed();

return _jsRuntime.Invoke<TValue>(identifier, Id, args);
}

/// <inheritdoc />
public void Dispose()
{
if (!Disposed)
{
Disposed = true;

_jsRuntime.InvokeVoid("DotNet.jsCallDispatcher.disposeJSObjectReferenceById", Id);
}
}
}
}
Loading