Simplify JSRuntime method signature (dotnet/extensions#2188)

\n\nCommit migrated from 9c392a92ef
This commit is contained in:
Pranav K 2019-08-16 16:19:23 -07:00 committed by GitHub
parent ef83e3359d
commit d46d569b81
12 changed files with 249 additions and 122 deletions

View File

@ -50,7 +50,7 @@ namespace Microsoft.JSInterop
protected System.TimeSpan? DefaultAsyncTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } protected System.TimeSpan? DefaultAsyncTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
protected internal System.Text.Json.JsonSerializerOptions JsonSerializerOptions { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } protected internal System.Text.Json.JsonSerializerOptions JsonSerializerOptions { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
protected abstract void BeginInvokeJS(long taskId, string identifier, string argsJson); protected abstract void BeginInvokeJS(long taskId, string identifier, string argsJson);
protected internal abstract void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId); protected internal abstract void EndInvokeDotNet(Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo invocationInfo, in Microsoft.JSInterop.Infrastructure.DotNetInvocationResult invocationResult);
public System.Threading.Tasks.ValueTask<TValue> InvokeAsync<TValue>(string identifier, object[] args) { throw null; } public System.Threading.Tasks.ValueTask<TValue> InvokeAsync<TValue>(string identifier, object[] args) { throw null; }
public System.Threading.Tasks.ValueTask<TValue> InvokeAsync<TValue>(string identifier, System.Threading.CancellationToken cancellationToken, object[] args) { throw null; } public System.Threading.Tasks.ValueTask<TValue> InvokeAsync<TValue>(string identifier, System.Threading.CancellationToken cancellationToken, object[] args) { throw null; }
} }
@ -72,8 +72,31 @@ namespace Microsoft.JSInterop.Infrastructure
{ {
public static partial class DotNetDispatcher public static partial class DotNetDispatcher
{ {
public static void BeginInvokeDotNet(Microsoft.JSInterop.JSRuntime jsRuntime, string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { } public static void BeginInvokeDotNet(Microsoft.JSInterop.JSRuntime jsRuntime, Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo invocationInfo, string argsJson) { }
public static void EndInvokeJS(Microsoft.JSInterop.JSRuntime jsRuntime, string arguments) { } public static void EndInvokeJS(Microsoft.JSInterop.JSRuntime jsRuntime, string arguments) { }
public static string Invoke(Microsoft.JSInterop.JSRuntime jsRuntime, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { throw null; } public static string Invoke(Microsoft.JSInterop.JSRuntime jsRuntime, in Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo invocationInfo, string argsJson) { throw null; }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct DotNetInvocationInfo
{
private readonly object _dummy;
private readonly int _dummyPrimitive;
public DotNetInvocationInfo(string assemblyName, string methodIdentifier, long dotNetObjectId, string callId) { throw null; }
public string AssemblyName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public string CallId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public long DotNetObjectId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public string MethodIdentifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct DotNetInvocationResult
{
private readonly object _dummy;
private readonly int _dummyPrimitive;
public DotNetInvocationResult(System.Exception exception, string errorKind) { throw null; }
public DotNetInvocationResult(object result) { throw null; }
public string ErrorKind { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public object Result { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public bool Success { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
} }
} }

View File

@ -50,7 +50,7 @@ namespace Microsoft.JSInterop
protected System.TimeSpan? DefaultAsyncTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } protected System.TimeSpan? DefaultAsyncTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
protected internal System.Text.Json.JsonSerializerOptions JsonSerializerOptions { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } protected internal System.Text.Json.JsonSerializerOptions JsonSerializerOptions { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
protected abstract void BeginInvokeJS(long taskId, string identifier, string argsJson); protected abstract void BeginInvokeJS(long taskId, string identifier, string argsJson);
protected internal abstract void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId); protected internal abstract void EndInvokeDotNet(Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo invocationInfo, in Microsoft.JSInterop.Infrastructure.DotNetInvocationResult invocationResult);
public System.Threading.Tasks.ValueTask<TValue> InvokeAsync<TValue>(string identifier, object[] args) { throw null; } public System.Threading.Tasks.ValueTask<TValue> InvokeAsync<TValue>(string identifier, object[] args) { throw null; }
public System.Threading.Tasks.ValueTask<TValue> InvokeAsync<TValue>(string identifier, System.Threading.CancellationToken cancellationToken, object[] args) { throw null; } public System.Threading.Tasks.ValueTask<TValue> InvokeAsync<TValue>(string identifier, System.Threading.CancellationToken cancellationToken, object[] args) { throw null; }
} }
@ -72,8 +72,31 @@ namespace Microsoft.JSInterop.Infrastructure
{ {
public static partial class DotNetDispatcher public static partial class DotNetDispatcher
{ {
public static void BeginInvokeDotNet(Microsoft.JSInterop.JSRuntime jsRuntime, string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { } public static void BeginInvokeDotNet(Microsoft.JSInterop.JSRuntime jsRuntime, Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo invocationInfo, string argsJson) { }
public static void EndInvokeJS(Microsoft.JSInterop.JSRuntime jsRuntime, string arguments) { } public static void EndInvokeJS(Microsoft.JSInterop.JSRuntime jsRuntime, string arguments) { }
public static string Invoke(Microsoft.JSInterop.JSRuntime jsRuntime, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { throw null; } public static string Invoke(Microsoft.JSInterop.JSRuntime jsRuntime, in Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo invocationInfo, string argsJson) { throw null; }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct DotNetInvocationInfo
{
private readonly object _dummy;
private readonly int _dummyPrimitive;
public DotNetInvocationInfo(string assemblyName, string methodIdentifier, long dotNetObjectId, string callId) { throw null; }
public string AssemblyName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public string CallId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public long DotNetObjectId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public string MethodIdentifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct DotNetInvocationResult
{
private readonly object _dummy;
private readonly int _dummyPrimitive;
public DotNetInvocationResult(System.Exception exception, string errorKind) { throw null; }
public DotNetInvocationResult(object result) { throw null; }
public string ErrorKind { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public object Result { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public bool Success { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
} }
} }

View File

@ -28,12 +28,10 @@ namespace Microsoft.JSInterop.Infrastructure
/// Receives a call from JS to .NET, locating and invoking the specified method. /// Receives a call from JS to .NET, locating and invoking the specified method.
/// </summary> /// </summary>
/// <param name="jsRuntime">The <see cref="JSRuntime"/>.</param> /// <param name="jsRuntime">The <see cref="JSRuntime"/>.</param>
/// <param name="assemblyName">The assembly containing the method to be invoked.</param> /// <param name="invocationInfo">The <see cref="DotNetInvocationInfo"/>.</param>
/// <param name="methodIdentifier">The identifier of the method to be invoked. The method must be annotated with a <see cref="JSInvokableAttribute"/> matching this identifier string.</param>
/// <param name="dotNetObjectId">For instance method calls, identifies the target object.</param>
/// <param name="argsJson">A JSON representation of the parameters.</param> /// <param name="argsJson">A JSON representation of the parameters.</param>
/// <returns>A JSON representation of the return value, or null.</returns> /// <returns>A JSON representation of the return value, or null.</returns>
public static string Invoke(JSRuntime jsRuntime, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) public static string Invoke(JSRuntime jsRuntime, in DotNetInvocationInfo invocationInfo, string argsJson)
{ {
// This method doesn't need [JSInvokable] because the platform is responsible for having // This method doesn't need [JSInvokable] because the platform is responsible for having
// some way to dispatch calls here. The logic inside here is the thing that checks whether // some way to dispatch calls here. The logic inside here is the thing that checks whether
@ -41,12 +39,12 @@ namespace Microsoft.JSInterop.Infrastructure
// because there would be nobody to police that. This method *is* the police. // because there would be nobody to police that. This method *is* the police.
IDotNetObjectReference targetInstance = default; IDotNetObjectReference targetInstance = default;
if (dotNetObjectId != default) if (invocationInfo.DotNetObjectId != default)
{ {
targetInstance = jsRuntime.GetObjectReference(dotNetObjectId); targetInstance = jsRuntime.GetObjectReference(invocationInfo.DotNetObjectId);
} }
var syncResult = InvokeSynchronously(jsRuntime, assemblyName, methodIdentifier, targetInstance, argsJson); var syncResult = InvokeSynchronously(jsRuntime, invocationInfo, targetInstance, argsJson);
if (syncResult == null) if (syncResult == null)
{ {
return null; return null;
@ -59,13 +57,10 @@ namespace Microsoft.JSInterop.Infrastructure
/// Receives a call from JS to .NET, locating and invoking the specified method asynchronously. /// Receives a call from JS to .NET, locating and invoking the specified method asynchronously.
/// </summary> /// </summary>
/// <param name="jsRuntime">The <see cref="JSRuntime"/>.</param> /// <param name="jsRuntime">The <see cref="JSRuntime"/>.</param>
/// <param name="callId">A value identifying the asynchronous call that should be passed back with the result, or null if no result notification is required.</param> /// <param name="invocationInfo">The <see cref="DotNetInvocationInfo"/>.</param>
/// <param name="assemblyName">The assembly containing the method to be invoked.</param>
/// <param name="methodIdentifier">The identifier of the method to be invoked. The method must be annotated with a <see cref="JSInvokableAttribute"/> matching this identifier string.</param>
/// <param name="dotNetObjectId">For instance method calls, identifies the target object.</param>
/// <param name="argsJson">A JSON representation of the parameters.</param> /// <param name="argsJson">A JSON representation of the parameters.</param>
/// <returns>A JSON representation of the return value, or null.</returns> /// <returns>A JSON representation of the return value, or null.</returns>
public static void BeginInvokeDotNet(JSRuntime jsRuntime, string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) public static void BeginInvokeDotNet(JSRuntime jsRuntime, DotNetInvocationInfo invocationInfo, string argsJson)
{ {
// This method doesn't need [JSInvokable] because the platform is responsible for having // This method doesn't need [JSInvokable] because the platform is responsible for having
// some way to dispatch calls here. The logic inside here is the thing that checks whether // some way to dispatch calls here. The logic inside here is the thing that checks whether
@ -75,18 +70,19 @@ namespace Microsoft.JSInterop.Infrastructure
// Using ExceptionDispatchInfo here throughout because we want to always preserve // Using ExceptionDispatchInfo here throughout because we want to always preserve
// original stack traces. // original stack traces.
var callId = invocationInfo.CallId;
object syncResult = null; object syncResult = null;
ExceptionDispatchInfo syncException = null; ExceptionDispatchInfo syncException = null;
IDotNetObjectReference targetInstance = null; IDotNetObjectReference targetInstance = null;
try try
{ {
if (dotNetObjectId != default) if (invocationInfo.DotNetObjectId != default)
{ {
targetInstance = jsRuntime.GetObjectReference(dotNetObjectId); targetInstance = jsRuntime.GetObjectReference(invocationInfo.DotNetObjectId);
} }
syncResult = InvokeSynchronously(jsRuntime, assemblyName, methodIdentifier, targetInstance, argsJson); syncResult = InvokeSynchronously(jsRuntime, invocationInfo, targetInstance, argsJson);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -101,7 +97,7 @@ namespace Microsoft.JSInterop.Infrastructure
else if (syncException != null) else if (syncException != null)
{ {
// Threw synchronously, let's respond. // Threw synchronously, let's respond.
jsRuntime.EndInvokeDotNet(callId, false, syncException, assemblyName, methodIdentifier, dotNetObjectId); jsRuntime.EndInvokeDotNet(invocationInfo, new DotNetInvocationResult(syncException.SourceException, "InvocationFailure"));
} }
else if (syncResult is Task task) else if (syncResult is Task task)
{ {
@ -111,23 +107,27 @@ namespace Microsoft.JSInterop.Infrastructure
{ {
if (t.Exception != null) if (t.Exception != null)
{ {
var exception = t.Exception.GetBaseException(); var exceptionDispatchInfo = ExceptionDispatchInfo.Capture(t.Exception.GetBaseException());
var dispatchResult = new DotNetInvocationResult(exceptionDispatchInfo.SourceException, "InvocationFailure");
jsRuntime.EndInvokeDotNet(callId, false, ExceptionDispatchInfo.Capture(exception), assemblyName, methodIdentifier, dotNetObjectId); jsRuntime.EndInvokeDotNet(invocationInfo, dispatchResult);
} }
var result = TaskGenericsUtil.GetTaskResult(task); var result = TaskGenericsUtil.GetTaskResult(task);
jsRuntime.EndInvokeDotNet(callId, true, result, assemblyName, methodIdentifier, dotNetObjectId); jsRuntime.EndInvokeDotNet(invocationInfo, new DotNetInvocationResult(result));
}, TaskScheduler.Current); }, TaskScheduler.Current);
} }
else else
{ {
jsRuntime.EndInvokeDotNet(callId, true, syncResult, assemblyName, methodIdentifier, dotNetObjectId); var dispatchResult = new DotNetInvocationResult(syncResult);
jsRuntime.EndInvokeDotNet(invocationInfo, dispatchResult);
} }
} }
private static object InvokeSynchronously(JSRuntime jsRuntime, string assemblyName, string methodIdentifier, IDotNetObjectReference objectReference, string argsJson) private static object InvokeSynchronously(JSRuntime jsRuntime, in DotNetInvocationInfo callInfo, IDotNetObjectReference objectReference, string argsJson)
{ {
var assemblyName = callInfo.AssemblyName;
var methodIdentifier = callInfo.MethodIdentifier;
AssemblyKey assemblyKey; AssemblyKey assemblyKey;
if (objectReference is null) if (objectReference is null)
{ {

View File

@ -0,0 +1,48 @@
// 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.Infrastructure
{
/// <summary>
/// Information about a JSInterop call from JavaScript to .NET.
/// </summary>
public readonly struct DotNetInvocationInfo
{
/// <summary>
/// Initializes a new instance of <see cref="DotNetInvocationInfo"/>.
/// </summary>
/// <param name="assemblyName">The name of the assembly containing the method.</param>
/// <param name="methodIdentifier">The identifier of the method to be invoked.</param>
/// <param name="dotNetObjectId">The object identifier for instance method calls.</param>
/// <param name="callId">The call identifier.</param>
public DotNetInvocationInfo(string assemblyName, string methodIdentifier, long dotNetObjectId, string callId)
{
CallId = callId;
AssemblyName = assemblyName;
MethodIdentifier = methodIdentifier;
DotNetObjectId = dotNetObjectId;
}
/// <summary>
/// Gets the name of the assembly containing the method.
/// Only one of <see cref="DotNetObjectId"/> or <see cref="AssemblyName"/> may be specified.
/// </summary>
public string AssemblyName { get; }
/// <summary>
/// Gets the identifier of the method to be invoked. This is the value specified in the <see cref="JSInvokableAttribute"/>.
/// </summary>
public string MethodIdentifier { get; }
/// <summary>
/// Gets the object identifier for instance method calls.
/// Only one of <see cref="DotNetObjectId"/> or <see cref="AssemblyName"/> may be specified.
/// </summary>
public long DotNetObjectId { get; }
/// <summary>
/// Gets the call identifier. This value is <see langword="null"/> when the client does not expect a value to be returned.
/// </summary>
public string CallId { get; }
}
}

View File

@ -0,0 +1,55 @@
using System;
namespace Microsoft.JSInterop.Infrastructure
{
/// <summary>
/// Result of a .NET invocation that is returned to JavaScript.
/// </summary>
public readonly struct DotNetInvocationResult
{
/// <summary>
/// Constructor for a failed invocation.
/// </summary>
/// <param name="exception">The <see cref="System.Exception"/> that caused the failure.</param>
/// <param name="errorKind">The error kind.</param>
public DotNetInvocationResult(Exception exception, string errorKind)
{
Result = default;
Exception = exception ?? throw new ArgumentNullException(nameof(exception));
ErrorKind = errorKind;
Success = false;
}
/// <summary>
/// Constructor for a successful invocation.
/// </summary>
/// <param name="result">The result.</param>
public DotNetInvocationResult(object result)
{
Result = result;
Exception = default;
ErrorKind = default;
Success = true;
}
/// <summary>
/// Gets the <see cref="System.Exception"/> that caused the failure.
/// </summary>
public Exception Exception { get; }
/// <summary>
/// Gets the error kind.
/// </summary>
public string ErrorKind { get; }
/// <summary>
/// Gets the result of a successful invocation.
/// </summary>
public object Result { get; }
/// <summary>
/// <see langword="true"/> if the invocation succeeded, otherwise <see langword="false"/>.
/// </summary>
public bool Success { get; }
}
}

View File

@ -150,19 +150,11 @@ namespace Microsoft.JSInterop
/// <summary> /// <summary>
/// Completes an async JS interop call from JavaScript to .NET /// Completes an async JS interop call from JavaScript to .NET
/// </summary> /// </summary>
/// <param name="callId">The id of the JavaScript callback to execute on completion.</param> /// <param name="invocationInfo">The <see cref="DotNetInvocationInfo"/>.</param>
/// <param name="success">Whether the operation succeeded or not.</param> /// <param name="invocationResult">The <see cref="DotNetInvocationResult"/>.</param>
/// <param name="resultOrError">The result of the operation or an object containing error details.</param>
/// <param name="assemblyName">The name of the method assembly if the invocation was for a static method.</param>
/// <param name="methodIdentifier">The identifier for the method within the assembly.</param>
/// <param name="dotNetObjectId">The tracking id of the dotnet object if the invocation was for an instance method.</param>
protected internal abstract void EndInvokeDotNet( protected internal abstract void EndInvokeDotNet(
string callId, DotNetInvocationInfo invocationInfo,
bool success, in DotNetInvocationResult invocationResult);
object resultOrError,
string assemblyName,
string methodIdentifier,
long dotNetObjectId);
internal void EndInvokeJS(long taskId, bool succeeded, ref Utf8JsonReader jsonReader) internal void EndInvokeJS(long taskId, bool succeeded, ref Utf8JsonReader jsonReader)
{ {

View File

@ -20,7 +20,7 @@ namespace Microsoft.JSInterop.Infrastructure
{ {
var ex = Assert.Throws<ArgumentException>(() => var ex = Assert.Throws<ArgumentException>(() =>
{ {
DotNetDispatcher.Invoke(new TestJSRuntime(), " ", "SomeMethod", default, "[]"); DotNetDispatcher.Invoke(new TestJSRuntime(), new DotNetInvocationInfo(" ", "SomeMethod", default, default), "[]");
}); });
Assert.StartsWith("Cannot be null, empty, or whitespace.", ex.Message); Assert.StartsWith("Cannot be null, empty, or whitespace.", ex.Message);
@ -32,7 +32,7 @@ namespace Microsoft.JSInterop.Infrastructure
{ {
var ex = Assert.Throws<ArgumentException>(() => var ex = Assert.Throws<ArgumentException>(() =>
{ {
DotNetDispatcher.Invoke(new TestJSRuntime(), "SomeAssembly", " ", default, "[]"); DotNetDispatcher.Invoke(new TestJSRuntime(), new DotNetInvocationInfo("SomeAssembly", " ", default, default), "[]");
}); });
Assert.StartsWith("Cannot be null, empty, or whitespace.", ex.Message); Assert.StartsWith("Cannot be null, empty, or whitespace.", ex.Message);
@ -45,7 +45,7 @@ namespace Microsoft.JSInterop.Infrastructure
var assemblyName = "Some.Fake.Assembly"; var assemblyName = "Some.Fake.Assembly";
var ex = Assert.Throws<ArgumentException>(() => var ex = Assert.Throws<ArgumentException>(() =>
{ {
DotNetDispatcher.Invoke(new TestJSRuntime(), assemblyName, "SomeMethod", default, null); DotNetDispatcher.Invoke(new TestJSRuntime(), new DotNetInvocationInfo(assemblyName, "SomeMethod", default, default), null);
}); });
Assert.Equal($"There is no loaded assembly with the name '{assemblyName}'.", ex.Message); Assert.Equal($"There is no loaded assembly with the name '{assemblyName}'.", ex.Message);
@ -67,7 +67,7 @@ namespace Microsoft.JSInterop.Infrastructure
{ {
var ex = Assert.Throws<ArgumentException>(() => var ex = Assert.Throws<ArgumentException>(() =>
{ {
DotNetDispatcher.Invoke(new TestJSRuntime(), thisAssemblyName, methodIdentifier, default, null); DotNetDispatcher.Invoke(new TestJSRuntime(), new DotNetInvocationInfo(thisAssemblyName, methodIdentifier, default, default), null);
}); });
Assert.Equal($"The assembly '{thisAssemblyName}' does not contain a public method with [JSInvokableAttribute(\"{methodIdentifier}\")].", ex.Message); Assert.Equal($"The assembly '{thisAssemblyName}' does not contain a public method with [JSInvokableAttribute(\"{methodIdentifier}\")].", ex.Message);
@ -79,7 +79,7 @@ namespace Microsoft.JSInterop.Infrastructure
// Arrange/Act // Arrange/Act
var jsRuntime = new TestJSRuntime(); var jsRuntime = new TestJSRuntime();
SomePublicType.DidInvokeMyInvocableStaticVoid = false; SomePublicType.DidInvokeMyInvocableStaticVoid = false;
var resultJson = DotNetDispatcher.Invoke(jsRuntime, thisAssemblyName, "InvocableStaticVoid", default, null); var resultJson = DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(thisAssemblyName, "InvocableStaticVoid", default, default), null);
// Assert // Assert
Assert.Null(resultJson); Assert.Null(resultJson);
@ -91,7 +91,7 @@ namespace Microsoft.JSInterop.Infrastructure
{ {
// Arrange/Act // Arrange/Act
var jsRuntime = new TestJSRuntime(); var jsRuntime = new TestJSRuntime();
var resultJson = DotNetDispatcher.Invoke(jsRuntime, thisAssemblyName, "InvocableStaticNonVoid", default, null); var resultJson = DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(thisAssemblyName, "InvocableStaticNonVoid", default, default), null);
var result = JsonSerializer.Deserialize<TestDTO>(resultJson, jsRuntime.JsonSerializerOptions); var result = JsonSerializer.Deserialize<TestDTO>(resultJson, jsRuntime.JsonSerializerOptions);
// Assert // Assert
@ -104,7 +104,7 @@ namespace Microsoft.JSInterop.Infrastructure
{ {
// Arrange/Act // Arrange/Act
var jsRuntime = new TestJSRuntime(); var jsRuntime = new TestJSRuntime();
var resultJson = DotNetDispatcher.Invoke(jsRuntime, thisAssemblyName, nameof(SomePublicType.InvokableMethodWithoutCustomIdentifier), default, null); var resultJson = DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(thisAssemblyName, nameof(SomePublicType.InvokableMethodWithoutCustomIdentifier), default, default), null);
var result = JsonSerializer.Deserialize<TestDTO>(resultJson, jsRuntime.JsonSerializerOptions); var result = JsonSerializer.Deserialize<TestDTO>(resultJson, jsRuntime.JsonSerializerOptions);
// Assert // Assert
@ -130,7 +130,7 @@ namespace Microsoft.JSInterop.Infrastructure
}, jsRuntime.JsonSerializerOptions); }, jsRuntime.JsonSerializerOptions);
// Act // Act
var resultJson = DotNetDispatcher.Invoke(jsRuntime, thisAssemblyName, "InvocableStaticWithParams", default, argsJson); var resultJson = DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(thisAssemblyName, "InvocableStaticWithParams", default, default), argsJson);
var result = JsonDocument.Parse(resultJson); var result = JsonDocument.Parse(resultJson);
var root = result.RootElement; var root = result.RootElement;
@ -171,7 +171,7 @@ namespace Microsoft.JSInterop.Infrastructure
// Act & Assert // Act & Assert
var ex = Assert.Throws<InvalidOperationException>(() => var ex = Assert.Throws<InvalidOperationException>(() =>
DotNetDispatcher.Invoke(jsRuntime, thisAssemblyName, method, default, argsJson)); DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(thisAssemblyName, method, default, default), argsJson));
Assert.Equal($"In call to '{method}', parameter of type '{nameof(TestDTO)}' at index 3 must be declared as type 'DotNetObjectRef<TestDTO>' to receive the incoming value.", ex.Message); Assert.Equal($"In call to '{method}', parameter of type '{nameof(TestDTO)}' at index 3 must be declared as type 'DotNetObjectRef<TestDTO>' to receive the incoming value.", ex.Message);
} }
@ -185,7 +185,7 @@ namespace Microsoft.JSInterop.Infrastructure
jsRuntime.Invoke<object>("unimportant", objectRef); jsRuntime.Invoke<object>("unimportant", objectRef);
// Act // Act
var resultJson = DotNetDispatcher.Invoke(jsRuntime, null, "InvokableInstanceVoid", 1, null); var resultJson = DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(null, "InvokableInstanceVoid", 1, default), null);
// Assert // Assert
Assert.Null(resultJson); Assert.Null(resultJson);
@ -202,7 +202,7 @@ namespace Microsoft.JSInterop.Infrastructure
jsRuntime.Invoke<object>("unimportant", objectRef); jsRuntime.Invoke<object>("unimportant", objectRef);
// Act // Act
var resultJson = DotNetDispatcher.Invoke(jsRuntime, null, "BaseClassInvokableInstanceVoid", 1, null); var resultJson = DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(null, "BaseClassInvokableInstanceVoid", 1, default), null);
// Assert // Assert
Assert.Null(resultJson); Assert.Null(resultJson);
@ -219,7 +219,7 @@ namespace Microsoft.JSInterop.Infrastructure
jsRuntime.Invoke<object>("unimportant", objectRef); jsRuntime.Invoke<object>("unimportant", objectRef);
// Act // Act
DotNetDispatcher.BeginInvokeDotNet(jsRuntime, null, null, "__Dispose", objectRef.ObjectId, null); DotNetDispatcher.BeginInvokeDotNet(jsRuntime, new DotNetInvocationInfo(null, "__Dispose", objectRef.ObjectId, default), null);
// Assert // Assert
Assert.True(objectRef.Disposed); Assert.True(objectRef.Disposed);
@ -240,7 +240,7 @@ namespace Microsoft.JSInterop.Infrastructure
// Act/Assert // Act/Assert
var ex = Assert.Throws<ArgumentException>( var ex = Assert.Throws<ArgumentException>(
() => DotNetDispatcher.Invoke(jsRuntime, null, "InvokableInstanceVoid", 1, null)); () => DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(null, "InvokableInstanceVoid", 1, default), null));
Assert.StartsWith("There is no tracked object with id '1'.", ex.Message); Assert.StartsWith("There is no tracked object with id '1'.", ex.Message);
} }
@ -259,7 +259,7 @@ namespace Microsoft.JSInterop.Infrastructure
// Act/Assert // Act/Assert
var ex = Assert.Throws<ArgumentException>( var ex = Assert.Throws<ArgumentException>(
() => DotNetDispatcher.Invoke(jsRuntime, null, "InvokableInstanceVoid", 1, null)); () => DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(null, "InvokableInstanceVoid", 1, default), null));
Assert.StartsWith("There is no tracked object with id '1'.", ex.Message); Assert.StartsWith("There is no tracked object with id '1'.", ex.Message);
} }
@ -346,7 +346,7 @@ namespace Microsoft.JSInterop.Infrastructure
var argsJson = "[\"myvalue\",{\"__dotNetObject\":2}]"; var argsJson = "[\"myvalue\",{\"__dotNetObject\":2}]";
// Act // Act
var resultJson = DotNetDispatcher.Invoke(jsRuntime, null, "InvokableInstanceMethod", 1, argsJson); var resultJson = DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(null, "InvokableInstanceMethod", 1, default), argsJson);
// Assert // Assert
Assert.Equal("[\"You passed myvalue\",{\"__dotNetObject\":3}]", resultJson); Assert.Equal("[\"You passed myvalue\",{\"__dotNetObject\":3}]", resultJson);
@ -369,7 +369,7 @@ namespace Microsoft.JSInterop.Infrastructure
// Act/Assert // Act/Assert
var ex = Assert.Throws<ArgumentException>(() => var ex = Assert.Throws<ArgumentException>(() =>
{ {
DotNetDispatcher.Invoke(jsRuntime, thisAssemblyName, "InvocableStaticWithParams", default, argsJson); DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(thisAssemblyName, "InvocableStaticWithParams", default, default), argsJson);
}); });
Assert.Equal("The call to 'InvocableStaticWithParams' expects '3' parameters, but received '2'.", ex.Message); Assert.Equal("The call to 'InvocableStaticWithParams' expects '3' parameters, but received '2'.", ex.Message);
@ -392,7 +392,7 @@ namespace Microsoft.JSInterop.Infrastructure
// Act/Assert // Act/Assert
var ex = Assert.Throws<JsonException>(() => var ex = Assert.Throws<JsonException>(() =>
{ {
DotNetDispatcher.Invoke(jsRuntime, thisAssemblyName, "InvocableStaticWithParams", default, argsJson); DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(thisAssemblyName, "InvocableStaticWithParams", default, default), argsJson);
}); });
Assert.Equal("Unexpected JSON token Number. Ensure that the call to `InvocableStaticWithParams' is supplied with exactly '3' parameters.", ex.Message); Assert.Equal("Unexpected JSON token Number. Ensure that the call to `InvocableStaticWithParams' is supplied with exactly '3' parameters.", ex.Message);
@ -419,13 +419,13 @@ namespace Microsoft.JSInterop.Infrastructure
// Act // Act
var callId = "123"; var callId = "123";
var resultTask = jsRuntime.NextInvocationTask; var resultTask = jsRuntime.NextInvocationTask;
DotNetDispatcher.BeginInvokeDotNet(jsRuntime, callId, null, "InvokableAsyncMethod", 1, argsJson); DotNetDispatcher.BeginInvokeDotNet(jsRuntime, new DotNetInvocationInfo(null, "InvokableAsyncMethod", 1, callId), argsJson);
await resultTask; await resultTask;
// Assert: Correct completion information // Assert: Correct completion information
Assert.Equal(callId, jsRuntime.LastCompletionCallId); Assert.Equal(callId, jsRuntime.LastCompletionCallId);
Assert.True(jsRuntime.LastCompletionStatus); Assert.True(jsRuntime.LastCompletionResult.Success);
var result = Assert.IsType<object[]>(jsRuntime.LastCompletionResult); var result = Assert.IsType<object[]>(jsRuntime.LastCompletionResult.Result);
var resultDto1 = Assert.IsType<TestDTO>(result[0]); var resultDto1 = Assert.IsType<TestDTO>(result[0]);
Assert.Equal("STRING VIA JSON", resultDto1.StringVal); Assert.Equal("STRING VIA JSON", resultDto1.StringVal);
@ -447,18 +447,17 @@ namespace Microsoft.JSInterop.Infrastructure
// Act // Act
var callId = "123"; var callId = "123";
var resultTask = jsRuntime.NextInvocationTask; var resultTask = jsRuntime.NextInvocationTask;
DotNetDispatcher.BeginInvokeDotNet(jsRuntime, callId, thisAssemblyName, nameof(ThrowingClass.ThrowingMethod), default, default); DotNetDispatcher.BeginInvokeDotNet(jsRuntime, new DotNetInvocationInfo(thisAssemblyName, nameof(ThrowingClass.ThrowingMethod), default, callId), default);
await resultTask; // This won't throw, it sets properties on the jsRuntime. await resultTask; // This won't throw, it sets properties on the jsRuntime.
// Assert // Assert
Assert.Equal(callId, jsRuntime.LastCompletionCallId); Assert.Equal(callId, jsRuntime.LastCompletionCallId);
Assert.False(jsRuntime.LastCompletionStatus); // Fails Assert.False(jsRuntime.LastCompletionResult.Success); // Fails
// Make sure the method that threw the exception shows up in the call stack // Make sure the method that threw the exception shows up in the call stack
// https://github.com/aspnet/AspNetCore/issues/8612 // https://github.com/aspnet/AspNetCore/issues/8612
var exception = jsRuntime.LastCompletionResult is ExceptionDispatchInfo edi ? edi.SourceException.ToString() : null; Assert.Contains(nameof(ThrowingClass.ThrowingMethod), jsRuntime.LastCompletionResult.Exception.ToString());
Assert.Contains(nameof(ThrowingClass.ThrowingMethod), exception);
} }
[Fact] [Fact]
@ -470,18 +469,17 @@ namespace Microsoft.JSInterop.Infrastructure
// Act // Act
var callId = "123"; var callId = "123";
var resultTask = jsRuntime.NextInvocationTask; var resultTask = jsRuntime.NextInvocationTask;
DotNetDispatcher.BeginInvokeDotNet(jsRuntime, callId, thisAssemblyName, nameof(ThrowingClass.AsyncThrowingMethod), default, default); DotNetDispatcher.BeginInvokeDotNet(jsRuntime, new DotNetInvocationInfo(thisAssemblyName, nameof(ThrowingClass.AsyncThrowingMethod), default, callId), default);
await resultTask; // This won't throw, it sets properties on the jsRuntime. await resultTask; // This won't throw, it sets properties on the jsRuntime.
// Assert // Assert
Assert.Equal(callId, jsRuntime.LastCompletionCallId); Assert.Equal(callId, jsRuntime.LastCompletionCallId);
Assert.False(jsRuntime.LastCompletionStatus); // Fails Assert.False(jsRuntime.LastCompletionResult.Success); // Fails
// Make sure the method that threw the exception shows up in the call stack // Make sure the method that threw the exception shows up in the call stack
// https://github.com/aspnet/AspNetCore/issues/8612 // https://github.com/aspnet/AspNetCore/issues/8612
var exception = jsRuntime.LastCompletionResult is ExceptionDispatchInfo edi ? edi.SourceException.ToString() : null; Assert.Contains(nameof(ThrowingClass.AsyncThrowingMethod), jsRuntime.LastCompletionResult.Exception.ToString());
Assert.Contains(nameof(ThrowingClass.AsyncThrowingMethod), exception);
} }
[Fact] [Fact]
@ -491,15 +489,15 @@ namespace Microsoft.JSInterop.Infrastructure
var jsRuntime = new TestJSRuntime(); var jsRuntime = new TestJSRuntime();
var callId = "123"; var callId = "123";
var resultTask = jsRuntime.NextInvocationTask; var resultTask = jsRuntime.NextInvocationTask;
DotNetDispatcher.BeginInvokeDotNet(jsRuntime, callId, thisAssemblyName, "InvocableStaticWithParams", default, "<xml>not json</xml>"); DotNetDispatcher.BeginInvokeDotNet(jsRuntime, new DotNetInvocationInfo(thisAssemblyName, "InvocableStaticWithParams", default, callId), "<xml>not json</xml>");
await resultTask; // This won't throw, it sets properties on the jsRuntime. await resultTask; // This won't throw, it sets properties on the jsRuntime.
// Assert // Assert
Assert.Equal(callId, jsRuntime.LastCompletionCallId); Assert.Equal(callId, jsRuntime.LastCompletionCallId);
Assert.False(jsRuntime.LastCompletionStatus); // Fails Assert.False(jsRuntime.LastCompletionResult.Success); // Fails
var result = Assert.IsType<ExceptionDispatchInfo>(jsRuntime.LastCompletionResult); var exception = jsRuntime.LastCompletionResult.Exception;
Assert.Contains("JsonReaderException: '<' is an invalid start of a value.", result.SourceException.ToString()); Assert.Contains("JsonReaderException: '<' is an invalid start of a value.", exception.ToString());
} }
[Fact] [Fact]
@ -509,13 +507,13 @@ namespace Microsoft.JSInterop.Infrastructure
var jsRuntime = new TestJSRuntime(); var jsRuntime = new TestJSRuntime();
var callId = "123"; var callId = "123";
var resultTask = jsRuntime.NextInvocationTask; var resultTask = jsRuntime.NextInvocationTask;
DotNetDispatcher.BeginInvokeDotNet(jsRuntime, callId, null, "InvokableInstanceVoid", 1, null); DotNetDispatcher.BeginInvokeDotNet(jsRuntime, new DotNetInvocationInfo(null, "InvokableInstanceVoid", 1, callId), null);
// Assert // Assert
Assert.Equal(callId, jsRuntime.LastCompletionCallId); Assert.Equal(callId, jsRuntime.LastCompletionCallId);
Assert.False(jsRuntime.LastCompletionStatus); // Fails Assert.False(jsRuntime.LastCompletionResult.Success); // Fails
var result = Assert.IsType<ExceptionDispatchInfo>(jsRuntime.LastCompletionResult); var exception = jsRuntime.LastCompletionResult.Exception;
Assert.StartsWith("System.ArgumentException: There is no tracked object with id '1'. Perhaps the DotNetObjectReference instance was already disposed.", result.SourceException.ToString()); Assert.StartsWith("System.ArgumentException: There is no tracked object with id '1'. Perhaps the DotNetObjectReference instance was already disposed.", exception.ToString());
} }
[Theory] [Theory]
@ -801,8 +799,7 @@ namespace Microsoft.JSInterop.Infrastructure
public string LastInvocationArgsJson { get; private set; } public string LastInvocationArgsJson { get; private set; }
public string LastCompletionCallId { get; private set; } public string LastCompletionCallId { get; private set; }
public bool LastCompletionStatus { get; private set; } public DotNetInvocationResult LastCompletionResult { get; private set; }
public object LastCompletionResult { get; private set; }
protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson)
{ {
@ -823,17 +820,10 @@ namespace Microsoft.JSInterop.Infrastructure
return null; return null;
} }
protected internal override void EndInvokeDotNet( protected internal override void EndInvokeDotNet(DotNetInvocationInfo invocationInfo, in DotNetInvocationResult invocationResult)
string callId,
bool success,
object resultOrError,
string assemblyName,
string methodIdentifier,
long dotNetObjectId)
{ {
LastCompletionCallId = callId; LastCompletionCallId = invocationInfo.CallId;
LastCompletionStatus = success; LastCompletionResult = invocationResult;
LastCompletionResult = resultOrError;
_nextInvocationTcs.SetResult(null); _nextInvocationTcs.SetResult(null);
_nextInvocationTcs = new TaskCompletionSource<object>(); _nextInvocationTcs = new TaskCompletionSource<object>();
} }

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.JSInterop.Infrastructure;
using Xunit; using Xunit;
namespace Microsoft.JSInterop namespace Microsoft.JSInterop
@ -113,8 +114,8 @@ namespace Microsoft.JSInterop
protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson)
=> throw new NotImplementedException("This test only covers sync calls"); => throw new NotImplementedException("This test only covers sync calls");
protected internal override void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId) => protected internal override void EndInvokeDotNet(DotNetInvocationInfo invocationInfo, in DotNetInvocationResult invocationResult)
throw new NotImplementedException("This test only covers sync calls"); => throw new NotImplementedException("This test only covers sync calls");
} }
} }
} }

