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 😓
This commit is contained in:
parent
c61bdfdd25
commit
bbc7fd8192
File diff suppressed because one or more lines are too long
|
|
@ -130,7 +130,16 @@ function invokeJSFromDotNet(callInfo: Pointer, arg0: any, arg1: any, arg2: any):
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const func = DotNet.jsCallDispatcher.findJSFunction(functionIdentifier, targetInstanceId);
|
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}'.`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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 System.Text.Json;
|
||||||
using Microsoft.JSInterop.Infrastructure;
|
using Microsoft.JSInterop.Infrastructure;
|
||||||
using WebAssembly.JSInterop;
|
using WebAssembly.JSInterop;
|
||||||
|
|
@ -60,32 +61,50 @@ namespace Microsoft.JSInterop.WebAssembly
|
||||||
BeginInvokeJS(0, "DotNet.jsCallDispatcher.endInvokeDotNetFromJS", args, JSCallResultType.Default, 0);
|
BeginInvokeJS(0, "DotNet.jsCallDispatcher.endInvokeDotNetFromJS", args, JSCallResultType.Default, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
internal TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2, long targetInstanceId)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
|
var resultType = JSCallResultTypeHelper.FromGeneric<TResult>();
|
||||||
|
|
||||||
var callInfo = new JSCallInfo
|
var callInfo = new JSCallInfo
|
||||||
{
|
{
|
||||||
FunctionIdentifier = identifier,
|
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
|
switch (resultType)
|
||||||
? throw new JSException(exception)
|
{
|
||||||
: result;
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Rendering
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override Task UpdateDisplayAsync(in RenderBatch batch)
|
protected override Task UpdateDisplayAsync(in RenderBatch batch)
|
||||||
{
|
{
|
||||||
((IJSUnmarshalledRuntime)DefaultWebAssemblyJSRuntime.Instance).InvokeUnmarshalled<int, RenderBatch, object>(
|
DefaultWebAssemblyJSRuntime.Instance.InvokeUnmarshalled<int, RenderBatch, object>(
|
||||||
"Blazor._internal.renderBatch",
|
"Blazor._internal.renderBatch",
|
||||||
_webAssemblyRendererId,
|
_webAssemblyRendererId,
|
||||||
batch);
|
batch);
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Services
|
||||||
_jsRuntime.InvokeVoid("console.error", formattedMessage);
|
_jsRuntime.InvokeVoid("console.error", formattedMessage);
|
||||||
break;
|
break;
|
||||||
case LogLevel.Critical:
|
case LogLevel.Critical:
|
||||||
((IJSUnmarshalledRuntime)_jsRuntime).InvokeUnmarshalled<string, object>("Blazor._internal.dotNetCriticalError", formattedMessage);
|
_jsRuntime.InvokeUnmarshalled<string, object>("Blazor._internal.dotNetCriticalError", formattedMessage);
|
||||||
break;
|
break;
|
||||||
default: // LogLevel.None or invalid enum values
|
default: // LogLevel.None or invalid enum values
|
||||||
Console.WriteLine(formattedMessage);
|
Console.WriteLine(formattedMessage);
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,6 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Services
|
||||||
public static WebAssemblyJSRuntimeInvoker Instance = new WebAssemblyJSRuntimeInvoker();
|
public static WebAssemblyJSRuntimeInvoker Instance = new WebAssemblyJSRuntimeInvoker();
|
||||||
|
|
||||||
public virtual TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2)
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
|
||||||
["instanceMethodIncomingByRef"] = "123",
|
["instanceMethodIncomingByRef"] = "123",
|
||||||
["instanceMethodOutgoingByRef"] = "1234",
|
["instanceMethodOutgoingByRef"] = "1234",
|
||||||
["jsInProcessObjectReference.identity"] = "Invoked from JSInProcessObjectReference",
|
["jsInProcessObjectReference.identity"] = "Invoked from JSInProcessObjectReference",
|
||||||
|
["jsUnmarshalledObjectReference.unmarshalledFunction"] = "True",
|
||||||
["stringValueUpperSync"] = "MY STRING",
|
["stringValueUpperSync"] = "MY STRING",
|
||||||
["testDtoNonSerializedValueSync"] = "99999",
|
["testDtoNonSerializedValueSync"] = "99999",
|
||||||
["testDtoSync"] = "Same",
|
["testDtoSync"] = "Same",
|
||||||
|
|
|
||||||
|
|
@ -194,6 +194,14 @@
|
||||||
|
|
||||||
var jsInProcObjectReference = inProcRuntime.Invoke<IJSInProcessObjectReference>("returnJSObjectReference");
|
var jsInProcObjectReference = inProcRuntime.Invoke<IJSInProcessObjectReference>("returnJSObjectReference");
|
||||||
ReturnValues["jsInProcessObjectReference.identity"] = jsInProcObjectReference.Invoke<string>("identity", "Invoked from JSInProcessObjectReference");
|
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
|
public class PassDotNetObjectByRefArgs
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -233,6 +233,11 @@ function returnJSObjectReference() {
|
||||||
dispose: function () {
|
dispose: function () {
|
||||||
DotNet.disposeJSObjectReference(this);
|
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;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Microsoft.JSInterop
|
namespace Microsoft.JSInterop
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a reference to a JavaScript object whose functions can be invoked synchronously.
|
/// Represents a reference to a JavaScript object whose functions can be invoked synchronously.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IJSInProcessObjectReference : IJSObjectReference
|
public interface IJSInProcessObjectReference : IJSObjectReference, IDisposable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invokes the specified JavaScript function synchronously.
|
/// Invokes the specified JavaScript function synchronously.
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,29 +2,38 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Text.Json;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Microsoft.JSInterop
|
namespace Microsoft.JSInterop.Implementation
|
||||||
{
|
{
|
||||||
internal class JSObjectReference : IJSObjectReference
|
/// <summary>
|
||||||
|
/// Implements functionality for <see cref="IJSObjectReference"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class JSObjectReference : IJSObjectReference
|
||||||
{
|
{
|
||||||
public static readonly JsonEncodedText IdKey = JsonEncodedText.Encode("__jsObjectId");
|
|
||||||
|
|
||||||
private readonly JSRuntime _jsRuntime;
|
private readonly JSRuntime _jsRuntime;
|
||||||
|
|
||||||
private bool _disposed;
|
internal bool Disposed { get; set; }
|
||||||
|
|
||||||
public long Id { get; }
|
/// <summary>
|
||||||
|
/// The unique identifier assigned to this instance.
|
||||||
|
/// </summary>
|
||||||
|
protected internal long Id { get; }
|
||||||
|
|
||||||
public JSObjectReference(JSRuntime jsRuntime, long id)
|
/// <summary>
|
||||||
|
/// Inititializes a new <see cref="JSObjectReference"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jsRuntime">The <see cref="JSRuntime"/> used for invoking JS interop calls.</param>
|
||||||
|
/// <param name="id">The unique identifier.</param>
|
||||||
|
protected internal JSObjectReference(JSRuntime jsRuntime, long id)
|
||||||
{
|
{
|
||||||
_jsRuntime = jsRuntime;
|
_jsRuntime = jsRuntime;
|
||||||
|
|
||||||
Id = id;
|
Id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public ValueTask<TValue> InvokeAsync<TValue>(string identifier, object?[]? args)
|
public ValueTask<TValue> InvokeAsync<TValue>(string identifier, object?[]? args)
|
||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
|
|
@ -32,6 +41,7 @@ namespace Microsoft.JSInterop
|
||||||
return _jsRuntime.InvokeAsync<TValue>(Id, identifier, args);
|
return _jsRuntime.InvokeAsync<TValue>(Id, identifier, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToken cancellationToken, object?[]? args)
|
public ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToken cancellationToken, object?[]? args)
|
||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
|
|
@ -39,19 +49,21 @@ namespace Microsoft.JSInterop
|
||||||
return _jsRuntime.InvokeAsync<TValue>(Id, identifier, cancellationToken, args);
|
return _jsRuntime.InvokeAsync<TValue>(Id, identifier, cancellationToken, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public async ValueTask DisposeAsync()
|
public async ValueTask DisposeAsync()
|
||||||
{
|
{
|
||||||
if (!_disposed)
|
if (!Disposed)
|
||||||
{
|
{
|
||||||
_disposed = true;
|
Disposed = true;
|
||||||
|
|
||||||
await _jsRuntime.InvokeVoidAsync("DotNet.jsCallDispatcher.disposeJSObjectReferenceById", Id);
|
await _jsRuntime.InvokeVoidAsync("DotNet.jsCallDispatcher.disposeJSObjectReferenceById", Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
protected void ThrowIfDisposed()
|
protected void ThrowIfDisposed()
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (Disposed)
|
||||||
{
|
{
|
||||||
throw new ObjectDisposedException(GetType().Name);
|
throw new ObjectDisposedException(GetType().Name);
|
||||||
}
|
}
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
using Microsoft.JSInterop.Implementation;
|
||||||
|
|
||||||
namespace Microsoft.JSInterop.Infrastructure
|
namespace Microsoft.JSInterop.Infrastructure
|
||||||
{
|
{
|
||||||
|
|
@ -11,6 +12,8 @@ namespace Microsoft.JSInterop.Infrastructure
|
||||||
where TInterface : class, IJSObjectReference
|
where TInterface : class, IJSObjectReference
|
||||||
where TImplementation : JSObjectReference, TInterface
|
where TImplementation : JSObjectReference, TInterface
|
||||||
{
|
{
|
||||||
|
private static readonly JsonEncodedText _idKey = JsonEncodedText.Encode("__jsObjectId");
|
||||||
|
|
||||||
private readonly Func<long, TImplementation> _jsObjectReferenceFactory;
|
private readonly Func<long, TImplementation> _jsObjectReferenceFactory;
|
||||||
|
|
||||||
public JSObjectReferenceJsonConverter(Func<long, TImplementation> jsObjectReferenceFactory)
|
public JSObjectReferenceJsonConverter(Func<long, TImplementation> jsObjectReferenceFactory)
|
||||||
|
|
@ -29,7 +32,7 @@ namespace Microsoft.JSInterop.Infrastructure
|
||||||
{
|
{
|
||||||
if (reader.TokenType == JsonTokenType.PropertyName)
|
if (reader.TokenType == JsonTokenType.PropertyName)
|
||||||
{
|
{
|
||||||
if (id == -1 && reader.ValueTextEquals(JSObjectReference.IdKey.EncodedUtf8Bytes))
|
if (id == -1 && reader.ValueTextEquals(_idKey.EncodedUtf8Bytes))
|
||||||
{
|
{
|
||||||
reader.Read();
|
reader.Read();
|
||||||
id = reader.GetInt64();
|
id = reader.GetInt64();
|
||||||
|
|
@ -47,7 +50,7 @@ namespace Microsoft.JSInterop.Infrastructure
|
||||||
|
|
||||||
if (id == -1)
|
if (id == -1)
|
||||||
{
|
{
|
||||||
throw new JsonException($"Required property {JSObjectReference.IdKey} not found.");
|
throw new JsonException($"Required property {_idKey} not found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return _jsObjectReferenceFactory(id);
|
return _jsObjectReferenceFactory(id);
|
||||||
|
|
@ -56,7 +59,7 @@ namespace Microsoft.JSInterop.Infrastructure
|
||||||
public override void Write(Utf8JsonWriter writer, TInterface value, JsonSerializerOptions options)
|
public override void Write(Utf8JsonWriter writer, TInterface value, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
writer.WriteStartObject();
|
writer.WriteStartObject();
|
||||||
writer.WriteNumber(JSObjectReference.IdKey, ((TImplementation)value).Id);
|
writer.WriteNumber(_idKey, ((TImplementation)value).Id);
|
||||||
writer.WriteEndObject();
|
writer.WriteEndObject();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
// 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
|
|
||||||
{
|
|
||||||
internal class JSInProcessObjectReference : JSObjectReference, IJSInProcessObjectReference
|
|
||||||
{
|
|
||||||
private readonly JSInProcessRuntime _jsRuntime;
|
|
||||||
|
|
||||||
internal JSInProcessObjectReference(JSInProcessRuntime jsRuntime, long id) : base(jsRuntime, id)
|
|
||||||
{
|
|
||||||
_jsRuntime = jsRuntime;
|
|
||||||
}
|
|
||||||
|
|
||||||
[return: MaybeNull]
|
|
||||||
public TValue Invoke<TValue>(string identifier, params object?[]? args)
|
|
||||||
{
|
|
||||||
ThrowIfDisposed();
|
|
||||||
|
|
||||||
return _jsRuntime.Invoke<TValue>(identifier, Id, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using Microsoft.JSInterop.Implementation;
|
||||||
using Microsoft.JSInterop.Infrastructure;
|
using Microsoft.JSInterop.Infrastructure;
|
||||||
|
|
||||||
namespace Microsoft.JSInterop
|
namespace Microsoft.JSInterop
|
||||||
|
|
@ -17,8 +18,9 @@ namespace Microsoft.JSInterop
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected JSInProcessRuntime()
|
protected JSInProcessRuntime()
|
||||||
{
|
{
|
||||||
JsonSerializerOptions.Converters.Add(new JSObjectReferenceJsonConverter<IJSInProcessObjectReference, JSInProcessObjectReference>(
|
JsonSerializerOptions.Converters.Add(
|
||||||
id => new JSInProcessObjectReference(this, id)));
|
new JSObjectReferenceJsonConverter<IJSInProcessObjectReference, JSInProcessObjectReference>(
|
||||||
|
id => new JSInProcessObjectReference(this, id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
[return: MaybeNull]
|
[return: MaybeNull]
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.JSInterop.Implementation;
|
||||||
using Microsoft.JSInterop.Infrastructure;
|
using Microsoft.JSInterop.Infrastructure;
|
||||||
|
|
||||||
namespace Microsoft.JSInterop
|
namespace Microsoft.JSInterop
|
||||||
|
|
@ -37,7 +38,8 @@ namespace Microsoft.JSInterop
|
||||||
Converters =
|
Converters =
|
||||||
{
|
{
|
||||||
new DotNetObjectReferenceJsonConverterFactory(this),
|
new DotNetObjectReferenceJsonConverterFactory(this),
|
||||||
new JSObjectReferenceJsonConverter<IJSObjectReference, JSObjectReference>(id => new JSObjectReference(this, id)),
|
new JSObjectReferenceJsonConverter<IJSObjectReference, JSObjectReference>(
|
||||||
|
id => new JSObjectReference(this, id)),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
|
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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 System.Text.Json;
|
||||||
|
using Microsoft.JSInterop.Implementation;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.JSInterop.Infrastructure
|
namespace Microsoft.JSInterop.Infrastructure
|
||||||
|
|
@ -9,7 +11,16 @@ namespace Microsoft.JSInterop.Infrastructure
|
||||||
public class JSObjectReferenceJsonConverterTest
|
public class JSObjectReferenceJsonConverterTest
|
||||||
{
|
{
|
||||||
private readonly JSRuntime JSRuntime = new TestJSRuntime();
|
private readonly JSRuntime JSRuntime = new TestJSRuntime();
|
||||||
private JsonSerializerOptions JsonSerializerOptions => JSRuntime.JsonSerializerOptions;
|
private readonly JsonSerializerOptions JsonSerializerOptions;
|
||||||
|
|
||||||
|
public JSObjectReferenceJsonConverterTest()
|
||||||
|
{
|
||||||
|
JsonSerializerOptions = JSRuntime.JsonSerializerOptions;
|
||||||
|
JsonSerializerOptions.Converters.Add(new JSObjectReferenceJsonConverter<IJSInProcessObjectReference, JSInProcessObjectReference>(
|
||||||
|
id => new JSInProcessObjectReference(default!, id)));
|
||||||
|
JsonSerializerOptions.Converters.Add(new JSObjectReferenceJsonConverter<IJSUnmarshalledObjectReference, TestJSUnmarshalledObjectReference>(
|
||||||
|
id => new TestJSUnmarshalledObjectReference(id)));
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Read_Throws_IfJsonIsMissingJSObjectIdProperty()
|
public void Read_Throws_IfJsonIsMissingJSObjectIdProperty()
|
||||||
|
|
@ -56,7 +67,7 @@ namespace Microsoft.JSInterop.Infrastructure
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Read_ReadsJson()
|
public void Read_ReadsJson_IJSObjectReference()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var expectedId = 3;
|
var expectedId = 3;
|
||||||
|
|
@ -69,6 +80,34 @@ namespace Microsoft.JSInterop.Infrastructure
|
||||||
Assert.Equal(expectedId, deserialized?.Id);
|
Assert.Equal(expectedId, deserialized?.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Read_ReadsJson_IJSInProcessObjectReference()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedId = 3;
|
||||||
|
var json = $"{{\"__jsObjectId\":{expectedId}}}";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var deserialized = (JSInProcessObjectReference)JsonSerializer.Deserialize<IJSInProcessObjectReference>(json, JsonSerializerOptions)!;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedId, deserialized?.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Read_ReadsJson_IJSUnmarshalledObjectReference()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedId = 3;
|
||||||
|
var json = $"{{\"__jsObjectId\":{expectedId}}}";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var deserialized = (TestJSUnmarshalledObjectReference)JsonSerializer.Deserialize<IJSUnmarshalledObjectReference>(json, JsonSerializerOptions)!;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedId, deserialized?.Id);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Write_WritesValidJson()
|
public void Write_WritesValidJson()
|
||||||
{
|
{
|
||||||
|
|
@ -81,5 +120,32 @@ namespace Microsoft.JSInterop.Infrastructure
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal($"{{\"__jsObjectId\":{jsObjectRef.Id}}}", json);
|
Assert.Equal($"{{\"__jsObjectId\":{jsObjectRef.Id}}}", json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TestJSUnmarshalledObjectReference : JSInProcessObjectReference, IJSUnmarshalledObjectReference
|
||||||
|
{
|
||||||
|
public TestJSUnmarshalledObjectReference(long id) : base(default!, id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public TResult InvokeUnmarshalled<TResult>(string identifier)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TResult InvokeUnmarshalled<T0, TResult>(string identifier, T0 arg0)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TResult InvokeUnmarshalled<T0, T1, TResult>(string identifier, T0 arg0, T1 arg1)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.JSInterop.Implementation;
|
||||||
using Microsoft.JSInterop.Infrastructure;
|
using Microsoft.JSInterop.Infrastructure;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@ namespace Microsoft.JSInterop
|
||||||
internal static class JSCallResultTypeHelper
|
internal static class JSCallResultTypeHelper
|
||||||
{
|
{
|
||||||
public static JSCallResultType FromGeneric<TResult>()
|
public static JSCallResultType FromGeneric<TResult>()
|
||||||
=> typeof(TResult) == typeof(IJSObjectReference) || typeof(TResult) == typeof(IJSInProcessObjectReference) ?
|
=> typeof(TResult) == typeof(IJSObjectReference)
|
||||||
|
|| typeof(TResult) == typeof(IJSInProcessObjectReference)
|
||||||
|
|| typeof(TResult) == typeof(IJSUnmarshalledObjectReference) ?
|
||||||
JSCallResultType.JSObjectReference :
|
JSCallResultType.JSObjectReference :
|
||||||
JSCallResultType.Default;
|
JSCallResultType.Default;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue