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:
Mackinnon Buck 2020-09-04 10:27:10 -07:00 committed by GitHub
parent c61bdfdd25
commit bbc7fd8192
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 337 additions and 71 deletions

File diff suppressed because one or more lines are too long

View File

@ -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}'.`);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
@ -60,32 +61,50 @@ namespace Microsoft.JSInterop.WebAssembly
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);
}
}

View File

@ -98,7 +98,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Rendering
/// <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);

View File

@ -99,7 +99,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Services
_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);

View File

@ -22,6 +22,6 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Services
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);
}
}

View File

@ -110,6 +110,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
["instanceMethodIncomingByRef"] = "123",
["instanceMethodOutgoingByRef"] = "1234",
["jsInProcessObjectReference.identity"] = "Invoked from JSInProcessObjectReference",
["jsUnmarshalledObjectReference.unmarshalledFunction"] = "True",
["stringValueUpperSync"] = "MY STRING",
["testDtoNonSerializedValueSync"] = "99999",
["testDtoSync"] = "Same",

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;
}
};
}

View File

@ -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.

View File

@ -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);
}
}

View File

@ -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);
}
}
}
}

View File

@ -2,29 +2,38 @@
// 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.Threading;
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 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;
Id = id;
}
/// <inheritdoc />
public ValueTask<TValue> InvokeAsync<TValue>(string identifier, object?[]? args)
{
ThrowIfDisposed();
@ -32,6 +41,7 @@ namespace Microsoft.JSInterop
return _jsRuntime.InvokeAsync<TValue>(Id, identifier, args);
}
/// <inheritdoc />
public ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToken cancellationToken, object?[]? args)
{
ThrowIfDisposed();
@ -39,19 +49,21 @@ namespace Microsoft.JSInterop
return _jsRuntime.InvokeAsync<TValue>(Id, identifier, cancellationToken, args);
}
/// <inheritdoc />
public async ValueTask DisposeAsync()
{
if (!_disposed)
if (!Disposed)
{
_disposed = true;
Disposed = true;
await _jsRuntime.InvokeVoidAsync("DotNet.jsCallDispatcher.disposeJSObjectReferenceById", Id);
}
}
/// <inheritdoc />
protected void ThrowIfDisposed()
{
if (_disposed)
if (Disposed)
{
throw new ObjectDisposedException(GetType().Name);
}

View File

@ -4,6 +4,7 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.JSInterop.Implementation;
namespace Microsoft.JSInterop.Infrastructure
{
@ -11,6 +12,8 @@ namespace Microsoft.JSInterop.Infrastructure
where TInterface : class, IJSObjectReference
where TImplementation : JSObjectReference, TInterface
{
private static readonly JsonEncodedText _idKey = JsonEncodedText.Encode("__jsObjectId");
private readonly Func<long, TImplementation> _jsObjectReferenceFactory;
public JSObjectReferenceJsonConverter(Func<long, TImplementation> jsObjectReferenceFactory)
@ -29,7 +32,7 @@ namespace Microsoft.JSInterop.Infrastructure
{
if (reader.TokenType == JsonTokenType.PropertyName)
{
if (id == -1 && reader.ValueTextEquals(JSObjectReference.IdKey.EncodedUtf8Bytes))
if (id == -1 && reader.ValueTextEquals(_idKey.EncodedUtf8Bytes))
{
reader.Read();
id = reader.GetInt64();
@ -47,7 +50,7 @@ namespace Microsoft.JSInterop.Infrastructure
if (id == -1)
{
throw new JsonException($"Required property {JSObjectReference.IdKey} not found.");
throw new JsonException($"Required property {_idKey} not found.");
}
return _jsObjectReferenceFactory(id);
@ -56,7 +59,7 @@ namespace Microsoft.JSInterop.Infrastructure
public override void Write(Utf8JsonWriter writer, TInterface value, JsonSerializerOptions options)
{
writer.WriteStartObject();
writer.WriteNumber(JSObjectReference.IdKey, ((TImplementation)value).Id);
writer.WriteNumber(_idKey, ((TImplementation)value).Id);
writer.WriteEndObject();
}
}

View File

@ -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);
}
}
}

View File

@ -3,6 +3,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using Microsoft.JSInterop.Implementation;
using Microsoft.JSInterop.Infrastructure;
namespace Microsoft.JSInterop
@ -17,8 +18,9 @@ namespace Microsoft.JSInterop
/// </summary>
protected JSInProcessRuntime()
{
JsonSerializerOptions.Converters.Add(new JSObjectReferenceJsonConverter<IJSInProcessObjectReference, JSInProcessObjectReference>(
id => new JSInProcessObjectReference(this, id)));
JsonSerializerOptions.Converters.Add(
new JSObjectReferenceJsonConverter<IJSInProcessObjectReference, JSInProcessObjectReference>(
id => new JSInProcessObjectReference(this, id)));
}
[return: MaybeNull]

View File

@ -8,6 +8,7 @@ using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.JSInterop.Implementation;
using Microsoft.JSInterop.Infrastructure;
namespace Microsoft.JSInterop
@ -37,7 +38,8 @@ namespace Microsoft.JSInterop
Converters =
{
new DotNetObjectReferenceJsonConverterFactory(this),
new JSObjectReferenceJsonConverter<IJSObjectReference, JSObjectReference>(id => new JSObjectReference(this, id)),
new JSObjectReferenceJsonConverter<IJSObjectReference, JSObjectReference>(
id => new JSObjectReference(this, id)),
}
};
}

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>

View File

@ -1,7 +1,9 @@
// 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.Implementation;
using Xunit;
namespace Microsoft.JSInterop.Infrastructure
@ -9,7 +11,16 @@ namespace Microsoft.JSInterop.Infrastructure
public class JSObjectReferenceJsonConverterTest
{
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]
public void Read_Throws_IfJsonIsMissingJSObjectIdProperty()
@ -56,7 +67,7 @@ namespace Microsoft.JSInterop.Infrastructure
}
[Fact]
public void Read_ReadsJson()
public void Read_ReadsJson_IJSObjectReference()
{
// Arrange
var expectedId = 3;
@ -69,6 +80,34 @@ namespace Microsoft.JSInterop.Infrastructure
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]
public void Write_WritesValidJson()
{
@ -81,5 +120,32 @@ namespace Microsoft.JSInterop.Infrastructure
// Assert
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();
}
}
}
}

View File

@ -4,6 +4,7 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.JSInterop.Implementation;
using Microsoft.JSInterop.Infrastructure;
using Xunit;

View File

@ -6,7 +6,9 @@ namespace Microsoft.JSInterop
internal static class JSCallResultTypeHelper
{
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.Default;
}