View File

@ -8,6 +8,7 @@ using System.Text;
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.Infrastructure;
using Xunit; using Xunit;
namespace Microsoft.JSInterop namespace Microsoft.JSInterop
@ -296,18 +297,20 @@ namespace Microsoft.JSInterop
var expectedMessage = "An error ocurred while invoking '[Assembly]::Method'. Swapping to 'Development' environment will " + var expectedMessage = "An error ocurred while invoking '[Assembly]::Method'. Swapping to 'Development' environment will " +
"display more detailed information about the error that occurred."; "display more detailed information about the error that occurred.";
string GetMessage(string assembly, string method) => $"An error ocurred while invoking '[{assembly}]::{method}'. Swapping to 'Development' environment will " + string GetMessage(DotNetInvocationInfo info) => $"An error ocurred while invoking '[{info.AssemblyName}]::{info.MethodIdentifier}'. Swapping to 'Development' environment will " +
"display more detailed information about the error that occurred."; "display more detailed information about the error that occurred.";
var runtime = new TestJSRuntime() var runtime = new TestJSRuntime()
{ {
OnDotNetException = (e, a, m) => new JSError { Message = GetMessage(a, m) } OnDotNetException = (invocationInfo) => new JSError { Message = GetMessage(invocationInfo) }
}; };
var exception = new Exception("Some really sensitive data in here"); var exception = new Exception("Some really sensitive data in here");
var invocation = new DotNetInvocationInfo("Assembly", "Method", 0, "0");
var result = new DotNetInvocationResult(exception, default);
// Act // Act
runtime.EndInvokeDotNet("0", false, exception, "Assembly", "Method", 0); runtime.EndInvokeDotNet(invocation, result);
// Assert // Assert
var call = runtime.EndInvokeDotNetCalls.Single(); var call = runtime.EndInvokeDotNetCalls.Single();
@ -356,20 +359,21 @@ namespace Microsoft.JSInterop
public object ResultOrError { get; set; } public object ResultOrError { get; set; }
} }
public Func<Exception, string, string, object> OnDotNetException { get; set; } public Func<DotNetInvocationInfo, object> OnDotNetException { get; set; }
protected internal override void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId) protected internal override void EndInvokeDotNet(DotNetInvocationInfo invocationInfo, in DotNetInvocationResult invocationResult)
{ {
if (OnDotNetException != null && !success) var resultOrError = invocationResult.Success ? invocationResult.Result : invocationResult.Exception;
if (OnDotNetException != null && !invocationResult.Success)
{ {
resultOrError = OnDotNetException(resultOrError as Exception, assemblyName, methodIdentifier); resultOrError = OnDotNetException(invocationInfo);
} }
EndInvokeDotNetCalls.Add(new EndInvokeDotNetArgs EndInvokeDotNetCalls.Add(new EndInvokeDotNetArgs
{ {
CallId = callId, CallId = invocationInfo.CallId,
Success = success, Success = invocationResult.Success,
ResultOrError = resultOrError ResultOrError = resultOrError,
}); });
} }

