From 3c06726d77cc40580558eababbc45cb4f9e6daeb Mon Sep 17 00:00:00 2001 From: Pranav K Date: Fri, 22 Nov 2019 14:59:52 -0800 Subject: [PATCH] Move Mono.WebAssembly.Interop to blazor-wasm branch --- eng/ProjectReferences.props | 1 + eng/Version.Details.xml | 4 - eng/Versions.props | 1 - ...rosoft.AspNetCore.Blazor.HttpClient.csproj | 2 +- .../src/InternalCalls.cs | 25 +++ .../src/Mono.WebAssembly.Interop.csproj | 17 ++ .../src/MonoWebAssemblyJSRuntime.cs | 157 ++++++++++++++++++ src/Components/Components.sln | 18 ++ src/Components/ComponentsNoDeps.slnf | 1 + 9 files changed, 220 insertions(+), 6 deletions(-) create mode 100644 src/Components/Blazor/Mono.WebAssembly.Interop/src/InternalCalls.cs create mode 100644 src/Components/Blazor/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj create mode 100644 src/Components/Blazor/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs diff --git a/eng/ProjectReferences.props b/eng/ProjectReferences.props index 3ac3e8dbf6..d170c5aa24 100644 --- a/eng/ProjectReferences.props +++ b/eng/ProjectReferences.props @@ -15,6 +15,7 @@ + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index e5f59d2cb6..92ed43ccfb 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -289,10 +289,6 @@ https://github.com/aspnet/Extensions 4ebd75b961136c7ca331866eed3403becda75681 - - https://github.com/aspnet/Extensions - 4ebd75b961136c7ca331866eed3403becda75681 - https://github.com/dotnet/corefx 0f7f38c4fd323b26da10cce95f857f77f0f09b48 diff --git a/eng/Versions.props b/eng/Versions.props index 4c61494afa..e2dd6a11fe 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -158,7 +158,6 @@ 3.1.0 3.1.0-rtm.19568.3 3.1.0 - 3.1.0-preview4.19568.3 3.1.0 3.1.0 diff --git a/src/Components/Blazor/Http/src/Microsoft.AspNetCore.Blazor.HttpClient.csproj b/src/Components/Blazor/Http/src/Microsoft.AspNetCore.Blazor.HttpClient.csproj index be140d936e..18bdc5e1fe 100644 --- a/src/Components/Blazor/Http/src/Microsoft.AspNetCore.Blazor.HttpClient.csproj +++ b/src/Components/Blazor/Http/src/Microsoft.AspNetCore.Blazor.HttpClient.csproj @@ -1,7 +1,7 @@ - netstandard2.1 + netstandard2.0 Provides experimental support for using System.Text.Json with HttpClient. Intended for use with Blazor running under WebAssembly. true diff --git a/src/Components/Blazor/Mono.WebAssembly.Interop/src/InternalCalls.cs b/src/Components/Blazor/Mono.WebAssembly.Interop/src/InternalCalls.cs new file mode 100644 index 0000000000..60c0cdc429 --- /dev/null +++ b/src/Components/Blazor/Mono.WebAssembly.Interop/src/InternalCalls.cs @@ -0,0 +1,25 @@ +// 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.Runtime.CompilerServices; + +namespace WebAssembly.JSInterop +{ + /// + /// Methods that map to the functions compiled into the Mono WebAssembly runtime, + /// as defined by 'mono_add_internal_call' calls in driver.c + /// + internal class InternalCalls + { + // The exact namespace, type, and method names must match the corresponding entries + // in driver.c in the Mono distribution + + // We're passing asyncHandle by ref not because we want it to be writable, but so it gets + // passed as a pointer (4 bytes). We can pass 4-byte values, but not 8-byte ones. + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern string InvokeJSMarshalled(out string exception, ref long asyncHandle, string functionIdentifier, string argsJson); + + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern TRes InvokeJSUnmarshalled(out string exception, string functionIdentifier, T0 arg0, T1 arg1, T2 arg2); + } +} diff --git a/src/Components/Blazor/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj b/src/Components/Blazor/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj new file mode 100644 index 0000000000..ea714b2d92 --- /dev/null +++ b/src/Components/Blazor/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj @@ -0,0 +1,17 @@ + + + + netstandard2.1 + Abstractions and features for interop between Mono WebAssembly and JavaScript code. + wasm;javascript;interop + true + true + true + false + + + + + + + diff --git a/src/Components/Blazor/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs b/src/Components/Blazor/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs new file mode 100644 index 0000000000..654263a123 --- /dev/null +++ b/src/Components/Blazor/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs @@ -0,0 +1,157 @@ +// 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; +using Microsoft.JSInterop.Infrastructure; +using WebAssembly.JSInterop; + +namespace Mono.WebAssembly.Interop +{ + /// + /// Provides methods for invoking JavaScript functions for applications running + /// on the Mono WebAssembly runtime. + /// + public class MonoWebAssemblyJSRuntime : JSInProcessRuntime + { + /// + /// Gets the used to perform operations using . + /// + private static MonoWebAssemblyJSRuntime Instance { get; set; } + + /// + /// Initializes the to be used to perform operations using . + /// + /// The instance. + protected static void Initialize(MonoWebAssemblyJSRuntime jsRuntime) + { + if (Instance != null) + { + throw new InvalidOperationException("MonoWebAssemblyJSRuntime has already been initialized."); + } + + Instance = jsRuntime ?? throw new ArgumentNullException(nameof(jsRuntime)); + } + + /// + protected override string InvokeJS(string identifier, string argsJson) + { + var noAsyncHandle = default(long); + var result = InternalCalls.InvokeJSMarshalled(out var exception, ref noAsyncHandle, identifier, argsJson); + return exception != null + ? throw new JSException(exception) + : result; + } + + /// + protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) + { + InternalCalls.InvokeJSMarshalled(out _, ref asyncHandle, identifier, argsJson); + } + + // Invoked via Mono's JS interop mechanism (invoke_method) + private static string InvokeDotNet(string assemblyName, string methodIdentifier, string dotNetObjectId, string 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) + private static void EndInvokeJS(string argsJson) + => DotNetDispatcher.EndInvokeJS(Instance, argsJson); + + // Invoked via Mono's JS interop mechanism (invoke_method) + private 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); + assemblyName = null; + } + else + { + dotNetObjectId = default; + assemblyName = assemblyNameOrDotNetObjectId; + } + + var callInfo = new DotNetInvocationInfo(assemblyName, methodIdentifier, dotNetObjectId, callId); + DotNetDispatcher.BeginInvokeDotNet(Instance, callInfo, argsJson); + } + + protected override void EndInvokeDotNet(DotNetInvocationInfo callInfo, in DotNetInvocationResult dispatchResult) + { + // 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. + // If the value is not an Exception, we'll just rely on it being directly JSON-serializable. + var resultOrError = dispatchResult.Success ? dispatchResult.Result : dispatchResult.Exception.ToString(); + + // 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) + var args = JsonSerializer.Serialize(new[] { callInfo.CallId, dispatchResult.Success, resultOrError }, JsonSerializerOptions); + BeginInvokeJS(0, "DotNet.jsCallDispatcher.endInvokeDotNetFromJS", args); + } + + #region Custom MonoWebAssemblyJSRuntime methods + + /// + /// Invokes the JavaScript function registered with the specified identifier. + /// + /// The .NET type corresponding to the function's return value type. + /// The identifier used when registering the target function. + /// The result of the function invocation. + public TRes InvokeUnmarshalled(string identifier) + => InvokeUnmarshalled(identifier, null, null, null); + + /// + /// Invokes the JavaScript function registered with the specified identifier. + /// + /// The type of the first argument. + /// The .NET type corresponding to the function's return value type. + /// The identifier used when registering the target function. + /// The first argument. + /// The result of the function invocation. + public TRes InvokeUnmarshalled(string identifier, T0 arg0) + => InvokeUnmarshalled(identifier, arg0, null, null); + + /// + /// Invokes the JavaScript function registered with the specified identifier. + /// + /// The type of the first argument. + /// The type of the second argument. + /// The .NET type corresponding to the function's return value type. + /// The identifier used when registering the target function. + /// The first argument. + /// The second argument. + /// The result of the function invocation. + public TRes InvokeUnmarshalled(string identifier, T0 arg0, T1 arg1) + => InvokeUnmarshalled(identifier, arg0, arg1, null); + + /// + /// Invokes the JavaScript function registered with the specified identifier. + /// + /// The type of the first argument. + /// The type of the second argument. + /// The type of the third argument. + /// The .NET type corresponding to the function's return value type. + /// The identifier used when registering the target function. + /// The first argument. + /// The second argument. + /// The third argument. + /// The result of the function invocation. + public TRes InvokeUnmarshalled(string identifier, T0 arg0, T1 arg1, T2 arg2) + { + var result = InternalCalls.InvokeJSUnmarshalled(out var exception, identifier, arg0, arg1, arg2); + return exception != null + ? throw new JSException(exception) + : result; + } + + #endregion + } +} diff --git a/src/Components/Components.sln b/src/Components/Components.sln index ba0b2476ff..36cde86285 100644 --- a/src/Components/Components.sln +++ b/src/Components/Components.sln @@ -246,6 +246,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Blazor EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Blazor.DataAnnotations.Validation.Tests", "Blazor\Validation\test\Microsoft.AspNetCore.Blazor.DataAnnotations.Validation.Tests.csproj", "{A5617A9D-C71E-44DE-936C-27611EB40A02}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mono.WebAssembly.Interop", "Mono.WebAssembly.Interop", "{21BB9C13-20C1-4F2B-80E4-D7C64AA3BD05}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.WebAssembly.Interop", "Blazor\Mono.WebAssembly.Interop\src\Mono.WebAssembly.Interop.csproj", "{D141CFEE-D10A-406B-8963-F86FA13732E3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1516,6 +1520,18 @@ Global {A5617A9D-C71E-44DE-936C-27611EB40A02}.Release|x64.Build.0 = Release|Any CPU {A5617A9D-C71E-44DE-936C-27611EB40A02}.Release|x86.ActiveCfg = Release|Any CPU {A5617A9D-C71E-44DE-936C-27611EB40A02}.Release|x86.Build.0 = Release|Any CPU + {D141CFEE-D10A-406B-8963-F86FA13732E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D141CFEE-D10A-406B-8963-F86FA13732E3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D141CFEE-D10A-406B-8963-F86FA13732E3}.Debug|x64.ActiveCfg = Debug|Any CPU + {D141CFEE-D10A-406B-8963-F86FA13732E3}.Debug|x64.Build.0 = Debug|Any CPU + {D141CFEE-D10A-406B-8963-F86FA13732E3}.Debug|x86.ActiveCfg = Debug|Any CPU + {D141CFEE-D10A-406B-8963-F86FA13732E3}.Debug|x86.Build.0 = Debug|Any CPU + {D141CFEE-D10A-406B-8963-F86FA13732E3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D141CFEE-D10A-406B-8963-F86FA13732E3}.Release|Any CPU.Build.0 = Release|Any CPU + {D141CFEE-D10A-406B-8963-F86FA13732E3}.Release|x64.ActiveCfg = Release|Any CPU + {D141CFEE-D10A-406B-8963-F86FA13732E3}.Release|x64.Build.0 = Release|Any CPU + {D141CFEE-D10A-406B-8963-F86FA13732E3}.Release|x86.ActiveCfg = Release|Any CPU + {D141CFEE-D10A-406B-8963-F86FA13732E3}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1629,6 +1645,8 @@ Global {FD9BD646-9D50-42ED-A3E1-90558BA0C6B2} = {7260DED9-22A9-4E9D-92F4-5E8A4404DEAF} {B70F90C7-2696-4050-B24E-BF0308F4E059} = {7260DED9-22A9-4E9D-92F4-5E8A4404DEAF} {A5617A9D-C71E-44DE-936C-27611EB40A02} = {7260DED9-22A9-4E9D-92F4-5E8A4404DEAF} + {21BB9C13-20C1-4F2B-80E4-D7C64AA3BD05} = {7260DED9-22A9-4E9D-92F4-5E8A4404DEAF} + {D141CFEE-D10A-406B-8963-F86FA13732E3} = {21BB9C13-20C1-4F2B-80E4-D7C64AA3BD05} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {CC3C47E1-AD1A-4619-9CD3-E08A0148E5CE} diff --git a/src/Components/ComponentsNoDeps.slnf b/src/Components/ComponentsNoDeps.slnf index 09f6a0859f..61501c5101 100644 --- a/src/Components/ComponentsNoDeps.slnf +++ b/src/Components/ComponentsNoDeps.slnf @@ -17,6 +17,7 @@ "Blazor\\Templates\\src\\Microsoft.AspNetCore.Blazor.Templates.csproj", "Blazor\\Validation\\src\\Microsoft.AspNetCore.Blazor.DataAnnotations.Validation.csproj", "Blazor\\Validation\\test\\Microsoft.AspNetCore.Blazor.DataAnnotations.Validation.Tests.csproj", + "Blazor\\Mono.WebAssembly.Interop\\src\\Mono.WebAssembly.Interop.csproj", "Blazor\\testassets\\HostedInAspNet.Client\\HostedInAspNet.Client.csproj", "Blazor\\testassets\\HostedInAspNet.Server\\HostedInAspNet.Server.csproj", "Blazor\\testassets\\Microsoft.AspNetCore.Blazor.E2EPerformance\\Microsoft.AspNetCore.Blazor.E2EPerformance.csproj",