File: Services\DefaultWebAssemblyJSRuntime.Interop.cs
Web Access
Project: src\src\Components\WebAssembly\WebAssembly\src\Microsoft.AspNetCore.Components.WebAssembly.csproj (Microsoft.AspNetCore.Components.WebAssembly)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Globalization;
using System.Runtime.InteropServices.JavaScript;
using System.Runtime.Versioning;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.JSInterop.Infrastructure;
 
namespace Microsoft.AspNetCore.Components.WebAssembly.Services;
 
internal sealed partial class DefaultWebAssemblyJSRuntime
{
 
    [JSExport]
    [SupportedOSPlatform("browser")]
    public static string? InvokeDotNet(
        string? assemblyName,
        string methodIdentifier,
        [JSMarshalAs<JSType.Number>] long dotNetObjectId,
        string argsJson)
    {
        var callInfo = new DotNetInvocationInfo(assemblyName, methodIdentifier, dotNetObjectId, callId: null);
        return DotNetDispatcher.Invoke(Instance, callInfo, argsJson);
    }
 
    [JSExport]
    [SupportedOSPlatform("browser")]
    public static void EndInvokeJS(string argsJson)
    {
        WebAssemblyCallQueue.Schedule(argsJson, static argsJson =>
        {
            // This is not expected to throw, as it takes care of converting any unhandled user code
            // exceptions into a failure on the Task that was returned when calling InvokeAsync.
            DotNetDispatcher.EndInvokeJS(Instance, argsJson);
        });
    }
 
    [JSExport]
    [SupportedOSPlatform("browser")]
    public static void BeginInvokeDotNet(string? callId, string assemblyNameOrDotNetObjectId, string methodIdentifier, string argsJson)
    {
        // Figure out whether 'assemblyNameOrDotNetObjectId' is the assembly name or the instance ID
        // We only need one for any given call. This helps to work around the limitation that we can
        // only pass a maximum of 4 args in a call from JS to Mono WebAssembly.
        string? assemblyName;
        long dotNetObjectId;
        if (char.IsDigit(assemblyNameOrDotNetObjectId[0]))
        {
            dotNetObjectId = long.Parse(assemblyNameOrDotNetObjectId, CultureInfo.InvariantCulture);
            assemblyName = null;
        }
        else
        {
            dotNetObjectId = default;
            assemblyName = assemblyNameOrDotNetObjectId;
        }
 
        var callInfo = new DotNetInvocationInfo(assemblyName, methodIdentifier, dotNetObjectId, callId);
        WebAssemblyCallQueue.Schedule((callInfo, argsJson), static state =>
        {
            // This is not expected to throw, as it takes care of converting any unhandled user code
            // exceptions into a failure on the JS Promise object.
            DotNetDispatcher.BeginInvokeDotNet(Instance, state.callInfo, state.argsJson);
        });
    }
 
    [JSExport]
    [SupportedOSPlatform("browser")]
    private static void ReceiveByteArrayFromJS(int id, byte[] data)
    {
        DotNetDispatcher.ReceiveByteArray(Instance, id, data);
    }
 
    private static Task ScheduleOnCallQueue<TState>(TState state, Action<TState> action)
    {
        var tcs = new TaskCompletionSource();
        WebAssemblyCallQueue.Schedule((tcs, state, action), static s =>
        {
            try
            {
                s.action(s.state);
                s.tcs.TrySetResult();
            }
            catch (Exception ex)
            {
                s.tcs.TrySetException(ex);
            }
        });
 
        return tcs.Task;
    }
 
    private static Task<TResult> ScheduleOnCallQueue<TState, TResult>(TState state, Func<TState, Task<TResult>> action)
    {
        var tcs = new TaskCompletionSource<TResult>();
        WebAssemblyCallQueue.Schedule((tcs, state, action), static async s =>
        {
            try
            {
                var result = await s.action(s.state);
                s.tcs.TrySetResult(result);
            }
            catch (Exception ex)
            {
                s.tcs.TrySetException(ex);
            }
        });
 
        return tcs.Task;
    }
}