View File

@ -2,6 +2,7 @@
// 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 Microsoft.JSInterop.Infrastructure;
namespace Microsoft.JSInterop namespace Microsoft.JSInterop
{ {
@ -12,7 +13,7 @@ namespace Microsoft.JSInterop
throw new NotImplementedException(); throw new NotImplementedException();
} }
protected internal override void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId) protected internal override void EndInvokeDotNet(DotNetInvocationInfo invocationInfo, in DotNetInvocationResult invocationResult)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

View File

@ -7,7 +7,7 @@ namespace Mono.WebAssembly.Interop
{ {
public MonoWebAssemblyJSRuntime() { } public MonoWebAssemblyJSRuntime() { }
protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) { } protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) { }
protected override void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId) { } protected override void EndInvokeDotNet(Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo callInfo, in Microsoft.JSInterop.Infrastructure.DotNetInvocationResult dispatchResult) { }
protected static void Initialize(Mono.WebAssembly.Interop.MonoWebAssemblyJSRuntime jsRuntime) { } protected static void Initialize(Mono.WebAssembly.Interop.MonoWebAssemblyJSRuntime jsRuntime) { }
protected override string InvokeJS(string identifier, string argsJson) { throw null; } protected override string InvokeJS(string identifier, string argsJson) { throw null; }
public TRes InvokeUnmarshalled<TRes>(string identifier) { throw null; } public TRes InvokeUnmarshalled<TRes>(string identifier) { throw null; }

View File

@ -2,7 +2,6 @@
// 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.Runtime.ExceptionServices;
using System.Text.Json; using System.Text.Json;
using Microsoft.JSInterop; using Microsoft.JSInterop;
using Microsoft.JSInterop.Infrastructure; using Microsoft.JSInterop.Infrastructure;
@ -53,7 +52,10 @@ namespace Mono.WebAssembly.Interop
// Invoked via Mono's JS interop mechanism (invoke_method) // Invoked via Mono's JS interop mechanism (invoke_method)
private static string InvokeDotNet(string assemblyName, string methodIdentifier, string dotNetObjectId, string argsJson) private static string InvokeDotNet(string assemblyName, string methodIdentifier, string dotNetObjectId, string argsJson)
=> DotNetDispatcher.Invoke(Instance, assemblyName, methodIdentifier, dotNetObjectId == null ? default : long.Parse(dotNetObjectId), argsJson); {
var callInfo = new DotNetInvocationInfo(assemblyName, methodIdentifier, dotNetObjectId == null ? default : long.Parse(dotNetObjectId), callId: null);
return DotNetDispatcher.Invoke(Instance, callInfo, argsJson);
}
// Invoked via Mono's JS interop mechanism (invoke_method) // Invoked via Mono's JS interop mechanism (invoke_method)
private static void EndInvokeJS(string argsJson) private static void EndInvokeJS(string argsJson)
@ -78,32 +80,20 @@ namespace Mono.WebAssembly.Interop
assemblyName = assemblyNameOrDotNetObjectId; assemblyName = assemblyNameOrDotNetObjectId;
} }
DotNetDispatcher.BeginInvokeDotNet(Instance, callId, assemblyName, methodIdentifier, dotNetObjectId, argsJson); var callInfo = new DotNetInvocationInfo(assemblyName, methodIdentifier, dotNetObjectId, callId);
DotNetDispatcher.BeginInvokeDotNet(Instance, callInfo, argsJson);
} }
protected override void EndInvokeDotNet( protected override void EndInvokeDotNet(DotNetInvocationInfo callInfo, in DotNetInvocationResult dispatchResult)
string callId,
bool success,
object resultOrError,
string assemblyName,
string methodIdentifier,
long dotNetObjectId)
{ {
// For failures, the common case is to call EndInvokeDotNet with the Exception object. // For failures, the common case is to call EndInvokeDotNet with the Exception object.
// For these we'll serialize as something that's useful to receive on the JS side. // For these we'll serialize as something that's useful to receive on the JS side.
// If the value is not an Exception, we'll just rely on it being directly JSON-serializable. // If the value is not an Exception, we'll just rely on it being directly JSON-serializable.
if (!success && resultOrError is Exception ex) var resultOrError = dispatchResult.Success ? dispatchResult.Result : dispatchResult.Exception.ToString();
{
resultOrError = ex.ToString();
}
else if (!success && resultOrError is ExceptionDispatchInfo edi)
{
resultOrError = edi.SourceException.ToString();
}
// We pass 0 as the async handle because we don't want the JS-side code to // We pass 0 as the async handle because we don't want the JS-side code to
// send back any notification (we're just providing a result for an existing async call) // send back any notification (we're just providing a result for an existing async call)
var args = JsonSerializer.Serialize(new[] { callId, success, resultOrError }, JsonSerializerOptions); var args = JsonSerializer.Serialize(new[] { callInfo.CallId, dispatchResult.Success, resultOrError }, JsonSerializerOptions);
BeginInvokeJS(0, "DotNet.jsCallDispatcher.endInvokeDotNetFromJS", args); BeginInvokeJS(0, "DotNet.jsCallDispatcher.endInvokeDotNetFromJS", args);
} }