Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/runtime.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props1
-rw-r--r--src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs34
-rw-r--r--src/libraries/NetCoreAppLibrary.props2
-rw-r--r--src/libraries/System.Console/src/System.Console.csproj3
-rw-r--r--src/libraries/System.Console/src/System/ConsolePal.WebAssembly.cs20
-rw-r--r--src/libraries/System.Net.Http/src/System.Net.Http.csproj8
-rw-r--r--src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs658
-rw-r--r--src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpInterop.cs141
-rw-r--r--src/libraries/System.Net.WebSockets.Client/src/System.Net.WebSockets.Client.csproj9
-rw-r--r--src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/BrowserWebSockets/BrowserInterop.cs62
-rw-r--r--src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/BrowserWebSockets/BrowserWebSocket.cs43
-rw-r--r--src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System.Private.Runtime.InteropServices.JavaScript.TypeForwards.cs14
-rw-r--r--src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System.Private.Runtime.InteropServices.JavaScript.csproj13
-rw-r--r--src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System.Private.Runtime.InteropServices.JavaScript.Tests.csproj10
-rw-r--r--src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/TimerTests.cs66
-rw-r--r--src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/timers.js47
-rw-r--r--src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/timers.mjs47
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/System.Runtime.InteropServices.JavaScript.sln302
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj7
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/CancelablePromise.cs27
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs92
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.Generated.cs48
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.cs90
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSException.cs41
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs125
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHost.cs8
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.Types.cs7
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.cs70
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSMarshalerArgument.cs38
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.cs42
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Array.cs4
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Runtime.cs23
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Uint8Array.cs4
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/MarshalerType.cs2
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.BigInt64.cs27
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Bool.cs27
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Byte.cs69
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Char.cs27
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.DateTime.cs54
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Double.cs69
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Exception.cs57
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Func.cs496
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Int16.cs29
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Int32.cs71
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Int52.cs39
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.IntPtr.cs37
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.JSObject.cs60
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Object.cs329
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Single.cs27
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.String.cs69
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs426
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/tests/JSImportGenerator.UnitTest/Compiles.cs2
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/tests/JSImportGenerator.UnitTest/Fails.cs2
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/tests/JSImportGenerator.UnitTest/JSImportGenerator.Unit.Tests.csproj11
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Tests.csproj24
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs1929
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs915
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs254
-rw-r--r--src/libraries/System.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/Utils.cs145
-rw-r--r--src/mono/System.Private.CoreLib/src/System/ValueType.cs2
-rw-r--r--src/mono/sample/wasm/browser/Program.cs32
-rw-r--r--src/mono/sample/wasm/browser/Wasm.Browser.ES6.Sample.csproj5
-rw-r--r--src/mono/sample/wasm/browser/main.js27
-rw-r--r--src/mono/wasm/runtime/cancelable-promise.ts52
-rw-r--r--src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js10
-rw-r--r--src/mono/wasm/runtime/corebindings.c25
-rw-r--r--src/mono/wasm/runtime/corebindings.ts4
-rw-r--r--src/mono/wasm/runtime/cs-to-js.ts7
-rw-r--r--src/mono/wasm/runtime/cwraps.ts13
-rw-r--r--src/mono/wasm/runtime/dotnet.d.ts75
-rw-r--r--src/mono/wasm/runtime/driver.c36
-rw-r--r--src/mono/wasm/runtime/es6/dotnet.es6.lib.js10
-rw-r--r--src/mono/wasm/runtime/export-types.ts36
-rw-r--r--src/mono/wasm/runtime/exports.ts50
-rw-r--r--src/mono/wasm/runtime/gc-common.h6
-rw-r--r--src/mono/wasm/runtime/http.ts150
-rw-r--r--src/mono/wasm/runtime/invoke-cs.ts206
-rw-r--r--src/mono/wasm/runtime/invoke-js.ts189
-rw-r--r--src/mono/wasm/runtime/marshal-to-cs.ts651
-rw-r--r--src/mono/wasm/runtime/marshal-to-js.ts581
-rw-r--r--src/mono/wasm/runtime/marshal.ts509
-rw-r--r--src/mono/wasm/runtime/startup.ts17
-rw-r--r--src/mono/wasm/runtime/types.ts3
-rw-r--r--src/mono/wasm/runtime/web-socket.ts484
84 files changed, 9057 insertions, 1426 deletions
diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props
index 6704af1c528..0c2e31734b9 100644
--- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props
+++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props
@@ -210,7 +210,6 @@
<PlatformManifestFileEntry Include="libmono-profiler-aot.a" IsNative="true" />
<PlatformManifestFileEntry Include="libmono-wasm-eh-js.a" IsNative="true" />
<PlatformManifestFileEntry Include="libmono-wasm-eh-wasm.a" IsNative="true" />
- <PlatformManifestFileEntry Include="System.Private.Runtime.InteropServices.Javascript.dll" />
<PlatformManifestFileEntry Include="dotnet-crypto-worker.js" IsNative="true" />
<PlatformManifestFileEntry Include="dotnet.js" IsNative="true" />
<PlatformManifestFileEntry Include="dotnet.worker.js" IsNative="true" />
diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs
index cd2ef6f3cc3..d67afaba600 100644
--- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs
+++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs
@@ -9,6 +9,23 @@ internal static partial class Interop
internal static unsafe partial class Runtime
{
[MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern void ReleaseCSOwnedObject(IntPtr jsHandle);
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public static extern unsafe void BindJSFunction(in string function_name, in string module_name, void* signature, out IntPtr bound_function_js_handle, out int is_exception, out object result);
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public static extern void InvokeJSFunction(IntPtr bound_function_js_handle, void* data);
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public static extern unsafe void BindCSFunction(in string fully_qualified_name, int signature_hash, void* signature, out int is_exception, out object result);
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public static extern void MarshalPromise(void* data);
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public static extern IntPtr RegisterGCRoot(IntPtr start, int bytesSize, IntPtr name);
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public static extern void DeregisterGCRoot(IntPtr handle);
+
+ #region Legacy
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern string InvokeJS(string str, out int exceptionalResult);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern void InvokeJSWithArgsRef(IntPtr jsHandle, in string method, in object?[] parms, out int exceptionalResult, out object result);
@@ -26,8 +43,6 @@ internal static partial class Interop
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern void TypedArrayToArrayRef(IntPtr jsHandle, out int exceptionalResult, out object result);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
- internal static extern void ReleaseCSOwnedObject(IntPtr jsHandle);
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern void CreateCSOwnedObjectRef(in string className, in object[] parms, out int exceptionalResult, out object result);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern void TypedArrayCopyToRef(IntPtr jsHandle, int arrayPtr, int begin, int end, int bytesPerElement, out int exceptionalResult, out object result);
@@ -36,21 +51,6 @@ internal static partial class Interop
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern void TypedArrayCopyFromRef(IntPtr jsHandle, int arrayPtr, int begin, int end, int bytesPerElement, out int exceptionalResult, out object result);
-
- #region Legacy
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- internal static extern void WebSocketSend(IntPtr webSocketJSHandle, IntPtr messagePtr, int offset, int length, int messageType, bool endOfMessage, out IntPtr promiseJSHandle, out int exceptionalResult, out object result);
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- internal static extern void WebSocketReceive(IntPtr webSocketJSHandle, IntPtr bufferPtr, int offset, int length, IntPtr responsePtr, out IntPtr promiseJSHandle, out int exceptionalResult, out object result);
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- internal static extern void WebSocketOpenRef(in string uri, in object[]? subProtocols, in Delegate onClosed, out IntPtr webSocketJSHandle, out IntPtr promiseJSHandle, out int exceptionalResult, out object result);
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- internal static extern void WebSocketAbort(IntPtr webSocketJSHandle, out int exceptionalResult, out string result);
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- internal static extern void WebSocketCloseRef(IntPtr webSocketJSHandle, int code, in string? reason, bool waitForCloseReceived, out IntPtr promiseJSHandle, out int exceptionalResult, out object result);
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- internal static extern void CancelPromiseRef(IntPtr promiseJSHandle, out int exceptionalResult, out string result);
-
#endregion
}
diff --git a/src/libraries/NetCoreAppLibrary.props b/src/libraries/NetCoreAppLibrary.props
index 5cb1d73dc7d..25a0a917299 100644
--- a/src/libraries/NetCoreAppLibrary.props
+++ b/src/libraries/NetCoreAppLibrary.props
@@ -105,7 +105,6 @@
System.ObjectModel;
System.Private.CoreLib;
System.Private.DataContractSerialization;
- System.Private.Runtime.InteropServices.JavaScript;
System.Private.Uri;
System.Private.Xml;
System.Private.Xml.Linq;
@@ -179,7 +178,6 @@
System.Net.Quic;
System.Private.CoreLib;
System.Private.DataContractSerialization;
- System.Private.Runtime.InteropServices.JavaScript;
System.Private.Uri;
System.Private.Xml;
System.Private.Xml.Linq;
diff --git a/src/libraries/System.Console/src/System.Console.csproj b/src/libraries/System.Console/src/System.Console.csproj
index 9b77767dcb6..b63628073c5 100644
--- a/src/libraries/System.Console/src/System.Console.csproj
+++ b/src/libraries/System.Console/src/System.Console.csproj
@@ -232,6 +232,7 @@
<Reference Include="Microsoft.Win32.Primitives" />
</ItemGroup>
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'Browser'">
- <ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\src\System.Runtime.InteropServices.JavaScript.csproj" SkipUseReferenceAssembly="true"/>
+ <ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\gen\JSImportGenerator\JSImportGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
+ <Reference Include="System.Runtime.InteropServices.JavaScript" />
</ItemGroup>
</Project>
diff --git a/src/libraries/System.Console/src/System/ConsolePal.WebAssembly.cs b/src/libraries/System.Console/src/System/ConsolePal.WebAssembly.cs
index e72fe6c24a9..0594f0f3930 100644
--- a/src/libraries/System.Console/src/System/ConsolePal.WebAssembly.cs
+++ b/src/libraries/System.Console/src/System/ConsolePal.WebAssembly.cs
@@ -3,12 +3,9 @@
using System.IO;
using System.Text;
-using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices.JavaScript;
-using JSObject = System.Runtime.InteropServices.JavaScript.JSObject;
-
#pragma warning disable CS0612 // using obsolete members until we finish https://github.com/dotnet/runtime/pull/66304/
namespace System
@@ -75,10 +72,10 @@ namespace System
}
}
- internal static class ConsolePal
+ internal static partial class ConsolePal
{
- private static volatile bool s_consoleInitialized;
- private static JSObject? s_console;
+ [JSImport("globalThis.console.clear")]
+ public static partial void Clear();
private static Encoding? s_outputEncoding;
@@ -169,17 +166,6 @@ namespace System
char sourceChar, ConsoleColor sourceForeColor,
ConsoleColor sourceBackColor) => throw new PlatformNotSupportedException();
- public static void Clear()
- {
- if (!s_consoleInitialized)
- {
- s_console = (JSObject)System.Runtime.InteropServices.JavaScript.Runtime.GetGlobalObject("console");
- s_consoleInitialized = true;
- }
-
- s_console?.Invoke("clear");
- }
-
public static void SetCursorPosition(int left, int top) => throw new PlatformNotSupportedException();
public static int BufferWidth
diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj
index 428ac78a8dd..a40be7c1c50 100644
--- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj
+++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj
@@ -486,8 +486,11 @@
<Compile Include="System\Net\Http\BrowserHttpHandler\SocketsHttpHandler.cs" />
<Compile Include="System\Net\Http\BrowserHttpHandler\BrowserHttpHandler.cs" />
<Compile Include="System\Net\Http\BrowserHttpHandler\HttpTelemetry.Browser.cs" />
- <Compile Include="$(CommonPath)System\Net\Http\HttpHandlerDefaults.cs"
+ <Compile Include="System\Net\Http\BrowserHttpHandler\BrowserHttpInterop.cs" />
+ <Compile Include="$(CommonPath)System\Net\Http\HttpHandlerDefaults.cs"
Link="Common\System\Net\Http\HttpHandlerDefaults.cs" />
+ <Compile Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\src\System\Runtime\InteropServices\JavaScript\CancelablePromise.cs"
+ Link="InteropServices\JavaScript\CancelablePromise.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(LibrariesProjectRoot)System.Net.Quic\src\System.Net.Quic.csproj" />
@@ -520,7 +523,8 @@
<Reference Include="System.Security.Cryptography" />
</ItemGroup>
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'Browser'">
- <ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\src\System.Runtime.InteropServices.JavaScript.csproj" SkipUseReferenceAssembly="true"/>
+ <ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\gen\JSImportGenerator\JSImportGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
+ <Reference Include="System.Runtime.InteropServices.JavaScript" />
</ItemGroup>
<ItemGroup Condition="'$(UseManagedNtlm)' == 'true'">
<ProjectReference Include="$(LibrariesProjectRoot)System.Formats.Asn1\src\System.Formats.Asn1.csproj" />
diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs
index 9f484a74840..ccca155d4bc 100644
--- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs
+++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs
@@ -8,8 +8,6 @@ using System.Threading;
using System.Threading.Tasks;
using System.Runtime.InteropServices.JavaScript;
-#pragma warning disable CS0612 // using obsolete members until we finish https://github.com/dotnet/runtime/pull/66304/
-
namespace System.Net.Http
{
// **Note** on `Task.ConfigureAwait(continueOnCapturedContext: true)` for the WebAssembly Browser.
@@ -21,10 +19,6 @@ namespace System.Net.Http
// we should put this in place now.
internal sealed class BrowserHttpHandler : HttpMessageHandler
{
- // This partial implementation contains members common to Browser WebAssembly running on .NET Core.
- private static readonly JSObject? s_fetch = (JSObject)Runtime.InteropServices.JavaScript.Runtime.GetGlobalObject("fetch");
- private static readonly JSObject? s_window = (JSObject)Runtime.InteropServices.JavaScript.Runtime.GetGlobalObject("window");
-
private static readonly HttpRequestOptionsKey<bool> EnableStreamingResponse = new HttpRequestOptionsKey<bool>("WebAssemblyEnableStreamingResponse");
private static readonly HttpRequestOptionsKey<IDictionary<string, object>> FetchOptions = new HttpRequestOptionsKey<IDictionary<string, object>>("WebAssemblyFetchOptions");
private bool _allowAutoRedirect = HttpHandlerDefaults.DefaultAutomaticRedirection;
@@ -34,17 +28,9 @@ namespace System.Net.Http
/// <summary>
/// Gets whether the current Browser supports streaming responses
/// </summary>
- private static bool StreamingSupported { get; } = GetIsStreamingSupported();
- private static bool GetIsStreamingSupported()
- {
- using (var streamingSupported = new Function("return typeof Response !== 'undefined' && 'body' in Response.prototype && typeof ReadableStream === 'function'"))
- return (bool)streamingSupported.Call();
- }
-
- private static Function AbortControllerFactory = new Function("return new AbortController()");
- private static Function HeadersFactory = new Function("return new Headers()");
- private static Function ObjectFactory = new Function("return new Object()");
+ private static bool StreamingSupported { get; } = BrowserHttpInterop.SupportsStreamingResponse();
+ #region PlatformNotSupported
#pragma warning disable CA1822
public bool UseCookies
{
@@ -118,6 +104,7 @@ namespace System.Net.Http
set => throw new PlatformNotSupportedException();
}
#pragma warning restore CA1822
+ #endregion
public bool AllowAutoRedirect
{
@@ -141,480 +128,343 @@ namespace System.Net.Http
throw new PlatformNotSupportedException();
}
- protected internal override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
+ private static async Task<WasmFetchResponse> CallFetch(HttpRequestMessage request, CancellationToken cancellationToken, bool? allowAutoRedirect)
{
- ArgumentNullException.ThrowIfNull(request);
- return Impl(request, cancellationToken);
-
- async Task<HttpResponseMessage> Impl(HttpRequestMessage request, CancellationToken cancellationToken)
+ int headerCount = request.Headers.Count + request.Content?.Headers.Count ?? 0;
+ List<string> headerNames = new List<string>(headerCount);
+ List<string> headerValues = new List<string>(headerCount);
+ List<string> optionNames = new List<string>();
+ List<object?> optionValues = new List<object?>();
+
+ JSObject abortController = BrowserHttpInterop.CreateAbortController();
+ CancellationTokenRegistration? abortRegistration = cancellationToken.Register(() =>
{
- CancellationTokenRegistration? abortRegistration = null;
- try
+ if (!abortController.IsDisposed)
{
- using var requestObject = (JSObject)ObjectFactory.Call();
-
- if (request.Options.TryGetValue(FetchOptions, out IDictionary<string, object>? fetchOptions))
- {
- foreach (KeyValuePair<string, object> item in fetchOptions)
- {
- requestObject.SetObjectProperty(item.Key, item.Value);
- }
- }
-
- requestObject.SetObjectProperty("method", request.Method.Method);
+ BrowserHttpInterop.AbortRequest(abortController);
+ }
+ abortController.Dispose();
+ });
+ try
+ {
+ optionNames.Add("method");
+ optionValues.Add(request.Method.Method);
+ if (allowAutoRedirect.HasValue)
+ {
+ optionNames.Add("redirect");
+ optionValues.Add(allowAutoRedirect.Value ? "follow" : "manual");
+ }
- // Only set if property was specifically modified and is not default value
- if (_isAllowAutoRedirectTouched)
+ foreach (KeyValuePair<string, IEnumerable<string>> header in request.Headers)
+ {
+ foreach (string value in header.Value)
{
- // Allowing or Disallowing redirects.
- // Here we will set redirect to `manual` instead of error if AllowAutoRedirect is
- // false so there is no exception thrown
- //
- // https://developer.mozilla.org/en-US/docs/Web/API/Response/type
- //
- // other issues from whatwg/fetch:
- //
- // https://github.com/whatwg/fetch/issues/763
- // https://github.com/whatwg/fetch/issues/601
- requestObject.SetObjectProperty("redirect", AllowAutoRedirect ? "follow" : "manual");
+ headerNames.Add(header.Key);
+ headerValues.Add(value);
}
+ }
- // We need to check for body content
- if (request.Content != null)
+ if (request.Content != null)
+ {
+ foreach (KeyValuePair<string, IEnumerable<string>> header in request.Content.Headers)
{
- if (request.Content is StringContent)
+ foreach (string value in header.Value)
{
- requestObject.SetObjectProperty("body", await request.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: true));
- }
- else
- {
- using (Uint8Array uint8Buffer = Uint8Array.From(await request.Content.ReadAsByteArrayAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: true)))
- {
- requestObject.SetObjectProperty("body", uint8Buffer);
- }
+ headerNames.Add(header.Key);
+ headerValues.Add(value);
}
}
+ }
- // Process headers
- // Cors has its own restrictions on headers.
- // https://developer.mozilla.org/en-US/docs/Web/API/Headers
-
- using (JSObject jsHeaders = (JSObject)HeadersFactory.Call())
+ if (request.Options.TryGetValue(FetchOptions, out IDictionary<string, object>? fetchOptions))
+ {
+ foreach (KeyValuePair<string, object> item in fetchOptions)
{
- foreach (KeyValuePair<string, IEnumerable<string>> header in request.Headers)
- {
- foreach (string value in header.Value)
- {
- jsHeaders.Invoke("append", header.Key, value);
- }
- }
- if (request.Content != null)
- {
- foreach (KeyValuePair<string, IEnumerable<string>> header in request.Content.Headers)
- {
- foreach (string value in header.Value)
- {
- jsHeaders.Invoke("append", header.Key, value);
- }
- }
- }
- requestObject.SetObjectProperty("headers", jsHeaders);
+ optionNames.Add(item.Key);
+ optionValues.Add(item.Value);
}
+ }
+ if (request.RequestUri == null)
+ {
+ throw new ArgumentNullException(nameof(request.RequestUri));
+ }
- JSObject abortController = (JSObject)AbortControllerFactory.Call();
- using JSObject signal = (JSObject)abortController.GetObjectProperty("signal");
- requestObject.SetObjectProperty("signal", signal);
-
- abortRegistration = cancellationToken.Register(() =>
+ string uri = request.RequestUri.IsAbsoluteUri ? request.RequestUri.AbsoluteUri : request.RequestUri.ToString();
+ Task<JSObject>? promise;
+ cancellationToken.ThrowIfCancellationRequested();
+ if (request.Content != null)
+ {
+ if (request.Content is StringContent)
{
- if (!abortController.IsDisposed)
- {
- abortController.Invoke("abort");
- abortController?.Dispose();
- }
- });
+ string body = await request.Content.ReadAsStringAsync(cancellationToken)
+ .ConfigureAwait(true);
+ cancellationToken.ThrowIfCancellationRequested();
- using var args = new System.Runtime.InteropServices.JavaScript.Array();
- if (request.RequestUri != null)
- {
- args.Push(request.RequestUri.IsAbsoluteUri ? request.RequestUri.AbsoluteUri : request.RequestUri.ToString());
- args.Push(requestObject);
+ promise = BrowserHttpInterop.Fetch(uri, headerNames.ToArray(), headerValues.ToArray(), optionNames.ToArray(), optionValues.ToArray(), abortController, body);
}
-
-
- var responseTask = s_fetch?.Invoke("apply", s_window, args) as Task<object>;
- if (responseTask == null)
- throw new Exception(SR.net_http_marshalling_response_promise_from_fetch);
-
- cancellationToken.ThrowIfCancellationRequested();
-
- var fetchResponseJs = (JSObject)await responseTask.ConfigureAwait(continueOnCapturedContext: true);
-
- var fetchResponse = new WasmFetchResponse(fetchResponseJs, abortController, abortRegistration.Value);
- abortRegistration = null;
- var responseMessage = new HttpResponseMessage((HttpStatusCode)fetchResponse.Status);
- responseMessage.RequestMessage = request;
-
- // Here we will set the ReasonPhrase so that it can be evaluated later.
- // We do not have a status code but this will signal some type of what happened
- // after interrogating the status code for success or not i.e. IsSuccessStatusCode
- //
- // https://developer.mozilla.org/en-US/docs/Web/API/Response/type
- // opaqueredirect: The fetch request was made with redirect: "manual".
- // The Response's status is 0, headers are empty, body is null and trailer is empty.
- if (fetchResponse.ResponseType == "opaqueredirect")
+ else
{
- responseMessage.SetReasonPhraseWithoutValidation(fetchResponse.ResponseType);
- }
+ byte[] buffer = await request.Content.ReadAsByteArrayAsync(cancellationToken)
+ .ConfigureAwait(true);
+ cancellationToken.ThrowIfCancellationRequested();
- bool streamingEnabled = false;
- if (StreamingSupported)
- {
- request.Options.TryGetValue(EnableStreamingResponse, out streamingEnabled);
+ promise = BrowserHttpInterop.Fetch(uri, headerNames.ToArray(), headerValues.ToArray(), optionNames.ToArray(), optionValues.ToArray(), abortController, buffer);
}
-
- responseMessage.Content = streamingEnabled
- ? new StreamContent(new WasmHttpReadStream(fetchResponse))
- : new BrowserHttpContent(fetchResponse);
-
- // Fill the response headers
- // CORS will only allow access to certain headers.
- // If a request is made for a resource on another origin which returns the CORs headers, then the type is cors.
- // cors and basic responses are almost identical except that a cors response restricts the headers you can view to
- // `Cache-Control`, `Content-Language`, `Content-Type`, `Expires`, `Last-Modified`, and `Pragma`.
- // View more information https://developers.google.com/web/updates/2015/03/introduction-to-fetch#response_types
- //
- // Note: Some of the headers may not even be valid header types in .NET thus we use TryAddWithoutValidation
- using (JSObject respHeaders = fetchResponse.Headers)
- {
- if (respHeaders != null)
- {
- using (var entriesIterator = (JSObject)respHeaders.Invoke("entries"))
- {
- JSObject? nextResult = null;
- try
- {
- nextResult = (JSObject)entriesIterator.Invoke("next");
- while (!(bool)nextResult.GetObjectProperty("done"))
- {
- using (var resultValue = (System.Runtime.InteropServices.JavaScript.Array)nextResult.GetObjectProperty("value"))
- {
- var name = (string)resultValue[0];
- var value = (string)resultValue[1];
- if (!responseMessage.Headers.TryAddWithoutValidation(name, value))
- responseMessage.Content.Headers.TryAddWithoutValidation(name, value);
- }
- nextResult?.Dispose();
- nextResult = (JSObject)entriesIterator.Invoke("next");
- }
- }
- finally
- {
- nextResult?.Dispose();
- }
- }
- }
- }
- return responseMessage;
-
- }
- catch (OperationCanceledException oce) when (cancellationToken.IsCancellationRequested)
- {
- throw CancellationHelper.CreateOperationCanceledException(oce, cancellationToken);
}
- catch (JSException jse)
+ else
{
- throw TranslateJSException(jse, cancellationToken);
- }
- finally
- {
- abortRegistration?.Dispose();
+ promise = BrowserHttpInterop.Fetch(uri, headerNames.ToArray(), headerValues.ToArray(), optionNames.ToArray(), optionValues.ToArray(), abortController);
}
+ cancellationToken.ThrowIfCancellationRequested();
+ ValueTask<JSObject> wrappedTask = BrowserHttpInterop.CancelationHelper(promise, cancellationToken, abortController);
+ JSObject fetchResponse = await wrappedTask.ConfigureAwait(true);
+ return new WasmFetchResponse(fetchResponse, abortRegistration.Value);
+ }
+ catch (Exception)
+ {
+ // this would also trigger abort
+ abortRegistration?.Dispose();
+ throw;
}
}
- private static Exception TranslateJSException(JSException jse, CancellationToken cancellationToken)
+ private static HttpResponseMessage ConvertResponse(HttpRequestMessage request, WasmFetchResponse fetchResponse)
{
- if (jse.Message.StartsWith("AbortError", StringComparison.Ordinal))
+ string? responseType = fetchResponse.ResponseType;
+ HttpResponseMessage responseMessage = new HttpResponseMessage((HttpStatusCode)fetchResponse.Status);
+ responseMessage.RequestMessage = request;
+ if (responseType == "opaqueredirect")
{
- return CancellationHelper.CreateOperationCanceledException(jse, CancellationToken.None);
+ // Here we will set the ReasonPhrase so that it can be evaluated later.
+ // We do not have a status code but this will signal some type of what happened
+ // after interrogating the status code for success or not i.e. IsSuccessStatusCode
+ //
+ // https://developer.mozilla.org/en-US/docs/Web/API/Response/type
+ // opaqueredirect: The fetch request was made with redirect: "manual".
+ // The Response's status is 0, headers are empty, body is null and trailer is empty.
+ responseMessage.SetReasonPhraseWithoutValidation(fetchResponse.ResponseType);
}
- if (cancellationToken.IsCancellationRequested)
+
+ bool streamingEnabled = false;
+ if (StreamingSupported)
{
- return CancellationHelper.CreateOperationCanceledException(jse, cancellationToken);
+ request.Options.TryGetValue(EnableStreamingResponse, out streamingEnabled);
}
- return new HttpRequestException(jse.Message, jse);
+
+ responseMessage.Content = streamingEnabled
+ ? new StreamContent(new WasmHttpReadStream(fetchResponse))
+ : new BrowserHttpContent(fetchResponse);
+
+
+ // Some of the headers may not even be valid header types in .NET thus we use TryAddWithoutValidation
+ // CORS will only allow access to certain headers on browser.
+ BrowserHttpInterop.GetResponseHeaders(fetchResponse.FetchResponse, responseMessage.Headers, responseMessage.Content.Headers);
+
+ return responseMessage;
}
- private sealed class WasmFetchResponse : IDisposable
+ protected internal override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
- private readonly JSObject _fetchResponse;
- private readonly JSObject _abortController;
- private readonly CancellationTokenRegistration _abortRegistration;
- private bool _isDisposed;
+ ArgumentNullException.ThrowIfNull(request);
+ return Impl(request, cancellationToken, _isAllowAutoRedirectTouched ? AllowAutoRedirect : null);
- public WasmFetchResponse(JSObject fetchResponse, JSObject abortController, CancellationTokenRegistration abortRegistration)
+ static async Task<HttpResponseMessage> Impl(HttpRequestMessage request, CancellationToken cancellationToken, bool? allowAutoRedirect)
{
- ArgumentNullException.ThrowIfNull(fetchResponse);
- ArgumentNullException.ThrowIfNull(abortController);
-
- _fetchResponse = fetchResponse;
- _abortController = abortController;
- _abortRegistration = abortRegistration;
+ WasmFetchResponse fetchRespose = await CallFetch(request, cancellationToken, allowAutoRedirect).ConfigureAwait(true);
+ return ConvertResponse(request, fetchRespose);
}
+ }
+ }
- public bool IsOK => (bool)_fetchResponse.GetObjectProperty("ok");
- public bool IsRedirected => (bool)_fetchResponse.GetObjectProperty("redirected");
- public int Status => (int)_fetchResponse.GetObjectProperty("status");
- public string StatusText => (string)_fetchResponse.GetObjectProperty("statusText");
- public string ResponseType => (string)_fetchResponse.GetObjectProperty("type");
- public string Url => (string)_fetchResponse.GetObjectProperty("url");
- public bool IsBodyUsed => (bool)_fetchResponse.GetObjectProperty("bodyUsed");
- public JSObject Headers => (JSObject)_fetchResponse.GetObjectProperty("headers");
- public JSObject Body => (JSObject)_fetchResponse.GetObjectProperty("body");
-
- public Task<object> ArrayBuffer() => (Task<object>)_fetchResponse.Invoke("arrayBuffer");
- public Task<object> Text() => (Task<object>)_fetchResponse.Invoke("text");
- public Task<object> JSON() => (Task<object>)_fetchResponse.Invoke("json");
-
- public void Dispose()
- {
- if (_isDisposed)
- return;
-
- _isDisposed = true;
+ internal sealed class WasmFetchResponse : IDisposable
+ {
+ public readonly JSObject FetchResponse;
+ private readonly CancellationTokenRegistration _abortRegistration;
+ private bool _isDisposed;
- _abortRegistration.Dispose();
+ public WasmFetchResponse(JSObject fetchResponse, CancellationTokenRegistration abortRegistration)
+ {
+ ArgumentNullException.ThrowIfNull(fetchResponse);
- _fetchResponse?.Dispose();
- if (_abortController != null && !_abortController.IsDisposed)
- {
- _abortController.Invoke("abort");
- }
- _abortController?.Dispose();
- }
+ FetchResponse = fetchResponse;
+ _abortRegistration = abortRegistration;
}
- private sealed class BrowserHttpContent : HttpContent
+ public string ResponseType
{
- private byte[]? _data;
- private readonly WasmFetchResponse _status;
-
- public BrowserHttpContent(WasmFetchResponse status)
+ get
{
- ArgumentNullException.ThrowIfNull(status);
-
- _status = status;
+ return FetchResponse.GetPropertyAsString("type")!;
}
+ }
- private async Task<byte[]> GetResponseData(CancellationToken cancellationToken)
+ public int Status
+ {
+ get
{
- if (_data != null)
- {
- return _data;
- }
- try
- {
- using (System.Runtime.InteropServices.JavaScript.ArrayBuffer dataBuffer = (System.Runtime.InteropServices.JavaScript.ArrayBuffer)await _status.ArrayBuffer().ConfigureAwait(continueOnCapturedContext: true))
- {
- using (Uint8Array dataBinView = new Uint8Array(dataBuffer))
- {
- _data = dataBinView.ToArray();
- _status.Dispose();
- }
- }
- }
- catch (JSException jse)
- {
- throw TranslateJSException(jse, cancellationToken);
- }
-
- return _data;
+ return FetchResponse.GetPropertyAsInt32("status");
}
+ }
- protected override async Task<Stream> CreateContentReadStreamAsync()
+ public void ThrowIfDisposed()
+ {
+ if (_isDisposed && FetchResponse.IsDisposed)
{
- byte[] data = await GetResponseData(CancellationToken.None).ConfigureAwait(continueOnCapturedContext: true);
- return new MemoryStream(data, writable: false);
+ throw new ObjectDisposedException(nameof(WasmFetchResponse));
}
+ }
- protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context) =>
- SerializeToStreamAsync(stream, context, CancellationToken.None);
- protected override async Task SerializeToStreamAsync(Stream stream, TransportContext? context, CancellationToken cancellationToken)
- {
- byte[] data = await GetResponseData(cancellationToken).ConfigureAwait(continueOnCapturedContext: true);
- await stream.WriteAsync(data, cancellationToken).ConfigureAwait(continueOnCapturedContext: true);
- }
- protected internal override bool TryComputeLength(out long length)
- {
- if (_data != null)
- {
- length = _data.Length;
- return true;
- }
+ public void Dispose()
+ {
+ if (_isDisposed)
+ return;
- length = 0;
- return false;
- }
+ _isDisposed = true;
+ _abortRegistration.Dispose();
- protected override void Dispose(bool disposing)
+ if (FetchResponse != null && !FetchResponse.IsDisposed)
{
- _status?.Dispose();
- base.Dispose(disposing);
+ BrowserHttpInterop.AbortResponse(FetchResponse);
}
+ FetchResponse?.Dispose();
}
+ }
+
+ internal sealed class BrowserHttpContent : HttpContent
+ {
+ private byte[]? _data;
+ private int _length = -1;
+ private readonly WasmFetchResponse _fetchResponse;
- private sealed class WasmHttpReadStream : Stream
+ public BrowserHttpContent(WasmFetchResponse fetchResponse)
{
- private WasmFetchResponse? _fetchResponse;
- private JSObject? _reader;
+ ArgumentNullException.ThrowIfNull(fetchResponse);
- private byte[]? _bufferedBytes;
- private int _position;
+ _fetchResponse = fetchResponse;
+ }
- public WasmHttpReadStream(WasmFetchResponse fetchResponse)
+ // TODO alocate smaller buffer and call multiple times
+ private async ValueTask<byte[]> GetResponseData(CancellationToken cancellationToken)
+ {
+ if (_data != null)
{
- _fetchResponse = fetchResponse;
+ return _data;
}
+ _fetchResponse.ThrowIfDisposed();
+ Task<int> promise = BrowserHttpInterop.GetResponseLength(_fetchResponse.FetchResponse);
+ _length = await BrowserHttpInterop.CancelationHelper(promise, cancellationToken, null, _fetchResponse.FetchResponse).ConfigureAwait(true);
+ _data = new byte[_length];
- public override bool CanRead => true;
- public override bool CanSeek => false;
- public override bool CanWrite => false;
- public override long Length => throw new NotSupportedException();
- public override long Position
- {
- get => throw new NotSupportedException();
- set => throw new NotSupportedException();
- }
+ BrowserHttpInterop.GetResponseBytes(_fetchResponse.FetchResponse, new Span<byte>(_data));
- public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
- {
- ValidateBufferArguments(buffer, offset, count);
- return ReadAsync(new Memory<byte>(buffer, offset, count), cancellationToken).AsTask();
- }
+ return _data;
+ }
- public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken)
- {
- CancellationHelper.ThrowIfCancellationRequested(cancellationToken);
+ protected override async Task<Stream> CreateContentReadStreamAsync()
+ {
+ byte[] data = await GetResponseData(CancellationToken.None).ConfigureAwait(true);
+ return new MemoryStream(data, writable: false);
+ }
- if (_reader == null)
- {
- // If we've read everything, then _reader and _status will be null
- if (_fetchResponse == null)
- {
- return 0;
- }
+ protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context) =>
+ SerializeToStreamAsync(stream, context, CancellationToken.None);
- try
- {
- using (JSObject body = _fetchResponse.Body)
- {
- _reader = (JSObject)body.Invoke("getReader");
- }
- }
- catch (OperationCanceledException oce) when (cancellationToken.IsCancellationRequested)
- {
- throw CancellationHelper.CreateOperationCanceledException(oce, cancellationToken);
- }
- catch (JSException jse)
- {
- throw TranslateJSException(jse, cancellationToken);
- }
- }
+ protected override async Task SerializeToStreamAsync(Stream stream, TransportContext? context, CancellationToken cancellationToken)
+ {
+ ArgumentNullException.ThrowIfNull(stream, nameof(stream));
+ byte[] data = await GetResponseData(cancellationToken).ConfigureAwait(true);
+ await stream.WriteAsync(data, cancellationToken).ConfigureAwait(true);
+ }
- using var abortRegistration = cancellationToken.Register(() =>
- {
- _reader.Invoke("cancel");
- });
+ protected internal override bool TryComputeLength(out long length)
+ {
+ if (_length != -1)
+ {
+ length = _length;
+ return true;
+ }
- if (_bufferedBytes != null && _position < _bufferedBytes.Length)
- {
- return ReadBuffered();
- }
+ length = 0;
+ return false;
+ }
- try
- {
- var t = (Task<object>)_reader.Invoke("read");
- using (var read = (JSObject)await t.ConfigureAwait(continueOnCapturedContext: true))
- {
- if (cancellationToken.IsCancellationRequested)
- {
- _reader.Invoke("cancel");
- throw CancellationHelper.CreateOperationCanceledException(null, cancellationToken);
- }
+ protected override void Dispose(bool disposing)
+ {
+ _fetchResponse?.Dispose();
+ base.Dispose(disposing);
+ }
+ }
- if ((bool)read.GetObjectProperty("done"))
- {
- _reader.Dispose();
- _reader = null;
+ internal sealed class WasmHttpReadStream : Stream
+ {
+ private WasmFetchResponse _fetchResponse;
- _fetchResponse?.Dispose();
- _fetchResponse = null;
- return 0;
- }
+ public WasmHttpReadStream(WasmFetchResponse fetchResponse)
+ {
+ _fetchResponse = fetchResponse;
+ }
- _position = 0;
- // value for fetch streams is a Uint8Array
- using (Uint8Array binValue = (Uint8Array)read.GetObjectProperty("value"))
- _bufferedBytes = binValue.ToArray();
- }
- }
- catch (OperationCanceledException oce) when (cancellationToken.IsCancellationRequested)
- {
- throw CancellationHelper.CreateOperationCanceledException(oce, cancellationToken);
- }
- catch (JSException jse)
- {
- throw TranslateJSException(jse, cancellationToken);
- }
+ public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken)
+ {
+ ArgumentNullException.ThrowIfNull(buffer, nameof(buffer));
- return ReadBuffered();
+ _fetchResponse.ThrowIfDisposed();
+ cancellationToken.ThrowIfCancellationRequested();
+ using (Buffers.MemoryHandle handle = buffer.Pin())
+ {
+ Task<int> promise = GetStreamedResponseBytesUnsafe(_fetchResponse, buffer, handle);
+ int response = await BrowserHttpInterop.CancelationHelper(promise, cancellationToken, null, _fetchResponse.FetchResponse).ConfigureAwait(true);
+ return response;
+ }
- int ReadBuffered()
- {
- int n = Math.Min(_bufferedBytes.Length - _position, buffer.Length);
- if (n <= 0)
- {
- return 0;
- }
+ unsafe static Task<int> GetStreamedResponseBytesUnsafe(WasmFetchResponse _fetchResponse, Memory<byte> buffer, System.Buffers.MemoryHandle handle)
+ => BrowserHttpInterop.GetStreamedResponseBytes(_fetchResponse.FetchResponse, (IntPtr)handle.Pointer, buffer.Length);
+ }
- _bufferedBytes.AsSpan(_position, n).CopyTo(buffer.Span);
- _position += n;
+ public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ {
+ ValidateBufferArguments(buffer, offset, count);
+ return ReadAsync(new Memory<byte>(buffer, offset, count), cancellationToken).AsTask();
+ }
- return n;
- }
- }
+ public override bool CanRead => true;
+ public override bool CanSeek => false;
+ public override bool CanWrite => false;
+ public override long Length => throw new NotSupportedException();
+ public override long Position
+ {
+ get => throw new NotSupportedException();
+ set => throw new NotSupportedException();
+ }
- protected override void Dispose(bool disposing)
- {
- _reader?.Dispose();
- _fetchResponse?.Dispose();
- }
+ protected override void Dispose(bool disposing)
+ {
+ _fetchResponse?.Dispose();
+ }
- public override void Flush()
- {
- }
+ public override void Flush()
+ {
+ }
- public override int Read(byte[] buffer, int offset, int count)
- {
- throw new NotSupportedException(SR.net_http_synchronous_reads_not_supported);
- }
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ throw new NotSupportedException(SR.net_http_synchronous_reads_not_supported);
+ }
- public override long Seek(long offset, SeekOrigin origin)
- {
- throw new NotSupportedException();
- }
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotSupportedException();
+ }
- public override void SetLength(long value)
- {
- throw new NotSupportedException();
- }
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException();
+ }
- public override void Write(byte[] buffer, int offset, int count)
- {
- throw new NotSupportedException();
- }
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ throw new NotSupportedException();
}
}
}
diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpInterop.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpInterop.cs
new file mode 100644
index 00000000000..0d6aec34548
--- /dev/null
+++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpInterop.cs
@@ -0,0 +1,141 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.IO;
+using System.Net.Http.Headers;
+using System.Runtime.InteropServices.JavaScript;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace System.Net.Http
+{
+ internal static partial class BrowserHttpInterop
+ {
+ [JSImport("INTERNAL.http_wasm_supports_streaming_response")]
+ public static partial bool SupportsStreamingResponse();
+
+ [JSImport("INTERNAL.http_wasm_create_abort_controler")]
+ public static partial JSObject CreateAbortController();
+
+ [JSImport("INTERNAL.http_wasm_abort_request")]
+ public static partial void AbortRequest(
+ JSObject abortController);
+
+ [JSImport("INTERNAL.http_wasm_abort_response")]
+ public static partial void AbortResponse(
+ JSObject fetchResponse);
+
+ [JSImport("INTERNAL.http_wasm_get_response_header_names")]
+ private static partial string[] _GetResponseHeaderNames(
+ JSObject fetchResponse);
+
+ [JSImport("INTERNAL.http_wasm_get_response_header_values")]
+ private static partial string[] _GetResponseHeaderValues(
+ JSObject fetchResponse);
+
+ public static void GetResponseHeaders(JSObject fetchResponse, HttpHeaders resposeHeaders, HttpHeaders contentHeaders)
+ {
+ string[] headerNames = _GetResponseHeaderNames(fetchResponse);
+ string[] headerValues = _GetResponseHeaderValues(fetchResponse);
+
+ for (int i = 0; i < headerNames.Length; i++)
+ {
+ if (!resposeHeaders.TryAddWithoutValidation(headerNames[i], headerValues[i]))
+ {
+ contentHeaders.TryAddWithoutValidation(headerNames[i], headerValues[i]);
+ }
+ }
+ }
+
+
+ [JSImport("INTERNAL.http_wasm_fetch")]
+ public static partial Task<JSObject> Fetch(
+ string uri,
+ string[] headerNames,
+ string[] headerValues,
+ string[] optionNames,
+ [JSMarshalAs<JSType.Array<JSType.Any>>] object?[] optionValues,
+ JSObject abortControler,
+ string? body = null);
+
+ [JSImport("INTERNAL.http_wasm_fetch_bytes")]
+ private static partial Task<JSObject> FetchBytes(
+ string uri,
+ string[] headerNames,
+ string[] headerValues,
+ string[] optionNames,
+ [JSMarshalAs<JSType.Array<JSType.Any>>] object?[] optionValues,
+ JSObject abortControler,
+ IntPtr bodyPtr,
+ int bodyLength
+ );
+
+ public static unsafe Task<JSObject> Fetch(string uri, string[] headerNames, string[] headerValues, string[] optionNames, object?[] optionValues, JSObject abortControler, byte[] body)
+ {
+ fixed (byte* ptr = body)
+ {
+ return FetchBytes(uri, headerNames, headerValues, optionNames, optionValues, abortControler, (IntPtr)ptr, body.Length);
+ }
+ }
+
+ [JSImport("INTERNAL.http_wasm_get_streamed_response_bytes")]
+ public static partial Task<int> GetStreamedResponseBytes(
+ JSObject fetchResponse,
+ IntPtr bufferPtr,
+ int bufferLength);
+
+ [JSImport("INTERNAL.http_wasm_get_response_length")]
+ public static partial Task<int> GetResponseLength(
+ JSObject fetchResponse);
+
+ [JSImport("INTERNAL.http_wasm_get_response_bytes")]
+ public static partial int GetResponseBytes(
+ JSObject fetchResponse,
+ [JSMarshalAs<JSType.MemoryView>] Span<byte> buffer);
+
+
+ public static async ValueTask<T> CancelationHelper<T>(Task<T> promise, CancellationToken cancellationToken, JSObject? abortController = null, JSObject? fetchResponse = null)
+ {
+ if (promise.IsCompletedSuccessfully)
+ {
+ return promise.Result;
+ }
+ try
+ {
+ using (var operationRegistration = cancellationToken.Register(() =>
+ {
+ CancelablePromise.CancelPromise(promise);
+ if (abortController != null)
+ {
+ AbortRequest(abortController);
+ }
+ if (fetchResponse != null)
+ {
+ AbortResponse(fetchResponse);
+ }
+
+ }))
+ {
+ return await promise.ConfigureAwait(true);
+ }
+ }
+ catch (OperationCanceledException oce) when (cancellationToken.IsCancellationRequested)
+ {
+ throw CancellationHelper.CreateOperationCanceledException(oce, cancellationToken);
+ }
+ catch (JSException jse)
+ {
+ if (jse.Message.StartsWith("AbortError", StringComparison.Ordinal))
+ {
+ throw CancellationHelper.CreateOperationCanceledException(jse, CancellationToken.None);
+ }
+ if (cancellationToken.IsCancellationRequested)
+ {
+ throw CancellationHelper.CreateOperationCanceledException(jse, cancellationToken);
+ }
+ throw new HttpRequestException(jse.Message, jse);
+ }
+ }
+ }
+
+}
diff --git a/src/libraries/System.Net.WebSockets.Client/src/System.Net.WebSockets.Client.csproj b/src/libraries/System.Net.WebSockets.Client/src/System.Net.WebSockets.Client.csproj
index cff3d6e0041..f15169b1fdd 100644
--- a/src/libraries/System.Net.WebSockets.Client/src/System.Net.WebSockets.Client.csproj
+++ b/src/libraries/System.Net.WebSockets.Client/src/System.Net.WebSockets.Client.csproj
@@ -3,6 +3,10 @@
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<TargetFrameworks>$(NetCoreAppCurrent);$(NetCoreAppCurrent)-Browser</TargetFrameworks>
</PropertyGroup>
+ <PropertyGroup Condition="'$(TargetPlatformIdentifier)' == 'Browser'">
+ <DefineConstants>$(DefineConstants);TARGET_BROWSER</DefineConstants>
+ </PropertyGroup>
+
<ItemGroup>
<Compile Include="System\Net\WebSockets\ClientWebSocket.cs" />
<Compile Include="System\Net\WebSockets\ClientWebSocketDeflateConstants.cs" />
@@ -16,8 +20,10 @@
</ItemGroup>
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'Browser'">
<Compile Include="System\Net\WebSockets\WebSocketHandle.Browser.cs" />
+ <Compile Include="System\Net\WebSockets\BrowserWebSockets\BrowserInterop.cs" />
<Compile Include="System\Net\WebSockets\BrowserWebSockets\BrowserWebSocket.cs" />
<Compile Include="System\Net\WebSockets\BrowserWebSockets\ClientWebSocketOptions.cs" />
+ <Compile Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\src\System\Runtime\InteropServices\JavaScript\CancelablePromise.cs" Link="InteropServices\JavaScript\CancelablePromise.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.Win32.Primitives" />
@@ -37,6 +43,7 @@
<Reference Include="System.Memory" />
</ItemGroup>
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'Browser'">
- <ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\src\System.Runtime.InteropServices.JavaScript.csproj" SkipUseReferenceAssembly="true"/>
+ <ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\gen\JSImportGenerator\JSImportGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
+ <Reference Include="System.Runtime.InteropServices.JavaScript" />
</ItemGroup>
</Project>
diff --git a/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/BrowserWebSockets/BrowserInterop.cs b/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/BrowserWebSockets/BrowserInterop.cs
new file mode 100644
index 00000000000..4e16d14aff3
--- /dev/null
+++ b/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/BrowserWebSockets/BrowserInterop.cs
@@ -0,0 +1,62 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Threading.Tasks;
+using System.Runtime.InteropServices.JavaScript;
+using System.Runtime.InteropServices;
+
+namespace System.Net.WebSockets
+{
+ internal static partial class BrowserInterop
+ {
+ public static string? GetProtocol(JSObject? webSocket)
+ {
+ if (webSocket == null || webSocket.IsDisposed) return null;
+ string? protocol = webSocket.GetPropertyAsString("protocol");
+ return protocol;
+ }
+
+ public static int GetReadyState(JSObject? webSocket)
+ {
+ if (webSocket == null || webSocket.IsDisposed) return -1;
+ int? readyState = webSocket.GetPropertyAsInt32("readyState");
+ if (!readyState.HasValue) return -1;
+ return readyState.Value;
+ }
+
+ [JSImport("INTERNAL.ws_wasm_create")]
+ public static partial JSObject WebSocketCreate(
+ string uri,
+ string?[]? subProtocols,
+ [JSMarshalAs<JSType.Function<JSType.Number, JSType.String>>] Action<int, string> onClosed);
+
+ [JSImport("INTERNAL.ws_wasm_open")]
+ public static partial Task WebSocketOpen(
+ JSObject webSocket);
+
+ [JSImport("INTERNAL.ws_wasm_send")]
+ public static partial Task? WebSocketSend(
+ JSObject webSocket,
+ [JSMarshalAs<JSType.MemoryView>] ArraySegment<byte> buffer,
+ int messageType,
+ bool endOfMessage);
+
+ [JSImport("INTERNAL.ws_wasm_receive")]
+ public static partial Task? WebSocketReceive(
+ JSObject webSocket,
+ [JSMarshalAs<JSType.MemoryView>] ArraySegment<byte> buffer,
+ [JSMarshalAs<JSType.MemoryView>] ArraySegment<int> response);
+
+ [JSImport("INTERNAL.ws_wasm_close")]
+ public static partial Task? WebSocketClose(
+ JSObject webSocket,
+ int code,
+ string? reason,
+ bool waitForCloseReceived);
+
+ [JSImport("INTERNAL.ws_wasm_abort")]
+ public static partial void WebSocketAbort(
+ JSObject webSocket);
+
+ }
+}
diff --git a/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/BrowserWebSockets/BrowserWebSocket.cs b/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/BrowserWebSockets/BrowserWebSocket.cs
index a4d6a2c53ab..2fea50537a8 100644
--- a/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/BrowserWebSockets/BrowserWebSocket.cs
+++ b/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/BrowserWebSockets/BrowserWebSocket.cs
@@ -6,11 +6,6 @@ using System.Threading;
using System.Threading.Tasks;
using System.Runtime.InteropServices.JavaScript;
-using JavaScript = System.Runtime.InteropServices.JavaScript;
-using JSObject = System.Runtime.InteropServices.JavaScript.JSObject;
-
-#pragma warning disable CS0612 // using obsolete members until we finish https://github.com/dotnet/runtime/pull/66304/
-
namespace System.Net.WebSockets
{
/// <summary>
@@ -41,7 +36,7 @@ namespace System.Net.WebSockets
public override WebSocketCloseStatus? CloseStatus => _closeStatus;
public override string? CloseStatusDescription => _closeStatusDescription;
- public override string? SubProtocol => _innerWebSocket != null && !_innerWebSocket.IsDisposed ? _innerWebSocket!.GetObjectProperty("protocol")?.ToString() : null;
+ public override string? SubProtocol => BrowserInterop.GetProtocol(_innerWebSocket);
#endregion Properties
@@ -146,7 +141,7 @@ namespace System.Net.WebSockets
_aborted = true;
if (_innerWebSocket != null)
{
- JavaScript.Runtime.WebSocketAbort(_innerWebSocket!);
+ BrowserInterop.WebSocketAbort(_innerWebSocket!);
}
}
}
@@ -174,7 +169,7 @@ namespace System.Net.WebSockets
{
try
{
- object[]? subProtocols = requestedSubProtocols?.ToArray();
+ string[]? subProtocols = requestedSubProtocols?.ToArray();
var onClose = (int code, string reason) =>
{
_closeStatus = (WebSocketCloseStatus)code;
@@ -186,8 +181,9 @@ namespace System.Net.WebSockets
}
};
- var openTask = JavaScript.Runtime.WebSocketOpen(uri.ToString(), subProtocols, onClose, out _innerWebSocket, out IntPtr promiseJSHandle);
- var wrappedTask = CancelationHelper(openTask, promiseJSHandle, cancellationToken, _state);
+ _innerWebSocket = BrowserInterop.WebSocketCreate(uri.ToString(), subProtocols, onClose);
+ var openTask = BrowserInterop.WebSocketOpen(_innerWebSocket);
+ var wrappedTask = CancelationHelper(openTask!, cancellationToken, _state);
await wrappedTask.ConfigureAwait(true);
if (State == WebSocketState.Connecting)
@@ -215,13 +211,13 @@ namespace System.Net.WebSockets
{
try
{
- var sendTask = JavaScript.Runtime.WebSocketSend(_innerWebSocket!, buffer, (int)messageType, endOfMessage, out IntPtr promiseJSHandle);
+ var sendTask = BrowserInterop.WebSocketSend(_innerWebSocket!, buffer, (int)messageType, endOfMessage);
if (sendTask == null)
{
// return synchronously
return;
}
- var wrappedTask = CancelationHelper(sendTask, promiseJSHandle, cancellationToken, _state);
+ var wrappedTask = CancelationHelper(sendTask, cancellationToken, _state);
await wrappedTask.ConfigureAwait(true);
}
@@ -244,14 +240,14 @@ namespace System.Net.WebSockets
try
{
ArraySegment<int> response = new ArraySegment<int>(new int[3]);
- var receiveTask = JavaScript.Runtime.WebSocketReceive(_innerWebSocket!, buffer, response, out IntPtr promiseJSHandle);
+ var receiveTask = BrowserInterop.WebSocketReceive(_innerWebSocket!, buffer, response);
if (receiveTask == null)
{
// return synchronously
return ConvertResponse(response);
}
- var wrappedTask = CancelationHelper(receiveTask, promiseJSHandle, cancellationToken, _state);
+ var wrappedTask = CancelationHelper(receiveTask, cancellationToken, _state);
await wrappedTask.ConfigureAwait(true);
return ConvertResponse(response);
@@ -289,10 +285,10 @@ namespace System.Net.WebSockets
_closeStatus = closeStatus;
_closeStatusDescription = statusDescription;
- var closeTask = JavaScript.Runtime.WebSocketClose(_innerWebSocket!, (int)closeStatus, statusDescription, waitForCloseReceived, out IntPtr promiseJSHandle);
+ var closeTask = BrowserInterop.WebSocketClose(_innerWebSocket!, (int)closeStatus, statusDescription, waitForCloseReceived);
if (closeTask != null)
{
- var wrappedTask = CancelationHelper(closeTask, promiseJSHandle, cancellationToken, _state);
+ var wrappedTask = CancelationHelper(closeTask, cancellationToken, _state);
await wrappedTask.ConfigureAwait(true);
}
@@ -303,24 +299,21 @@ namespace System.Net.WebSockets
}
}
- private async ValueTask<object> CancelationHelper(Task<object> jsTask, IntPtr promiseJSHandle, CancellationToken cancellationToken, WebSocketState previousState)
+ private async ValueTask CancelationHelper(Task jsTask, CancellationToken cancellationToken, WebSocketState previousState)
{
if (jsTask.IsCompletedSuccessfully)
{
- return jsTask.Result;
+ return;
}
try
{
using (var receiveRegistration = cancellationToken.Register(() =>
{
- // this check makes sure that promiseJSHandle is still valid handle
- if (!jsTask.IsCompleted)
- {
- JavaScript.Runtime.CancelPromise(promiseJSHandle);
- }
+ CancelablePromise.CancelPromise(jsTask);
}))
{
- return await jsTask.ConfigureAwait(true);
+ await jsTask.ConfigureAwait(true);
+ return;
}
}
catch (JSException ex)
@@ -355,7 +348,7 @@ namespace System.Net.WebSockets
private WebSocketState GetReadyState()
{
- int readyState = (int)_innerWebSocket!.GetObjectProperty("readyState");
+ int readyState = BrowserInterop.GetReadyState(_innerWebSocket);
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/readyState
return readyState switch
diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System.Private.Runtime.InteropServices.JavaScript.TypeForwards.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System.Private.Runtime.InteropServices.JavaScript.TypeForwards.cs
deleted file mode 100644
index 381bb4d7e64..00000000000
--- a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System.Private.Runtime.InteropServices.JavaScript.TypeForwards.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-#pragma warning disable CS0612 // using obsolete members until we finish https://github.com/dotnet/runtime/pull/66304/
-
-[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.JavaScript.Array))]
-[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.JavaScript.ArrayBuffer))]
-[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.JavaScript.DataView))]
-[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.JavaScript.Function))]
-[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.JavaScript.Uint8Array))]
-
-[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.JavaScript.Runtime))]
-[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.JavaScript.JSObject))]
-[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.JavaScript.JSException))]
diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System.Private.Runtime.InteropServices.JavaScript.csproj b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System.Private.Runtime.InteropServices.JavaScript.csproj
deleted file mode 100644
index 1660560c24f..00000000000
--- a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System.Private.Runtime.InteropServices.JavaScript.csproj
+++ /dev/null
@@ -1,13 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
- <PropertyGroup>
- <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
- <TargetFramework>$(NetCoreAppCurrent)-Browser</TargetFramework>
- </PropertyGroup>
- <ItemGroup>
- <Reference Include="System.Runtime" />
- <ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\src\System.Runtime.InteropServices.JavaScript.csproj" SkipUseReferenceAssembly="true"/>
- </ItemGroup>
- <ItemGroup>
- <Compile Include="System.Private.Runtime.InteropServices.JavaScript.TypeForwards.cs" />
- </ItemGroup>
-</Project>
diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System.Private.Runtime.InteropServices.JavaScript.Tests.csproj b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System.Private.Runtime.InteropServices.JavaScript.Tests.csproj
index c99e34b5316..260e965efe4 100644
--- a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System.Private.Runtime.InteropServices.JavaScript.Tests.csproj
+++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System.Private.Runtime.InteropServices.JavaScript.Tests.csproj
@@ -18,14 +18,14 @@
<Compile Include="System\Runtime\InteropServices\JavaScript\Http\HttpRequestMessageTest.cs" />
<Compile Include="System\Runtime\InteropServices\JavaScript\ParallelTests.cs" />
<Compile Include="System\Runtime\InteropServices\JavaScript\TimerTests.cs" />
+ <Compile Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\tests\System\Runtime\InteropServices\JavaScript\Utils.cs" Link="System\Runtime\InteropServices\JavaScript\Utils.cs" />
</ItemGroup>
<ItemGroup>
- <WasmExtraFilesToDeploy Include="timers.js" />
- <None Include="timers.js" />
- </ItemGroup>
- <ItemGroup>
- <!-- Part of the shared framework but not exposed. -->
+ <WasmExtraFilesToDeploy Include="timers.mjs" />
+ <None Include="timers.mjs" />
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\src\System.Runtime.InteropServices.JavaScript.csproj" SkipUseReferenceAssembly="true"/>
+ <ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\gen\JSImportGenerator\JSImportGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
+ <ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices\gen\Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
</Project>
diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/TimerTests.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/TimerTests.cs
index 2bf8dee0b82..3a12086a93a 100644
--- a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/TimerTests.cs
+++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/TimerTests.cs
@@ -13,13 +13,6 @@ namespace System.Runtime.InteropServices.JavaScript.Tests
[ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsBrowserDomSupported))]
public class TimerTests : IAsyncLifetime
{
- static JSObject _timersHelper;
- static Function _installWrapper;
- static Function _getRegisterCount;
- static Function _getHitCount;
- static Function _cleanupWrapper;
- static Function _log;
-
public static IEnumerable<object[]> TestCases()
{
yield return new object[] { new int[0], 0, null, null };
@@ -37,24 +30,24 @@ namespace System.Runtime.InteropServices.JavaScript.Tests
Timer[] timers = new Timer[timeouts.Length];
try
{
- _log.Call(_timersHelper, $"Waiting for runtime to settle");
+ TimersJS.Log($"Waiting for runtime to settle");
// the test is quite sensitive to timing and order of execution. Here we are giving time to timers of XHarness and previous tests to finish.
await Task.Delay(2000);
- _installWrapper.Call(_timersHelper);
- _log.Call(_timersHelper, $"Ready!");
+ TimersJS.Install();
+ TimersJS.Log($"Ready!");
for (int i = 0; i < timeouts.Length; i++)
{
int index = i;
- _log.Call(_timersHelper, $"Registering {index} delay {timeouts[i]}");
+ TimersJS.Log($"Registering {index} delay {timeouts[i]}");
timers[i] = new Timer((_) =>
{
- _log.Call(_timersHelper, $"In timer{index}");
+ TimersJS.Log($"In timer{index}");
wasCalled++;
}, null, timeouts[i], 0);
}
- var setCounter = (int)_getRegisterCount.Call(_timersHelper);
+ var setCounter = TimersJS.GetRegisterCount();
Assert.True(0 == wasCalled, $"wasCalled: {wasCalled}");
Assert.True((expectedSetCounter ?? timeouts.Length) == setCounter, $"setCounter: actual {setCounter} expected {expectedSetCounter}");
@@ -65,23 +58,23 @@ namespace System.Runtime.InteropServices.JavaScript.Tests
// Here we are giving time to our timers to finish.
var afterLastTimer = timeouts.Length == 0 ? 500 : 500 + timeouts.Max();
- _log.Call(_timersHelper, "wait for timers to run");
+ TimersJS.Log("wait for timers to run");
// this delay is also implemented as timer, so it counts to asserts
await Task.Delay(afterLastTimer);
- _log.Call(_timersHelper, "cleanup");
- _cleanupWrapper.Call(_timersHelper);
+ TimersJS.Log("cleanup");
+ TimersJS.Cleanup();
Assert.True(timeouts.Length == wasCalled, $"wasCalled: actual {wasCalled} expected {timeouts.Length}");
if (expectedSetCounterAfterCleanUp != null)
{
- var setCounter = (int)_getRegisterCount.Call(_timersHelper);
+ var setCounter = TimersJS.GetRegisterCount();
Assert.True(expectedSetCounterAfterCleanUp.Value == setCounter, $"setCounter: actual {setCounter} expected {expectedSetCounterAfterCleanUp.Value}");
}
if (expectedHitCount != null)
{
- var hitCounter = (int)_getHitCount.Call(_timersHelper);
+ var hitCounter = TimersJS.GetHitCount();
Assert.True(expectedHitCount == hitCounter, $"hitCounter: actual {hitCounter} expected {expectedHitCount}");
}
@@ -92,27 +85,34 @@ namespace System.Runtime.InteropServices.JavaScript.Tests
}
}
+ static JSObject _module;
public async Task InitializeAsync()
{
- if (_timersHelper == null)
+ if (_module == null)
{
- Function helper = new Function(@"
- const loadTimersJs = async () => {
- await import('./timers.js');
- };
- return loadTimersJs();
- ");
- await (Task)helper.Call(_timersHelper);
-
- _timersHelper = (JSObject)Runtime.GetGlobalObject("timersHelper");
- _installWrapper = (Function)_timersHelper.GetObjectProperty("install");
- _getRegisterCount = (Function)_timersHelper.GetObjectProperty("getRegisterCount");
- _getHitCount = (Function)_timersHelper.GetObjectProperty("getHitCount");
- _cleanupWrapper = (Function)_timersHelper.GetObjectProperty("cleanup");
- _log = (Function)_timersHelper.GetObjectProperty("log");
+ _module = await JSHost.ImportAsync("Timers", "./timers.mjs");
}
}
public Task DisposeAsync() => Task.CompletedTask;
}
+
+ public static partial class TimersJS
+ {
+ [JSImport("log", "Timers")]
+ public static partial void Log(string message);
+
+ [JSImport("install", "Timers")]
+ public static partial void Install();
+
+ [JSImport("cleanup", "Timers")]
+ public static partial void Cleanup();
+
+ [JSImport("getRegisterCount", "Timers")]
+ public static partial int GetRegisterCount();
+
+ [JSImport("getHitCount", "Timers")]
+ public static partial int GetHitCount();
+
+ }
}
diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/timers.js b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/timers.js
deleted file mode 100644
index ca2b7f31d52..00000000000
--- a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/timers.js
+++ /dev/null
@@ -1,47 +0,0 @@
-class TimersHelper {
- log(message) {
- // uncomment for debugging
- // console.log(message);
- }
- install() {
- const measuredCallbackName = "mono_wasm_set_timeout_exec";
- globalThis.registerCount = 0;
- globalThis.hitCount = 0;
- this.log("install")
- if (!globalThis.originalSetTimeout) {
- globalThis.originalSetTimeout = globalThis.setTimeout;
- }
- globalThis.setTimeout = (cb, time) => {
- var start = Date.now().valueOf();
- if (cb.name === measuredCallbackName) {
- globalThis.registerCount++;
- this.log(`registerCount: ${globalThis.registerCount} now:${start} delay:${time}`)
- }
- return globalThis.originalSetTimeout(() => {
- if (cb.name === measuredCallbackName) {
- var hit = Date.now().valueOf();
- globalThis.hitCount++;
- this.log(`hitCount: ${globalThis.hitCount} now:${hit} delay:${time} delta:${hit - start}`)
- }
- cb();
- }, time);
- };
- }
-
- getRegisterCount() {
- this.log(`registerCount: ${globalThis.registerCount} `)
- return globalThis.registerCount;
- }
-
- getHitCount() {
- this.log(`hitCount: ${globalThis.hitCount} `)
- return globalThis.hitCount;
- }
-
- cleanup() {
- this.log(`cleanup registerCount: ${globalThis.registerCount} hitCount: ${globalThis.hitCount} `)
- globalThis.setTimeout = globalThis.originalSetTimeout;
- }
-}
-
-globalThis.timersHelper = new TimersHelper();
diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/timers.mjs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/timers.mjs
new file mode 100644
index 00000000000..4e471c397c2
--- /dev/null
+++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/timers.mjs
@@ -0,0 +1,47 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+export function log(message) {
+ // uncomment for debugging
+ console.log(message);
+}
+
+export function install() {
+ const measuredCallbackName = "mono_wasm_set_timeout_exec";
+ globalThis.registerCount = 0;
+ globalThis.hitCount = 0;
+ log("install")
+ if (!globalThis.originalSetTimeout) {
+ globalThis.originalSetTimeout = globalThis.setTimeout;
+ }
+ globalThis.setTimeout = (cb, time) => {
+ var start = Date.now().valueOf();
+ if (cb.name === measuredCallbackName) {
+ globalThis.registerCount++;
+ log(`registerCount: ${globalThis.registerCount} now:${start} delay:${time}`)
+ }
+ return globalThis.originalSetTimeout(() => {
+ if (cb.name === measuredCallbackName) {
+ var hit = Date.now().valueOf();
+ globalThis.hitCount++;
+ log(`hitCount: ${globalThis.hitCount} now:${hit} delay:${time} delta:${hit - start}`)
+ }
+ cb();
+ }, time);
+ };
+}
+
+export function getRegisterCount() {
+ log(`registerCount: ${globalThis.registerCount} `)
+ return globalThis.registerCount;
+}
+
+export function getHitCount() {
+ log(`hitCount: ${globalThis.hitCount} `)
+ return globalThis.hitCount;
+}
+
+export function cleanup() {
+ log(`cleanup registerCount: ${globalThis.registerCount} hitCount: ${globalThis.hitCount} `)
+ globalThis.setTimeout = globalThis.originalSetTimeout;
+}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/System.Runtime.InteropServices.JavaScript.sln b/src/libraries/System.Runtime.InteropServices.JavaScript/System.Runtime.InteropServices.JavaScript.sln
index 15cd8ab85c1..8676c09e39f 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/System.Runtime.InteropServices.JavaScript.sln
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/System.Runtime.InteropServices.JavaScript.sln
@@ -15,6 +15,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.InteropServi
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{F2C2C78A-CEDD-4DE0-9C3A-0195F00E0B4E}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JSImportGenerator", "gen\JSImportGenerator\JSImportGenerator.csproj", "{CE5E53C1-F9B5-41EE-8D00-837913EC57D1}"
+EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.InteropServices", "..\System.Runtime.InteropServices\ref\System.Runtime.InteropServices.csproj", "{6B7FDECD-61C2-4764-A994-AB117B405E33}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.InteropServices", "..\System.Runtime.InteropServices\src\System.Runtime.InteropServices.csproj", "{9BEEB101-BBD6-45D0-9B50-819EDB5FF3D3}"
@@ -29,7 +31,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.InteropServi
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Private.CoreLib.Generators", "..\System.Private.CoreLib\gen\System.Private.CoreLib.Generators.csproj", "{3355B50F-2794-4BD5-8D03-AF54CDB7DF98}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibraryImportGenerator", "..\System.Runtime.InteropServices\gen\LibraryImportGenerator\LibraryImportGenerator.csproj", "{BD7963AE-23CA-4F52-9500-9C67F1F5B42D}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.InteropServices.JavaScript.Tests", "tests\System.Runtime.InteropServices.JavaScript.Tests.csproj", "{765B4AA5-723A-44FF-BC4E-EB0F03103F6D}"
+ ProjectSection(ProjectDependencies) = postProject
+ {3355B50F-2794-4BD5-8D03-AF54CDB7DF98} = {3355B50F-2794-4BD5-8D03-AF54CDB7DF98}
+ EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ancillary.Interop", "..\System.Runtime.InteropServices\tests\Ancillary.Interop\Ancillary.Interop.csproj", "{EC3ADEFA-1FF3-482C-8CCB-FE4C77292532}"
EndProject
@@ -37,6 +42,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Net.WebSockets.Clien
ProjectSection(ProjectDependencies) = postProject
{28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1} = {28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1}
{AD1E9074-194E-478A-B43F-BEC704EAFD11} = {AD1E9074-194E-478A-B43F-BEC704EAFD11}
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1} = {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}
{71A845ED-4344-41FC-8FCA-3AC9B6BA6C45} = {71A845ED-4344-41FC-8FCA-3AC9B6BA6C45}
EndProjectSection
EndProject
@@ -50,14 +56,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Net.WebSockets.Clien
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Private.CoreLib", "..\..\mono\System.Private.CoreLib\System.Private.CoreLib.csproj", "{C8FB882D-DB06-4F4B-BCFB-5B7F936E127B}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Private.Runtime.InteropServices.JavaScript", "..\System.Private.Runtime.InteropServices.JavaScript\src\System.Private.Runtime.InteropServices.JavaScript.csproj", "{963B6EB8-13FE-44EB-9B02-762A8AB9C459}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibraryImportGenerator.Unit.Tests", "..\System.Runtime.InteropServices\tests\LibraryImportGenerator.UnitTests\LibraryImportGenerator.Unit.Tests.csproj", "{518D3BDB-323D-43EB-B868-A7EDC08A15F5}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JSImportGenerator.Unit.Tests", "tests\JSImportGenerator.UnitTest\JSImportGenerator.Unit.Tests.csproj", "{BFED925C-18F2-4C98-833E-66F205234598}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JSImportGenerator", "gen\JSImportGenerator\JSImportGenerator.csproj", "{BE06AA07-F96F-4B63-8445-AFBC688D5CE0}"
-EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Checked|Any CPU = Checked|Any CPU
@@ -182,6 +182,48 @@ Global
{71A845ED-4344-41FC-8FCA-3AC9B6BA6C45}.Release|x64.Build.0 = Release|Any CPU
{71A845ED-4344-41FC-8FCA-3AC9B6BA6C45}.Release|x86.ActiveCfg = Release|Any CPU
{71A845ED-4344-41FC-8FCA-3AC9B6BA6C45}.Release|x86.Build.0 = Release|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Checked|Any CPU.Build.0 = Debug|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Checked|arm.ActiveCfg = Debug|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Checked|arm64.ActiveCfg = Debug|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Checked|armv6.ActiveCfg = Release|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Checked|armv6.Build.0 = Release|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Checked|s390x.ActiveCfg = Release|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Checked|s390x.Build.0 = Release|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Checked|wasm.ActiveCfg = Release|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Checked|wasm.Build.0 = Release|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Checked|x64.ActiveCfg = Debug|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Checked|x64.Build.0 = Debug|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Checked|x86.ActiveCfg = Debug|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Checked|x86.Build.0 = Debug|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Debug|arm.ActiveCfg = Debug|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Debug|arm64.ActiveCfg = Debug|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Debug|armv6.ActiveCfg = Debug|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Debug|armv6.Build.0 = Debug|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Debug|s390x.ActiveCfg = Debug|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Debug|s390x.Build.0 = Debug|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Debug|wasm.ActiveCfg = Debug|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Debug|wasm.Build.0 = Debug|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Debug|x64.Build.0 = Debug|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Debug|x86.Build.0 = Debug|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Release|arm.ActiveCfg = Release|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Release|arm64.ActiveCfg = Release|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Release|armv6.ActiveCfg = Release|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Release|armv6.Build.0 = Release|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Release|s390x.ActiveCfg = Release|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Release|s390x.Build.0 = Release|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Release|wasm.ActiveCfg = Release|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Release|wasm.Build.0 = Release|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Release|x64.ActiveCfg = Release|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Release|x64.Build.0 = Release|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Release|x86.ActiveCfg = Release|Any CPU
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Release|x86.Build.0 = Release|Any CPU
{6B7FDECD-61C2-4764-A994-AB117B405E33}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
{6B7FDECD-61C2-4764-A994-AB117B405E33}.Checked|Any CPU.Build.0 = Debug|Any CPU
{6B7FDECD-61C2-4764-A994-AB117B405E33}.Checked|arm.ActiveCfg = Debug|Any CPU
@@ -509,54 +551,54 @@ Global
{3355B50F-2794-4BD5-8D03-AF54CDB7DF98}.Release|x64.Build.0 = Release|Any CPU
{3355B50F-2794-4BD5-8D03-AF54CDB7DF98}.Release|x86.ActiveCfg = Release|Any CPU
{3355B50F-2794-4BD5-8D03-AF54CDB7DF98}.Release|x86.Build.0 = Release|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Checked|Any CPU.Build.0 = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Checked|arm.ActiveCfg = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Checked|arm.Build.0 = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Checked|arm64.ActiveCfg = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Checked|arm64.Build.0 = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Checked|armv6.ActiveCfg = Release|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Checked|armv6.Build.0 = Release|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Checked|s390x.ActiveCfg = Release|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Checked|s390x.Build.0 = Release|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Checked|wasm.ActiveCfg = Release|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Checked|wasm.Build.0 = Release|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Checked|x64.ActiveCfg = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Checked|x64.Build.0 = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Checked|x86.ActiveCfg = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Checked|x86.Build.0 = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Debug|arm.ActiveCfg = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Debug|arm.Build.0 = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Debug|arm64.ActiveCfg = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Debug|arm64.Build.0 = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Debug|armv6.ActiveCfg = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Debug|armv6.Build.0 = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Debug|s390x.ActiveCfg = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Debug|s390x.Build.0 = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Debug|wasm.ActiveCfg = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Debug|wasm.Build.0 = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Debug|x64.ActiveCfg = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Debug|x64.Build.0 = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Debug|x86.ActiveCfg = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Debug|x86.Build.0 = Debug|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Release|Any CPU.Build.0 = Release|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Release|arm.ActiveCfg = Release|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Release|arm.Build.0 = Release|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Release|arm64.ActiveCfg = Release|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Release|arm64.Build.0 = Release|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Release|armv6.ActiveCfg = Release|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Release|armv6.Build.0 = Release|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Release|s390x.ActiveCfg = Release|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Release|s390x.Build.0 = Release|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Release|wasm.ActiveCfg = Release|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Release|wasm.Build.0 = Release|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Release|x64.ActiveCfg = Release|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Release|x64.Build.0 = Release|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Release|x86.ActiveCfg = Release|Any CPU
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D}.Release|x86.Build.0 = Release|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|Any CPU.Build.0 = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|arm.ActiveCfg = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|arm.Build.0 = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|arm64.ActiveCfg = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|arm64.Build.0 = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|armv6.ActiveCfg = Release|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|armv6.Build.0 = Release|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|s390x.ActiveCfg = Release|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|s390x.Build.0 = Release|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|wasm.ActiveCfg = Release|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|wasm.Build.0 = Release|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|x64.ActiveCfg = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|x64.Build.0 = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|x86.ActiveCfg = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|x86.Build.0 = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Debug|arm.ActiveCfg = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Debug|arm.Build.0 = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Debug|arm64.ActiveCfg = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Debug|arm64.Build.0 = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Debug|armv6.ActiveCfg = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Debug|armv6.Build.0 = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Debug|s390x.ActiveCfg = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Debug|s390x.Build.0 = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Debug|wasm.ActiveCfg = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Debug|wasm.Build.0 = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Debug|x64.Build.0 = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Debug|x86.Build.0 = Debug|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Release|arm.ActiveCfg = Release|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Release|arm.Build.0 = Release|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Release|arm64.ActiveCfg = Release|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Release|arm64.Build.0 = Release|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Release|armv6.ActiveCfg = Release|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Release|armv6.Build.0 = Release|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Release|s390x.ActiveCfg = Release|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Release|s390x.Build.0 = Release|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Release|wasm.ActiveCfg = Release|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Release|wasm.Build.0 = Release|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Release|x64.ActiveCfg = Release|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Release|x64.Build.0 = Release|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Release|x86.ActiveCfg = Release|Any CPU
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Release|x86.Build.0 = Release|Any CPU
{EC3ADEFA-1FF3-482C-8CCB-FE4C77292532}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
{EC3ADEFA-1FF3-482C-8CCB-FE4C77292532}.Checked|Any CPU.Build.0 = Debug|Any CPU
{EC3ADEFA-1FF3-482C-8CCB-FE4C77292532}.Checked|arm.ActiveCfg = Debug|Any CPU
@@ -866,102 +908,6 @@ Global
{C8FB882D-DB06-4F4B-BCFB-5B7F936E127B}.Release|x64.Build.0 = Release|x64
{C8FB882D-DB06-4F4B-BCFB-5B7F936E127B}.Release|x86.ActiveCfg = Release|x86
{C8FB882D-DB06-4F4B-BCFB-5B7F936E127B}.Release|x86.Build.0 = Release|x86
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Checked|Any CPU.Build.0 = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Checked|arm.ActiveCfg = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Checked|arm.Build.0 = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Checked|arm64.ActiveCfg = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Checked|arm64.Build.0 = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Checked|armv6.ActiveCfg = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Checked|armv6.Build.0 = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Checked|s390x.ActiveCfg = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Checked|s390x.Build.0 = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Checked|wasm.ActiveCfg = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Checked|wasm.Build.0 = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Checked|x64.ActiveCfg = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Checked|x64.Build.0 = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Checked|x86.ActiveCfg = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Checked|x86.Build.0 = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Debug|arm.ActiveCfg = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Debug|arm.Build.0 = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Debug|arm64.ActiveCfg = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Debug|arm64.Build.0 = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Debug|armv6.ActiveCfg = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Debug|armv6.Build.0 = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Debug|s390x.ActiveCfg = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Debug|s390x.Build.0 = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Debug|wasm.ActiveCfg = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Debug|wasm.Build.0 = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Debug|x64.ActiveCfg = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Debug|x64.Build.0 = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Debug|x86.ActiveCfg = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Debug|x86.Build.0 = Debug|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Release|Any CPU.Build.0 = Release|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Release|arm.ActiveCfg = Release|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Release|arm.Build.0 = Release|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Release|arm64.ActiveCfg = Release|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Release|arm64.Build.0 = Release|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Release|armv6.ActiveCfg = Release|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Release|armv6.Build.0 = Release|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Release|s390x.ActiveCfg = Release|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Release|s390x.Build.0 = Release|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Release|wasm.ActiveCfg = Release|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Release|wasm.Build.0 = Release|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Release|x64.ActiveCfg = Release|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Release|x64.Build.0 = Release|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Release|x86.ActiveCfg = Release|Any CPU
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459}.Release|x86.Build.0 = Release|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Checked|Any CPU.Build.0 = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Checked|arm.ActiveCfg = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Checked|arm.Build.0 = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Checked|arm64.ActiveCfg = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Checked|arm64.Build.0 = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Checked|armv6.ActiveCfg = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Checked|armv6.Build.0 = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Checked|s390x.ActiveCfg = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Checked|s390x.Build.0 = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Checked|wasm.ActiveCfg = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Checked|wasm.Build.0 = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Checked|x64.ActiveCfg = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Checked|x64.Build.0 = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Checked|x86.ActiveCfg = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Checked|x86.Build.0 = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Debug|arm.ActiveCfg = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Debug|arm.Build.0 = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Debug|arm64.ActiveCfg = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Debug|arm64.Build.0 = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Debug|armv6.ActiveCfg = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Debug|armv6.Build.0 = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Debug|s390x.ActiveCfg = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Debug|s390x.Build.0 = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Debug|wasm.ActiveCfg = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Debug|wasm.Build.0 = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Debug|x64.ActiveCfg = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Debug|x64.Build.0 = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Debug|x86.ActiveCfg = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Debug|x86.Build.0 = Debug|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Release|Any CPU.Build.0 = Release|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Release|arm.ActiveCfg = Release|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Release|arm.Build.0 = Release|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Release|arm64.ActiveCfg = Release|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Release|arm64.Build.0 = Release|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Release|armv6.ActiveCfg = Release|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Release|armv6.Build.0 = Release|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Release|s390x.ActiveCfg = Release|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Release|s390x.Build.0 = Release|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Release|wasm.ActiveCfg = Release|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Release|wasm.Build.0 = Release|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Release|x64.ActiveCfg = Release|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Release|x64.Build.0 = Release|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Release|x86.ActiveCfg = Release|Any CPU
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5}.Release|x86.Build.0 = Release|Any CPU
{BFED925C-18F2-4C98-833E-66F205234598}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
{BFED925C-18F2-4C98-833E-66F205234598}.Checked|Any CPU.Build.0 = Debug|Any CPU
{BFED925C-18F2-4C98-833E-66F205234598}.Checked|arm.ActiveCfg = Debug|Any CPU
@@ -1010,54 +956,6 @@ Global
{BFED925C-18F2-4C98-833E-66F205234598}.Release|x64.Build.0 = Release|Any CPU
{BFED925C-18F2-4C98-833E-66F205234598}.Release|x86.ActiveCfg = Release|Any CPU
{BFED925C-18F2-4C98-833E-66F205234598}.Release|x86.Build.0 = Release|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Checked|Any CPU.Build.0 = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Checked|arm.ActiveCfg = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Checked|arm.Build.0 = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Checked|arm64.ActiveCfg = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Checked|arm64.Build.0 = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Checked|armv6.ActiveCfg = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Checked|armv6.Build.0 = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Checked|s390x.ActiveCfg = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Checked|s390x.Build.0 = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Checked|wasm.ActiveCfg = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Checked|wasm.Build.0 = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Checked|x64.ActiveCfg = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Checked|x64.Build.0 = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Checked|x86.ActiveCfg = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Checked|x86.Build.0 = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Debug|arm.ActiveCfg = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Debug|arm.Build.0 = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Debug|arm64.ActiveCfg = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Debug|arm64.Build.0 = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Debug|armv6.ActiveCfg = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Debug|armv6.Build.0 = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Debug|s390x.ActiveCfg = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Debug|s390x.Build.0 = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Debug|wasm.ActiveCfg = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Debug|wasm.Build.0 = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Debug|x64.ActiveCfg = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Debug|x64.Build.0 = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Debug|x86.ActiveCfg = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Debug|x86.Build.0 = Debug|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Release|Any CPU.Build.0 = Release|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Release|arm.ActiveCfg = Release|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Release|arm.Build.0 = Release|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Release|arm64.ActiveCfg = Release|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Release|arm64.Build.0 = Release|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Release|armv6.ActiveCfg = Release|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Release|armv6.Build.0 = Release|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Release|s390x.ActiveCfg = Release|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Release|s390x.Build.0 = Release|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Release|wasm.ActiveCfg = Release|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Release|wasm.Build.0 = Release|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Release|x64.ActiveCfg = Release|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Release|x64.Build.0 = Release|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Release|x86.ActiveCfg = Release|Any CPU
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1065,6 +963,7 @@ Global
GlobalSection(NestedProjects) = preSolution
{28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1} = {C22C479B-769A-4859-B974-E9B9D65918DE}
{71A845ED-4344-41FC-8FCA-3AC9B6BA6C45} = {7973EAA3-43B6-4D78-B24C-38BA6BC0D1E3}
+ {CE5E53C1-F9B5-41EE-8D00-837913EC57D1} = {F2C2C78A-CEDD-4DE0-9C3A-0195F00E0B4E}
{6B7FDECD-61C2-4764-A994-AB117B405E33} = {C22C479B-769A-4859-B974-E9B9D65918DE}
{9BEEB101-BBD6-45D0-9B50-819EDB5FF3D3} = {7973EAA3-43B6-4D78-B24C-38BA6BC0D1E3}
{B4E3E774-2C16-4CBF-87EF-88C547529B94} = {F2C2C78A-CEDD-4DE0-9C3A-0195F00E0B4E}
@@ -1072,7 +971,7 @@ Global
{ED86AB26-1CFB-457D-BF87-B7A0D8FAF272} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C}
{0BBBDBE2-423D-48EB-8B6D-BF1140B4DF5A} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C}
{3355B50F-2794-4BD5-8D03-AF54CDB7DF98} = {F2C2C78A-CEDD-4DE0-9C3A-0195F00E0B4E}
- {BD7963AE-23CA-4F52-9500-9C67F1F5B42D} = {F2C2C78A-CEDD-4DE0-9C3A-0195F00E0B4E}
+ {765B4AA5-723A-44FF-BC4E-EB0F03103F6D} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C}
{EC3ADEFA-1FF3-482C-8CCB-FE4C77292532} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C}
{F4D8364B-F523-4E70-9273-D5E7393DCC99} = {7973EAA3-43B6-4D78-B24C-38BA6BC0D1E3}
{802982C1-94A4-47E4-9E5E-351A9721FFF0} = {7973EAA3-43B6-4D78-B24C-38BA6BC0D1E3}
@@ -1080,10 +979,7 @@ Global
{18E039DE-1A5F-447C-90DE-4385B97CE566} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C}
{32E55B8B-06CA-4FB0-BA58-67314FB6BC15} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C}
{C8FB882D-DB06-4F4B-BCFB-5B7F936E127B} = {7973EAA3-43B6-4D78-B24C-38BA6BC0D1E3}
- {963B6EB8-13FE-44EB-9B02-762A8AB9C459} = {7973EAA3-43B6-4D78-B24C-38BA6BC0D1E3}
- {518D3BDB-323D-43EB-B868-A7EDC08A15F5} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C}
{BFED925C-18F2-4C98-833E-66F205234598} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C}
- {BE06AA07-F96F-4B63-8445-AFBC688D5CE0} = {F2C2C78A-CEDD-4DE0-9C3A-0195F00E0B4E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3FE64246-4AFA-424A-AE5D-7007E20451B5}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj
index 93265dc706e..0f1d6e8058e 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj
@@ -3,8 +3,6 @@
<TargetFrameworks>$(NetCoreAppCurrent)-Browser;$(NetCoreAppCurrent)</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
- <!-- Use following lines to write the generated files to disk. -->
- <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<RootNamespace></RootNamespace>
</PropertyGroup>
@@ -28,6 +26,7 @@
</ItemGroup>
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'Browser'">
<Compile Include="$(CommonPath)Interop\Browser\Interop.Runtime.cs" Link="System\Runtime\InteropServices\JavaScript\Interop\Interop.Runtime.cs" />
+ <Compile Include="System\Runtime\InteropServices\JavaScript\Interop\JavaScriptImports.Generated.cs" />
<Compile Include="System\Runtime\InteropServices\JavaScript\Interop\JavaScriptExports.cs" />
<Compile Include="System\Runtime\InteropServices\JavaScript\Interop\JavaScriptImports.cs" />
@@ -51,6 +50,7 @@
<Compile Include="System\Runtime\InteropServices\JavaScript\JSException.cs" />
<Compile Include="System\Runtime\InteropServices\JavaScript\JSExportAttribute.cs" />
<Compile Include="System\Runtime\InteropServices\JavaScript\JSImportAttribute.cs" />
+ <Compile Include="System\Runtime\InteropServices\JavaScript\CancelablePromise.cs" />
<Compile Include="System\Runtime\InteropServices\JavaScript\MarshalerType.cs" />
<Compile Include="System\Runtime\InteropServices\JavaScript\Marshaling\JSMarshalerArgument.BigInt64.cs" />
@@ -72,4 +72,7 @@
<Compile Include="System\Runtime\InteropServices\JavaScript\Marshaling\JSMarshalerArgument.Exception.cs" />
</ItemGroup>
+ <ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'Browser'">
+ <AnalyzerReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\gen\JSImportGenerator\JSImportGenerator.csproj" Pack="true" ReferenceAnalyzer="true" />
+ </ItemGroup>
</Project>
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/CancelablePromise.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/CancelablePromise.cs
new file mode 100644
index 00000000000..391f692e0ba
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/CancelablePromise.cs
@@ -0,0 +1,27 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+
+using System.Threading.Tasks;
+
+namespace System.Runtime.InteropServices.JavaScript
+{
+ internal static partial class CancelablePromise
+ {
+ [JSImport("INTERNAL.mono_wasm_cancel_promise")]
+ private static partial void _CancelPromise(IntPtr promiseGCHandle);
+
+ public static void CancelPromise(Task promise)
+ {
+ // this check makes sure that promiseGCHandle is still valid handle
+ if (promise.IsCompleted)
+ {
+ return;
+ }
+ GCHandle? promiseGCHandle = promise.AsyncState as GCHandle?;
+ if (promiseGCHandle == null) throw new InvalidOperationException("Expected Task converted from JS Promise");
+
+ _CancelPromise((IntPtr)promiseGCHandle.Value);
+ }
+ }
+}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs
index 305caf4b729..619dec1a5f3 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs
@@ -1,13 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace System.Runtime.InteropServices.JavaScript
{
+ // this maps to src\mono\wasm\runtime\corebindings.c
+ // the methods are protected from trimming by DynamicDependency on JSFunctionBinding
internal static unsafe partial class JavaScriptExports
{
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static void GetCSOwnedObjectByJSHandleRef(IntPtr jsHandle, int shouldAddInflight, out JSObject? result)
{
lock (JSHostImplementation.s_csOwnedObjects)
@@ -26,6 +31,7 @@ namespace System.Runtime.InteropServices.JavaScript
result = null;
}
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static IntPtr TryGetCSOwnedObjectJSHandleRef(in object rawObj, int shouldAddInflight)
{
JSObject? jsObject = rawObj as JSObject;
@@ -36,18 +42,19 @@ namespace System.Runtime.InteropServices.JavaScript
return jsObject?.JSHandle ?? IntPtr.Zero;
}
- public static void CreateCSOwnedProxyRef(IntPtr jsHandle, JSHostImplementation.MappedType mappedType, int shouldAddInflight, out JSObject? jsObject)
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
+ public static void CreateCSOwnedProxyRef(IntPtr jsHandle, JSHostImplementation.MappedType mappedType, int shouldAddInflight, out JSObject jsObject)
{
- jsObject = null;
+ JSObject? res = null;
lock (JSHostImplementation.s_csOwnedObjects)
{
if (!JSHostImplementation.s_csOwnedObjects.TryGetValue((int)jsHandle, out WeakReference<JSObject>? reference) ||
- !reference.TryGetTarget(out jsObject) ||
- jsObject.IsDisposed)
+ !reference.TryGetTarget(out res) ||
+ res.IsDisposed)
{
#pragma warning disable CS0612 // Type or member is obsolete
- jsObject = mappedType switch
+ res = mappedType switch
{
JSHostImplementation.MappedType.JSObject => new JSObject(jsHandle),
JSHostImplementation.MappedType.Array => new Array(jsHandle),
@@ -58,21 +65,24 @@ namespace System.Runtime.InteropServices.JavaScript
_ => throw new ArgumentOutOfRangeException(nameof(mappedType))
};
#pragma warning restore CS0612 // Type or member is obsolete
- JSHostImplementation.s_csOwnedObjects[(int)jsHandle] = new WeakReference<JSObject>(jsObject, trackResurrection: true);
+ JSHostImplementation.s_csOwnedObjects[(int)jsHandle] = new WeakReference<JSObject>(res, trackResurrection: true);
}
}
if (shouldAddInflight != 0)
{
- jsObject.AddInFlight();
+ res.AddInFlight();
}
+ jsObject = res;
}
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static void GetJSOwnedObjectByGCHandleRef(int gcHandle, out object result)
{
GCHandle h = (GCHandle)(IntPtr)gcHandle;
result = h.Target!;
}
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static IntPtr GetJSOwnedObjectGCHandleRef(in object obj)
{
return JSHostImplementation.GetJSOwnedObjectGCHandleRef(obj, GCHandleType.Normal);
@@ -80,6 +90,7 @@ namespace System.Runtime.InteropServices.JavaScript
// The JS layer invokes this method when the JS wrapper for a JS owned object
// has been collected by the JS garbage collector
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static void ReleaseJSOwnedObjectByGCHandle(IntPtr gcHandle)
{
GCHandle handle = (GCHandle)gcHandle;
@@ -90,12 +101,14 @@ namespace System.Runtime.InteropServices.JavaScript
}
}
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static IntPtr CreateTaskSource()
{
var tcs = new TaskCompletionSource<object>();
return GetJSOwnedObjectGCHandleRef(tcs);
}
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static void SetTaskSourceResultRef(int tcsGCHandle, in object result)
{
GCHandle handle = (GCHandle)(IntPtr)tcsGCHandle;
@@ -104,6 +117,7 @@ namespace System.Runtime.InteropServices.JavaScript
tcs.SetResult(result);
}
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static void SetTaskSourceFailure(int tcsGCHandle, string reason)
{
GCHandle handle = (GCHandle)(IntPtr)tcsGCHandle;
@@ -112,6 +126,7 @@ namespace System.Runtime.InteropServices.JavaScript
tcs.SetException(new JSException(reason));
}
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static void GetTaskSourceTaskRef(int tcsGCHandle, out object result)
{
GCHandle handle = (GCHandle)(IntPtr)tcsGCHandle;
@@ -120,11 +135,13 @@ namespace System.Runtime.InteropServices.JavaScript
result = tcs.Task;
}
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static void TaskFromResultRef(in object obj, out object result)
{
result = Task.FromResult(obj);
}
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static void SetupJSContinuationRef(in Task _task, JSObject continuationObj)
{
// HACK: Attempting to use the in-param will produce CS1628, so we make a temporary copy
@@ -171,11 +188,13 @@ namespace System.Runtime.InteropServices.JavaScript
}
}
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static string ObjectToStringRef(ref object o)
{
return o.ToString() ?? string.Empty;
}
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static double GetDateValueRef(ref object dtv)
{
ArgumentNullException.ThrowIfNull(dtv);
@@ -191,22 +210,27 @@ namespace System.Runtime.InteropServices.JavaScript
// HACK: We need to implicitly box by using an 'object' out-param.
// Note that the return value would have been boxed on the C#->JS transition anyway.
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static void CreateDateTimeRef(double ticks, out object result)
{
DateTimeOffset unixTime = DateTimeOffset.FromUnixTimeMilliseconds((long)ticks);
result = unixTime.DateTime;
}
+ // TODO remove this to allow trimming of Uri assembly
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static void CreateUriRef(string uri, out Uri result)
{
result = new Uri(uri);
}
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static bool IsSimpleArrayRef(ref object a)
{
return a is System.Array arr && arr.Rank == 1 && arr.GetLowerBound(0) == 0;
}
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static string GetCallSignatureRef(IntPtr _methodHandle, in object objForRuntimeType)
{
var methodHandle = JSHostImplementation.GetMethodHandleFromIntPtr(_methodHandle);
@@ -231,13 +255,13 @@ namespace System.Runtime.InteropServices.JavaScript
return new string(result);
}
- [MethodImplAttribute(MethodImplOptions.NoInlining)]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static void StopProfile()
{
}
// Called by the AOT profiler to save profile data into INTERNAL.aot_profile_data
- [MethodImplAttribute(MethodImplOptions.NoInlining)]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static unsafe void DumpAotProfileData(ref byte buf, int len, string extraArg)
{
if (len == 0)
@@ -248,12 +272,52 @@ namespace System.Runtime.InteropServices.JavaScript
{
var span = new ReadOnlySpan<byte>(p, len);
// Send it to JS
-#pragma warning disable CS0612 // Type or member is obsolete
- var internalJS = (JSObject)JavaScriptImports.GetGlobalObject("INTERNAL");
- if (internalJS == null) throw new InvalidOperationException();
- internalJS.SetObjectProperty("aot_profile_data", Uint8Array.From(span));
-#pragma warning restore CS0612 // Type or member is obsolete
+ var module = JSHost.DotnetInstance.GetPropertyAsJSObject("INTERNAL");
+ if (module == null)
+ throw new InvalidOperationException();
+
+ module.SetProperty("aot_profile_data", span.ToArray());
}
}
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
+ internal static JSObject CreateCSOwnedProxy(IntPtr jsHandle)
+ {
+ CreateCSOwnedProxyRef(jsHandle, JSHostImplementation.MappedType.JSObject, 0, out JSObject? res);
+ return res;
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
+ public static IntPtr CreateTaskCallback()
+ {
+ JSHostImplementation.TaskCallback holder = new JSHostImplementation.TaskCallback();
+ return GetJSOwnedObjectGCHandleRef(holder);
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
+ public static void CallDelegate(JSMarshalerArgument* arguments_buffer)
+ {
+ ref JSMarshalerArgument arg_return = ref arguments_buffer[1];
+ GCHandle callback_gc_handle = (GCHandle)arg_return.slot.GCHandle;
+
+ JSHostImplementation.ToManagedCallback? cb = (JSHostImplementation.ToManagedCallback?)callback_gc_handle.Target;
+ if (cb == null)
+ throw new InvalidOperationException("ToManagedCallback is null");
+
+ cb(arguments_buffer);
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
+ public static void CompleteTask(JSMarshalerArgument* arguments_buffer)
+ {
+ ref JSMarshalerArgument arg_return = ref arguments_buffer[1];
+ GCHandle callback_gc_handle = (GCHandle)arg_return.slot.GCHandle;
+
+ JSHostImplementation.TaskCallback? holder = (JSHostImplementation.TaskCallback?)callback_gc_handle.Target;
+ if (holder == null || holder.Callback == null)
+ throw new InvalidOperationException("TaskCallback is null");
+
+ holder.Callback(arguments_buffer);
+ }
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.Generated.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.Generated.cs
new file mode 100644
index 00000000000..4a7b92ddb19
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.Generated.cs
@@ -0,0 +1,48 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics.CodeAnalysis;
+using System.Threading.Tasks;
+
+namespace System.Runtime.InteropServices.JavaScript
+{
+ internal static unsafe partial class JavaScriptImports
+ {
+ [JSImport("INTERNAL.has_property")]
+ public static partial bool HasProperty(JSObject self, string propertyName);
+ [JSImport("INTERNAL.get_typeof_property")]
+ public static partial string GetTypeOfProperty(JSObject self, string propertyName);
+ [JSImport("INTERNAL.get_property")]
+ public static partial bool GetPropertyAsBoolean(JSObject self, string propertyName);
+ [JSImport("INTERNAL.get_property")]
+ public static partial int GetPropertyAsInt32(JSObject self, string propertyName);
+ [JSImport("INTERNAL.get_property")]
+ public static partial double GetPropertyAsDouble(JSObject self, string propertyName);
+ [JSImport("INTERNAL.get_property")]
+ public static partial string GetPropertyAsString(JSObject self, string propertyName);
+ [JSImport("INTERNAL.get_property")]
+ public static partial JSObject GetPropertyAsJSObject(JSObject self, string propertyName);
+ [JSImport("INTERNAL.get_property")]
+ public static partial byte[] GetPropertyAsByteArray(JSObject self, string propertyName);
+
+ [JSImport("INTERNAL.set_property")]
+ public static partial void SetPropertyBool(JSObject self, string propertyName, bool value);
+ [JSImport("INTERNAL.set_property")]
+ public static partial void SetPropertyInt(JSObject self, string propertyName, int value);
+ [JSImport("INTERNAL.set_property")]
+ public static partial void SetPropertyDouble(JSObject self, string propertyName, double value);
+ [JSImport("INTERNAL.set_property")]
+ public static partial void SetPropertyString(JSObject self, string propertyName, string value);
+ [JSImport("INTERNAL.set_property")]
+ public static partial void SetPropertyJSObject(JSObject self, string propertyName, JSObject value);
+ [JSImport("INTERNAL.set_property")]
+ public static partial void SetPropertyBytes(JSObject self, string propertyName, byte[] value);
+
+ [JSImport("INTERNAL.get_global_this")]
+ public static partial JSObject GetGlobalThis();
+ [JSImport("INTERNAL.get_dotnet_instance")]
+ public static partial JSObject GetDotnetInstance();
+ [JSImport("INTERNAL.dynamic_import")]
+ public static partial Task<JSObject> DynamicImport(string moduleName, string moduleUrl);
+ }
+}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.cs
index 76b4699f68c..e01dce221af 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.cs
@@ -1,13 +1,29 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System.Threading.Tasks;
+using System.Runtime.CompilerServices;
namespace System.Runtime.InteropServices.JavaScript
{
internal static unsafe partial class JavaScriptImports
{
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
+ public static void MarshalPromise(Span<JSMarshalerArgument> arguments)
+ {
+ fixed (JSMarshalerArgument* ptr = arguments)
+ {
+ Interop.Runtime.MarshalPromise(ptr);
+ ref JSMarshalerArgument exceptionArg = ref arguments[0];
+ if (exceptionArg.slot.Type != MarshalerType.None)
+ {
+ JSHostImplementation.ThrowException(ref exceptionArg);
+ }
+ }
+ }
+ #region legacy
+
+ [MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static string InvokeJS(string str)
{
string res = Interop.Runtime.InvokeJS(str, out int exception);
@@ -16,6 +32,7 @@ namespace System.Runtime.InteropServices.JavaScript
return res;
}
+ [MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static object GetGlobalObject(string? str = null)
{
int exception;
@@ -28,6 +45,7 @@ namespace System.Runtime.InteropServices.JavaScript
return jsObj;
}
+ [MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static IntPtr CreateCSOwnedObject(string typeName, object[] parms)
{
Interop.Runtime.CreateCSOwnedObjectRef(typeName, parms, out int exception, out object res);
@@ -37,74 +55,6 @@ namespace System.Runtime.InteropServices.JavaScript
return (IntPtr)(int)res;
}
- public static void CancelPromise(IntPtr promiseJSHandle)
- {
- Interop.Runtime.CancelPromiseRef(promiseJSHandle, out int exception, out string res);
- if (exception != 0)
- throw new JSException(res);
- }
-
- public static Task<object> WebSocketOpen(string uri, object[]? subProtocols, Delegate onClosed, out JSObject webSocket, out IntPtr promiseJSHandle)
- {
- Interop.Runtime.WebSocketOpenRef(uri, subProtocols, onClosed, out IntPtr webSocketJSHandle, out promiseJSHandle, out int exception, out object res);
- if (exception != 0)
- throw new JSException((string)res);
- webSocket = new JSObject((IntPtr)webSocketJSHandle);
-
- return (Task<object>)res;
- }
-
- public static unsafe Task<object>? WebSocketSend(JSObject webSocket, ArraySegment<byte> buffer, int messageType, bool endOfMessage, out IntPtr promiseJSHandle)
- {
- fixed (byte* messagePtr = buffer.Array)
- {
- Interop.Runtime.WebSocketSend(webSocket.JSHandle, (IntPtr)messagePtr, buffer.Offset, buffer.Count, messageType, endOfMessage, out promiseJSHandle, out int exception, out object res);
- if (exception != 0)
- throw new JSException((string)res);
-
- if (res == null)
- {
- return null;
- }
-
- return (Task<object>)res;
- }
- }
-
- public static unsafe Task<object>? WebSocketReceive(JSObject webSocket, ArraySegment<byte> buffer, ReadOnlySpan<int> response, out IntPtr promiseJSHandle)
- {
- fixed (int* responsePtr = response)
- fixed (byte* bufferPtr = buffer.Array)
- {
- Interop.Runtime.WebSocketReceive(webSocket.JSHandle, (IntPtr)bufferPtr, buffer.Offset, buffer.Count, (IntPtr)responsePtr, out promiseJSHandle, out int exception, out object res);
- if (exception != 0)
- throw new JSException((string)res);
- if (res == null)
- {
- return null;
- }
- return (Task<object>)res;
- }
- }
-
- public static Task<object>? WebSocketClose(JSObject webSocket, int code, string? reason, bool waitForCloseReceived, out IntPtr promiseJSHandle)
- {
- Interop.Runtime.WebSocketCloseRef(webSocket.JSHandle, code, reason, waitForCloseReceived, out promiseJSHandle, out int exception, out object res);
- if (exception != 0)
- throw new JSException((string)res);
-
- if (res == null)
- {
- return null;
- }
- return (Task<object>)res;
- }
-
- public static void WebSocketAbort(JSObject webSocket)
- {
- Interop.Runtime.WebSocketAbort(webSocket.JSHandle, out int exception, out string res);
- if (exception != 0)
- throw new JSException(res);
- }
+ #endregion
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSException.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSException.cs
index e3ca5f7bc31..de5715f99cd 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSException.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSException.cs
@@ -11,12 +11,20 @@ namespace System.Runtime.InteropServices.JavaScript
[SupportedOSPlatform("browser")] // @kg: Do we really need to platform-lock JSException?
public sealed class JSException : Exception
{
+ internal JSObject? jsException;
+
/// <summary>
/// Initializes a new instance of the JSException class with a specified error message.
/// </summary>
/// <param name="msg">The message that describes the error.</param>
public JSException(string msg) : base(msg)
{
+ jsException = null;
+ }
+
+ internal JSException(string msg, JSObject? jsException) : base(msg)
+ {
+ this.jsException = jsException;
}
/// <inheritdoc />
@@ -24,20 +32,47 @@ namespace System.Runtime.InteropServices.JavaScript
{
get
{
- return null;
+ var bs = base.StackTrace;
+ if (jsException == null)
+ {
+ return bs;
+ }
+ string? jsStackTrace = jsException.GetPropertyAsString("stack");
+ if (jsStackTrace == null)
+ {
+ if (bs == null)
+ {
+ return null;
+ }
+ }
+ else if (jsStackTrace.StartsWith(Message + "\n"))
+ {
+ // Some JS runtimes insert the error message at the top of the stack, some don't,
+ // so normalize it by using the stack as the result if it already contains the error
+ jsStackTrace = jsStackTrace.Substring(Message.Length + 1);
+ }
+
+ if (bs == null)
+ {
+ return jsStackTrace;
+ }
+ return base.StackTrace + "\r\n" + jsStackTrace;
}
+
}
/// <inheritdoc />
public override bool Equals(object? obj)
{
- return base.Equals(obj);
+ return obj is JSException other && other.jsException == jsException;
}
/// <inheritdoc />
public override int GetHashCode()
{
- return base.GetHashCode();
+ return jsException == null
+ ? base.GetHashCode()
+ : base.GetHashCode() * jsException.GetHashCode();
}
/// <inheritdoc />
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs
index b82756736b9..f73515faaea 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs
@@ -18,12 +18,9 @@ namespace System.Runtime.InteropServices.JavaScript
public sealed partial class JSFunctionBinding
{
#region intentionaly opaque internal structure
-
-#pragma warning disable CS0649 // temporary until we have implementation
internal unsafe JSBindingHeader* Header;
internal unsafe JSBindingType* Sigs;// points to first arg, not exception, not result
internal JSObject? JSFunction;
-#pragma warning restore CS0649
[StructLayout(LayoutKind.Sequential, Pack = 4)]
internal struct JSBindingHeader
@@ -49,6 +46,72 @@ namespace System.Runtime.InteropServices.JavaScript
internal MarshalerType Arg3MarshalerType;
}
+ internal unsafe int ArgumentCount
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return Header[0].ArgumentCount;
+ }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set
+ {
+ Header[0].ArgumentCount = value;
+ }
+ }
+
+ internal unsafe int Version
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return Header[0].Version;
+ }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set
+ {
+ Header[0].Version = value;
+ }
+ }
+
+ internal unsafe JSBindingType Result
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return Header[0].Result;
+ }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set
+ {
+ Header[0].Result = value;
+ }
+ }
+
+ internal unsafe JSBindingType Exception
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return Header[0].Exception;
+ }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set
+ {
+ Header[0].Exception = value;
+ }
+ }
+
+ // one based position of args, not exception, not result
+ internal unsafe JSBindingType this[int position]
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return Sigs[position - 1];
+ }
+ }
+
#endregion
/// <summary>
@@ -58,18 +121,21 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InvokeJS(JSFunctionBinding signature, Span<JSMarshalerArgument> arguments)
{
- throw new NotImplementedException();
+ InvokeJSImpl(signature.JSFunction!, arguments);
}
/// <summary>
/// Locates and binds a JavaScript function given name and module so that it can later be invoked by managed callers.
/// This API supports JSImport infrastructure and is not intended to be used directly from your code.
/// </summary>
- // JavaScriptExports need to be protected from trimming because they are used from C/JS code which linker can't see
+ // JavaScriptExports need to be protected from trimming because they are used from C/JS code which IL linker can't see
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, "System.Runtime.InteropServices.JavaScript.JavaScriptExports", "System.Runtime.InteropServices.JavaScript")]
public static JSFunctionBinding BindJSFunction(string functionName, string moduleName, ReadOnlySpan<JSMarshalerType> signatures)
{
- throw new NotImplementedException();
+ if (RuntimeInformation.OSArchitecture != Architecture.Wasm)
+ throw new PlatformNotSupportedException();
+
+ return BindJSFunctionImpl(functionName, moduleName, signatures);
}
/// <summary>
@@ -78,7 +144,52 @@ namespace System.Runtime.InteropServices.JavaScript
/// </summary>
public static JSFunctionBinding BindManagedFunction(string fullyQualifiedName, int signatureHash, ReadOnlySpan<JSMarshalerType> signatures)
{
- throw new NotImplementedException();
+ if (RuntimeInformation.OSArchitecture != Architecture.Wasm)
+ throw new PlatformNotSupportedException();
+
+ return BindManagedFunctionImpl(fullyQualifiedName, signatureHash, signatures);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static unsafe void InvokeJSImpl(JSObject jsFunction, Span<JSMarshalerArgument> arguments)
+ {
+ IntPtr functionJSHandle = jsFunction.JSHandle;
+ fixed (JSMarshalerArgument* ptr = arguments)
+ {
+ Interop.Runtime.InvokeJSFunction(functionJSHandle, ptr);
+ ref JSMarshalerArgument exceptionArg = ref arguments[0];
+ if (exceptionArg.slot.Type != MarshalerType.None)
+ {
+ JSHostImplementation.ThrowException(ref exceptionArg);
+ }
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ internal static unsafe JSFunctionBinding BindJSFunctionImpl(string functionName, string moduleName, ReadOnlySpan<JSMarshalerType> signatures)
+ {
+ var signature = JSHostImplementation.GetMethodSignature(signatures);
+
+ Interop.Runtime.BindJSFunction(functionName, moduleName, signature.Header, out IntPtr jsFunctionHandle, out int isException, out object exceptionMessage);
+ if (isException != 0)
+ throw new JSException((string)exceptionMessage);
+
+ signature.JSFunction = JavaScriptExports.CreateCSOwnedProxy(jsFunctionHandle);
+
+ return signature;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ internal static unsafe JSFunctionBinding BindManagedFunctionImpl(string fullyQualifiedName, int signatureHash, ReadOnlySpan<JSMarshalerType> signatures)
+ {
+ var signature = JSHostImplementation.GetMethodSignature(signatures);
+
+ Interop.Runtime.BindCSFunction(fullyQualifiedName, signatureHash, signature.Header, out int isException, out object exceptionMessage);
+ if (isException != 0)
+ {
+ throw new JSException((string)exceptionMessage);
+ }
+ return signature;
}
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHost.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHost.cs
index 0ea4fa39315..b549ceb7f85 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHost.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHost.cs
@@ -4,6 +4,7 @@
using System.Threading.Tasks;
using System.Runtime.Versioning;
using System.Threading;
+using System.Runtime.CompilerServices;
namespace System.Runtime.InteropServices.JavaScript
{
@@ -20,7 +21,7 @@ namespace System.Runtime.InteropServices.JavaScript
{
get
{
- throw new NotImplementedException();
+ return JavaScriptImports.GetGlobalThis();
}
}
@@ -31,7 +32,7 @@ namespace System.Runtime.InteropServices.JavaScript
{
get
{
- throw new NotImplementedException();
+ return JavaScriptImports.GetDotnetInstance();
}
}
@@ -43,9 +44,10 @@ namespace System.Runtime.InteropServices.JavaScript
/// <param name="moduleUrl">The location of the module file.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A proxy for the JavaScript object that contains the module's exports.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Task<JSObject> ImportAsync(string moduleName, string moduleUrl, CancellationToken cancellationToken = default)
{
- throw new NotImplementedException();
+ return JSHostImplementation.ImportAsync(moduleName, moduleUrl, cancellationToken);
}
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.Types.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.Types.cs
index a8a1ea610c5..3009089133e 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.Types.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.Types.cs
@@ -5,6 +5,13 @@ namespace System.Runtime.InteropServices.JavaScript
{
internal static partial class JSHostImplementation
{
+ internal unsafe delegate void ToManagedCallback(JSMarshalerArgument* arguments_buffer);
+
+ public sealed class TaskCallback
+ {
+ public ToManagedCallback? Callback;
+ }
+
[StructLayout(LayoutKind.Explicit)]
public struct IntPtrAndHandle
{
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.cs
index 3e2820b0e00..aa1ad66a34c 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.cs
@@ -5,10 +5,12 @@ using System.Threading.Tasks;
using System.Reflection;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using System.Threading;
namespace System.Runtime.InteropServices.JavaScript
{
- internal static unsafe partial class JSHostImplementation
+ internal static partial class JSHostImplementation
{
private const string TaskGetResultName = "get_Result";
private static readonly MethodInfo s_taskGetResultMethodInfo = typeof(Task<>).GetMethod(TaskGetResultName)!;
@@ -17,6 +19,7 @@ namespace System.Runtime.InteropServices.JavaScript
// we use this to maintain identity of GCHandle for a managed object
public static Dictionary<object, IntPtr> s_gcHandleFromJSOwnedObject = new Dictionary<object, IntPtr>(ReferenceEqualityComparer.Instance);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void RegisterCSOwnedObject(JSObject proxy)
{
lock (s_csOwnedObjects)
@@ -25,6 +28,7 @@ namespace System.Runtime.InteropServices.JavaScript
}
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ReleaseCSOwnedObject(IntPtr jsHandle)
{
if (jsHandle != IntPtr.Zero)
@@ -37,6 +41,7 @@ namespace System.Runtime.InteropServices.JavaScript
}
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static object? GetTaskResult(Task task)
{
MethodInfo method = GetTaskResultMethodInfo(task.GetType());
@@ -47,6 +52,7 @@ namespace System.Runtime.InteropServices.JavaScript
throw new InvalidOperationException();
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ReleaseInFlight(object obj)
{
JSObject? jsObj = obj as JSObject;
@@ -78,6 +84,7 @@ namespace System.Runtime.InteropServices.JavaScript
}
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static RuntimeMethodHandle GetMethodHandleFromIntPtr(IntPtr ptr)
{
var temp = new IntPtrAndHandle { ptr = ptr };
@@ -247,5 +254,66 @@ namespace System.Runtime.InteropServices.JavaScript
throw new InvalidOperationException();
}
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static void ThrowException(ref JSMarshalerArgument arg)
+ {
+ arg.ToManaged(out Exception? ex);
+
+ if (ex != null)
+ {
+ throw ex;
+ }
+ throw new InvalidProgramException();
+ }
+
+ public static async Task<JSObject> ImportAsync(string moduleName, string moduleUrl, CancellationToken cancellationToken )
+ {
+ Task<JSObject> modulePromise = JavaScriptImports.DynamicImport(moduleName, moduleUrl);
+ var wrappedTask = CancelationHelper(modulePromise, cancellationToken);
+ await Task.Yield();// this helps to finish the import before we bind the module in [JSImport]
+ return await wrappedTask.ConfigureAwait(true);
+ }
+
+ private static async Task<JSObject> CancelationHelper(Task<JSObject> jsTask, CancellationToken cancellationToken)
+ {
+ if (jsTask.IsCompletedSuccessfully)
+ {
+ return jsTask.Result;
+ }
+ using (var receiveRegistration = cancellationToken.Register(() =>
+ {
+ CancelablePromise.CancelPromise(jsTask);
+ }))
+ {
+ return await jsTask.ConfigureAwait(true);
+ }
+ }
+
+ // res type is first argument
+ internal static unsafe JSFunctionBinding GetMethodSignature(ReadOnlySpan<JSMarshalerType> types)
+ {
+ int argsCount = types.Length - 1;
+ int size = JSFunctionBinding.JSBindingHeader.JSMarshalerSignatureHeaderSize + ((argsCount + 2) * sizeof(JSFunctionBinding.JSBindingType));
+ // this is never unallocated
+ IntPtr buffer = Marshal.AllocHGlobal(size);
+
+ var signature = new JSFunctionBinding
+ {
+ Header = (JSFunctionBinding.JSBindingHeader*)buffer,
+ Sigs = (JSFunctionBinding.JSBindingType*)(buffer + JSFunctionBinding.JSBindingHeader.JSMarshalerSignatureHeaderSize + (2 * sizeof(JSFunctionBinding.JSBindingType))),
+ };
+
+ signature.Version = 1;
+ signature.ArgumentCount = argsCount;
+ signature.Exception = JSMarshalerType.Exception._signatureType;
+ signature.Result = types[0]._signatureType;
+ for (int i = 0; i < argsCount; i++)
+ {
+ signature.Sigs[i] = types[i + 1]._signatureType;
+ }
+
+ return signature;
+ }
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSMarshalerArgument.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSMarshalerArgument.cs
index d370f18e32b..268c41385e2 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSMarshalerArgument.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSMarshalerArgument.cs
@@ -16,13 +16,45 @@ namespace System.Runtime.InteropServices.JavaScript
[EditorBrowsable(EditorBrowsableState.Never)]
public partial struct JSMarshalerArgument
{
-#pragma warning disable CS0649 // temporary until we have implementation
internal JSMarshalerArgumentImpl slot;
-#pragma warning restore CS0649
[StructLayout(LayoutKind.Explicit, Pack = 16, Size = 16)]
internal struct JSMarshalerArgumentImpl
{
+ [FieldOffset(0)]
+ internal bool BooleanValue;
+ [FieldOffset(0)]
+ internal byte ByteValue;
+ [FieldOffset(0)]
+ internal char CharValue;
+ [FieldOffset(0)]
+ internal short Int16Value;
+ [FieldOffset(0)]
+ internal int Int32Value;
+ [FieldOffset(0)]
+ internal long Int64Value;// must be alligned to 8 because of HEAPI64 alignment
+ [FieldOffset(0)]
+ internal float SingleValue;
+ [FieldOffset(0)]
+ internal double DoubleValue;// must be alligned to 8 because of Module.HEAPF64 view alignment
+ [FieldOffset(0)]
+ internal IntPtr IntPtrValue;
+
+ [FieldOffset(4)]
+ internal IntPtr JSHandle;
+ [FieldOffset(4)]
+ internal IntPtr GCHandle;
+ [FieldOffset(4)]
+ internal MarshalerType ElementType;
+
+ [FieldOffset(8)]
+ internal int Length;
+
+ /// <summary>
+ /// Discriminator
+ /// </summary>
+ [FieldOffset(12)]
+ internal MarshalerType Type;
}
/// <summary>
@@ -31,7 +63,7 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void Initialize()
{
- throw new NotImplementedException();
+ slot.Type = MarshalerType.None;
}
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.cs
index 4b7f75536a1..551379f5569 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.cs
@@ -24,7 +24,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool HasProperty(string propertyName)
{
- throw new NotImplementedException();
+ ObjectDisposedException.ThrowIf(IsDisposed, this);
+ return JavaScriptImports.HasProperty(this, propertyName);
}
/// <summary>
@@ -33,7 +34,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string GetTypeOfProperty(string propertyName)
{
- throw new NotImplementedException();
+ ObjectDisposedException.ThrowIf(IsDisposed, this);
+ return JavaScriptImports.GetTypeOfProperty(this, propertyName);
}
/// <summary>
@@ -45,7 +47,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool GetPropertyAsBoolean(string propertyName)
{
- throw new NotImplementedException();
+ ObjectDisposedException.ThrowIf(IsDisposed, this);
+ return JavaScriptImports.GetPropertyAsBoolean(this, propertyName);
}
/// <summary>
@@ -57,7 +60,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetPropertyAsInt32(string propertyName)
{
- throw new NotImplementedException();
+ ObjectDisposedException.ThrowIf(IsDisposed, this);
+ return JavaScriptImports.GetPropertyAsInt32(this, propertyName);
}
/// <summary>
@@ -69,7 +73,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public double GetPropertyAsDouble(string propertyName)
{
- throw new NotImplementedException();
+ ObjectDisposedException.ThrowIf(IsDisposed, this);
+ return JavaScriptImports.GetPropertyAsDouble(this, propertyName);
}
/// <summary>
@@ -81,7 +86,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string? GetPropertyAsString(string propertyName)
{
- throw new NotImplementedException();
+ ObjectDisposedException.ThrowIf(IsDisposed, this);
+ return JavaScriptImports.GetPropertyAsString(this, propertyName);
}
/// <summary>
@@ -93,7 +99,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public JSObject? GetPropertyAsJSObject(string propertyName)
{
- throw new NotImplementedException();
+ ObjectDisposedException.ThrowIf(IsDisposed, this);
+ return JavaScriptImports.GetPropertyAsJSObject(this, propertyName);
}
/// <summary>
@@ -106,7 +113,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte[]? GetPropertyAsByteArray(string propertyName)
{
- throw new NotImplementedException();
+ ObjectDisposedException.ThrowIf(IsDisposed, this);
+ return JavaScriptImports.GetPropertyAsByteArray(this, propertyName);
}
/// <summary>
@@ -115,7 +123,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetProperty(string propertyName, bool value)
{
- throw new NotImplementedException();
+ ObjectDisposedException.ThrowIf(IsDisposed, this);
+ JavaScriptImports.SetPropertyBool(this, propertyName, value);
}
/// <summary>
@@ -124,7 +133,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetProperty(string propertyName, int value)
{
- throw new NotImplementedException();
+ ObjectDisposedException.ThrowIf(IsDisposed, this);
+ JavaScriptImports.SetPropertyInt(this, propertyName, value);
}
/// <summary>
@@ -133,7 +143,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetProperty(string propertyName, double value)
{
- throw new NotImplementedException();
+ ObjectDisposedException.ThrowIf(IsDisposed, this);
+ JavaScriptImports.SetPropertyDouble(this, propertyName, value);
}
/// <summary>
@@ -142,7 +153,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetProperty(string propertyName, string? value)
{
- throw new NotImplementedException();
+ ObjectDisposedException.ThrowIf(IsDisposed, this);
+ JavaScriptImports.SetPropertyString(this, propertyName, value);
}
/// <summary>
@@ -151,7 +163,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetProperty(string propertyName, JSObject? value)
{
- throw new NotImplementedException();
+ ObjectDisposedException.ThrowIf(IsDisposed, this);
+ JavaScriptImports.SetPropertyJSObject(this, propertyName, value);
}
/// <summary>
@@ -161,7 +174,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetProperty(string propertyName, byte[]? value)
{
- throw new NotImplementedException();
+ ObjectDisposedException.ThrowIf(IsDisposed, this);
+ JavaScriptImports.SetPropertyBytes(this, propertyName, value);
}
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Array.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Array.cs
index 180cc86e644..2c669c0673e 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Array.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Array.cs
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Runtime.CompilerServices;
+
namespace System.Runtime.InteropServices.JavaScript
{
/// <summary>
@@ -81,6 +83,7 @@ namespace System.Runtime.InteropServices.JavaScript
/// <param name="i">The index.</param>
public object this[int i]
{
+ [MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
get
{
this.AssertNotDisposed();
@@ -92,6 +95,7 @@ namespace System.Runtime.InteropServices.JavaScript
JSHostImplementation.ReleaseInFlight(indexValue);
return indexValue;
}
+ [MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
set
{
this.AssertNotDisposed();
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Runtime.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Runtime.cs
index 6559d7798d6..abafd63e932 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Runtime.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Runtime.cs
@@ -2,7 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics.CodeAnalysis;
-using System.Threading.Tasks;
+using System.Runtime.CompilerServices;
namespace System.Runtime.InteropServices.JavaScript
{
@@ -19,24 +19,6 @@ namespace System.Runtime.InteropServices.JavaScript
public static object GetGlobalObject(string str)
=> JavaScriptImports.GetGlobalObject(str);
- public static void CancelPromise(IntPtr promiseJSHandle)
- => JavaScriptImports.CancelPromise(promiseJSHandle);
-
- public static Task<object> WebSocketOpen(string uri, object[]? subProtocols, Delegate onClosed, out JSObject webSocket, out IntPtr promiseJSHandle)
- => JavaScriptImports.WebSocketOpen(uri, subProtocols, onClosed, out webSocket, out promiseJSHandle);
-
- public static unsafe Task<object>? WebSocketSend(JSObject webSocket, ArraySegment<byte> buffer, int messageType, bool endOfMessage, out IntPtr promiseJSHandle)
- => JavaScriptImports.WebSocketSend(webSocket, buffer, messageType, endOfMessage, out promiseJSHandle);
-
- public static unsafe Task<object>? WebSocketReceive(JSObject webSocket, ArraySegment<byte> buffer, ReadOnlySpan<int> response, out IntPtr promiseJSHandle)
- => JavaScriptImports.WebSocketReceive(webSocket, buffer, response, out promiseJSHandle);
-
- public static Task<object>? WebSocketClose(JSObject webSocket, int code, string? reason, bool waitForCloseReceived, out IntPtr promiseJSHandle)
- => JavaScriptImports.WebSocketClose(webSocket, code, reason, waitForCloseReceived, out promiseJSHandle);
-
- public static void WebSocketAbort(JSObject webSocket)
- => JavaScriptImports.WebSocketAbort(webSocket);
-
/// <summary>
/// Invoke a named method of the object, or throws a JSException on error.
/// </summary>
@@ -57,6 +39,7 @@ namespace System.Runtime.InteropServices.JavaScript
/// valuews.
/// </para>
/// </returns>
+ [MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static object Invoke(this JSObject self, string method, params object?[] args)
{
ArgumentNullException.ThrowIfNull(self);
@@ -91,6 +74,7 @@ namespace System.Runtime.InteropServices.JavaScript
/// valuews.
/// </para>
/// </returns>
+ [MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static object GetObjectProperty(this JSObject self, string name)
{
ArgumentNullException.ThrowIfNull(self);
@@ -115,6 +99,7 @@ namespace System.Runtime.InteropServices.JavaScript
/// float[], double[]) </param>
/// <param name="createIfNotExists">Defaults to <see langword="true"/> and creates the property on the javascript object if not found, if set to <see langword="false"/> it will not create the property if it does not exist. If the property exists, the value is updated with the provided value.</param>
/// <param name="hasOwnProperty"></param>
+ [MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static void SetObjectProperty(this JSObject self, string name, object? value, bool createIfNotExists = true, bool hasOwnProperty = false)
{
ArgumentNullException.ThrowIfNull(self);
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Uint8Array.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Uint8Array.cs
index e829155a550..6bea6d54d5d 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Uint8Array.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Uint8Array.cs
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Runtime.CompilerServices;
+
namespace System.Runtime.InteropServices.JavaScript
{
[Obsolete]
@@ -34,6 +36,7 @@ namespace System.Runtime.InteropServices.JavaScript
public static implicit operator Uint8Array(Span<byte> span) => From(span);
+ [MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public byte[] ToArray()
{
this.AssertNotDisposed();
@@ -45,6 +48,7 @@ namespace System.Runtime.InteropServices.JavaScript
return (byte[])res;
}
+ [MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
public static unsafe Uint8Array From(ReadOnlySpan<byte> span)
{
// source has to be instantiated.
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/MarshalerType.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/MarshalerType.cs
index 0674cdc3ba0..4e245866c0e 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/MarshalerType.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/MarshalerType.cs
@@ -35,8 +35,6 @@ namespace System.Runtime.InteropServices.JavaScript
Action,
Function,
- // FUTURE NativeMarshalling,
-
#if !JSIMPORTGENERATOR
// only on runtime
JSException,
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.BigInt64.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.BigInt64.cs
index d9a232835b6..f53960e618e 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.BigInt64.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.BigInt64.cs
@@ -14,7 +14,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManagedBig(out long value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = default;
+ return;
+ }
+ value = slot.Int64Value;
}
/// <summary>
@@ -24,7 +29,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJSBig(long value)
{
- throw new NotImplementedException();
+ slot.Type = MarshalerType.BigInt64;
+ slot.Int64Value = value;
}
/// <summary>
@@ -34,7 +40,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManagedBig(out long? value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+ value = slot.Int64Value;
}
/// <summary>
@@ -44,7 +55,15 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJSBig(long? value)
{
- throw new NotImplementedException();
+ if (value.HasValue)
+ {
+ slot.Type = MarshalerType.BigInt64;
+ slot.Int64Value = value.Value;
+ }
+ else
+ {
+ slot.Type = MarshalerType.None;
+ }
}
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Bool.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Bool.cs
index eec69ac0e82..336db0c50d2 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Bool.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Bool.cs
@@ -14,7 +14,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out bool value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = default;
+ return;
+ }
+ value = slot.BooleanValue;
}
/// <summary>
@@ -24,7 +29,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJS(bool value)
{
- throw new NotImplementedException();
+ slot.Type = MarshalerType.Boolean;
+ slot.BooleanValue = value;
}
/// <summary>
@@ -34,7 +40,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out bool? value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+ value = slot.BooleanValue;
}
/// <summary>
@@ -44,7 +55,15 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJS(bool? value)
{
- throw new NotImplementedException();
+ if (value.HasValue)
+ {
+ slot.Type = MarshalerType.Boolean;
+ slot.BooleanValue = value.Value;
+ }
+ else
+ {
+ slot.Type = MarshalerType.None;
+ }
}
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Byte.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Byte.cs
index 706348e26df..218b40336f6 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Byte.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Byte.cs
@@ -14,7 +14,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out byte value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = default;
+ return;
+ }
+ value = slot.ByteValue;
}
/// <summary>
@@ -24,7 +29,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJS(byte value)
{
- throw new NotImplementedException();
+ slot.Type = MarshalerType.Byte;
+ slot.ByteValue = value;
}
/// <summary>
@@ -34,7 +40,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out byte? value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+ value = slot.ByteValue;
}
/// <summary>
@@ -44,7 +55,15 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJS(byte? value)
{
- throw new NotImplementedException();
+ if (value.HasValue)
+ {
+ slot.Type = MarshalerType.Byte;
+ slot.ByteValue = value.Value;
+ }
+ else
+ {
+ slot.Type = MarshalerType.None;
+ }
}
/// <summary>
@@ -53,7 +72,14 @@ namespace System.Runtime.InteropServices.JavaScript
/// </summary>
public unsafe void ToManaged(out byte[]? value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+ value = new byte[slot.Length];
+ Marshal.Copy(slot.IntPtrValue, value, 0, slot.Length);
+ Marshal.FreeHGlobal(slot.IntPtrValue);
}
/// <summary>
@@ -62,7 +88,16 @@ namespace System.Runtime.InteropServices.JavaScript
/// </summary>
public unsafe void ToJS(byte[]? value)
{
- throw new NotImplementedException();
+ if (value == null)
+ {
+ slot.Type = MarshalerType.None;
+ return;
+ }
+ slot.Length = value.Length;
+ slot.Type = MarshalerType.Array;
+ slot.IntPtrValue = Marshal.AllocHGlobal(value.Length * sizeof(byte));
+ slot.ElementType = MarshalerType.Byte;
+ Marshal.Copy(value, 0, slot.IntPtrValue, slot.Length);
}
/// <summary>
@@ -72,7 +107,10 @@ namespace System.Runtime.InteropServices.JavaScript
// this only supports array round-trip, there is no way how to create ArraySegment in JS
public unsafe void ToManaged(out ArraySegment<byte> value)
{
- throw new NotImplementedException();
+ var array = (byte[])((GCHandle)slot.GCHandle).Target!;
+ var refPtr = (IntPtr)Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(array));
+ int byteOffset = (int)(slot.IntPtrValue - (nint)refPtr);
+ value = new ArraySegment<byte>(array, byteOffset, slot.Length);
}
/// <summary>
@@ -81,7 +119,16 @@ namespace System.Runtime.InteropServices.JavaScript
/// </summary>
public unsafe void ToJS(ArraySegment<byte> value)
{
- throw new NotImplementedException();
+ if (value.Array == null)
+ {
+ slot.Type = MarshalerType.None;
+ return;
+ }
+ slot.Type = MarshalerType.ArraySegment;
+ slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandleRef(value.Array, GCHandleType.Pinned);
+ var refPtr = (IntPtr)Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(value.Array));
+ slot.IntPtrValue = refPtr + value.Offset;
+ slot.Length = value.Count;
}
/// <summary>
@@ -90,7 +137,7 @@ namespace System.Runtime.InteropServices.JavaScript
/// </summary>
public unsafe void ToManaged(out Span<byte> value)
{
- throw new NotImplementedException();
+ value = new Span<byte>((void*)slot.IntPtrValue, slot.Length);
}
/// <summary>
@@ -100,7 +147,9 @@ namespace System.Runtime.InteropServices.JavaScript
/// <remarks>caller is responsible for pinning</remarks>
public unsafe void ToJS(Span<byte> value)
{
- throw new NotImplementedException();
+ slot.Length = value.Length;
+ slot.IntPtrValue = (IntPtr)Unsafe.AsPointer(ref value.GetPinnableReference());
+ slot.Type = MarshalerType.Span;
}
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Char.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Char.cs
index b56a4135871..8a0500068e2 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Char.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Char.cs
@@ -14,7 +14,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out char value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = default;
+ return;
+ }
+ value = slot.CharValue;
}
/// <summary>
@@ -24,7 +29,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJS(char value)
{
- throw new NotImplementedException();
+ slot.Type = MarshalerType.Char;
+ slot.CharValue = value;
}
/// <summary>
@@ -34,7 +40,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out char? value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+ value = slot.CharValue;
}
/// <summary>
@@ -44,7 +55,15 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJS(char? value)
{
- throw new NotImplementedException();
+ if (value.HasValue)
+ {
+ slot.Type = MarshalerType.Char;
+ slot.CharValue = value.Value;
+ }
+ else
+ {
+ slot.Type = MarshalerType.None;
+ }
}
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.DateTime.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.DateTime.cs
index 1d963a92bc4..3984941ab52 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.DateTime.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.DateTime.cs
@@ -14,7 +14,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out DateTimeOffset value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = default;
+ return;
+ }
+ value = DateTimeOffset.FromUnixTimeMilliseconds((long)slot.DoubleValue);
}
/// <summary>
@@ -24,7 +29,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJS(DateTimeOffset value)
{
- throw new NotImplementedException();
+ slot.Type = MarshalerType.DateTimeOffset;
+ slot.DoubleValue = (double)value.ToUnixTimeMilliseconds();
}
/// <summary>
@@ -34,7 +40,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out DateTimeOffset? value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+ value = DateTimeOffset.FromUnixTimeMilliseconds((long)slot.DoubleValue);
}
/// <summary>
@@ -44,7 +55,15 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJS(DateTimeOffset? value)
{
- throw new NotImplementedException();
+ if (value.HasValue)
+ {
+ slot.Type = MarshalerType.DateTimeOffset;
+ slot.DoubleValue = value.Value.ToUnixTimeMilliseconds(); ;
+ }
+ else
+ {
+ slot.Type = MarshalerType.None;
+ }
}
/// <summary>
@@ -54,7 +73,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out DateTime value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = default;
+ return;
+ }
+ value = DateTimeOffset.FromUnixTimeMilliseconds((long)slot.DoubleValue).UtcDateTime;
}
/// <summary>
@@ -64,7 +88,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJS(DateTime value)
{
- throw new NotImplementedException();
+ slot.Type = MarshalerType.DateTime;
+ slot.DoubleValue = new DateTimeOffset(value).ToUnixTimeMilliseconds();
}
/// <summary>
@@ -74,7 +99,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out DateTime? value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+ value = DateTimeOffset.FromUnixTimeMilliseconds((long)slot.DoubleValue).UtcDateTime;
}
/// <summary>
@@ -84,7 +114,15 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJS(DateTime? value)
{
- throw new NotImplementedException();
+ if (value.HasValue)
+ {
+ slot.Type = MarshalerType.DateTime;
+ slot.DoubleValue = new DateTimeOffset(value.Value).ToUnixTimeMilliseconds();
+ }
+ else
+ {
+ slot.Type = MarshalerType.None;
+ }
}
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Double.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Double.cs
index 88dabbed021..2dcb1d445d5 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Double.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Double.cs
@@ -14,7 +14,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out double value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = default;
+ return;
+ }
+ value = slot.DoubleValue;
}
/// <summary>
@@ -24,7 +29,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJS(double value)
{
- throw new NotImplementedException();
+ slot.Type = MarshalerType.Double;
+ slot.DoubleValue = value;
}
/// <summary>
@@ -34,7 +40,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out double? value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+ value = slot.DoubleValue;
}
/// <summary>
@@ -44,7 +55,15 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJS(double? value)
{
- throw new NotImplementedException();
+ if (value.HasValue)
+ {
+ slot.Type = MarshalerType.Double;
+ slot.DoubleValue = value.Value;
+ }
+ else
+ {
+ slot.Type = MarshalerType.None;
+ }
}
/// <summary>
@@ -54,7 +73,14 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out double[]? value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+ value = new double[slot.Length];
+ Marshal.Copy(slot.IntPtrValue, value, 0, slot.Length);
+ Marshal.FreeHGlobal(slot.IntPtrValue);
}
/// <summary>
@@ -64,7 +90,16 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJS(double[] value)
{
- throw new NotImplementedException();
+ if (value == null)
+ {
+ slot.Type = MarshalerType.None;
+ return;
+ }
+ slot.Type = MarshalerType.Array;
+ slot.IntPtrValue = Marshal.AllocHGlobal(value.Length * sizeof(double));
+ slot.Length = value.Length;
+ slot.ElementType = MarshalerType.Double;
+ Marshal.Copy(value, 0, slot.IntPtrValue, slot.Length);
}
/// <summary>
@@ -74,7 +109,10 @@ namespace System.Runtime.InteropServices.JavaScript
// this only supports array round-trip
public unsafe void ToManaged(out ArraySegment<double> value)
{
- throw new NotImplementedException();
+ var array = (double[])((GCHandle)slot.GCHandle).Target!;
+ var refPtr = (IntPtr)Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(array));
+ int byteOffset = (int)(slot.IntPtrValue - (nint)refPtr);
+ value = new ArraySegment<double>(array, byteOffset / sizeof(double), slot.Length);
}
/// <summary>
@@ -83,7 +121,16 @@ namespace System.Runtime.InteropServices.JavaScript
/// </summary>
public unsafe void ToJS(ArraySegment<double> value)
{
- throw new NotImplementedException();
+ if (value.Array == null)
+ {
+ slot.Type = MarshalerType.None;
+ return;
+ }
+ slot.Type = MarshalerType.ArraySegment;
+ slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandleRef(value.Array, GCHandleType.Pinned);
+ var refPtr = (IntPtr)Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(value.Array));
+ slot.IntPtrValue = refPtr + (value.Offset * sizeof(double));
+ slot.Length = value.Count;
}
/// <summary>
@@ -92,7 +139,7 @@ namespace System.Runtime.InteropServices.JavaScript
/// </summary>
public unsafe void ToManaged(out Span<double> value)
{
- throw new NotImplementedException();
+ value = new Span<double>((void*)slot.IntPtrValue, slot.Length);
}
/// <summary>
@@ -102,7 +149,9 @@ namespace System.Runtime.InteropServices.JavaScript
/// <remarks>caller is responsible for pinning</remarks>
public unsafe void ToJS(Span<double> value)
{
- throw new NotImplementedException();
+ slot.Length = value.Length;
+ slot.IntPtrValue = (IntPtr)Unsafe.AsPointer(ref value.GetPinnableReference());
+ slot.Type = MarshalerType.Span;
}
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Exception.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Exception.cs
index ac5eb413645..674b5899ceb 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Exception.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Exception.cs
@@ -14,7 +14,31 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out Exception? value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+ if (slot.Type == MarshalerType.Exception)
+ {
+ // this is managed exception round-trip
+#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
+ value = (Exception)((GCHandle)slot.GCHandle).Target;
+#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type.
+ return;
+ }
+
+ JSObject? jsException = null;
+ if (slot.JSHandle != IntPtr.Zero)
+ {
+ // this is JSException round-trip
+ jsException = JavaScriptExports.CreateCSOwnedProxy(slot.JSHandle);
+ }
+
+ string? message;
+ ToManaged(out message);
+
+ value = new JSException(message!, jsException);
}
/// <summary>
@@ -24,7 +48,36 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToJS(Exception? value)
{
- throw new NotImplementedException();
+ if (value == null)
+ {
+ slot.Type = MarshalerType.None;
+ }
+ else
+ {
+ Exception cpy = value;
+ if (cpy is AggregateException ae && ae.InnerExceptions.Count == 1)
+ {
+ cpy = ae.InnerExceptions[0];
+ }
+
+ var jse = cpy as JSException;
+ if (jse != null && jse.jsException != null)
+ {
+ // this is JSException roundtrip
+ if (jse.jsException.IsDisposed)
+ {
+ throw new ObjectDisposedException(nameof(value));
+ }
+ slot.Type = MarshalerType.JSException;
+ slot.JSHandle = jse.jsException.JSHandle;
+ }
+ else
+ {
+ ToJS(cpy.Message);
+ slot.Type = MarshalerType.Exception;
+ slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(cpy);
+ }
+ }
}
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Func.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Func.cs
index 9bcc9fd4eb3..b4cd56423b9 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Func.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Func.cs
@@ -5,76 +5,375 @@ namespace System.Runtime.InteropServices.JavaScript
{
public partial struct JSMarshalerArgument
{
+ private sealed class ActionJS
+ {
+ private JSObject JSObject;
+
+ public ActionJS(IntPtr jsHandle)
+ {
+ JSObject = JavaScriptExports.CreateCSOwnedProxy(jsHandle);
+ }
+
+ public void InvokeJS()
+ {
+ // JSObject (held by this lambda) would be collected by GC after the lambda is collected
+ // and would also allow the JS function to be collected
+
+ Span<JSMarshalerArgument> arguments = stackalloc JSMarshalerArgument[4];
+ ref JSMarshalerArgument args_exception = ref arguments[0];
+ ref JSMarshalerArgument args_return = ref arguments[1];
+ args_exception.Initialize();
+ args_return.Initialize();
+
+ JSFunctionBinding.InvokeJSImpl(JSObject, arguments);
+ }
+
+ }
+
+ private sealed class ActionJS<T>
+ {
+ private ArgumentToJSCallback<T> Arg1Marshaler;
+ private JSObject JSObject;
+
+ public ActionJS(IntPtr jsHandle, ArgumentToJSCallback<T> arg1Marshaler)
+ {
+ JSObject = JavaScriptExports.CreateCSOwnedProxy(jsHandle);
+ Arg1Marshaler = arg1Marshaler;
+ }
+
+ public void InvokeJS(T arg1)
+ {
+ Span<JSMarshalerArgument> arguments = stackalloc JSMarshalerArgument[4];
+ ref JSMarshalerArgument args_exception = ref arguments[0];
+ ref JSMarshalerArgument args_return = ref arguments[1];
+ ref JSMarshalerArgument args_arg1 = ref arguments[2];
+
+ args_exception.Initialize();
+ args_return.Initialize();
+ Arg1Marshaler(ref args_arg1, arg1);
+
+ JSFunctionBinding.InvokeJSImpl(JSObject, arguments);
+ }
+ }
+
+ private sealed class ActionJS<T1, T2>
+ {
+ private ArgumentToJSCallback<T1> Arg1Marshaler;
+ private ArgumentToJSCallback<T2> Arg2Marshaler;
+ private JSObject JSObject;
+
+ public ActionJS(IntPtr jsHandle, ArgumentToJSCallback<T1> arg1Marshaler, ArgumentToJSCallback<T2> arg2Marshaler)
+ {
+ JSObject = JavaScriptExports.CreateCSOwnedProxy(jsHandle);
+ Arg1Marshaler = arg1Marshaler;
+ Arg2Marshaler = arg2Marshaler;
+ }
+
+ public void InvokeJS(T1 arg1, T2 arg2)
+ {
+ Span<JSMarshalerArgument> arguments = stackalloc JSMarshalerArgument[4];
+ ref JSMarshalerArgument args_exception = ref arguments[0];
+ ref JSMarshalerArgument args_return = ref arguments[1];
+ ref JSMarshalerArgument args_arg1 = ref arguments[2];
+ ref JSMarshalerArgument args_arg2 = ref arguments[3];
+
+ args_exception.Initialize();
+ args_return.Initialize();
+ Arg1Marshaler(ref args_arg1, arg1);
+ Arg2Marshaler(ref args_arg2, arg2);
+
+ JSFunctionBinding.InvokeJSImpl(JSObject, arguments);
+ }
+ }
+
+ private sealed class ActionJS<T1, T2, T3>
+ {
+ private ArgumentToJSCallback<T1> Arg1Marshaler;
+ private ArgumentToJSCallback<T2> Arg2Marshaler;
+ private ArgumentToJSCallback<T3> Arg3Marshaler;
+ private JSObject JSObject;
+
+ public ActionJS(IntPtr jsHandle, ArgumentToJSCallback<T1> arg1Marshaler, ArgumentToJSCallback<T2> arg2Marshaler, ArgumentToJSCallback<T3> arg3Marshaler)
+ {
+ JSObject = JavaScriptExports.CreateCSOwnedProxy(jsHandle);
+ Arg1Marshaler = arg1Marshaler;
+ Arg2Marshaler = arg2Marshaler;
+ Arg3Marshaler = arg3Marshaler;
+ }
+
+ public void InvokeJS(T1 arg1, T2 arg2, T3 arg3)
+ {
+ Span<JSMarshalerArgument> arguments = stackalloc JSMarshalerArgument[5];
+ ref JSMarshalerArgument args_exception = ref arguments[0];
+ ref JSMarshalerArgument args_return = ref arguments[1];
+ ref JSMarshalerArgument args_arg1 = ref arguments[2];
+ ref JSMarshalerArgument args_arg2 = ref arguments[3];
+ ref JSMarshalerArgument args_arg3 = ref arguments[4];
+
+ args_exception.Initialize();
+ args_return.Initialize();
+ Arg1Marshaler(ref args_arg1, arg1);
+ Arg2Marshaler(ref args_arg2, arg2);
+ Arg3Marshaler(ref args_arg3, arg3);
+
+ JSFunctionBinding.InvokeJSImpl(JSObject, arguments);
+ }
+ }
+
/// <summary>
/// Implementation of the argument marshaling.
/// It's used by JSImport code generator and should not be used by developers in source code.
/// </summary>
- public unsafe void ToManaged(out Action value)
+ public unsafe void ToManaged(out Action? value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+
+ value = new ActionJS(slot.JSHandle).InvokeJS;
}
/// <summary>
/// Implementation of the argument marshaling.
/// It's used by JSImport code generator and should not be used by developers in source code.
/// </summary>
- public unsafe void ToManaged<T>(out Action<T> value, ArgumentToJSCallback<T> arg1Marshaler)
+ public unsafe void ToManaged<T>(out Action<T>? value, ArgumentToJSCallback<T> arg1Marshaler)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+
+ value = new ActionJS<T>(slot.JSHandle, arg1Marshaler).InvokeJS;
}
/// <summary>
/// Implementation of the argument marshaling.
/// It's used by JSImport code generator and should not be used by developers in source code.
/// </summary>
- public unsafe void ToManaged<T1, T2>(out Action<T1, T2> value, ArgumentToJSCallback<T1> arg1Marshaler, ArgumentToJSCallback<T2> arg2Marshaler)
+ public unsafe void ToManaged<T1, T2>(out Action<T1, T2>? value, ArgumentToJSCallback<T1> arg1Marshaler, ArgumentToJSCallback<T2> arg2Marshaler)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+
+ value = new ActionJS<T1, T2>(slot.JSHandle, arg1Marshaler, arg2Marshaler).InvokeJS;
}
/// <summary>
/// Implementation of the argument marshaling.
/// It's used by JSImport code generator and should not be used by developers in source code.
/// </summary>
- public unsafe void ToManaged<T1, T2, T3>(out Action<T1, T2, T3> value, ArgumentToJSCallback<T1> arg1Marshaler, ArgumentToJSCallback<T2> arg2Marshaler, ArgumentToJSCallback<T3> arg3Marshaler)
+ public unsafe void ToManaged<T1, T2, T3>(out Action<T1, T2, T3>? value, ArgumentToJSCallback<T1> arg1Marshaler, ArgumentToJSCallback<T2> arg2Marshaler, ArgumentToJSCallback<T3> arg3Marshaler)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+
+ value = new ActionJS<T1, T2, T3>(slot.JSHandle, arg1Marshaler, arg2Marshaler, arg3Marshaler).InvokeJS;
+ }
+
+ private sealed class FuncJS<TResult>
+ {
+ private JSObject JSObject;
+ private ArgumentToManagedCallback<TResult> ResMarshaler;
+
+ public FuncJS(IntPtr jsHandle, ArgumentToManagedCallback<TResult> resMarshaler)
+ {
+ JSObject = JavaScriptExports.CreateCSOwnedProxy(jsHandle);
+ ResMarshaler = resMarshaler;
+ }
+
+ public TResult InvokeJS()
+ {
+ // JSObject (held by this lambda) would be collected by GC after the lambda is collected
+ // and would also allow the JS function to be collected
+
+ Span<JSMarshalerArgument> arguments = stackalloc JSMarshalerArgument[4];
+ ref JSMarshalerArgument args_exception = ref arguments[0];
+ ref JSMarshalerArgument args_return = ref arguments[1];
+ args_exception.Initialize();
+ args_return.Initialize();
+
+ JSFunctionBinding.InvokeJSImpl(JSObject, arguments);
+
+ ResMarshaler(ref args_return, out TResult res);
+ return res;
+ }
+
+ }
+
+ private sealed class FuncJS<T, TResult>
+ {
+ private ArgumentToJSCallback<T> Arg1Marshaler;
+ private ArgumentToManagedCallback<TResult> ResMarshaler;
+ private JSObject JSObject;
+
+ public FuncJS(IntPtr jsHandle, ArgumentToJSCallback<T> arg1Marshaler, ArgumentToManagedCallback<TResult> resMarshaler)
+ {
+ JSObject = JavaScriptExports.CreateCSOwnedProxy(jsHandle);
+ Arg1Marshaler = arg1Marshaler;
+ ResMarshaler = resMarshaler;
+ }
+
+ public TResult InvokeJS(T arg1)
+ {
+ Span<JSMarshalerArgument> arguments = stackalloc JSMarshalerArgument[4];
+ ref JSMarshalerArgument args_exception = ref arguments[0];
+ ref JSMarshalerArgument args_return = ref arguments[1];
+ ref JSMarshalerArgument args_arg1 = ref arguments[2];
+
+ args_exception.Initialize();
+ args_return.Initialize();
+ Arg1Marshaler(ref args_arg1, arg1);
+
+ JSFunctionBinding.InvokeJSImpl(JSObject, arguments);
+
+ ResMarshaler(ref args_return, out TResult res);
+ return res;
+ }
+ }
+
+ private sealed class FuncJS<T1, T2, TResult>
+ {
+ private ArgumentToJSCallback<T1> Arg1Marshaler;
+ private ArgumentToJSCallback<T2> Arg2Marshaler;
+ private ArgumentToManagedCallback<TResult> ResMarshaler;
+ private JSObject JSObject;
+
+ public FuncJS(IntPtr jsHandle, ArgumentToJSCallback<T1> arg1Marshaler, ArgumentToJSCallback<T2> arg2Marshaler, ArgumentToManagedCallback<TResult> resMarshaler)
+ {
+ JSObject = JavaScriptExports.CreateCSOwnedProxy(jsHandle);
+ Arg1Marshaler = arg1Marshaler;
+ Arg2Marshaler = arg2Marshaler;
+ ResMarshaler = resMarshaler;
+ }
+
+ public TResult InvokeJS(T1 arg1, T2 arg2)
+ {
+ Span<JSMarshalerArgument> arguments = stackalloc JSMarshalerArgument[4];
+ ref JSMarshalerArgument args_exception = ref arguments[0];
+ ref JSMarshalerArgument args_return = ref arguments[1];
+ ref JSMarshalerArgument args_arg1 = ref arguments[2];
+ ref JSMarshalerArgument args_arg2 = ref arguments[3];
+
+ args_exception.Initialize();
+ args_return.Initialize();
+ Arg1Marshaler(ref args_arg1, arg1);
+ Arg2Marshaler(ref args_arg2, arg2);
+
+ JSFunctionBinding.InvokeJSImpl(JSObject, arguments);
+
+ ResMarshaler(ref args_return, out TResult res);
+ return res;
+ }
+ }
+
+ private sealed class FuncJS<T1, T2, T3, TResult>
+ {
+ private ArgumentToJSCallback<T1> Arg1Marshaler;
+ private ArgumentToJSCallback<T2> Arg2Marshaler;
+ private ArgumentToJSCallback<T3> Arg3Marshaler;
+ private ArgumentToManagedCallback<TResult> ResMarshaler;
+ private JSObject JSObject;
+
+ public FuncJS(IntPtr jsHandle, ArgumentToJSCallback<T1> arg1Marshaler, ArgumentToJSCallback<T2> arg2Marshaler, ArgumentToJSCallback<T3> arg3Marshaler, ArgumentToManagedCallback<TResult> resMarshaler)
+ {
+ JSObject = JavaScriptExports.CreateCSOwnedProxy(jsHandle);
+ Arg1Marshaler = arg1Marshaler;
+ Arg2Marshaler = arg2Marshaler;
+ Arg3Marshaler = arg3Marshaler;
+ ResMarshaler = resMarshaler;
+ }
+
+ public TResult InvokeJS(T1 arg1, T2 arg2, T3 arg3)
+ {
+ Span<JSMarshalerArgument> arguments = stackalloc JSMarshalerArgument[5];
+ ref JSMarshalerArgument args_exception = ref arguments[0];
+ ref JSMarshalerArgument args_return = ref arguments[1];
+ ref JSMarshalerArgument args_arg1 = ref arguments[2];
+ ref JSMarshalerArgument args_arg2 = ref arguments[3];
+ ref JSMarshalerArgument args_arg3 = ref arguments[4];
+
+ args_exception.Initialize();
+ args_return.Initialize();
+ Arg1Marshaler(ref args_arg1, arg1);
+ Arg2Marshaler(ref args_arg2, arg2);
+ Arg3Marshaler(ref args_arg3, arg3);
+
+ JSFunctionBinding.InvokeJSImpl(JSObject, arguments);
+
+ ResMarshaler(ref args_return, out TResult res);
+ return res;
+ }
}
/// <summary>
/// Implementation of the argument marshaling.
/// It's used by JSImport code generator and should not be used by developers in source code.
/// </summary>
- public unsafe void ToManaged<TResult>(out Func<TResult> value, ArgumentToManagedCallback<TResult> resMarshaler)
+ public unsafe void ToManaged<TResult>(out Func<TResult>? value, ArgumentToManagedCallback<TResult> resMarshaler)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+
+ value = new FuncJS<TResult>(slot.JSHandle, resMarshaler).InvokeJS;
}
/// <summary>
/// Implementation of the argument marshaling.
/// It's used by JSImport code generator and should not be used by developers in source code.
/// </summary>
- public unsafe void ToManaged<T, TResult>(out Func<T, TResult> value, ArgumentToJSCallback<T> arg1Marshaler, ArgumentToManagedCallback<TResult> resMarshaler)
+ public unsafe void ToManaged<T, TResult>(out Func<T, TResult>? value, ArgumentToJSCallback<T> arg1Marshaler, ArgumentToManagedCallback<TResult> resMarshaler)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+
+ value = new FuncJS<T, TResult>(slot.JSHandle, arg1Marshaler, resMarshaler).InvokeJS;
+
}
/// <summary>
/// Implementation of the argument marshaling.
/// It's used by JSImport code generator and should not be used by developers in source code.
/// </summary>
- public unsafe void ToManaged<T1, T2, TResult>(out Func<T1, T2, TResult> value, ArgumentToJSCallback<T1> arg1Marshaler, ArgumentToJSCallback<T2> arg2Marshaler, ArgumentToManagedCallback<TResult> resMarshaler)
+ public unsafe void ToManaged<T1, T2, TResult>(out Func<T1, T2, TResult>? value, ArgumentToJSCallback<T1> arg1Marshaler, ArgumentToJSCallback<T2> arg2Marshaler, ArgumentToManagedCallback<TResult> resMarshaler)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+
+ value = new FuncJS<T1, T2, TResult>(slot.JSHandle, arg1Marshaler, arg2Marshaler, resMarshaler).InvokeJS;
}
/// <summary>
/// Implementation of the argument marshaling.
/// It's used by JSImport code generator and should not be used by developers in source code.
/// </summary>
- public unsafe void ToManaged<T1, T2, T3, TResult>(out Func<T1, T2, T3, TResult> value, ArgumentToJSCallback<T1> arg1Marshaler, ArgumentToJSCallback<T2> arg2Marshaler, ArgumentToJSCallback<T3> arg3Marshaler, ArgumentToManagedCallback<TResult> resMarshaler)
+ public unsafe void ToManaged<T1, T2, T3, TResult>(out Func<T1, T2, T3, TResult>? value, ArgumentToJSCallback<T1> arg1Marshaler, ArgumentToJSCallback<T2> arg2Marshaler, ArgumentToJSCallback<T3> arg3Marshaler, ArgumentToManagedCallback<TResult> resMarshaler)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+
+ value = new FuncJS<T1, T2, T3, TResult>(slot.JSHandle, arg1Marshaler, arg2Marshaler, arg3Marshaler, resMarshaler).InvokeJS;
}
/// <summary>
@@ -83,7 +382,22 @@ namespace System.Runtime.InteropServices.JavaScript
/// </summary>
public unsafe void ToJS(Action value)
{
- throw new NotImplementedException();
+ Action cpy = value;
+ // TODO: we could try to cache value -> exising GCHandle
+ JSHostImplementation.ToManagedCallback cb = (JSMarshalerArgument* arguments) =>
+ {
+ ref JSMarshalerArgument exc = ref arguments[0];
+ try
+ {
+ cpy.Invoke();
+ }
+ catch (Exception ex)
+ {
+ exc.ToJS(ex);
+ }
+ };
+ slot.Type = MarshalerType.Function;
+ slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(cb);
}
/// <summary>
@@ -92,7 +406,23 @@ namespace System.Runtime.InteropServices.JavaScript
/// </summary>
public unsafe void ToJS<T>(Action<T> value, ArgumentToManagedCallback<T> arg1Marshaler)
{
- throw new NotImplementedException();
+ Action<T> cpy = value;
+ JSHostImplementation.ToManagedCallback cb = (JSMarshalerArgument* arguments) =>
+ {
+ ref JSMarshalerArgument exc = ref arguments[0];
+ ref JSMarshalerArgument arg1 = ref arguments[2];
+ try
+ {
+ arg1Marshaler(ref arg1, out T arg1cs);
+ cpy.Invoke(arg1cs);
+ }
+ catch (Exception ex)
+ {
+ exc.ToJS(ex);
+ }
+ };
+ slot.Type = MarshalerType.Action;
+ slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(cb);
}
/// <summary>
@@ -101,7 +431,25 @@ namespace System.Runtime.InteropServices.JavaScript
/// </summary>
public unsafe void ToJS<T1, T2>(Action<T1, T2> value, ArgumentToManagedCallback<T1> arg1Marshaler, ArgumentToManagedCallback<T2> arg2Marshaler)
{
- throw new NotImplementedException();
+ Action<T1, T2> cpy = value;
+ JSHostImplementation.ToManagedCallback cb = (JSMarshalerArgument* arguments) =>
+ {
+ ref JSMarshalerArgument exc = ref arguments[0];
+ ref JSMarshalerArgument arg1 = ref arguments[2];
+ ref JSMarshalerArgument arg2 = ref arguments[3];
+ try
+ {
+ arg1Marshaler(ref arg1, out T1 arg1cs);
+ arg2Marshaler(ref arg2, out T2 arg2cs);
+ cpy.Invoke(arg1cs, arg2cs);
+ }
+ catch (Exception ex)
+ {
+ exc.ToJS(ex);
+ }
+ };
+ slot.Type = MarshalerType.Action;
+ slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(cb);
}
/// <summary>
@@ -110,7 +458,27 @@ namespace System.Runtime.InteropServices.JavaScript
/// </summary>
public unsafe void ToJS<T1, T2, T3>(Action<T1, T2, T3> value, ArgumentToManagedCallback<T1> arg1Marshaler, ArgumentToManagedCallback<T2> arg2Marshaler, ArgumentToManagedCallback<T3> arg3Marshaler)
{
- throw new NotImplementedException();
+ Action<T1, T2, T3> cpy = value;
+ JSHostImplementation.ToManagedCallback cb = (JSMarshalerArgument* arguments) =>
+ {
+ ref JSMarshalerArgument exc = ref arguments[0];
+ ref JSMarshalerArgument arg1 = ref arguments[2];
+ ref JSMarshalerArgument arg2 = ref arguments[3];
+ ref JSMarshalerArgument arg3 = ref arguments[4];
+ try
+ {
+ arg1Marshaler(ref arg1, out T1 arg1cs);
+ arg2Marshaler(ref arg2, out T2 arg2cs);
+ arg3Marshaler(ref arg3, out T3 arg3cs);
+ cpy.Invoke(arg1cs, arg2cs, arg3cs);
+ }
+ catch (Exception ex)
+ {
+ exc.ToJS(ex);
+ }
+ };
+ slot.Type = MarshalerType.Action;
+ slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(cb);
}
/// <summary>
@@ -119,7 +487,23 @@ namespace System.Runtime.InteropServices.JavaScript
/// </summary>
public unsafe void ToJS<TResult>(Func<TResult> value, ArgumentToJSCallback<TResult> resMarshaler)
{
- throw new NotImplementedException();
+ Func<TResult> cpy = value;
+ JSHostImplementation.ToManagedCallback cb = (JSMarshalerArgument* arguments) =>
+ {
+ ref JSMarshalerArgument exc = ref arguments[0];
+ ref JSMarshalerArgument res = ref arguments[1];
+ try
+ {
+ TResult resCs = cpy.Invoke();
+ resMarshaler(ref res, resCs);
+ }
+ catch (Exception ex)
+ {
+ exc.ToJS(ex);
+ }
+ };
+ slot.Type = MarshalerType.Function;
+ slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(cb);
}
/// <summary>
@@ -128,7 +512,25 @@ namespace System.Runtime.InteropServices.JavaScript
/// </summary>
public unsafe void ToJS<T, TResult>(Func<T, TResult> value, ArgumentToManagedCallback<T> arg1Marshaler, ArgumentToJSCallback<TResult> resMarshaler)
{
- throw new NotImplementedException();
+ Func<T, TResult> cpy = value;
+ JSHostImplementation.ToManagedCallback cb = (JSMarshalerArgument* arguments) =>
+ {
+ ref JSMarshalerArgument exc = ref arguments[0];
+ ref JSMarshalerArgument res = ref arguments[1];
+ ref JSMarshalerArgument arg1 = ref arguments[2];
+ try
+ {
+ arg1Marshaler(ref arg1, out T arg1cs);
+ TResult resCs = cpy.Invoke(arg1cs);
+ resMarshaler(ref res, resCs);
+ }
+ catch (Exception ex)
+ {
+ exc.ToJS(ex);
+ }
+ };
+ slot.Type = MarshalerType.Function;
+ slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(cb);
}
/// <summary>
@@ -137,7 +539,29 @@ namespace System.Runtime.InteropServices.JavaScript
/// </summary>
public unsafe void ToJS<T1, T2, TResult>(Func<T1, T2, TResult> value, ArgumentToManagedCallback<T1> arg1Marshaler, ArgumentToManagedCallback<T2> arg2Marshaler, ArgumentToJSCallback<TResult> resMarshaler)
{
- throw new NotImplementedException();
+ Func<T1, T2, TResult> cpy = value;
+ JSHostImplementation.ToManagedCallback cb = (JSMarshalerArgument* arguments) =>
+ {
+ ref JSMarshalerArgument exc = ref arguments[0];
+ ref JSMarshalerArgument res = ref arguments[1];
+ ref JSMarshalerArgument arg1 = ref arguments[2];
+ ref JSMarshalerArgument arg2 = ref arguments[3];
+ try
+ {
+ arg1Marshaler(ref arg1, out T1 arg1cs);
+ arg2Marshaler(ref arg2, out T2 arg2cs);
+ TResult resCs = cpy.Invoke(arg1cs, arg2cs);
+ resMarshaler(ref res, resCs);
+ }
+ catch (Exception ex)
+ {
+ exc.ToJS(ex);
+ }
+ };
+
+
+ slot.Type = MarshalerType.Function;
+ slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(cb);
}
/// <summary>
@@ -146,7 +570,31 @@ namespace System.Runtime.InteropServices.JavaScript
/// </summary>
public unsafe void ToJS<T1, T2, T3, TResult>(Func<T1, T2, T3, TResult> value, ArgumentToManagedCallback<T1> arg1Marshaler, ArgumentToManagedCallback<T2> arg2Marshaler, ArgumentToManagedCallback<T3> arg3Marshaler, ArgumentToJSCallback<TResult> resMarshaler)
{
- throw new NotImplementedException();
+ Func<T1, T2, T3, TResult> cpy = value;
+ JSHostImplementation.ToManagedCallback cb = (JSMarshalerArgument* arguments) =>
+ {
+ ref JSMarshalerArgument exc = ref arguments[0];
+ ref JSMarshalerArgument res = ref arguments[1];
+ ref JSMarshalerArgument arg1 = ref arguments[2];
+ ref JSMarshalerArgument arg2 = ref arguments[3];
+ ref JSMarshalerArgument arg3 = ref arguments[4];
+ try
+ {
+ arg1Marshaler(ref arg1, out T1 arg1cs);
+ arg2Marshaler(ref arg2, out T2 arg2cs);
+ arg3Marshaler(ref arg3, out T3 arg3cs);
+ TResult resCs = cpy.Invoke(arg1cs, arg2cs, arg3cs);
+ resMarshaler(ref res, resCs);
+ }
+ catch (Exception ex)
+ {
+ exc.ToJS(ex);
+ }
+ };
+
+
+ slot.Type = MarshalerType.Function;
+ slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(cb);
}
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Int16.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Int16.cs
index f866e6e5775..aa1ea30fbfb 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Int16.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Int16.cs
@@ -1,4 +1,4 @@
-// Licensed to the .NET Foundation under one or more agreements.
+// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Runtime.CompilerServices;
@@ -14,7 +14,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out short value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = default;
+ return;
+ }
+ value = slot.Int16Value;
}
/// <summary>
@@ -24,7 +29,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJS(short value)
{
- throw new NotImplementedException();
+ slot.Type = MarshalerType.Int16;
+ slot.Int16Value = value;
}
/// <summary>
@@ -34,7 +40,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out short? value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+ value = slot.Int16Value;
}
/// <summary>
@@ -44,7 +55,15 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJS(short? value)
{
- throw new NotImplementedException();
+ if (value.HasValue)
+ {
+ slot.Type = MarshalerType.Int16;
+ slot.Int16Value = value.Value;
+ }
+ else
+ {
+ slot.Type = MarshalerType.None;
+ }
}
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Int32.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Int32.cs
index aece8ae1db1..05ddab050c9 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Int32.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Int32.cs
@@ -1,4 +1,4 @@
-// Licensed to the .NET Foundation under one or more agreements.
+// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Runtime.CompilerServices;
@@ -14,7 +14,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out int value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = default;
+ return;
+ }
+ value = slot.Int32Value;
}
/// <summary>
@@ -24,7 +29,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJS(int value)
{
- throw new NotImplementedException();
+ slot.Type = MarshalerType.Int32;
+ slot.Int32Value = value;
}
/// <summary>
@@ -34,7 +40,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out int? value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+ value = slot.Int32Value;
}
/// <summary>
@@ -44,7 +55,15 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJS(int? value)
{
- throw new NotImplementedException();
+ if (value.HasValue)
+ {
+ slot.Type = MarshalerType.Int32;
+ slot.Int32Value = value.Value;
+ }
+ else
+ {
+ slot.Type = MarshalerType.None;
+ }
}
/// <summary>
@@ -53,7 +72,14 @@ namespace System.Runtime.InteropServices.JavaScript
/// </summary>
public unsafe void ToManaged(out int[]? value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+ value = new int[slot.Length];
+ Marshal.Copy(slot.IntPtrValue, value, 0, slot.Length);
+ Marshal.FreeHGlobal(slot.IntPtrValue);
}
/// <summary>
@@ -62,7 +88,16 @@ namespace System.Runtime.InteropServices.JavaScript
/// </summary>
public unsafe void ToJS(int[]? value)
{
- throw new NotImplementedException();
+ if (value == null)
+ {
+ slot.Type = MarshalerType.None;
+ return;
+ }
+ slot.Type = MarshalerType.Array;
+ slot.IntPtrValue = Marshal.AllocHGlobal(value.Length * sizeof(int));
+ slot.Length = value.Length;
+ slot.ElementType = MarshalerType.Int32;
+ Marshal.Copy(value, 0, slot.IntPtrValue, slot.Length);
}
/// <summary>
@@ -72,7 +107,10 @@ namespace System.Runtime.InteropServices.JavaScript
// this only supports array round-trip
public unsafe void ToManaged(out ArraySegment<int> value)
{
- throw new NotImplementedException();
+ var array = (int[])((GCHandle)slot.GCHandle).Target!;
+ var refPtr = (IntPtr)Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(array));
+ int byteOffset = (int)(slot.IntPtrValue - (nint)refPtr);
+ value = new ArraySegment<int>(array, byteOffset / sizeof(int), slot.Length);
}
/// <summary>
@@ -81,7 +119,16 @@ namespace System.Runtime.InteropServices.JavaScript
/// </summary>
public unsafe void ToJS(ArraySegment<int> value)
{
- throw new NotImplementedException();
+ if (value.Array == null)
+ {
+ slot.Type = MarshalerType.None;
+ return;
+ }
+ slot.Type = MarshalerType.ArraySegment;
+ slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandleRef(value.Array, GCHandleType.Pinned);
+ var refPtr = (IntPtr)Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(value.Array));
+ slot.IntPtrValue = refPtr + (value.Offset * sizeof(int));
+ slot.Length = value.Count;
}
/// <summary>
@@ -90,7 +137,7 @@ namespace System.Runtime.InteropServices.JavaScript
/// </summary>
public unsafe void ToManaged(out Span<int> value)
{
- throw new NotImplementedException();
+ value = new Span<int>((void*)slot.IntPtrValue, slot.Length);
}
/// <summary>
@@ -100,7 +147,9 @@ namespace System.Runtime.InteropServices.JavaScript
/// <remarks>caller is responsible for pinning</remarks>
public unsafe void ToJS(Span<int> value)
{
- throw new NotImplementedException();
+ slot.Length = value.Length;
+ slot.IntPtrValue = (IntPtr)Unsafe.AsPointer(ref value.GetPinnableReference());
+ slot.Type = MarshalerType.Span;
}
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Int52.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Int52.cs
index 1adbf33558b..a22902bf4e0 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Int52.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Int52.cs
@@ -7,6 +7,9 @@ namespace System.Runtime.InteropServices.JavaScript
{
public partial struct JSMarshalerArgument
{
+ private const long I52_MAX_VALUE =((1L << 53) - 1);
+ private const long I52_MIN_VALUE =-I52_MAX_VALUE;
+
/// <summary>
/// Implementation of the argument marshaling.
/// It's used by JSImport code generator and should not be used by developers in source code.
@@ -14,7 +17,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out long value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = default;
+ return;
+ }
+ value = (long)slot.DoubleValue;
}
/// <summary>
@@ -24,7 +32,13 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJS(long value)
{
- throw new NotImplementedException();
+ if (value < I52_MIN_VALUE || value > I52_MAX_VALUE)
+ {
+ throw new OverflowException($"Overflow: value ${value} is out of ${I52_MIN_VALUE} ${I52_MAX_VALUE} range");
+ }
+
+ slot.Type = MarshalerType.Int52;
+ slot.DoubleValue = value;
}
/// <summary>
@@ -34,7 +48,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out long? value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+ value = (long)slot.DoubleValue;
}
/// <summary>
@@ -44,7 +63,19 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJS(long? value)
{
- throw new NotImplementedException();
+ if (value.HasValue)
+ {
+ if (value.Value < I52_MIN_VALUE || value.Value > I52_MAX_VALUE)
+ {
+ throw new OverflowException($"Overflow: value ${value} is out of ${I52_MIN_VALUE} ${I52_MAX_VALUE} range");
+ }
+ slot.Type = MarshalerType.Int52;
+ slot.DoubleValue = value.Value;
+ }
+ else
+ {
+ slot.Type = MarshalerType.None;
+ }
}
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.IntPtr.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.IntPtr.cs
index 5944a6f7af8..88a9776ecad 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.IntPtr.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.IntPtr.cs
@@ -14,7 +14,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out IntPtr value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = default;
+ return;
+ }
+ value = slot.IntPtrValue;
}
/// <summary>
@@ -24,7 +29,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJS(IntPtr value)
{
- throw new NotImplementedException();
+ slot.Type = MarshalerType.IntPtr;
+ slot.IntPtrValue = value;
}
/// <summary>
@@ -34,7 +40,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out IntPtr? value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+ value = slot.IntPtrValue;
}
/// <summary>
@@ -44,7 +55,15 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJS(IntPtr? value)
{
- throw new NotImplementedException();
+ if (value.HasValue)
+ {
+ slot.Type = MarshalerType.IntPtr;
+ slot.IntPtrValue = value.Value;
+ }
+ else
+ {
+ slot.Type = MarshalerType.None;
+ }
}
/// <summary>
@@ -54,7 +73,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out void* value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = default;
+ return;
+ }
+ value = (byte*)slot.IntPtrValue;
}
/// <summary>
@@ -64,7 +88,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToJS(void* value)
{
- throw new NotImplementedException();
+ slot.Type = MarshalerType.IntPtr;
+ slot.IntPtrValue = (IntPtr)value;
}
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.JSObject.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.JSObject.cs
index 73f5de5501c..ea632d02db3 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.JSObject.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.JSObject.cs
@@ -5,7 +5,6 @@ using System.Runtime.CompilerServices;
namespace System.Runtime.InteropServices.JavaScript
{
- // TODO this is only JSObject now, should it handle Uint8Array C# private type too ?
public partial struct JSMarshalerArgument
{
/// <summary>
@@ -15,7 +14,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out JSObject? value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+ value = JavaScriptExports.CreateCSOwnedProxy(slot.JSHandle);
}
/// <summary>
@@ -25,7 +29,20 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJS(JSObject? value)
{
- throw new NotImplementedException();
+ if (value == null)
+ {
+ slot.Type = MarshalerType.None;
+ }
+ else
+ {
+ if (value.IsDisposed)
+ {
+ throw new ObjectDisposedException(nameof(value));
+ }
+ slot.Type = MarshalerType.JSObject;
+ slot.JSHandle = value.JSHandle;
+ if (slot.JSHandle == IntPtr.Zero) throw new ObjectDisposedException(nameof(value));
+ }
}
/// <summary>
@@ -35,7 +52,22 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out JSObject?[]? value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+
+ value = new JSObject?[slot.Length];
+ JSMarshalerArgument* payload = (JSMarshalerArgument*)slot.IntPtrValue;
+ for (int i = 0; i < slot.Length; i++)
+ {
+ ref JSMarshalerArgument arg = ref payload[i];
+ JSObject? val;
+ arg.ToManaged(out val);
+ value[i] = val;
+ }
+ Marshal.FreeHGlobal(slot.IntPtrValue);
}
/// <summary>
@@ -45,7 +77,25 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToJS(JSObject?[] value)
{
- throw new NotImplementedException();
+ if (value == null)
+ {
+ slot.Type = MarshalerType.None;
+ return;
+ }
+ slot.Length = value.Length;
+ int bytes = value.Length * Marshal.SizeOf(typeof(JSMarshalerArgument));
+ slot.Type = MarshalerType.Array;
+ slot.ElementType = MarshalerType.JSObject;
+ JSMarshalerArgument* payload = (JSMarshalerArgument*)Marshal.AllocHGlobal(bytes);
+ Unsafe.InitBlock(payload, 0, (uint)bytes);
+ for (int i = 0; i < slot.Length; i++)
+ {
+ ref JSMarshalerArgument arg = ref payload[i];
+ JSObject? val = value[i];
+ arg.ToJS(val);
+ value[i] = val;
+ }
+ slot.IntPtrValue = (IntPtr)payload;
}
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Object.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Object.cs
index db8a5b22a3b..46ff369eecc 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Object.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Object.cs
@@ -20,7 +20,88 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out object? value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = default;
+ }
+ else if (slot.Type == MarshalerType.Object)
+ {
+ value = ((GCHandle)slot.GCHandle).Target;
+ }
+ else if (slot.Type == MarshalerType.Boolean)
+ {
+ ToManaged(out bool v);
+ value = v;
+ }
+ else if (slot.Type == MarshalerType.Double)
+ {
+ ToManaged(out double v);
+ value = v;
+ }
+ else if (slot.Type == MarshalerType.JSObject)
+ {
+ ToManaged(out JSObject? val);
+ value = val;
+ }
+ else if (slot.Type == MarshalerType.String)
+ {
+ ToManaged(out string? val);
+ value = val;
+ }
+ else if (slot.Type == MarshalerType.Exception)
+ {
+ ToManaged(out Exception? val);
+ value = val;
+ }
+ else if (slot.Type == MarshalerType.DateTime)
+ {
+ ToManaged(out DateTime? val);
+ value = val;
+ }
+ else if (slot.Type == MarshalerType.JSException)
+ {
+ ToManaged(out Exception? val);
+ value = val;
+ }
+ else if (slot.Type == MarshalerType.Array)
+ {
+ if(slot.ElementType == MarshalerType.Byte)
+ {
+ ToManaged(out byte[]? val);
+ value = val;
+ }
+ else if (slot.ElementType == MarshalerType.Double)
+ {
+ ToManaged(out double[]? val);
+ value = val;
+ }
+ else if (slot.ElementType == MarshalerType.Int32)
+ {
+ ToManaged(out int[]? val);
+ value = val;
+ }
+ else if (slot.ElementType == MarshalerType.Object)
+ {
+ ToManaged(out object?[]? val);
+ value = val;
+ }
+ else
+ {
+ throw new NotImplementedException("ToManaged: " + slot.ElementType+ "[]");
+ }
+ }
+ else if (slot.Type == MarshalerType.Task)
+ {
+ ToManaged(out Task<object?>? val, static (ref JSMarshalerArgument arg, out object? value) =>
+ {
+ arg.ToManaged(out value);
+ });
+ value = val;
+ }
+ else
+ {
+ throw new NotImplementedException("ToManaged: " + slot.Type);
+ }
}
/// <summary>
@@ -30,7 +111,213 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJS(object? value)
{
- throw new NotImplementedException();
+ if (value == null)
+ {
+ slot.Type = MarshalerType.None;
+ return;
+ }
+
+ Type type = value.GetType();
+ if (type.IsPrimitive)
+ {
+ if (typeof(long) == type)
+ {
+ // we do it because not all Int64 could fit into Int52 of the JS Number
+ throw new NotImplementedException("ToJS: " + type.FullName);
+ }
+ else if (typeof(int) == type)
+ {
+ var v = (int)value;
+ ToJS(v);
+ }
+ else if (typeof(short) == type)
+ {
+ var v = (short)value;
+ ToJS(v);
+ }
+ else if (typeof(byte) == type)
+ {
+ var v = (byte)value;
+ ToJS(v);
+ }
+ else if (typeof(char) == type)
+ {
+ var v = (char)value;
+ ToJS(v);
+ }
+ else if (typeof(bool) == type)
+ {
+ var v = (bool)value;
+ ToJS(v);
+ }
+ else if (typeof(double) == type)
+ {
+ var v = (double)value;
+ ToJS(v);
+ }
+ else if (typeof(float) == type)
+ {
+ var v = (float)value;
+ ToJS(v);
+ }
+ else if (typeof(IntPtr) == type)
+ {
+ var v = (IntPtr)value;
+ ToJS(v);
+ }
+ else
+ {
+ throw new NotImplementedException("ToJS: " + type.FullName);
+ }
+ }
+ else if (typeof(string) == type)
+ {
+ string? str = value as string;
+ ToJS(str);
+ }
+ else if (typeof(DateTimeOffset) == type)
+ {
+ var v = (DateTimeOffset)value;
+ ToJS(v);
+ }
+ else if (typeof(DateTime) == type)
+ {
+ var v = (DateTime)value;
+ ToJS(v);
+ }
+ else if (Nullable.GetUnderlyingType(type) is Type ut && ut != null)
+ {
+ if (typeof(long) == ut)
+ {
+ // we do it because not all Int64 could fit into Int52 of the JS Number
+ throw new NotImplementedException("ToJS: " + type.FullName);
+ }
+ else if (typeof(int) == ut)
+ {
+ var nv = value as int?;
+ ToJS(nv);
+ }
+ else if (typeof(short) == ut)
+ {
+ var nv = value as short?;
+ ToJS(nv);
+ }
+ else if (typeof(byte) == ut)
+ {
+ var nv = value as byte?;
+ ToJS(nv);
+ }
+ else if (typeof(char) == ut)
+ {
+ var nv = value as char?;
+ ToJS(nv);
+ }
+ else if (typeof(bool) == ut)
+ {
+ var nv = value as bool?;
+ ToJS(nv);
+ }
+ else if (typeof(double) == ut)
+ {
+ var nv = value as double?;
+ ToJS(nv);
+ }
+ else if (typeof(float) == ut)
+ {
+ var nv = value as float?;
+ ToJS(nv);
+ }
+ else if (typeof(IntPtr) == ut)
+ {
+ var nv = value as IntPtr?;
+ ToJS(nv);
+ }
+ else if (typeof(DateTimeOffset) == ut)
+ {
+ var nv = value as DateTimeOffset?;
+ ToJS(nv);
+ }
+ else if (typeof(DateTime) == ut)
+ {
+ var nv = value as DateTime?;
+ ToJS(nv);
+ }
+ else
+ {
+ throw new NotImplementedException("ToJS: " + type.FullName);
+ }
+ }
+ else if (typeof(JSObject).IsAssignableFrom(type))
+ {
+ JSObject? val = value as JSObject;
+ ToJS(val);
+ }
+ else if (typeof(Exception).IsAssignableFrom(type))
+ {
+ Exception? val = value as Exception;
+ ToJS(val);
+ }
+ else if (typeof(Task<object>)==type)
+ {
+ Task<object>? val = value as Task<object>;
+ ToJS<object>(val, (ref JSMarshalerArgument arg, object value) =>
+ {
+ object? valueRef= value;
+ arg.ToJS(valueRef);
+ });
+ }
+ else if (typeof(Task).IsAssignableFrom(type))
+ {
+ Task? val = value as Task;
+ ToJSDynamic(val);
+ }
+ else if (typeof(byte[]) == type)
+ {
+ byte[] val = (byte[])value;
+ ToJS(val);
+ slot.ElementType = MarshalerType.Byte;
+ }
+ else if (typeof(int[]) == type)
+ {
+ int[] val = (int[])value;
+ ToJS(val);
+ }
+ else if (typeof(double[]) == type)
+ {
+ double[] val = (double[])value;
+ ToJS(val);
+ }
+ else if (typeof(string[]) == type)
+ {
+ string[] val = (string[])value;
+ ToJS(val);
+ }
+ else if (typeof(object[]) == type)
+ {
+ object[] val = (object[])value;
+ ToJS(val);
+ }
+ else if (type.IsArray)
+ {
+ throw new NotImplementedException("ToJS: " + type.FullName);
+ }
+ else if (typeof(MulticastDelegate).IsAssignableFrom(type.BaseType))
+ {
+ throw new NotImplementedException("ToJS: " + type.FullName);
+ }
+ else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ArraySegment<>))
+ {
+ throw new NotImplementedException("ToJS: " + type.FullName);
+ }
+ else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Span<>))
+ {
+ throw new NotImplementedException("ToJS: " + type.FullName);
+ }
+ else
+ {
+ slot.Type = MarshalerType.Object;
+ slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(value);
+ }
}
/// <summary>
@@ -40,8 +327,23 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out object?[]? value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+ value = new object?[slot.Length];
+ JSMarshalerArgument* payload = (JSMarshalerArgument*)slot.IntPtrValue;
+ for (int i = 0; i < slot.Length; i++)
+ {
+ ref JSMarshalerArgument arg = ref payload[i];
+ object? val;
+ arg.ToManaged(out val);
+ value[i] = val;
+ }
+ Interop.Runtime.DeregisterGCRoot(slot.IntPtrValue);
+ Marshal.FreeHGlobal(slot.IntPtrValue);
}
/// <summary>
@@ -51,7 +353,26 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToJS(object?[] value)
{
- throw new NotImplementedException();
+ if (value == null)
+ {
+ slot.Type = MarshalerType.None;
+ return;
+ }
+ slot.Length = value.Length;
+ int bytes = value.Length * Marshal.SizeOf(typeof(JSMarshalerArgument));
+ slot.Type = MarshalerType.Array;
+ JSMarshalerArgument* payload = (JSMarshalerArgument*)Marshal.AllocHGlobal(bytes);
+ Unsafe.InitBlock(payload, 0, (uint)bytes);
+ Interop.Runtime.RegisterGCRoot((IntPtr)payload, bytes, IntPtr.Zero);
+ for (int i = 0; i < slot.Length; i++)
+ {
+ ref JSMarshalerArgument arg = ref payload[i];
+ object? val = value[i];
+ arg.ToJS(val);
+ value[i] = val;
+ }
+ slot.ElementType = MarshalerType.Object;
+ slot.IntPtrValue = (IntPtr)payload;
}
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Single.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Single.cs
index a83f3ee9ac3..08dfe11ed37 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Single.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Single.cs
@@ -14,7 +14,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out float value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = default;
+ return;
+ }
+ value = slot.SingleValue;
}
/// <summary>
@@ -24,7 +29,8 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJS(float value)
{
- throw new NotImplementedException();
+ slot.Type = MarshalerType.Single;
+ slot.SingleValue = value;
}
/// <summary>
@@ -34,7 +40,12 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out float? value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+ value = slot.SingleValue;
}
/// <summary>
@@ -44,7 +55,15 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToJS(float? value)
{
- throw new NotImplementedException();
+ if (value.HasValue)
+ {
+ slot.Type = MarshalerType.Single;
+ slot.SingleValue = value.Value;
+ }
+ else
+ {
+ slot.Type = MarshalerType.None;
+ }
}
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.String.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.String.cs
index de1b70acda4..afabd54c14e 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.String.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.String.cs
@@ -14,7 +14,16 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out string? value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+
+ fixed (void* argAsRoot = &slot.IntPtrValue)
+ {
+ value = Unsafe.AsRef<string>(argAsRoot);
+ }
}
/// <summary>
@@ -24,7 +33,24 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToJS(string? value)
{
- throw new NotImplementedException();
+ if (value == null)
+ {
+ slot.Type = MarshalerType.None;
+ }
+ else
+ {
+ slot.Type = MarshalerType.String;
+ // here we treat JSMarshalerArgument.IntPtrValue as root, because it's allocated on stack
+ // or we register the buffer with JSFunctionBinding._RegisterGCRoot
+ // We assume that GC would keep updating on GC move
+ // On JS side we wrap it with WasmExternalRoot
+ fixed (IntPtr* argAsRoot = &slot.IntPtrValue)
+ {
+ string cpy = value;
+ var currentRoot = (IntPtr*)Unsafe.AsPointer(ref cpy);
+ argAsRoot[0] = currentRoot[0];
+ }
+ }
}
/// <summary>
@@ -34,7 +60,23 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToManaged(out string?[]? value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+
+ value = new string?[slot.Length];
+ JSMarshalerArgument* payload = (JSMarshalerArgument*)slot.IntPtrValue;
+ for (int i = 0; i < slot.Length; i++)
+ {
+ ref JSMarshalerArgument arg = ref payload[i];
+ string? val;
+ arg.ToManaged(out val);
+ value[i] = val;
+ }
+ Interop.Runtime.DeregisterGCRoot(slot.IntPtrValue);
+ Marshal.FreeHGlobal(slot.IntPtrValue);
}
/// <summary>
@@ -44,7 +86,26 @@ namespace System.Runtime.InteropServices.JavaScript
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ToJS(string?[] value)
{
- throw new NotImplementedException();
+ if (value == null)
+ {
+ slot.Type = MarshalerType.None;
+ return;
+ }
+ slot.Length = value.Length;
+ int bytes = value.Length * Marshal.SizeOf(typeof(JSMarshalerArgument));
+ slot.Type = MarshalerType.Array;
+ JSMarshalerArgument* payload = (JSMarshalerArgument*)Marshal.AllocHGlobal(bytes);
+ Unsafe.InitBlock(payload, 0, (uint)bytes);
+ Interop.Runtime.RegisterGCRoot((IntPtr)payload, bytes, IntPtr.Zero);
+ for (int i = 0; i < slot.Length; i++)
+ {
+ ref JSMarshalerArgument arg = ref payload[i];
+ string? val = value[i];
+ arg.ToJS(val);
+ value[i] = val;
+ }
+ slot.IntPtrValue = (IntPtr)payload;
+ slot.ElementType = MarshalerType.String;
}
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs
index 8ee70ae7dfd..608443cc45c 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs
@@ -25,18 +25,163 @@ namespace System.Runtime.InteropServices.JavaScript
/// Implementation of the argument marshaling.
/// It's used by JSImport code generator and should not be used by developers in source code.
/// </summary>
- public unsafe void ToManaged(out Task value)
+ public unsafe void ToManaged(out Task? value)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+
+ GCHandle gcHandle = (GCHandle)slot.GCHandle;
+ JSHostImplementation.TaskCallback? holder = (JSHostImplementation.TaskCallback?)gcHandle.Target;
+ if (holder == null) throw new NullReferenceException("JSHostImplementation.TaskCallback");
+
+ TaskCompletionSource tcs = new TaskCompletionSource(gcHandle);
+ JSHostImplementation.ToManagedCallback callback = (JSMarshalerArgument* arguments_buffer) =>
+ {
+ ref JSMarshalerArgument arg_exception = ref arguments_buffer[0];
+ try
+ {
+ if (arg_exception.slot.Type != MarshalerType.None)
+ {
+ arg_exception.ToManaged(out Exception? fail);
+ tcs.SetException(fail!);
+ }
+ else
+ {
+ tcs.SetResult();
+ }
+ arg_exception.slot.Type = MarshalerType.None;
+ }
+ catch (Exception ex)
+ {
+ arg_exception.ToJS(ex);
+ }
+ };
+ holder.Callback = callback;
+ value = tcs.Task;
}
/// <summary>
/// Implementation of the argument marshaling.
/// It's used by JSImport code generator and should not be used by developers in source code.
/// </summary>
- public unsafe void ToManaged<T>(out Task<T> value, ArgumentToManagedCallback<T> marshaler)
+ public unsafe void ToManaged<T>(out Task<T>? value, ArgumentToManagedCallback<T> marshaler)
{
- throw new NotImplementedException();
+ if (slot.Type == MarshalerType.None)
+ {
+ value = null;
+ return;
+ }
+
+ GCHandle gcHandle = (GCHandle)slot.GCHandle;
+ JSHostImplementation.TaskCallback? holder = (JSHostImplementation.TaskCallback?)gcHandle.Target;
+ if (holder == null) throw new NullReferenceException("JSHostImplementation.TaskCallback");
+
+ TaskCompletionSource<T> tcs = new TaskCompletionSource<T>(gcHandle);
+ JSHostImplementation.ToManagedCallback callback = (JSMarshalerArgument* arguments_buffer) =>
+ {
+ ref JSMarshalerArgument arg_exception = ref arguments_buffer[0];
+ ref JSMarshalerArgument arg1 = ref arguments_buffer[2];
+ try
+ {
+ if (arg_exception.slot.Type != MarshalerType.None)
+ {
+ arg_exception.ToManaged(out Exception? fail);
+ if (fail == null) throw new NullReferenceException("Exception");
+ tcs.SetException(fail);
+ }
+ else
+ {
+ marshaler(ref arg1, out T result);
+ tcs.SetResult(result);
+ }
+ arg_exception.slot.Type = MarshalerType.None;
+ }
+ catch (Exception ex)
+ {
+ arg_exception.ToJS(ex);
+ }
+ };
+ holder.Callback = callback;
+ value = tcs.Task;
+ }
+
+ internal void ToJSDynamic(Task? value)
+ {
+ Task? task = value;
+
+ if (task == null)
+ {
+ slot.Type = MarshalerType.None;
+ return;
+ }
+ slot.Type = MarshalerType.Task;
+
+ if (task.IsCompleted)
+ {
+ if (task.Exception != null)
+ {
+ Exception ex = task.Exception;
+ slot.JSHandle = CreateFailedPromise(ex);
+ return;
+ }
+ else
+ {
+ object? result = JSHostImplementation.GetTaskResult(task);
+ slot.JSHandle = CreateResolvedPromise(result, MarshalResult);
+ return;
+ }
+ }
+
+
+ IntPtr jsHandle = CreatePendingPromise();
+ slot.JSHandle = jsHandle;
+ JSObject promise = JavaScriptExports.CreateCSOwnedProxy(jsHandle);
+
+ task.GetAwaiter().OnCompleted(Complete);
+
+ /* TODO multi-threading
+ * tasks could resolve on any thread and so this code will have race condition between task.IsCompleted and OnCompleted(Complete) callback
+ * This probably needs SynchronizationContext to marshal this call to main thread
+ */
+ Debug.Assert(!task.IsCompleted, "multithreading race condition");
+
+ void Complete()
+ {
+ // When this task was never resolved/rejected
+ // promise (held by this lambda) would be collected by GC after the Task is collected
+ // and would also allow the JS promise to be collected
+
+ try
+ {
+ if (task.Exception != null)
+ {
+ FailPromise(promise, task.Exception);
+ }
+ else
+ {
+ object? result = JSHostImplementation.GetTaskResult(task);
+
+ ResolvePromise(promise, result, MarshalResult);
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new InvalidProgramException(ex.Message, ex);
+ }
+ finally
+ {
+ // this should never happen after the task was GC'd
+ promise.Dispose();
+ }
+ }
+
+ static void MarshalResult(ref JSMarshalerArgument arg, object? taskResult)
+ {
+ arg.ToJS(taskResult);
+ }
}
/// <summary>
@@ -45,16 +190,283 @@ namespace System.Runtime.InteropServices.JavaScript
/// </summary>
public void ToJS(Task value)
{
- throw new NotImplementedException();
+ Task task = value;
+
+ if (task == null)
+ {
+ slot.Type = MarshalerType.None;
+ return;
+ }
+ slot.Type = MarshalerType.Task;
+
+ if (task.IsCompleted)
+ {
+ if (task.Exception != null)
+ {
+ Exception ex = task.Exception;
+ slot.JSHandle = CreateFailedPromise(ex);
+ return;
+ }
+ else
+ {
+ slot.JSHandle = IntPtr.Zero;
+ return;
+ }
+ }
+
+ IntPtr jsHandle = CreatePendingPromise();
+ slot.JSHandle = jsHandle;
+ JSObject promise = JavaScriptExports.CreateCSOwnedProxy(jsHandle);
+
+ task.GetAwaiter().OnCompleted(Complete);
+
+ /* TODO multi-threading
+ * tasks could resolve on any thread and so this code will have race condition between task.IsCompleted and OnCompleted(Complete) callback
+ * This probably needs SynchronizationContext to marshal this call to main thread
+ */
+ Debug.Assert(!task.IsCompleted, "multithreading race condition");
+
+ void Complete()
+ {
+ // When this task was never resolved/rejected
+ // promise (held by this lambda) would be collected by GC after the Task is collected
+ // and would also allow the JS promise to be collected
+
+ try
+ {
+ if (task.Exception != null)
+ {
+ FailPromise(promise, task.Exception);
+ }
+ else
+ {
+ ResolveVoidPromise(promise);
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new InvalidProgramException(ex.Message, ex);
+ }
+ finally
+ {
+ // this should never happen after the task was GC'd
+ promise.Dispose();
+ }
+ }
}
/// <summary>
/// Implementation of the argument marshaling.
/// It's used by JSImport code generator and should not be used by developers in source code.
/// </summary>
- public void ToJS<T>(Task<T> value, ArgumentToJSCallback<T> marshaler)
+ public void ToJS<T>(Task<T>? value, ArgumentToJSCallback<T> marshaler)
+ {
+ Task<T>? task = value;
+
+ if (task == null)
+ {
+ slot.Type = MarshalerType.None;
+ return;
+ }
+ slot.Type = MarshalerType.Task;
+
+ if (task.IsCompleted)
+ {
+ if (task.Exception != null)
+ {
+ Exception ex = task.Exception;
+ slot.JSHandle = CreateFailedPromise(ex);
+ return;
+ }
+ else
+ {
+ T result = task.Result;
+ slot.JSHandle = CreateResolvedPromise(result, marshaler);
+ return;
+ }
+ }
+
+
+ IntPtr jsHandle = CreatePendingPromise();
+ slot.JSHandle = jsHandle;
+ JSObject promise = JavaScriptExports.CreateCSOwnedProxy(jsHandle);
+
+ task.GetAwaiter().OnCompleted(Complete);
+
+ /* TODO multi-threading
+ * tasks could resolve on any thread and so this code will have race condition between task.IsCompleted and OnCompleted(Complete) callback
+ * This probably needs SynchronizationContext to marshal this call to main thread
+ */
+ Debug.Assert(!task.IsCompleted, "multithreading race condition");
+
+ void Complete()
+ {
+ // When this task was never resolved/rejected
+ // promise (held by this lambda) would be collected by GC after the Task is collected
+ // and would also allow the JS promise to be collected
+
+ try
+ {
+ if (task.Exception != null)
+ {
+ FailPromise(promise, task.Exception);
+ }
+ else
+ {
+ T result = task.Result;
+ ResolvePromise(promise, result, marshaler);
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new InvalidProgramException(ex.Message, ex);
+ }
+ finally
+ {
+ // this should never happen after the task was GC'd
+ promise.Dispose();
+ }
+ }
+ }
+
+ private static IntPtr CreatePendingPromise()
{
- throw new NotImplementedException();
+ Span<JSMarshalerArgument> args = stackalloc JSMarshalerArgument[4];
+ ref JSMarshalerArgument exc = ref args[0];
+ ref JSMarshalerArgument res = ref args[1];
+ ref JSMarshalerArgument arg_handle = ref args[2];
+ ref JSMarshalerArgument arg_value = ref args[3];
+
+ exc.Initialize();
+ res.Initialize();
+ arg_value.Initialize();
+
+ // should create new promise
+ arg_handle.slot.Type = MarshalerType.Task;
+ arg_handle.slot.JSHandle = IntPtr.Zero;
+ arg_value.slot.Type = MarshalerType.Task;
+
+ JavaScriptImports.MarshalPromise(args);
+ return res.slot.JSHandle;
+ }
+
+ private static IntPtr CreateFailedPromise(Exception ex)
+ {
+ Span<JSMarshalerArgument> args = stackalloc JSMarshalerArgument[4];
+ ref JSMarshalerArgument exc = ref args[0];
+ ref JSMarshalerArgument res = ref args[1];
+ ref JSMarshalerArgument arg_handle = ref args[2];
+ ref JSMarshalerArgument arg_value = ref args[3];
+ res.Initialize();
+ arg_value.Initialize();
+
+ // should create new promise
+ arg_handle.slot.Type = MarshalerType.Task;
+ arg_handle.slot.JSHandle = IntPtr.Zero;
+ // should fail it with exception
+ exc.ToJS(ex);
+ JavaScriptImports.MarshalPromise(args);
+ return res.slot.JSHandle;
+ }
+
+ private static void FailPromise(JSObject promise, Exception ex)
+ {
+ if (promise.IsDisposed)
+ {
+ throw new ObjectDisposedException(nameof(promise));
+ }
+
+ Span<JSMarshalerArgument> args = stackalloc JSMarshalerArgument[4];
+ ref JSMarshalerArgument exc = ref args[0];
+ ref JSMarshalerArgument res = ref args[1];
+ ref JSMarshalerArgument arg_handle = ref args[2];
+ ref JSMarshalerArgument arg_value = ref args[3];
+
+ exc.Initialize();
+ res.Initialize();
+ arg_value.Initialize();
+
+ // should update existing promise
+ arg_handle.slot.Type = MarshalerType.None;
+ arg_handle.slot.JSHandle = promise.JSHandle;
+
+ // should fail it with exception
+ exc.ToJS(ex);
+
+ JavaScriptImports.MarshalPromise(args);
+ }
+
+ private static IntPtr CreateResolvedPromise<T>(T value, ArgumentToJSCallback<T> marshaler)
+ {
+ Span<JSMarshalerArgument> args = stackalloc JSMarshalerArgument[4];
+ ref JSMarshalerArgument exc = ref args[0];
+ ref JSMarshalerArgument res = ref args[1];
+ ref JSMarshalerArgument arg_handle = ref args[2];
+ ref JSMarshalerArgument arg_value = ref args[3];
+
+ exc.Initialize();
+ res.Initialize();
+
+ // should create new promise
+ arg_handle.slot.Type = MarshalerType.Task;
+ arg_handle.slot.JSHandle = IntPtr.Zero;
+
+ // and resolve it with value
+ marshaler(ref arg_value, value);
+
+ JavaScriptImports.MarshalPromise(args);
+ return res.slot.JSHandle;
+ }
+
+ private static void ResolveVoidPromise(JSObject promise)
+ {
+ if (promise.IsDisposed)
+ {
+ throw new ObjectDisposedException(nameof(promise));
+ }
+
+ Span<JSMarshalerArgument> args = stackalloc JSMarshalerArgument[4];
+ ref JSMarshalerArgument exc = ref args[0];
+ ref JSMarshalerArgument res = ref args[1];
+ ref JSMarshalerArgument arg_handle = ref args[2];
+ ref JSMarshalerArgument arg_value = ref args[3];
+
+ exc.Initialize();
+ res.Initialize();
+
+ // should update existing promise
+ arg_handle.slot.Type = MarshalerType.None;
+ arg_handle.slot.JSHandle = promise.JSHandle;
+
+ arg_value.slot.Type = MarshalerType.None;
+
+ JavaScriptImports.MarshalPromise(args);
+ }
+
+ private static void ResolvePromise<T>(JSObject promise, T value, ArgumentToJSCallback<T> marshaler)
+ {
+ if (promise.IsDisposed)
+ {
+ throw new ObjectDisposedException(nameof(promise));
+ }
+
+ Span<JSMarshalerArgument> args = stackalloc JSMarshalerArgument[4];
+ ref JSMarshalerArgument exc = ref args[0];
+ ref JSMarshalerArgument res = ref args[1];
+ ref JSMarshalerArgument arg_handle = ref args[2];
+ ref JSMarshalerArgument arg_value = ref args[3];
+
+ exc.Initialize();
+ res.Initialize();
+
+ // should update existing promise
+ arg_handle.slot.Type = MarshalerType.None;
+ arg_handle.slot.JSHandle = promise.JSHandle;
+
+ // and resolve it with value
+ marshaler(ref arg_value, value);
+
+ JavaScriptImports.MarshalPromise(args);
}
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/JSImportGenerator.UnitTest/Compiles.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/JSImportGenerator.UnitTest/Compiles.cs
index 73c688df6ce..003881e5c01 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/JSImportGenerator.UnitTest/Compiles.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/JSImportGenerator.UnitTest/Compiles.cs
@@ -39,7 +39,7 @@ namespace JSImportGenerator.Unit.Tests
new Microsoft.Interop.JavaScript.JSImportGenerator(),
new Microsoft.Interop.JavaScript.JSExportGenerator());
- JSTestUtils.DumpCode(source, newComp, generatorDiags);
+ // uncomment for debugging JSTestUtils.DumpCode(source, newComp, generatorDiags);
Assert.Empty(generatorDiags);
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/JSImportGenerator.UnitTest/Fails.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/JSImportGenerator.UnitTest/Fails.cs
index f0f2f1ddb7f..c7f98039414 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/JSImportGenerator.UnitTest/Fails.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/JSImportGenerator.UnitTest/Fails.cs
@@ -61,7 +61,7 @@ namespace JSImportGenerator.Unit.Tests
var newComp = TestUtils.RunGenerators(comp, out var generatorDiags,
new Microsoft.Interop.JavaScript.JSImportGenerator(),
new Microsoft.Interop.JavaScript.JSExportGenerator());
- JSTestUtils.DumpCode(source, newComp, generatorDiags);
+ // uncomment for debugging JSTestUtils.DumpCode(source, newComp, generatorDiags);
if (generatorMessages != null)
{
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/JSImportGenerator.UnitTest/JSImportGenerator.Unit.Tests.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/JSImportGenerator.UnitTest/JSImportGenerator.Unit.Tests.csproj
index 0e254bcb909..73d8ac230d7 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/JSImportGenerator.UnitTest/JSImportGenerator.Unit.Tests.csproj
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/JSImportGenerator.UnitTest/JSImportGenerator.Unit.Tests.csproj
@@ -1,11 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
-
<PropertyGroup>
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
<Nullable>enable</Nullable>
<TestRunRequiresLiveRefPack>true</TestRunRequiresLiveRefPack>
</PropertyGroup>
-
<ItemGroup>
<Compile Include="$(CommonTestPath)SourceGenerators\LiveReferencePack.cs" Link="Common\SourceGenerators\LiveReferencePack.cs" />
<Compile Include="$(LibrariesProjectRoot)System.Runtime.InteropServices\tests\LibraryImportGenerator.UnitTests\TestUtils.cs" Link="LibraryImportGenerator\TestUtils.cs" />
@@ -14,19 +12,12 @@
<Compile Include="Compiles.cs" />
<Compile Include="JSTestUtils.cs" />
</ItemGroup>
-
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="$(MicrosoftCodeAnalysisVersion)" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="$(CompilerPlatformTestingVersion)" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="$(CompilerPlatformTestingVersion)" />
- </ItemGroup>
-
- <ItemGroup>
- <ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices\tests\Ancillary.Interop\Ancillary.Interop.csproj" />
+ <ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices\tests\Ancillary.Interop\Ancillary.Interop.csproj" />
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\gen\JSImportGenerator\JSImportGenerator.csproj" />
- </ItemGroup>
-
- <ItemGroup>
<None Include="$(RepoRoot)/NuGet.config" Link="NuGet.config" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Project>
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Tests.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Tests.csproj
new file mode 100644
index 00000000000..e5e80df11f2
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Tests.csproj
@@ -0,0 +1,24 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <TargetFrameworks>$(NetCoreAppCurrent)-Browser</TargetFrameworks>
+ <TestRuntime>true</TestRuntime>
+ <WasmXHarnessArgs>$(WasmXHarnessArgs) --engine-arg=--expose-gc --web-server-use-cop</WasmXHarnessArgs>
+ <!-- Use following lines to write the generated files to disk. -->
+ <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="System\Runtime\InteropServices\JavaScript\JavaScriptTestHelper.cs" />
+ <Compile Include="System\Runtime\InteropServices\JavaScript\JSImportExportTest.cs" />
+ <Compile Include="System\Runtime\InteropServices\JavaScript\Utils.cs" />
+ <None Include="System\Runtime\InteropServices\JavaScript\JavaScriptTestHelper.mjs" />
+ <None Include="$(CompilerGeneratedFilesOutputPath)\Microsoft.Interop.JavaScript.JSImportGenerator\Microsoft.Interop.JavaScript.JSImportGenerator\JSImports.g.cs" />
+ <None Include="$(CompilerGeneratedFilesOutputPath)\Microsoft.Interop.JavaScript.JSImportGenerator\Microsoft.Interop.JavaScript.JsExportGenerator\JSExports.g.cs" />
+ <WasmExtraFilesToDeploy Include="System\Runtime\InteropServices\JavaScript\JavaScriptTestHelper.mjs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\gen\JSImportGenerator\JSImportGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
+ <ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices\gen\Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
+ <ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\src\System.Runtime.InteropServices.JavaScript.csproj" />
+ </ItemGroup>
+</Project>
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs
new file mode 100644
index 00000000000..b2595736311
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs
@@ -0,0 +1,1929 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Xunit;
+#pragma warning disable xUnit1026 // Theory methods should use all of their parameters
+
+namespace System.Runtime.InteropServices.JavaScript.Tests
+{
+ public class JSImportExportTest : IAsyncLifetime
+ {
+ [Fact]
+ public unsafe void StructSize()
+ {
+ Assert.Equal(16, sizeof(JSMarshalerArgument));
+ }
+
+ [Fact]
+ public unsafe void GlobalThis()
+ {
+ Assert.Null(JSHost.GlobalThis.GetPropertyAsString("dummy"));
+ Assert.False(JSHost.GlobalThis.HasProperty("dummy"));
+ Assert.Equal("undefined", JSHost.GlobalThis.GetTypeOfProperty("dummy"));
+ Assert.Equal("function", JSHost.GlobalThis.GetTypeOfProperty("Array"));
+ Assert.NotNull(JSHost.GlobalThis.GetPropertyAsJSObject("javaScriptTestHelper"));
+ }
+
+ [Fact]
+ public unsafe void DotnetInstance()
+ {
+ Assert.True(JSHost.DotnetInstance.HasProperty("MONO"));
+ Assert.Equal("object", JSHost.DotnetInstance.GetTypeOfProperty("MONO"));
+
+ JSHost.DotnetInstance.SetProperty("testBool", true);
+ Assert.Equal("boolean", JSHost.DotnetInstance.GetTypeOfProperty("testBool"));
+
+ JSHost.DotnetInstance.SetProperty("testInt", 42);
+ Assert.Equal("number", JSHost.DotnetInstance.GetTypeOfProperty("testInt"));
+ Assert.Equal(42, JSHost.DotnetInstance.GetPropertyAsInt32("testInt"));
+
+ JSHost.DotnetInstance.SetProperty("testDouble", 3.14);
+ Assert.Equal("number", JSHost.DotnetInstance.GetTypeOfProperty("testDouble"));
+ Assert.Equal(3.14, JSHost.DotnetInstance.GetPropertyAsDouble("testDouble"));
+
+ JSHost.DotnetInstance.SetProperty("testString", "Yoda");
+ Assert.Equal("string", JSHost.DotnetInstance.GetTypeOfProperty("testString"));
+ Assert.Equal("Yoda", JSHost.DotnetInstance.GetPropertyAsString("testString"));
+ }
+
+ [Fact]
+ public unsafe void BadCast()
+ {
+ JSException ex;
+ JSHost.DotnetInstance.SetProperty("testBool", true);
+ ex = Assert.Throws<JSException>(() => JSHost.DotnetInstance.GetPropertyAsInt32("testBool"));
+ Assert.Contains("Value is not an integer", ex.Message);
+ ex = Assert.Throws<JSException>(() => JSHost.DotnetInstance.GetPropertyAsDouble("testBool"));
+ Assert.Contains("Value is not a Number", ex.Message);
+ ex = Assert.Throws<JSException>(() => JSHost.DotnetInstance.GetPropertyAsString("testBool"));
+ Assert.Contains("Value is not a String", ex.Message);
+ ex = Assert.Throws<JSException>(() => JSHost.DotnetInstance.GetPropertyAsJSObject("testBool"));
+ Assert.Contains("JSObject proxy of boolean is not supported", ex.Message);
+ ex = Assert.Throws<JSException>(() => JSHost.DotnetInstance.GetPropertyAsByteArray("testBool"));
+ Assert.Contains("Value is not an Array or Uint8Array", ex.Message);
+ JSHost.DotnetInstance.SetProperty("testInt", 42);
+ ex = Assert.Throws<JSException>(() => JSHost.DotnetInstance.GetPropertyAsBoolean("testInt"));
+ Assert.Contains("Value is not a Boolean", ex.Message);
+ }
+
+ [Fact]
+ public unsafe void OutOfRange()
+ {
+ JSException ex;
+ JSHost.DotnetInstance.SetProperty("testDouble", 9007199254740991L);
+ ex = Assert.Throws<JSException>(() => JSHost.DotnetInstance.GetPropertyAsInt32("testDouble"));
+ Assert.Contains("Overflow: value 9007199254740991 is out of -2147483648 2147483647 range", ex.Message);
+ }
+
+
+ #region Get/Set Property
+
+ [Fact]
+ public unsafe void JSObjectGetSet()
+ {
+ Func<double, JSObject> createObject = Utils.CreateFunctionDoubleJSObject("a", @"
+ var x = {a, x:42 };
+ return x;
+ ");
+ JSObject obj = createObject(1);
+ Assert.NotNull(obj);
+ double? a = obj.GetPropertyAsDouble("a");
+ Assert.Equal(1, a);
+
+ double? x = obj.GetPropertyAsDouble("x");
+ Assert.Equal(42, x);
+
+ int? xi = obj.GetPropertyAsInt32("x");
+ Assert.Equal(42, xi);
+
+ /*
+ obj.GetProperty("x", out string? xs);
+ Assert.Equal("42", xs);
+ */
+
+ obj.SetProperty("b", 3);
+ double? b = obj.GetPropertyAsDouble("b");
+ Assert.Equal(3, b);
+
+ obj.SetProperty("c", "test");
+ string? c = obj.GetPropertyAsString("c");
+ Assert.Equal("test", c);
+
+ obj.SetProperty("c", (string)null);
+ string? d = obj.GetPropertyAsString("c");
+ Assert.Null(d);
+ }
+
+ #endregion
+
+ #region CreateFunction
+
+ [Fact]
+ public unsafe void CreateFunctionDouble()
+ {
+ Func<double, double, double> doublePlus = Utils.CreateFunctionDoubleDoubleDouble("a", "b", "return a+b");
+ Assert.Equal(3, doublePlus(1, 2));
+ Assert.Equal(Math.PI * 2, doublePlus(Math.PI, Math.PI));
+ }
+
+ [Fact]
+ public unsafe void CreateFunctionDoubleThrow()
+ {
+ Func<double, double, double> doubleThrows = Utils.CreateFunctionDoubleDoubleDouble("a", "b", "throw Error('test '+a+' '+b);");
+ var ex = Assert.Throws<JSException>(() => doubleThrows(1, 2));
+ Assert.Equal("Error: test 1 2", ex.Message);
+ Assert.Contains("create_function", ex.StackTrace);
+ }
+
+ [Fact]
+ public unsafe void CreateFunctionString()
+ {
+ Func<string, string, string> stringPlus = Utils.CreateFunctionStringStringString("a", "b", "return a+b");
+ Assert.Equal("hello world", stringPlus("hello ", "world"));
+ Assert.Equal("hellonull", stringPlus("hello", null));
+ }
+
+ [Fact]
+ public unsafe void CreateFunctionInternal()
+ {
+ Func<string> internals = Utils.CreateFunctionString("return INTERNAL.BINDING_ASM");
+ Assert.Equal("[System.Runtime.InteropServices.JavaScript]System.Runtime.InteropServices.JavaScript.JavaScriptExports", internals());
+ }
+
+ #endregion
+
+ #region Arrays
+
+ public static IEnumerable<object[]> MarshalByteArrayCases()
+ {
+ yield return new object[] { new byte[] { 1, 2, 3, byte.MaxValue, byte.MinValue } };
+ yield return new object[] { new byte[] { } };
+ yield return new object[] { null };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalByteArrayCases))]
+ public unsafe void JsImportByteArray(byte[]? expected)
+ {
+ var actual = JavaScriptTestHelper.echo1_ByteArray(expected);
+ Assert.Equal(expected, actual);
+ if (expected != null) for (int i = 0; i < expected.Length; i++)
+ {
+ var actualI = JavaScriptTestHelper.store_ByteArray(expected, i);
+ Assert.Equal(expected[i], actualI);
+ }
+ }
+
+ public static IEnumerable<object[]> MarshalIntArrayCases()
+ {
+ yield return new object[] { new int[] { 1, 2, 3, int.MaxValue, int.MinValue } };
+ yield return new object[] { new int[] { } };
+ yield return new object[] { null };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalIntArrayCases))]
+ public unsafe void JsImportIntArray(int[]? expected)
+ {
+ var actual = JavaScriptTestHelper.echo1_Int32Array(expected);
+ Assert.Equal(expected, actual);
+ if (expected != null) for (int i = 0; i < expected.Length; i++)
+ {
+ var actualI = JavaScriptTestHelper.store_Int32Array(expected, i);
+ Assert.Equal(expected[i], actualI);
+ }
+ }
+
+ public static IEnumerable<object[]> MarshalDoubleArrayCases()
+ {
+ yield return new object[] { new double[] { 1, 2, 3, double.MaxValue, double.MinValue, double.Pi, double.NegativeInfinity, double.PositiveInfinity, double.NaN } };
+ yield return new object[] { new double[] { } };
+ yield return new object[] { null };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalDoubleArrayCases))]
+ public unsafe void JsImportDoubleArray(double[]? expected)
+ {
+ var actual = JavaScriptTestHelper.echo1_DoubleArray(expected);
+ Assert.Equal(expected, actual);
+ if (expected != null) for (int i = 0; i < expected.Length; i++)
+ {
+ var actualI = JavaScriptTestHelper.store_DoubleArray(expected, i);
+ Assert.Equal(expected[i], actualI);
+ }
+ }
+
+ public static IEnumerable<object[]> MarshalStringArrayCases()
+ {
+ yield return new object[] { new string[] { "\u0050\u0159\u00ed\u006c\u0069\u0161", "\u017e\u006c\u0075\u0165\u006f\u0075\u010d\u006b\u00fd" } };
+ yield return new object[] { new string[] { string.Intern("hello"), string.Empty, null } };
+ yield return new object[] { new string[] { } };
+ yield return new object[] { null };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalStringArrayCases))]
+ public unsafe void JsImportStringArray(string[]? expected)
+ {
+ var actual = JavaScriptTestHelper.echo1_StringArray(expected);
+ Assert.Equal(expected, actual);
+
+ if (expected != null) for (int i = 0; i < expected.Length; i++)
+ {
+ var actualI = JavaScriptTestHelper.store_StringArray(expected, i);
+ Assert.Equal(expected[i], actualI);
+ }
+ }
+
+ public class SomethingRef
+ {
+ }
+
+ public class SomethingStruct
+ {
+ }
+
+ public static IEnumerable<object[]> MarshalObjectArrayCases()
+ {
+ yield return new object[] { new object[] { string.Intern("hello"), string.Empty } };
+ yield return new object[] { new object[] { 1.1d, new DateTime(2022, 5, 8, 14, 55, 01, DateTimeKind.Utc), false, true } };
+ yield return new object[] { new object[] { new double?(1.1d), new DateTime?(new DateTime(2022, 5, 8, 14, 55, 01, DateTimeKind.Utc)), new bool?(false), new bool?(true) } };
+ yield return new object[] { new object[] { null, new object(), new SomethingRef(), new SomethingStruct(), new Exception("test") } };
+ yield return new object[] { new object[] { JavaScriptTestHelper.createData("test"), JavaScriptTestHelper.createException("test") } };
+ yield return new object[] { new object[] { new byte[] { }, new int[] { }, new double[] { }, new string[] { }, new object[] { } } };
+ yield return new object[] { new object[] { new byte[] { 1, 2, 3 }, new int[] { 1, 2, 3 }, new double[] { 1, 2, 3 }, new string[] { "a", "b", "c" }, new object[] { } } };
+ yield return new object[] { new object[] { new object[] { new byte[] { 1, 2, 3 }, new int[] { 1, 2, 3 }, new double[] { 1, 2, 3 }, new string[] { "a", "b", "c" } , new object(), new SomethingRef(), new SomethingStruct(), new Exception("test") } } };
+ yield return new object[] { new object[] { } };
+ yield return new object[] { null };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalObjectArrayCases))]
+ public unsafe void JsImportObjectArray(object[]? expected)
+ {
+ var actual = JavaScriptTestHelper.echo1_ObjectArray(expected);
+ Assert.Equal(expected, actual);
+
+
+ if (expected != null) for (int i = 0; i < expected.Length; i++)
+ {
+ var actualI = JavaScriptTestHelper.store_ObjectArray(expected, i);
+ Assert.Equal(expected[i], actualI);
+ }
+ }
+
+ public static IEnumerable<object[]> MarshalObjectArrayCasesToDouble()
+ {
+ yield return new object[] { new object[] { (byte)42 } };
+ yield return new object[] { new object[] { (short)42 } };
+ yield return new object[] { new object[] { 42 } };
+ yield return new object[] { new object[] { 3.14f } };
+ yield return new object[] { new object[] { 'A' } };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalObjectArrayCasesToDouble))]
+ public unsafe void JsImportObjectArrayToDouble(object[]? expected)
+ {
+ if (expected != null) for (int i = 0; i < expected.Length; i++)
+ {
+ var actualI = JavaScriptTestHelper.store_ObjectArray(expected, i);
+ if (expected[i].GetType() == typeof(char))
+ {
+ Assert.Equal((double)(int)(char)expected[i], actualI);
+ }
+ else
+ {
+ Assert.Equal(Convert.ToDouble(expected[i]), actualI);
+ }
+ }
+ }
+
+ public static IEnumerable<object[]> MarshalObjectArrayCasesThrow()
+ {
+ yield return new object[] { new object[] { () => { } } };
+ yield return new object[] { new object[] { (int a) => { } } };
+ yield return new object[] { new object[] { (int a) => { return a; } } };
+ yield return new object[] { new object[] { (dummyDelegate)dummyDelegateA } };
+ yield return new object[] { new object[] { 0L } };
+ yield return new object[] { new object[] { 0UL } };
+ yield return new object[] { new object[] { (sbyte)0 } };
+ yield return new object[] { new object[] { (ushort)0 } };
+ yield return new object[] { new object[] { new SomethingStruct[] { } } };
+ yield return new object[] { new object[] { new SomethingRef[] { }, } };
+ yield return new object[] { new object[] { new ArraySegment<byte>(new byte[] { 11 }) , } };
+ }
+ delegate void dummyDelegate();
+ static void dummyDelegateA()
+ {
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalObjectArrayCasesThrow))]
+ public unsafe void JsImportObjectArrayThrows(object[]? expected)
+ {
+ Assert.Throws<NotImplementedException>(() => JavaScriptTestHelper.echo1_ObjectArray(expected));
+ }
+
+ [Fact]
+ public async Task JsImportObjectArrayTask()
+ {
+ object[] expected = new object[] { Task.CompletedTask };
+ var actual = JavaScriptTestHelper.echo1_ObjectArray(expected);
+ Assert.True(typeof(Task).IsAssignableFrom(actual[0].GetType()));
+ await Task.Delay(100);
+ await Task.Yield();
+ var actual0 = actual[0] as Task;
+ Assert.True(actual0.IsCompleted);
+ Assert.True(actual0.IsCompletedSuccessfully);
+ var actualT = JavaScriptTestHelper.store_ObjectArray(expected, 0);
+ await Task.Delay(100);
+ await Task.Yield();
+ var actualT0 = actualT as Task;
+ Assert.True(actualT0.IsCompletedSuccessfully);
+ }
+
+ [Fact]
+ public async Task JsImportObjectArrayTaskObject()
+ {
+ object[] expected = new object[] { Task.FromResult((object)42) };
+ var actual = JavaScriptTestHelper.echo1_ObjectArray(expected);
+ var actual0 = Assert.IsType<Task<object>>(actual[0]);
+ await Task.Delay(100);
+ await Task.Yield();
+ Assert.True(actual0.IsCompleted);
+ Assert.True(actual0.IsCompletedSuccessfully);
+ Assert.Equal(42.0d, actual0.Result);
+ }
+
+ [Fact]
+ public async Task JsImportObjectArrayTaskObjectFail()
+ {
+ var exex = new Exception("test");
+ object[] expected = new object[] { Task.FromException(exex) };
+ var actual = JavaScriptTestHelper.echo1_ObjectArray(expected);
+ var actual0 = Assert.IsType<Task<object>>(actual[0]);
+ await Task.Delay(100);
+ await Task.Yield();
+ Assert.True(actual0.IsCompleted);
+ Assert.True(actual0.IsFaulted);
+ var actualEx = await Assert.ThrowsAsync<Exception>(async () => await actual0);
+ Assert.Same(exex, actualEx);
+ }
+
+ #endregion
+
+ #region Views
+
+ [Fact]
+ public unsafe void JsImportSpanOfByte()
+ {
+ var expectedBytes = stackalloc byte[] { 1, 2, 42, 0, 127, 255 };
+ Span<byte> expected = new Span<byte>(expectedBytes, 6);
+ Assert.True(Unsafe.AsPointer(ref expected.GetPinnableReference()) == expectedBytes);
+ Span<byte> actual = JavaScriptTestHelper.echo1_SpanOfByte(expected, false);
+ Assert.Equal(expected.Length, actual.Length);
+ Assert.NotEqual(expected[0], expected[1]);
+ Assert.Equal(expected.GetPinnableReference(), actual.GetPinnableReference());
+ Assert.True(actual.SequenceCompareTo(expected) == 0);
+ Assert.Equal(expected.ToArray(), actual.ToArray());
+ actual = JavaScriptTestHelper.echo1_SpanOfByte(expected, true);
+ Assert.Equal(expected[0], expected[1]);
+ Assert.Equal(actual[0], actual[1]);
+ }
+
+ [Fact]
+ public unsafe void JsImportSpanOfInt32()
+ {
+ var expectedBytes = stackalloc int[] { 0, 1, -2, 42, int.MaxValue, int.MinValue };
+ Span<int> expected = new Span<int>(expectedBytes, 6);
+ Assert.True(Unsafe.AsPointer(ref expected.GetPinnableReference()) == expectedBytes);
+ Span<int> actual = JavaScriptTestHelper.echo1_SpanOfInt32(expected, false);
+ Assert.Equal(expected.Length, actual.Length);
+ Assert.NotEqual(expected[0], expected[1]);
+ Assert.Equal(expected.GetPinnableReference(), actual.GetPinnableReference());
+ Assert.True(actual.SequenceCompareTo(expected) == 0);
+ Assert.Equal(expected.ToArray(), actual.ToArray());
+ actual = JavaScriptTestHelper.echo1_SpanOfInt32(expected, true);
+ Assert.Equal(expected[0], expected[1]);
+ Assert.Equal(actual[0], actual[1]);
+ }
+
+ [Fact]
+ public unsafe void JsImportSpanOfDouble()
+ {
+ var expectedBytes = stackalloc double[] { 0, 1, -1, double.Pi, 42, double.MaxValue, double.MinValue, double.NaN, double.PositiveInfinity, double.NegativeInfinity };
+ Span<double> expected = new Span<double>(expectedBytes, 10);
+ Assert.True(Unsafe.AsPointer(ref expected.GetPinnableReference()) == expectedBytes);
+ Span<double> actual = JavaScriptTestHelper.echo1_SpanOfDouble(expected, false);
+ Assert.Equal(expected.Length, actual.Length);
+ Assert.NotEqual(expected[0], expected[1]);
+ Assert.Equal(expected.GetPinnableReference(), actual.GetPinnableReference());
+ Assert.True(actual.SequenceCompareTo(expected) == 0);
+ Assert.Equal(expected.ToArray(), actual.ToArray());
+ actual = JavaScriptTestHelper.echo1_SpanOfDouble(expected, true);
+ Assert.Equal(expected[0], expected[1]);
+ Assert.Equal(actual[0], actual[1]);
+ }
+
+ [Fact]
+ public unsafe void JsImportArraySegmentOfByte()
+ {
+ var expectedBytes = new byte[] { 88, 1, 2, 42, 0, 127, 255 };
+ ArraySegment<byte> expected = new ArraySegment<byte>(expectedBytes, 1, 6);
+ ArraySegment<byte> actual = JavaScriptTestHelper.echo1_ArraySegmentOfByte(expected, false);
+ Assert.Equal(expected.Count, actual.Count);
+ Assert.NotEqual(expected[0], expected[1]);
+ Assert.Equal(expected.Array, actual.Array);
+ actual = JavaScriptTestHelper.echo1_ArraySegmentOfByte(expected, true);
+ Assert.Equal(expected[0], expected[1]);
+ Assert.Equal(actual[0], actual[1]);
+ }
+
+ [Fact]
+ public unsafe void JsImportArraySegmentOfInt32()
+ {
+ var expectedBytes = new int[] { 88, 0, 1, -2, 42, int.MaxValue, int.MinValue };
+ ArraySegment<int> expected = new ArraySegment<int>(expectedBytes, 1, 6);
+ ArraySegment<int> actual = JavaScriptTestHelper.echo1_ArraySegmentOfInt32(expected, false);
+ Assert.Equal(expected.Count, actual.Count);
+ Assert.NotEqual(expected[0], expected[1]);
+ Assert.Equal(expected.Array, actual.Array);
+ actual = JavaScriptTestHelper.echo1_ArraySegmentOfInt32(expected, true);
+ Assert.Equal(expected[0], expected[1]);
+ Assert.Equal(actual[0], actual[1]);
+ }
+
+ [Fact]
+ public unsafe void JsImportArraySegmentOfDouble()
+ {
+ var expectedBytes = new double[] { 88.88, 0, 1, -1, double.Pi, 42, double.MaxValue, double.MinValue, double.NaN, double.PositiveInfinity, double.NegativeInfinity };
+ ArraySegment<double> expected = new ArraySegment<double>(expectedBytes, 1, 10);
+ ArraySegment<double> actual = JavaScriptTestHelper.echo1_ArraySegmentOfDouble(expected, false);
+ Assert.Equal(expected.Count, actual.Count);
+ Assert.NotEqual(expected[0], expected[1]);
+ Assert.Equal(expected.Array, actual.Array);
+ actual = JavaScriptTestHelper.echo1_ArraySegmentOfDouble(expected, true);
+ Assert.Equal(expected[0], expected[1]);
+ Assert.Equal(actual[0], actual[1]);
+ }
+
+ #endregion
+
+ #region Boolean
+ public static IEnumerable<object[]> MarshalBooleanCases()
+ {
+ yield return new object[] { true };
+ yield return new object[] { false };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalBooleanCases))]
+ public void JsImportBoolean(bool value)
+ {
+ JsImportTest(value,
+ JavaScriptTestHelper.store1_Boolean,
+ JavaScriptTestHelper.retrieve1_Boolean,
+ JavaScriptTestHelper.echo1_Boolean,
+ JavaScriptTestHelper.throw1_Boolean,
+ JavaScriptTestHelper.identity1_Boolean,
+ "boolean");
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalBooleanCases))]
+ public void JsExportBoolean(bool value)
+ {
+ JsExportTest(value,
+ JavaScriptTestHelper.invoke1_Boolean,
+ nameof(JavaScriptTestHelper.EchoBoolean),
+ "boolean");
+ }
+ #endregion Boolean
+
+ #region Char
+ public static IEnumerable<object[]> MarshalCharCases()
+ {
+ yield return new object[] { (char)42 };
+ yield return new object[] { (char)1 };
+ yield return new object[] { 'Ž' };
+ yield return new object[] { '♡' };
+ yield return new object[] { char.MaxValue };
+ yield return new object[] { char.MinValue };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalCharCases))]
+ public void JsImportChar(char value)
+ {
+ JsImportTest(value,
+ JavaScriptTestHelper.store1_Char,
+ JavaScriptTestHelper.retrieve1_Char,
+ JavaScriptTestHelper.echo1_Char,
+ JavaScriptTestHelper.throw1_Char,
+ JavaScriptTestHelper.identity1_Char,
+ "number");
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalCharCases))]
+ public void JsExportChar(char value)
+ {
+ JsExportTest(value,
+ JavaScriptTestHelper.invoke1_Char,
+ nameof(JavaScriptTestHelper.EchoChar),
+ "number");
+ }
+ #endregion Char
+
+ #region Byte
+ public static IEnumerable<object[]> MarshalByteCases()
+ {
+ yield return new object[] { (byte)42 };
+ yield return new object[] { (byte)1 };
+ yield return new object[] { byte.MaxValue };
+ yield return new object[] { byte.MinValue };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalByteCases))]
+ public void JsImportByte(byte value)
+ {
+ JsImportTest(value,
+ JavaScriptTestHelper.store1_Byte,
+ JavaScriptTestHelper.retrieve1_Byte,
+ JavaScriptTestHelper.echo1_Byte,
+ JavaScriptTestHelper.throw1_Byte,
+ JavaScriptTestHelper.identity1_Byte,
+ "number");
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalByteCases))]
+ public void JsExportByte(byte value)
+ {
+ JsExportTest(value,
+ JavaScriptTestHelper.invoke1_Byte,
+ nameof(JavaScriptTestHelper.EchoByte),
+ "number");
+ }
+
+ [Theory]
+ [MemberData(nameof(OutOfRangeCases))]
+ public void ByteOutOfRange(double value, string message)
+ {
+ JavaScriptTestHelper.store1_Double(value);
+ var ex = Assert.Throws<JSException>(() => JavaScriptTestHelper.retrieve1_Byte());
+ Assert.Contains(message, ex.Message);
+ }
+
+ #endregion Byte
+
+ #region Int16
+ public static IEnumerable<object[]> MarshalInt16Cases()
+ {
+ yield return new object[] { 42 };
+ yield return new object[] { 0 };
+ yield return new object[] { 1 };
+ yield return new object[] { -1 };
+ yield return new object[] { short.MaxValue };
+ yield return new object[] { short.MinValue };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalInt16Cases))]
+ public void JsImportInt16(short value)
+ {
+ JsImportTest(value,
+ JavaScriptTestHelper.store1_Int16,
+ JavaScriptTestHelper.retrieve1_Int16,
+ JavaScriptTestHelper.echo1_Int16,
+ JavaScriptTestHelper.throw1_Int16,
+ JavaScriptTestHelper.identity1_Int16,
+ "number");
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalInt16Cases))]
+ public void JsExportInt16(short value)
+ {
+ JsExportTest(value,
+ JavaScriptTestHelper.invoke1_Int16,
+ nameof(JavaScriptTestHelper.EchoInt16),
+ "number");
+ }
+ #endregion Int16
+
+ #region Int32
+ public static IEnumerable<object[]> MarshalInt32Cases()
+ {
+ yield return new object[] { 42 };
+ yield return new object[] { 0 };
+ yield return new object[] { 1 };
+ yield return new object[] { -1 };
+ yield return new object[] { int.MaxValue };
+ yield return new object[] { int.MinValue };
+ }
+
+ public static IEnumerable<object[]> OutOfRangeCases()
+ {
+ yield return new object[] { double.MaxValue, "Value is not an integer" };
+ yield return new object[] { double.MinValue, "Value is not an integer" };
+ yield return new object[] { double.NaN, "Value is not an integer" };
+ yield return new object[] { double.NegativeInfinity, "Value is not an integer" };
+ yield return new object[] { double.PositiveInfinity, "Value is not an integer" };
+ yield return new object[] { (double)MAX_SAFE_INTEGER, "Overflow" };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalInt32Cases))]
+ public void JsImportInt32(int value)
+ {
+ JsImportTest(value,
+ JavaScriptTestHelper.store1_Int32,
+ JavaScriptTestHelper.retrieve1_Int32,
+ JavaScriptTestHelper.echo1_Int32,
+ JavaScriptTestHelper.throw1_Int32,
+ JavaScriptTestHelper.identity1_Int32,
+ "number");
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalInt32Cases))]
+ public void JsExportInt32(int value)
+ {
+ JsExportTest(value,
+ JavaScriptTestHelper.invoke1_Int32,
+ nameof(JavaScriptTestHelper.EchoInt32),
+ "number");
+ }
+
+ [Theory]
+ [MemberData(nameof(OutOfRangeCases))]
+ public void Int32OutOfRange(double value, string message)
+ {
+ JavaScriptTestHelper.store1_Double(value);
+ var ex = Assert.Throws<JSException>(() => JavaScriptTestHelper.retrieve1_Int32());
+ Assert.Contains(message, ex.Message);
+ }
+
+ #endregion Int32
+
+ #region Int52
+ const long MAX_SAFE_INTEGER = 9007199254740991L;// Number.MAX_SAFE_INTEGER
+ const long MIN_SAFE_INTEGER = -9007199254740991L;// Number.MIN_SAFE_INTEGER
+ public static IEnumerable<object[]> MarshalInt52Cases()
+ {
+ yield return new object[] { -1 };
+ yield return new object[] { 42 };
+ yield return new object[] { 0 };
+ yield return new object[] { 1 };
+ yield return new object[] { MAX_SAFE_INTEGER };
+ yield return new object[] { MIN_SAFE_INTEGER };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalInt52Cases))]
+ public void JsImportInt52(long value)
+ {
+ JsImportTest(value,
+ JavaScriptTestHelper.store1_Int52,
+ JavaScriptTestHelper.retrieve1_Int52,
+ JavaScriptTestHelper.echo1_Int52,
+ JavaScriptTestHelper.throw1_Int52,
+ JavaScriptTestHelper.identity1_Int52,
+ "number");
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalInt52Cases))]
+ public void JsExportInt52(long value)
+ {
+ JsExportTest(value,
+ JavaScriptTestHelper.invoke1_Int52,
+ nameof(JavaScriptTestHelper.EchoInt52),
+ "number");
+ }
+ #endregion Int52
+
+ #region BigInt64
+ public static IEnumerable<object[]> MarshalBigInt64Cases()
+ {
+ yield return new object[] { -1 };
+ yield return new object[] { 42 };
+ yield return new object[] { 0 };
+ yield return new object[] { 1 };
+ yield return new object[] { MAX_SAFE_INTEGER };
+ yield return new object[] { MIN_SAFE_INTEGER };
+ yield return new object[] { long.MinValue };
+ yield return new object[] { long.MaxValue };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalBigInt64Cases))]
+ public void JsImportBigInt64(long value)
+ {
+ JsImportTest(value,
+ JavaScriptTestHelper.store1_BigInt64,
+ JavaScriptTestHelper.retrieve1_BigInt64,
+ JavaScriptTestHelper.echo1_BigInt64,
+ JavaScriptTestHelper.throw1_BigInt64,
+ JavaScriptTestHelper.identity1_BigInt64,
+ "bigint");
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalBigInt64Cases))]
+ public void JsExportBigInt64(long value)
+ {
+ JsExportTest(value,
+ JavaScriptTestHelper.invoke1_BigInt64,
+ nameof(JavaScriptTestHelper.EchoBigInt64),
+ "bigint");
+ }
+ #endregion BigInt64
+
+ #region Double
+ public static IEnumerable<object[]> MarshalDoubleCases()
+ {
+ yield return new object[] { Math.PI };
+ yield return new object[] { 0.0 };
+ yield return new object[] { double.MaxValue };
+ yield return new object[] { double.MinValue };
+ yield return new object[] { double.NegativeInfinity };
+ yield return new object[] { double.PositiveInfinity };
+ yield return new object[] { double.NaN };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalDoubleCases))]
+ public void JsImportDouble(double value)
+ {
+ JsImportTest(value,
+ JavaScriptTestHelper.store1_Double,
+ JavaScriptTestHelper.retrieve1_Double,
+ JavaScriptTestHelper.echo1_Double,
+ JavaScriptTestHelper.throw1_Double,
+ JavaScriptTestHelper.identity1_Double,
+ "number");
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalDoubleCases))]
+ public void JsExportDouble(double value)
+ {
+ JsExportTest(value,
+ JavaScriptTestHelper.invoke1_Double,
+ nameof(JavaScriptTestHelper.EchoDouble),
+ "number");
+ }
+ #endregion Double
+
+ #region Single
+ public static IEnumerable<object[]> MarshalSingleCases()
+ {
+ yield return new object[] { (float)Math.PI };
+ yield return new object[] { 0.0f };
+ yield return new object[] { float.MaxValue };
+ yield return new object[] { float.MinValue };
+ yield return new object[] { float.NegativeInfinity };
+ yield return new object[] { float.PositiveInfinity };
+ yield return new object[] { float.NaN };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalSingleCases))]
+ public void JsImportSingle(float value)
+ {
+ JsImportTest(value,
+ JavaScriptTestHelper.store1_Single,
+ JavaScriptTestHelper.retrieve1_Single,
+ JavaScriptTestHelper.echo1_Single,
+ JavaScriptTestHelper.throw1_Single,
+ JavaScriptTestHelper.identity1_Single,
+ "number");
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalSingleCases))]
+ public void JsExportSingle(float value)
+ {
+ JsExportTest(value,
+ JavaScriptTestHelper.invoke1_Single,
+ nameof(JavaScriptTestHelper.EchoSingle),
+ "number");
+ }
+ #endregion Single
+
+ #region IntPtr
+ public static IEnumerable<object[]> MarshalIntPtrCases()
+ {
+ yield return new object[] { (IntPtr)42 };
+ yield return new object[] { IntPtr.Zero };
+ yield return new object[] { (IntPtr)1 };
+ yield return new object[] { (IntPtr)(-1) };
+ yield return new object[] { IntPtr.MaxValue };
+ yield return new object[] { IntPtr.MinValue };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalIntPtrCases))]
+ public void JsImportIntPtr(IntPtr value)
+ {
+ JsImportTest(value,
+ JavaScriptTestHelper.store1_IntPtr,
+ JavaScriptTestHelper.retrieve1_IntPtr,
+ JavaScriptTestHelper.echo1_IntPtr,
+ JavaScriptTestHelper.throw1_IntPtr,
+ JavaScriptTestHelper.identity1_IntPtr,
+ "number");
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalIntPtrCases))]
+ public void JsExportIntPtr(IntPtr value)
+ {
+ JsExportTest(value,
+ JavaScriptTestHelper.invoke1_IntPtr,
+ nameof(JavaScriptTestHelper.EchoIntPtr),
+ "number");
+ }
+ #endregion IntPtr
+
+ #region VoidPtr
+
+ [Theory]
+ [MemberData(nameof(MarshalIntPtrCases))]
+ public unsafe void JsImportVoidPtr(IntPtr xvalue)
+ {
+ void* value = (void*)xvalue;
+
+ JavaScriptTestHelper.store1_VoidPtr(value);
+ void* res = JavaScriptTestHelper.retrieve1_VoidPtr();
+ Assert.True(value == res);
+ res = JavaScriptTestHelper.echo1_VoidPtr(value);
+ Assert.True(value == res);
+
+ var actualJsType = JavaScriptTestHelper.getType1();
+ Assert.Equal("number", actualJsType);
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalIntPtrCases))]
+ public unsafe void JsExportVoidPtr(IntPtr xvalue)
+ {
+ void* value = (void*)xvalue;
+ void* res = JavaScriptTestHelper.invoke1_VoidPtr(value, nameof(JavaScriptTestHelper.EchoVoidPtr));
+ Assert.True(value == res);
+ }
+ #endregion VoidPtr
+
+ #region Datetime
+ public static IEnumerable<object[]> MarshalDateTimeCases()
+ {
+ yield return new object[] { new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) };
+ yield return new object[] { TrimNano(DateTime.UtcNow) };
+ yield return new object[] { TrimNano(DateTime.MaxValue) };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalDateTimeCases))]
+ public void JSImportDateTime(DateTime value)
+ {
+ JsImportTest(value,
+ JavaScriptTestHelper.store1_DateTime,
+ JavaScriptTestHelper.retrieve1_DateTime,
+ JavaScriptTestHelper.echo1_DateTime,
+ JavaScriptTestHelper.throw1_DateTime,
+ JavaScriptTestHelper.identity1_DateTime,
+ "object", "Date");
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalDateTimeCases))]
+ public void JsExportDateTime(DateTime value)
+ {
+ JsExportTest(value,
+ JavaScriptTestHelper.invoke1_DateTime,
+ nameof(JavaScriptTestHelper.EchoDateTime),
+ "object", "Date");
+ }
+ #endregion Datetime
+
+ #region DateTimeOffset
+ public static IEnumerable<object[]> MarshalDateTimeOffsetCases()
+ {
+ yield return new object[] { DateTimeOffset.FromUnixTimeSeconds(0) };
+ yield return new object[] { TrimNano(DateTimeOffset.UtcNow) };
+ yield return new object[] { TrimNano(DateTimeOffset.MaxValue) };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalDateTimeOffsetCases))]
+ public void JSImportDateTimeOffset(DateTimeOffset value)
+ {
+ JsImportTest(value,
+ JavaScriptTestHelper.store1_DateTimeOffset,
+ JavaScriptTestHelper.retrieve1_DateTimeOffset,
+ JavaScriptTestHelper.echo1_DateTimeOffset,
+ JavaScriptTestHelper.throw1_DateTimeOffset,
+ JavaScriptTestHelper.identity1_DateTimeOffset,
+ "object", "Date");
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalDateTimeOffsetCases))]
+ public void JsExportDateTimeOffset(DateTimeOffset value)
+ {
+ JsExportTest(value,
+ JavaScriptTestHelper.invoke1_DateTimeOffset,
+ nameof(JavaScriptTestHelper.EchoDateTimeOffset),
+ "object", "Date");
+ }
+ #endregion DateTimeOffset
+
+ #region NullableBoolean
+ public static IEnumerable<object[]> MarshalNullableBooleanCases()
+ {
+ yield return new object[] { null };
+ yield return new object[] { true };
+ yield return new object[] { false };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalNullableBooleanCases))]
+ public void JsImportNullableBoolean(bool? value)
+ {
+ JsImportTest(value,
+ JavaScriptTestHelper.store1_NullableBoolean,
+ JavaScriptTestHelper.retrieve1_NullableBoolean,
+ JavaScriptTestHelper.echo1_NullableBoolean,
+ JavaScriptTestHelper.throw1_NullableBoolean,
+ JavaScriptTestHelper.identity1_NullableBoolean,
+ "boolean");
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalNullableBooleanCases))]
+ public void JsExportNullableBoolean(bool? value)
+ {
+ JsExportTest(value,
+ JavaScriptTestHelper.invoke1_NullableBoolean,
+ nameof(JavaScriptTestHelper.EchoNullableBoolean),
+ "boolean");
+ }
+ #endregion NullableBoolean
+
+ #region NullableInt32
+ public static IEnumerable<object[]> MarshalNullableInt32Cases()
+ {
+ yield return new object[] { null };
+ yield return new object[] { 42 };
+ yield return new object[] { 0 };
+ yield return new object[] { 1 };
+ yield return new object[] { -1 };
+ yield return new object[] { int.MaxValue };
+ yield return new object[] { int.MinValue };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalNullableInt32Cases))]
+ public void JsImportNullableInt32(int? value)
+ {
+ JsImportTest(value,
+ JavaScriptTestHelper.store1_NullableInt32,
+ JavaScriptTestHelper.retrieve1_NullableInt32,
+ JavaScriptTestHelper.echo1_NullableInt32,
+ JavaScriptTestHelper.throw1_NullableInt32,
+ JavaScriptTestHelper.identity1_NullableInt32,
+ "number");
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalNullableInt32Cases))]
+ public void JsExportNullableInt32(int? value)
+ {
+ JsExportTest(value,
+ JavaScriptTestHelper.invoke1_NullableInt32,
+ nameof(JavaScriptTestHelper.EchoNullableInt32),
+ "number");
+ }
+ #endregion NullableInt32
+
+ #region NullableBigInt64
+ public static IEnumerable<object[]> MarshalNullableBigInt64Cases()
+ {
+ yield return new object[] { null };
+ yield return new object[] { 42L };
+ yield return new object[] { 0L };
+ yield return new object[] { 1L };
+ yield return new object[] { -1L };
+ yield return new object[] { MAX_SAFE_INTEGER };
+ yield return new object[] { MIN_SAFE_INTEGER };
+ yield return new object[] { long.MaxValue };
+ yield return new object[] { long.MinValue };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalNullableBigInt64Cases))]
+ public void JsImportNullableBigInt64(long? value)
+ {
+ JsImportTest(value,
+ JavaScriptTestHelper.store1_NullableBigInt64,
+ JavaScriptTestHelper.retrieve1_NullableBigInt64,
+ JavaScriptTestHelper.echo1_NullableBigInt64,
+ JavaScriptTestHelper.throw1_NullableBigInt64,
+ JavaScriptTestHelper.identity1_NullableBigInt64,
+ "bigint");
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalNullableBigInt64Cases))]
+ public void JsExportNullableBigInt64(long? value)
+ {
+ JsExportTest(value,
+ JavaScriptTestHelper.invoke1_NullableBigInt64,
+ nameof(JavaScriptTestHelper.EchoNullableBigInt64),
+ "bigint");
+ }
+ #endregion NullableBigInt64
+
+ #region NullableIntPtr
+ public static IEnumerable<object[]> MarshalNullableIntPtrCases()
+ {
+ yield return new object[] { null };
+ yield return new object[] { (IntPtr)42 };
+ yield return new object[] { IntPtr.Zero };
+ yield return new object[] { (IntPtr)1 };
+ yield return new object[] { (IntPtr)(-1) };
+ yield return new object[] { IntPtr.MaxValue };
+ yield return new object[] { IntPtr.MinValue };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalNullableIntPtrCases))]
+ public void JsImportNullableIntPtr(IntPtr? value)
+ {
+ JsImportTest(value,
+ JavaScriptTestHelper.store1_NullableIntPtr,
+ JavaScriptTestHelper.retrieve1_NullableIntPtr,
+ JavaScriptTestHelper.echo1_NullableIntPtr,
+ JavaScriptTestHelper.throw1_NullableIntPtr,
+ JavaScriptTestHelper.identity1_NullableIntPtr,
+ "number");
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalNullableIntPtrCases))]
+ public void JsExportNullableIntPtr(IntPtr? value)
+ {
+ JsExportTest(value,
+ JavaScriptTestHelper.invoke1_NullableIntPtr,
+ nameof(JavaScriptTestHelper.EchoNullableIntPtr),
+ "number");
+ }
+ #endregion NullableIntPtr
+
+ #region NullableDouble
+ public static IEnumerable<object[]> MarshalNullableDoubleCases()
+ {
+ yield return new object[] { null };
+ yield return new object[] { Math.PI };
+ yield return new object[] { 0.0 };
+ yield return new object[] { double.MaxValue };
+ yield return new object[] { double.MinValue };
+ yield return new object[] { double.NegativeInfinity };
+ yield return new object[] { double.PositiveInfinity };
+ yield return new object[] { double.NaN };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalNullableDoubleCases))]
+ public void JsImportNullableDouble(double? value)
+ {
+ JsImportTest(value,
+ JavaScriptTestHelper.store1_NullableDouble,
+ JavaScriptTestHelper.retrieve1_NullableDouble,
+ JavaScriptTestHelper.echo1_NullableDouble,
+ JavaScriptTestHelper.throw1_NullableDouble,
+ JavaScriptTestHelper.identity1_NullableDouble,
+ "number");
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalNullableDoubleCases))]
+ public void JsExportNullableDouble(double? value)
+ {
+ JsExportTest(value,
+ JavaScriptTestHelper.invoke1_NullableDouble,
+ nameof(JavaScriptTestHelper.EchoNullableDouble),
+ "number");
+ }
+ #endregion NullableDouble
+
+ #region NullableDateTime
+ public static IEnumerable<object[]> MarshalNullableDateTimeCases()
+ {
+ yield return new object[] { null };
+ yield return new object[] { new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) };
+ yield return new object[] { TrimNano(DateTime.UtcNow) };
+ yield return new object[] { TrimNano(DateTime.MaxValue) };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalNullableDateTimeCases))]
+ public void JsImportNullableDateTime(DateTime? value)
+ {
+ JsImportTest(value,
+ JavaScriptTestHelper.store1_NullableDateTime,
+ JavaScriptTestHelper.retrieve1_NullableDateTime,
+ JavaScriptTestHelper.echo1_NullableDateTime,
+ JavaScriptTestHelper.throw1_NullableDateTime,
+ JavaScriptTestHelper.identity1_NullableDateTime,
+ "object");
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalNullableDateTimeCases))]
+ public void JsExportNullableDateTime(DateTime? value)
+ {
+ JsExportTest(value,
+ JavaScriptTestHelper.invoke1_NullableDateTime,
+ nameof(JavaScriptTestHelper.EchoNullableDateTime),
+ "object");
+ }
+ #endregion NullableDateTime
+
+ #region String
+ public static IEnumerable<object[]> MarshalStringCases()
+ {
+ yield return new object[] { null };
+ yield return new object[] { string.Empty };
+ yield return new object[] { "Ahoj" + Random.Shared.Next() };// shorted than 256 -> check in JS interned
+ yield return new object[] { "Ahoj" + new string('!', 300) };// longer than 256 -> no check in JS interned
+ yield return new object[] { string.Intern("dotnet") };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalStringCases))]
+ public void JsImportString(string value)
+ {
+ JsImportTest(value,
+ JavaScriptTestHelper.store1_String,
+ JavaScriptTestHelper.retrieve1_String,
+ JavaScriptTestHelper.echo1_String,
+ JavaScriptTestHelper.throw1_String,
+ JavaScriptTestHelper.identity1_String
+ , "string");
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalStringCases))]
+ public void JsExportString(string value)
+ {
+ JsExportTest(value,
+ JavaScriptTestHelper.invoke1_String,
+ nameof(JavaScriptTestHelper.EchoString),
+ "string");
+ }
+
+ #endregion String
+
+ #region Object
+ public static IEnumerable<object[]> MarshalObjectCases()
+ {
+ yield return new object[] { new object(), "ManagedObject" };
+ yield return new object[] { null, null };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalObjectCases))]
+ public void JSImportObject(object value, string clazz)
+ {
+ JsImportTest(value,
+ JavaScriptTestHelper.store1_Object,
+ JavaScriptTestHelper.retrieve1_Object,
+ JavaScriptTestHelper.echo1_Object,
+ JavaScriptTestHelper.throw1_Object,
+ JavaScriptTestHelper.identity1_Object,
+ "object", clazz);
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalObjectCases))]
+ public void JsExportObject(object value, string clazz)
+ {
+ JsExportTest(value,
+ JavaScriptTestHelper.invoke1_Object,
+ nameof(JavaScriptTestHelper.EchoObject),
+ "object", clazz);
+ }
+ #endregion Object
+
+ #region Exception
+ public static IEnumerable<object[]> MarshalExceptionCases()
+ {
+ yield return new object[] { new Exception("Test"), "ManagedError" };
+ yield return new object[] { null, "JSTestError" };
+ yield return new object[] { null, null };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalExceptionCases))]
+ public void JSImportException(Exception value, string clazz)
+ {
+ if (clazz == "JSTestError")
+ {
+ value = JavaScriptTestHelper.createException("!CreateEx!");
+ }
+
+ JsImportTest(value,
+ JavaScriptTestHelper.store1_Exception,
+ JavaScriptTestHelper.retrieve1_Exception,
+ JavaScriptTestHelper.echo1_Exception,
+ JavaScriptTestHelper.throw1_Exception,
+ JavaScriptTestHelper.identity1_Exception,
+ "object", clazz);
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalExceptionCases))]
+ public void JsExportException(Exception value, string clazz)
+ {
+ if (clazz == "JSTestError")
+ {
+ value = JavaScriptTestHelper.createException("!CreateEx!");
+ }
+
+ JsExportTest(value,
+ JavaScriptTestHelper.invoke1_Exception,
+ nameof(JavaScriptTestHelper.EchoException),
+ "object", clazz);
+ }
+ #endregion Exception
+
+ #region JSObject
+ public static IEnumerable<object[]> MarshalIJSObjectCases()
+ {
+ yield return new object[] { null, "JSData" };
+ yield return new object[] { null, null };
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalIJSObjectCases))]
+ public void JSImportIJSObject(JSObject value, string clazz)
+ {
+ if (clazz == "JSData")
+ {
+ value = JavaScriptTestHelper.createData("!CreateJS!");
+ }
+
+ JsImportTest(value,
+ JavaScriptTestHelper.store1_JSObject,
+ JavaScriptTestHelper.retrieve1_JSObject,
+ JavaScriptTestHelper.echo1_JSObject,
+ JavaScriptTestHelper.throw1_JSObject,
+ JavaScriptTestHelper.identity1_JSObject,
+ "object", clazz);
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalIJSObjectCases))]
+ public void JsExportIJSObject(JSObject value, string clazz)
+ {
+ if (clazz == "JSData")
+ {
+ value = JavaScriptTestHelper.createData("!CreateJS!");
+ }
+
+ JsExportTest(value,
+ JavaScriptTestHelper.invoke1_JSObject,
+ nameof(JavaScriptTestHelper.EchoIJSObject),
+ "object", clazz);
+ }
+ #endregion JSObject
+
+ #region ProxyOfProxy
+ [Fact]
+ public void ProxyOfProxyThrows()
+ {
+ // proxy of proxy should throw
+ JavaScriptTestHelper.store1_Object(new object());
+ Assert.Throws<JSException>(() => JavaScriptTestHelper.retrieve1_JSObject());
+ }
+
+
+ [Fact]
+ public void ProxyOfIntThrows()
+ {
+ // JSObject proxy of int should throw
+ JavaScriptTestHelper.store1_Int32(13);
+ Assert.Throws<JSException>(() => JavaScriptTestHelper.retrieve1_JSObject());
+ }
+ #endregion
+
+ #region Task
+
+ [Fact]
+ public async Task JsImportSleep()
+ {
+ await JavaScriptTestHelper.sleep(100);
+ }
+
+ [Fact]
+ public async Task JsImportThenVoid()
+ {
+ TaskCompletionSource tcs = new TaskCompletionSource();
+ JavaScriptTestHelper.thenvoid(tcs.Task);
+ GC.Collect();
+ tcs.SetResult();
+
+ GC.Collect();
+
+ await Task.Yield();
+ }
+
+ [Fact]
+ [OuterLoop]
+ public async Task JsImportForeverMany()
+ {
+ for (int i = 0; i < 1000; i++)
+ {
+ if (i % 100 == 0)
+ {
+ GC.Collect();
+ await Task.Yield();
+ }
+ var forever = JavaScriptTestHelper.forever();
+ Assert.False(forever.IsCompleted);
+ }
+ }
+
+ [Fact]
+ public async Task JsImportVoidTaskPending()
+ {
+ GC.Collect();
+ var pending = Task.Delay(1000);
+ var res = JavaScriptTestHelper.await2(pending);
+ GC.Collect();
+ Assert.False(res.IsCompleted);
+ await Task.Yield();
+ GC.Collect();
+ await res;
+ GC.Collect();
+ Assert.True(res.IsCompleted);
+ GC.Collect();
+ }
+
+ [Fact]
+ public async Task JsImportVoidTaskComplete()
+ {
+ GC.Collect();
+ var resComplete = JavaScriptTestHelper.await2(Task.CompletedTask);
+ GC.Collect();
+ await Task.Delay(100);
+ GC.Collect();
+ await Task.Yield();
+ GC.Collect();
+ Assert.True(resComplete.IsCompleted);
+ GC.Collect();
+ await resComplete;
+ GC.Collect();
+ }
+
+ [Fact]
+ public async Task JsImportSleep2()
+ {
+ int ms = await JavaScriptTestHelper.sleep_Int(100);
+ Assert.Equal(100, ms);
+ }
+
+
+ [Fact]
+ public async Task JsImportTaskEchoComplete()
+ {
+ var task = JavaScriptTestHelper.echo1_Task(Task.CompletedTask);
+ Assert.NotEqual(Task.CompletedTask, task);
+ // yield to main loop, because "the respective handler function will be called asynchronously"
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then#return_value
+ await Task.Delay(100);
+ Assert.True(task.IsCompleted);
+ }
+
+ [Fact]
+ public async Task JsImportTaskEchoPendingResult()
+ {
+ TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
+ var task = JavaScriptTestHelper.echo1_Task(tcs.Task);
+ Assert.NotEqual(tcs.Task, task);
+ Assert.False(task.IsCompleted);
+
+ tcs.SetResult("test");
+ // yield to main loop, because "the respective handler function will be called asynchronously"
+ await Task.Delay(100);
+ Assert.True(task.IsCompleted);
+ Assert.Equal(typeof(Task), task.GetType());
+ }
+
+ [Fact]
+ public async Task JsImportTaskEchoPendingException()
+ {
+ TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
+ var task = JavaScriptTestHelper.echo1_Task(tcs.Task);
+ Assert.NotEqual(tcs.Task, task);
+ Assert.False(task.IsCompleted);
+
+ tcs.SetException(new Exception("Test"));
+ // yield to main loop, because "the respective handler function will be called asynchronously"
+ await Task.Delay(100);
+ Assert.True(task.IsFaulted);
+ await Assert.ThrowsAsync<Exception>(async () => await task);
+ }
+
+ public static IEnumerable<object[]> TaskCases()
+ {
+ yield return new object[] { Math.PI };
+ yield return new object[] { 0 };
+ yield return new object[] { "test" };
+ yield return new object[] { null };
+ }
+
+ [Theory]
+ [MemberData(nameof(TaskCases))]
+ public async Task JsImportTaskAwaitPendingResult(object result)
+ {
+ TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
+ var task = JavaScriptTestHelper.await1(tcs.Task);
+ Assert.NotEqual(tcs.Task, task);
+ Assert.False(task.IsCompleted);
+
+ tcs.SetResult(result);
+ // yield to main loop, because "the respective handler function will be called asynchronously"
+ await Task.Delay(100);
+ Assert.True(task.IsCompleted);
+ var res = await task;
+ if (result != null && result.GetType() == typeof(int))
+ {
+ Assert.Equal(result, Convert.ToInt32(res));
+ }
+ else
+ {
+ Assert.Equal(result, res);
+ }
+ }
+
+ [Fact]
+ public async Task JsImportTaskAwaitPendingException()
+ {
+ TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
+ var task = JavaScriptTestHelper.await1(tcs.Task);
+ Assert.NotEqual(tcs.Task, task);
+ Assert.False(task.IsCompleted);
+
+ tcs.SetException(new Exception("Test"));
+ // yield to main loop, because "the respective handler function will be called asynchronously"
+ await Task.Delay(100);
+ Assert.True(task.IsFaulted);
+ await Assert.ThrowsAsync<Exception>(async () => await task);
+ }
+
+ [Fact]
+ public async Task JsImportTaskAwaitPendingExceptionValue()
+ {
+ TaskCompletionSource<Exception> tcs = new TaskCompletionSource<Exception>();
+ var task = JavaScriptTestHelper.await1_TaskOfException(tcs.Task);
+ Assert.NotEqual(tcs.Task, task);
+ Assert.False(task.IsCompleted);
+
+ tcs.SetResult(new Exception("Test"));
+ // yield to main loop, because "the respective handler function will be called asynchronously"
+ await Task.Delay(100);
+ Assert.True(task.IsCompletedSuccessfully);
+ Assert.Equal("Test", task.Result.Message);
+ }
+
+
+ [Fact]
+ public async Task JsImportTaskAwait()
+ {
+ var task = JavaScriptTestHelper.awaitvoid(Task.CompletedTask);
+ await Task.Delay(100);
+ Assert.True(task.IsCompleted);
+ await task;
+ }
+
+ [Theory]
+ [MemberData(nameof(MarshalInt32Cases))]
+ public async Task JsExportTaskOfInt(int value)
+ {
+ TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
+
+ var res = JavaScriptTestHelper.invoke1_TaskOfInt(tcs.Task, nameof(JavaScriptTestHelper.AwaitTaskOfObject));
+ tcs.SetResult(value);
+ await Task.Yield();
+ var rr = await res;
+ await Task.Yield();
+ Assert.Equal(value, rr);
+ //GC.Collect();
+ }
+
+ #endregion
+
+ #region Action
+
+ [Fact]
+ public void JsImportCallback_EchoAction()
+ {
+ bool called = false;
+ Action expected = () =>
+ {
+ called = true;
+ };
+ var actual = JavaScriptTestHelper.echo1_ActionAction(expected);
+ Assert.NotEqual(expected, actual);
+ Assert.False(called);
+ actual();
+ Assert.True(called);
+ }
+
+
+ [Fact]
+ [OuterLoop]
+ public async Task JsImportCallback_EchoActionMany()
+ {
+ int a = 1;
+ for (int i = 0; i < 1000; i++)
+ {
+ Action expected = () =>
+ {
+ a += i;
+ };
+ var actual = JavaScriptTestHelper.echo1large_ActionAction(expected);
+ Assert.NotEqual(expected, actual);
+ if (i % 100 == 0)
+ {
+ await Task.Yield();
+ GC.Collect();
+ }
+ }
+ }
+
+ [Fact]
+ public void JsImportCallback_Action()
+ {
+ bool called = false;
+ JavaScriptTestHelper.back3_Action(() =>
+ {
+ called = true;
+ });
+ Assert.True(called);
+ }
+
+ [Fact]
+ public void JsImportEcho_ActionAction()
+ {
+ bool called = false;
+ Action res = JavaScriptTestHelper.echo1_ActionAction(() =>
+ {
+ called = true;
+ });
+ Assert.False(called);
+ res.Invoke();
+ Assert.True(called);
+ }
+
+ [Fact]
+ public void JsImportEcho_ActionIntActionInt()
+ {
+ int calledA = -1;
+ Action<int> res = JavaScriptTestHelper.echo1_ActionIntActionInt((a) =>
+ {
+ calledA = a;
+ });
+ Assert.Equal(-1, calledA);
+ res.Invoke(42);
+ Assert.Equal(42, calledA);
+ }
+
+ [Fact]
+ public void JsImportCallback_ActionInt()
+ {
+ int called = -1;
+ JavaScriptTestHelper.back3_ActionInt((a) =>
+ {
+ called = a;
+ }, 42);
+ Assert.Equal(42, called);
+ }
+
+ [Fact]
+ public void JsImportCallback_FunctionIntInt()
+ {
+ int called = -1;
+ int res = JavaScriptTestHelper.back3_FunctionIntInt((a) =>
+ {
+ called = a;
+ return a;
+ }, 42);
+ Assert.Equal(42, called);
+ Assert.Equal(42, res);
+ }
+
+ [Fact]
+ public void JsImportBackCallback_FunctionIntInt()
+ {
+ int called = -1;
+ Func<int, int> res = JavaScriptTestHelper.backback_FuncIntFuncInt((a) =>
+ {
+ called = a;
+ return a;
+ }, 42);
+ Assert.Equal(-1, called);
+ int actual = res.Invoke(42);
+ Assert.Equal(84, actual);
+ Assert.Equal(84, called);
+ }
+
+ [Fact]
+ public void JsImportBackCallback_FunctionIntIntIntInt()
+ {
+ int calledA = -1;
+ int calledB = -1;
+ Func<int, int, int> res = JavaScriptTestHelper.backback_FuncIntIntFuncIntInt((a, b) =>
+ {
+ calledA = a;
+ calledB = b;
+ return a + b;
+ }, 42, 43);
+ Assert.Equal(-1, calledA);
+ Assert.Equal(-1, calledB);
+ int actual = res.Invoke(40, 41);
+ Assert.Equal(166, actual);
+ Assert.Equal(82, calledA);
+ Assert.Equal(84, calledB);
+ }
+
+ [Fact]
+ public void JsImportCallback_ActionIntInt()
+ {
+ int calledA = -1;
+ int calledB = -1;
+ JavaScriptTestHelper.back3_ActionIntInt((a, b) =>
+ {
+ calledA = a;
+ calledB = b;
+ }, 42, 43);
+ Assert.Equal(42, calledA);
+ Assert.Equal(43, calledB);
+ }
+
+ [Fact]
+ public void JsImportCallback_ActionLongLong()
+ {
+ long calledA = -1;
+ long calledB = -1;
+ JavaScriptTestHelper.back3_ActionLongLong((a, b) =>
+ {
+ calledA = a;
+ calledB = b;
+ }, 42, 43);
+ Assert.Equal(42, calledA);
+ Assert.Equal(43, calledB);
+ }
+
+ [Fact]
+ public void JsImportCallback_ActionIntLong()
+ {
+ int calledA = -1;
+ long calledB = -1;
+ JavaScriptTestHelper.back3_ActionIntLong((a, b) =>
+ {
+ calledA = a;
+ calledB = b;
+ }, 42, 43);
+ Assert.Equal(42, calledA);
+ Assert.Equal(43, calledB);
+ }
+
+ [Fact]
+ public void JsImportCallback_ActionIntThrow()
+ {
+ int called = -1;
+ Exception expected = new Exception("test!!");
+ Exception actual = Assert.Throws<Exception>(() => JavaScriptTestHelper.back3_ActionInt((a) =>
+ {
+ called = a;
+ throw expected;
+ }, 42));
+ Assert.Equal(42, called);
+ Assert.Same(expected, actual);
+ }
+
+ [Fact]
+ public void JsExportCallback_FunctionIntInt()
+ {
+ int called = -1;
+ var chain = JavaScriptTestHelper.invoke1_FuncOfIntInt((int a) =>
+ {
+ called = a;
+ return a;
+ }, nameof(JavaScriptTestHelper.BackFuncOfIntInt));
+
+ Assert.Equal(-1, called);
+ var actual = chain(42);
+ Assert.Equal(42, actual);
+ Assert.Equal(42, called);
+ }
+
+ [Fact]
+ public void JsExportCallback_FunctionIntIntThrow()
+ {
+ int called = -1;
+ var expected = new Exception("test!!");
+ var chain = JavaScriptTestHelper.invoke1_FuncOfIntInt((int a) =>
+ {
+ called = a;
+ throw expected;
+ }, nameof(JavaScriptTestHelper.BackFuncOfIntInt));
+
+ Assert.Equal(-1, called);
+ var actual = Assert.Throws<Exception>(() => chain(42));
+ Assert.Equal(42, called);
+ Assert.Same(expected, actual);
+ }
+
+ [Fact]
+ public void JsImportMath()
+ {
+ Func<int, int, int> plus = Utils.CreateFunctionIntIntInt("a", "b", @"return a+b");
+ Assert.Equal(3, plus(1, 2));
+ }
+
+ #endregion
+
+ private void JsExportTest<T>(T value
+ , Func<T, string, T> invoke, string echoName, string jsType, string? jsClass = null)
+ {
+ T res;
+ res = invoke(value, echoName);
+ Assert.Equal(value, res);
+ }
+
+ private void JsImportTest<T>(T value
+ , Action<T> store1
+ , Func<T> retrieve1
+ , Func<T, T> echo1
+ , Func<T, T> throw1
+ , Func<T, bool> identity1
+ , string jsType, string? jsClass = null)
+ {
+ if (value == null)
+ {
+ jsClass = null;
+ jsType = "object";
+ }
+
+ // invoke
+ store1(value);
+ var res = retrieve1();
+ Assert.Equal(value, res);
+ res = echo1(value);
+ Assert.Equal(value, res);
+ var equals = identity1(value);
+ Assert.True(equals, "value not equals");
+
+ var actualJsType = JavaScriptTestHelper.getType1();
+ Assert.Equal(jsType, actualJsType);
+
+ if (jsClass != null)
+ {
+ var actualJsClass = JavaScriptTestHelper.getClass1();
+ Assert.Equal(jsClass, actualJsClass);
+ }
+ var exThrow0 = Assert.Throws<JSException>(() => JavaScriptTestHelper.throw0());
+ Assert.Contains("throw-0-msg", exThrow0.Message);
+ Assert.DoesNotContain(" at ", exThrow0.Message);
+ Assert.Contains(" at throw0", exThrow0.StackTrace);
+
+ var exThrow1 = Assert.Throws<JSException>(() => throw1(value));
+ Assert.Contains("throw1-msg", exThrow1.Message);
+ Assert.DoesNotContain(" at ", exThrow1.Message);
+ Assert.Contains(" at throw1", exThrow1.StackTrace);
+
+ // anything is a system.object, sometimes it would be JSObject wrapper
+ if (typeof(T).IsPrimitive)
+ {
+ if (typeof(T) != typeof(long))
+ {
+
+ object resBoxed = JavaScriptTestHelper.echo1_Object(value);
+ // js Number always boxes as double
+ if (typeof(T) == typeof(IntPtr))
+ {
+ //TODO Assert.Equal((IntPtr)(object)value, (IntPtr)(int)(double)resBoxed);
+ }
+ else if (typeof(T) == typeof(bool))
+ {
+ Assert.Equal((bool)(object)value, (bool)resBoxed);
+ }
+ else if (typeof(T) == typeof(char))
+ {
+ Assert.Equal((char)(object)value, (char)(double)resBoxed);
+ }
+ else
+ {
+ Assert.Equal(Convert.ToDouble(value), resBoxed);
+ }
+ }
+
+ //TODO var task = JavaScriptTestHelper.await1(Task.FromResult((object)value));
+ }
+ else if (typeof(T) == typeof(DateTime))
+ {
+ var resBoxed = JavaScriptTestHelper.echo1_Object(value);
+ Assert.Equal(value, resBoxed);
+ }
+ else if (typeof(T) == typeof(DateTimeOffset))
+ {
+ var resBoxed = JavaScriptTestHelper.echo1_Object(value);
+ Assert.Equal(((DateTimeOffset)(object)value).UtcDateTime, resBoxed);
+ }
+ else if (Nullable.GetUnderlyingType(typeof(T)) != null)
+ {
+ var vt = Nullable.GetUnderlyingType(typeof(T));
+ if (vt != typeof(long))
+ {
+ var resBoxed = JavaScriptTestHelper.echo1_Object(value);
+ if (resBoxed != null)
+ {
+ if (vt == typeof(bool))
+ {
+ Assert.Equal(((bool?)(object)value).Value, (bool)resBoxed);
+ }
+ else if (vt == typeof(char))
+ {
+ Assert.Equal(((char?)(object)value).Value, (char)resBoxed);
+ }
+ else if (vt == typeof(DateTime))
+ {
+ Assert.Equal(((DateTime?)(object)value).Value, resBoxed);
+ }
+ else if (vt == typeof(DateTimeOffset))
+ {
+ Assert.Equal(((DateTimeOffset?)(object)value).Value.UtcDateTime, resBoxed);
+ }
+ else if (vt == typeof(IntPtr))
+ {
+ // TODO Assert.Equal((double)((IntPtr?)(object)value).Value, resBoxed);
+ }
+ else
+ {
+ Assert.Equal(Convert.ToDouble(value), resBoxed);
+ }
+ }
+ else
+ {
+ Assert.Equal(value, default(T));
+ }
+ }
+ }
+ else
+ {
+ var resObj = JavaScriptTestHelper.retrieve1_Object();
+ if (resObj == null || resObj.GetType() != typeof(JSObject))
+ {
+ Assert.Equal(value, resObj);
+ }
+ }
+
+ if (typeof(Exception).IsAssignableFrom(typeof(T)))
+ {
+ // all exceptions are Exception
+ var resEx = JavaScriptTestHelper.retrieve1_Exception();
+ Assert.Equal((Exception)(object)value, resEx);
+ }
+ }
+
+ public async Task InitializeAsync()
+ {
+ await JavaScriptTestHelper.InitializeAsync();
+ }
+
+ public Task DisposeAsync() => Task.CompletedTask;
+
+ // js Date doesn't have nanosecond precision
+ public static DateTime TrimNano(DateTime date)
+ {
+ return new DateTime(date.Ticks - (date.Ticks % TimeSpan.TicksPerMillisecond), DateTimeKind.Utc);
+ }
+
+ public static DateTimeOffset TrimNano(DateTimeOffset date)
+ {
+ return new DateTime(date.Ticks - (date.Ticks % TimeSpan.TicksPerMillisecond), DateTimeKind.Utc);
+ }
+ }
+}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs
new file mode 100644
index 00000000000..61d92f193ab
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs
@@ -0,0 +1,915 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices.Marshalling;
+using System.Threading.Tasks;
+using System.Xml.Serialization;
+
+[assembly: DisableRuntimeMarshalling]
+
+namespace System.Runtime.InteropServices.JavaScript.Tests
+{
+ public partial class JavaScriptTestHelper
+ {
+ [JSImport("globalThis.console.log")]
+ public static partial void Log([JSMarshalAs<JSType.String>] string message);
+
+ [JSExport]
+ [return: JSMarshalAs<JSType.Discard>]
+ public static void ConsoleWriteLine([JSMarshalAs<JSType.String>] string message)
+ {
+ Console.WriteLine(message);
+ }
+
+ [JSExport]
+ [return: JSMarshalAs<JSType.Date>]
+ public static DateTime Now()
+ {
+ return DateTime.Now;
+ }
+
+ [JSImport("create_function", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Function<JSType.Number, JSType.Number, JSType.Number>>]
+ public static partial Func<int, int, int> createMath([JSMarshalAs<JSType.String>] string a, [JSMarshalAs<JSType.String>] string b, [JSMarshalAs<JSType.String>] string code);
+
+ [JSImport("getType1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.String>]
+ internal static partial string getType1();
+
+ [JSImport("getClass1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.String>]
+ internal static partial string getClass1();
+
+ [JSImport("throw0", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Discard>]
+ internal static partial void throw0();
+
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Promise<JSType.Void>>]
+ internal static partial Task echo1_Task([JSMarshalAs<JSType.Promise<JSType.Void>>] Task arg1);
+
+ [JSImport("createException", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Error>]
+ internal static partial Exception createException([JSMarshalAs<JSType.String>] string name);
+
+ [JSImport("createData", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Object>]
+ internal static partial JSObject createData([JSMarshalAs<JSType.String>] string name);
+
+ #region relaxed
+ [JSImport("dummy", "JavaScriptTestHelper")]
+ internal static partial void Relaxed(string a1, Exception ex,
+ bool ab, double a6, byte a2, char a3, short a4, float a5, IntPtr a7,
+ bool? nab, double? na6, byte? na2, char? na3, short? na4, float? na5, IntPtr? na7,
+ Task<string> ta1, Task<Exception> tex,
+ Task<bool> tab, Task<double> ta6, Task<byte> ta2, Task<char> ta3, Task<short> ta4, Task<float> ta5, Task<IntPtr> ta7,
+ string[] aa1, byte[] aab, double[] aad, int[] aai
+ );
+
+ [JSImport("dummy", "JavaScriptTestHelper")]
+ internal static partial double RelaxedDouble();
+
+ [JSImport("dummy", "JavaScriptTestHelper")]
+ internal static partial string RelaxedString();
+
+ [JSImport("dummy", "JavaScriptTestHelper")]
+ internal static partial string[] RelaxedStringArray();
+
+ [JSImport("dummy", "JavaScriptTestHelper")]
+ internal static partial Exception RelaxedException();
+
+ [JSImport("dummy", "JavaScriptTestHelper")]
+ internal static partial bool RelaxedBool();
+
+ [JSImport("dummy", "JavaScriptTestHelper")]
+ internal static partial double? RelaxedNullableDouble();
+
+ [JSImport("dummy", "JavaScriptTestHelper")]
+ internal static partial bool? RelaxedNullableBool();
+
+ [JSImport("dummy", "JavaScriptTestHelper")]
+ internal static partial Task RelaxedTask();
+
+ [JSImport("dummy", "JavaScriptTestHelper")]
+ internal static partial Task<double> RelaxedTaskDouble();
+
+ [JSImport("dummy", "JavaScriptTestHelper")]
+ internal static partial Task<string> RelaxedTaskString();
+
+ [JSImport("dummy", "JavaScriptTestHelper")]
+ internal static partial Task<Exception> RelaxedTaskException();
+
+ [JSImport("dummy", "JavaScriptTestHelper")]
+ internal static partial Task<bool> RelaxedTaskBool();
+
+
+ #endregion
+
+ #region Arrays
+
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Array<JSType.Number>>]
+ internal static partial byte[]? echo1_ByteArray([JSMarshalAs<JSType.Array<JSType.Number>>] byte[]? value);
+
+ [JSImport("storeAt", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial byte? store_ByteArray([JSMarshalAs<JSType.Array<JSType.Number>>] byte[]? value, [JSMarshalAs<JSType.Number>] int index);
+
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Array<JSType.Number>>]
+ internal static partial int[]? echo1_Int32Array([JSMarshalAs<JSType.Array<JSType.Number>>] int[]? value);
+
+ [JSImport("storeAt", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial int? store_Int32Array([JSMarshalAs<JSType.Array<JSType.Number>>] int[]? value, [JSMarshalAs<JSType.Number>] int index);
+
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Array<JSType.Number>>]
+ internal static partial double[]? echo1_DoubleArray([JSMarshalAs<JSType.Array<JSType.Number>>] double[]? value);
+
+ [JSImport("storeAt", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial double? store_DoubleArray([JSMarshalAs<JSType.Array<JSType.Number>>] double[]? value, [JSMarshalAs<JSType.Number>] int index);
+
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Array<JSType.String>>]
+ internal static partial string[]? echo1_StringArray([JSMarshalAs<JSType.Array<JSType.String>>] string[]? value);
+
+ [JSImport("storeAt", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.String>]
+ internal static partial string? store_StringArray([JSMarshalAs<JSType.Array<JSType.String>>] string[]? value, [JSMarshalAs<JSType.Number>] int index);
+
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Array<JSType.Any>>]
+ internal static partial object[]? echo1_ObjectArray([JSMarshalAs<JSType.Array<JSType.Any>>] object[]? value);
+
+ [JSImport("storeAt", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Any>]
+ internal static partial object? store_ObjectArray([JSMarshalAs<JSType.Array<JSType.Any>>] object[]? value, [JSMarshalAs<JSType.Number>] int index);
+
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Array<JSType.Object>>]
+ internal static partial JSObject[]? echo1_JSObjectArray([JSMarshalAs<JSType.Array<JSType.Object>>] JSObject[]? value);
+
+ [JSImport("storeAt", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Object>]
+ internal static partial JSObject? store_JSObjectArray([JSMarshalAs<JSType.Array<JSType.Object>>] JSObject[]? value, [JSMarshalAs<JSType.Number>] int index);
+
+ #endregion
+
+ #region Views
+
+ [JSImport("echo1view", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.MemoryView>]
+ internal static partial Span<byte> echo1_SpanOfByte([JSMarshalAs<JSType.MemoryView>] Span<byte> value, [JSMarshalAs<JSType.Boolean>] bool edit);
+
+ [JSImport("echo1view", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.MemoryView>]
+ internal static partial Span<int> echo1_SpanOfInt32([JSMarshalAs<JSType.MemoryView>] Span<int> value, [JSMarshalAs<JSType.Boolean>] bool edit);
+
+ [JSImport("echo1view", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.MemoryView>]
+ internal static partial Span<double> echo1_SpanOfDouble([JSMarshalAs<JSType.MemoryView>] Span<double> value, [JSMarshalAs<JSType.Boolean>] bool edit);
+
+ [JSImport("echo1view", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.MemoryView>]
+ internal static partial ArraySegment<byte> echo1_ArraySegmentOfByte([JSMarshalAs<JSType.MemoryView>] ArraySegment<byte> value, [JSMarshalAs<JSType.Boolean>] bool edit);
+
+ [JSImport("echo1view", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.MemoryView>]
+ internal static partial ArraySegment<int> echo1_ArraySegmentOfInt32([JSMarshalAs<JSType.MemoryView>] ArraySegment<int> value, [JSMarshalAs<JSType.Boolean>] bool edit);
+
+ [JSImport("echo1view", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.MemoryView>]
+ internal static partial ArraySegment<double> echo1_ArraySegmentOfDouble([JSMarshalAs<JSType.MemoryView>] ArraySegment<double> value, [JSMarshalAs<JSType.Boolean>] bool edit);
+
+ #endregion
+
+ #region Int32
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial int echo1_Int32([JSMarshalAs<JSType.Number>] int value);
+ [JSImport("store1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Void>]
+ internal static partial void store1_Int32([JSMarshalAs<JSType.Number>] int value);
+ [JSImport("retrieve1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial int retrieve1_Int32();
+ [JSImport("identity1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool identity1_Int32([JSMarshalAs<JSType.Number>] int value);
+ [JSImport("throw1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial int throw1_Int32([JSMarshalAs<JSType.Number>] int value);
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial int invoke1_Int32([JSMarshalAs<JSType.Number>] int value, [JSMarshalAs<JSType.String>] string name);
+ [JSExport]
+ [return: JSMarshalAs<JSType.Number>]
+ public static int EchoInt32([JSMarshalAs<JSType.Number>] int arg1)
+ {
+ return arg1;
+ }
+ #endregion Int32
+
+ #region String
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.String>]
+ internal static partial string echo1_String([JSMarshalAs<JSType.String>] string value);
+ [JSImport("store1", "JavaScriptTestHelper")]
+ internal static partial void store1_String([JSMarshalAs<JSType.String>] string value);
+ [JSImport("retrieve1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.String>]
+ internal static partial string retrieve1_String();
+ [JSImport("identity1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool identity1_String([JSMarshalAs<JSType.String>] string value);
+ [JSImport("throw1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.String>]
+ internal static partial string throw1_String([JSMarshalAs<JSType.String>] string value);
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.String>]
+ internal static partial string invoke1_String([JSMarshalAs<JSType.String>] string value, [JSMarshalAs<JSType.String>] string name);
+ [JSExport]
+ [return: JSMarshalAs<JSType.String>]
+ public static string EchoString([JSMarshalAs<JSType.String>] string arg1)
+ {
+ return arg1;
+ }
+ #endregion String
+
+ #region Object
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Any>]
+ internal static partial object echo1_Object([JSMarshalAs<JSType.Any>] object value);
+ [JSImport("store1", "JavaScriptTestHelper")]
+ internal static partial void store1_Object([JSMarshalAs<JSType.Any>] object value);
+ [JSImport("retrieve1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Any>]
+ internal static partial object retrieve1_Object();
+ [JSImport("identity1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool identity1_Object([JSMarshalAs<JSType.Any>] object value);
+ [JSImport("throw1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Any>]
+ internal static partial object throw1_Object([JSMarshalAs<JSType.Any>] object value);
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Any>]
+ internal static partial object invoke1_Object([JSMarshalAs<JSType.Any>] object value, [JSMarshalAs<JSType.String>] string name);
+ [JSExport]
+ [return: JSMarshalAs<JSType.Any>]
+ public static object EchoObject([JSMarshalAs<JSType.Any>] object arg1)
+ {
+ return arg1;
+ }
+ #endregion Object
+
+ #region Exception
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Error>]
+ internal static partial Exception echo1_Exception([JSMarshalAs<JSType.Error>] Exception value);
+ [JSImport("store1", "JavaScriptTestHelper")]
+ internal static partial void store1_Exception([JSMarshalAs<JSType.Error>] Exception value);
+ [JSImport("retrieve1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Error>]
+ internal static partial Exception retrieve1_Exception();
+ [JSImport("identity1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool identity1_Exception([JSMarshalAs<JSType.Error>] Exception value);
+ [JSImport("throw1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Error>]
+ internal static partial Exception throw1_Exception([JSMarshalAs<JSType.Error>] Exception value);
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Error>]
+ internal static partial Exception invoke1_Exception([JSMarshalAs<JSType.Error>] Exception value, [JSMarshalAs<JSType.String>] string name);
+ [JSExport]
+ [return: JSMarshalAs<JSType.Error>]
+ public static Exception EchoException([JSMarshalAs<JSType.Error>] Exception arg1)
+ {
+ return arg1;
+ }
+ #endregion Exception
+
+ #region Task
+ [JSImport("awaitvoid", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Promise<JSType.Void>>]
+ internal static partial Task awaitvoid([JSMarshalAs<JSType.Promise<JSType.Void>>] Task arg1);
+ [JSImport("sleep", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Promise<JSType.Void>>]
+ internal static partial Task sleep([JSMarshalAs<JSType.Number>] int ms);
+ [JSImport("forever", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Promise<JSType.Void>>]
+ internal static partial Task forever();
+ [JSImport("sleep", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Promise<JSType.Number>>]
+ internal static partial Task<int> sleep_Int([JSMarshalAs<JSType.Number>] int ms);
+
+ [JSImport("sleep", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Promise<JSType.Number>>]
+ internal static partial Task<int>? sleepMaybe_Int([JSMarshalAs<JSType.Number>] int ms);
+
+ [JSImport("await2", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Promise<JSType.Void>>]
+ internal static partial Task await2([JSMarshalAs<JSType.Promise<JSType.Void>>] Task arg1);
+
+ [JSImport("thenvoid", "JavaScriptTestHelper")]
+ internal static partial void thenvoid([JSMarshalAs<JSType.Promise<JSType.Void>>] Task arg1);
+
+ [JSImport("await1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Promise<JSType.Any>>]
+ internal static partial Task<object> await1([JSMarshalAs<JSType.Promise<JSType.Any>>] Task<object> arg1);
+ [JSImport("await1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Promise<JSType.Error>>]
+ internal static partial Task<Exception> await1_TaskOfException([JSMarshalAs<JSType.Promise<JSType.Error>>] Task<Exception> arg1);
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Promise<JSType.Any>>]
+ internal static partial Task<object> invoke1_TaskOfObject([JSMarshalAs<JSType.Promise<JSType.Any>>] Task<object> value, [JSMarshalAs<JSType.String>] string name);
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Promise<JSType.Number>>]
+ internal static partial Task<int> invoke1_TaskOfInt([JSMarshalAs<JSType.Promise<JSType.Number>>] Task<int> value, [JSMarshalAs<JSType.String>] string name);
+
+ [JSExport]
+ [return: JSMarshalAs<JSType.Promise<JSType.Any>>]
+ public static async Task<object> AwaitTaskOfObject([JSMarshalAs<JSType.Promise<JSType.Any>>] Task<object> arg1)
+ {
+ var res = await arg1;
+ return res;
+ }
+
+ #endregion
+
+ #region Action + Func
+
+ [JSImport("back3", "JavaScriptTestHelper")]
+ internal static partial void back3_Action([JSMarshalAs<JSType.Function>] Action action);
+
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Function>]
+ internal static partial Action echo1_ActionAction([JSMarshalAs<JSType.Function>] Action action);
+
+ [JSImport("echo1large", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Function>]
+ internal static partial Action echo1large_ActionAction([JSMarshalAs<JSType.Function>] Action action);
+
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Function<JSType.Number>>]
+ internal static partial Action<int> echo1_ActionIntActionInt([JSMarshalAs<JSType.Function<JSType.Number>>] Action<int> action);
+
+ [JSImport("backback", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Function<JSType.Number, JSType.Number>>]
+ internal static partial Func<int, int> backback_FuncIntFuncInt([JSMarshalAs<JSType.Function<JSType.Number, JSType.Number>>] Func<int, int> fun, [JSMarshalAs<JSType.Number>] int a);
+
+ [JSImport("backback", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Function<JSType.Number, JSType.Number, JSType.Number>>]
+ internal static partial Func<int, int, int> backback_FuncIntIntFuncIntInt([JSMarshalAs<JSType.Function<JSType.Number, JSType.Number, JSType.Number>>] Func<int, int, int> fun, [JSMarshalAs<JSType.Number>] int a, [JSMarshalAs<JSType.Number>] int b);
+
+ [JSImport("back3", "JavaScriptTestHelper")]
+ internal static partial void back3_ActionInt([JSMarshalAs<JSType.Function<JSType.Number>>] Action<int>? action, [JSMarshalAs<JSType.Number>] int a);
+
+ [JSImport("back3", "JavaScriptTestHelper")]
+ internal static partial void back3_ActionIntInt([JSMarshalAs<JSType.Function<JSType.Number, JSType.Number>>] Action<int, int>? action, [JSMarshalAs<JSType.Number>] int a, [JSMarshalAs<JSType.Number>] int b);
+
+ [JSImport("back3", "JavaScriptTestHelper")]
+ internal static partial void back3_ActionLongLong([JSMarshalAs<JSType.Function<JSType.Number, JSType.Number>>] Action<long, long>? action, [JSMarshalAs<JSType.Number>] long a, [JSMarshalAs<JSType.Number>] long b);
+
+ [JSImport("back3", "JavaScriptTestHelper")]
+ internal static partial void back3_ActionIntLong([JSMarshalAs<JSType.Function<JSType.Number, JSType.Number>>] Action<int, long>? action, [JSMarshalAs<JSType.Number>] int a, [JSMarshalAs<JSType.Number>] long b);
+
+ [JSImport("back3", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial int back3_FunctionIntInt([JSMarshalAs<JSType.Function<JSType.Number, JSType.Number>>] Func<int, int>? fun, [JSMarshalAs<JSType.Number>] int a);
+
+
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Function<JSType.Number, JSType.Number>>]
+ internal static partial Func<int, int> invoke1_FuncOfIntInt([JSMarshalAs<JSType.Function<JSType.Number, JSType.Number>>] Func<int, int> value, [JSMarshalAs<JSType.String>] string name);
+
+ [JSExport]
+ [return: JSMarshalAs<JSType.Function<JSType.Number, JSType.Number>>]
+ public static Func<int, int> BackFuncOfIntInt([JSMarshalAs<JSType.Function<JSType.Number, JSType.Number>>] Func<int, int> arg1)
+ {
+ return (int a) =>
+ {
+ return arg1(a);
+ };
+ }
+
+ #endregion
+
+ #region Boolean
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool echo1_Boolean([JSMarshalAs<JSType.Boolean>] bool value);
+ [JSImport("store1", "JavaScriptTestHelper")]
+ internal static partial void store1_Boolean([JSMarshalAs<JSType.Boolean>] bool value);
+ [JSImport("retrieve1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool retrieve1_Boolean();
+ [JSImport("identity1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool identity1_Boolean([JSMarshalAs<JSType.Boolean>] bool value);
+ [JSImport("throw1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool throw1_Boolean([JSMarshalAs<JSType.Boolean>] bool value);
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool invoke1_Boolean([JSMarshalAs<JSType.Boolean>] bool value, [JSMarshalAs<JSType.String>] string name);
+ [JSExport]
+ [return: JSMarshalAs<JSType.Boolean>]
+ public static bool EchoBoolean([JSMarshalAs<JSType.Boolean>] bool arg1)
+ {
+ return arg1;
+ }
+ #endregion Boolean
+
+ #region Char
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.String>]
+ internal static partial char echo1_Char([JSMarshalAs<JSType.String>] char value);
+ [JSImport("store1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Discard>]
+ internal static partial void store1_Char([JSMarshalAs<JSType.String>] char value);
+ [JSImport("retrieve1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.String>]
+ internal static partial char retrieve1_Char();
+ [JSImport("identity1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool identity1_Char([JSMarshalAs<JSType.String>] char value);
+ [JSImport("throw1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.String>]
+ internal static partial char throw1_Char([JSMarshalAs<JSType.String>] char value);
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.String>]
+ internal static partial char invoke1_Char([JSMarshalAs<JSType.String>] char value, [JSMarshalAs<JSType.String>] string name);
+ [JSExport]
+ [return: JSMarshalAs<JSType.String>]
+ public static char EchoChar([JSMarshalAs<JSType.String>] char arg1)
+ {
+ return arg1;
+ }
+ #endregion Byte
+
+ #region Byte
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial byte echo1_Byte([JSMarshalAs<JSType.Number>] byte value);
+ [JSImport("store1", "JavaScriptTestHelper")]
+ internal static partial void store1_Byte([JSMarshalAs<JSType.Number>] byte value);
+ [JSImport("retrieve1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial byte retrieve1_Byte();
+ [JSImport("identity1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool identity1_Byte([JSMarshalAs<JSType.Number>] byte value);
+ [JSImport("throw1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial byte throw1_Byte([JSMarshalAs<JSType.Number>] byte value);
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial byte invoke1_Byte([JSMarshalAs<JSType.Number>] byte value, [JSMarshalAs<JSType.String>] string name);
+ [JSExport]
+ [return: JSMarshalAs<JSType.Number>]
+ public static byte EchoByte([JSMarshalAs<JSType.Number>] byte arg1)
+ {
+ return arg1;
+ }
+ #endregion Byte
+
+ #region Int16
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial short echo1_Int16([JSMarshalAs<JSType.Number>] short value);
+ [JSImport("store1", "JavaScriptTestHelper")]
+ internal static partial void store1_Int16([JSMarshalAs<JSType.Number>] short value);
+ [JSImport("retrieve1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial short retrieve1_Int16();
+ [JSImport("identity1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool identity1_Int16([JSMarshalAs<JSType.Number>] short value);
+ [JSImport("throw1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial short throw1_Int16([JSMarshalAs<JSType.Number>] short value);
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial short invoke1_Int16([JSMarshalAs<JSType.Number>] short value, [JSMarshalAs<JSType.String>] string name);
+ [JSExport]
+ [return: JSMarshalAs<JSType.Number>]
+ public static short EchoInt16([JSMarshalAs<JSType.Number>] short arg1)
+ {
+ return arg1;
+ }
+ #endregion Int16
+
+ #region Int52
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial long echo1_Int52([JSMarshalAs<JSType.Number>] long value);
+ [JSImport("store1", "JavaScriptTestHelper")]
+ internal static partial void store1_Int52([JSMarshalAs<JSType.Number>] long value);
+ [JSImport("retrieve1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial long retrieve1_Int52();
+ [JSImport("identity1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool identity1_Int52([JSMarshalAs<JSType.Number>] long value);
+ [JSImport("throw1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial long throw1_Int52([JSMarshalAs<JSType.Number>] long value);
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial long invoke1_Int52([JSMarshalAs<JSType.Number>] long value, [JSMarshalAs<JSType.String>] string name);
+ [JSExport]
+ [return: JSMarshalAs<JSType.Number>]
+ public static long EchoInt52([JSMarshalAs<JSType.Number>] long arg1)
+ {
+ return arg1;
+ }
+ #endregion Int52
+
+ #region BigInt64
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.BigInt>]
+ internal static partial long echo1_BigInt64([JSMarshalAs<JSType.BigInt>] long value);
+ [JSImport("store1", "JavaScriptTestHelper")]
+ internal static partial void store1_BigInt64([JSMarshalAs<JSType.BigInt>] long value);
+ [JSImport("retrieve1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.BigInt>]
+ internal static partial long retrieve1_BigInt64();
+ [JSImport("identity1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool identity1_BigInt64([JSMarshalAs<JSType.BigInt>] long value);
+ [JSImport("throw1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.BigInt>]
+ internal static partial long throw1_BigInt64([JSMarshalAs<JSType.BigInt>] long value);
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.BigInt>]
+ internal static partial long invoke1_BigInt64([JSMarshalAs<JSType.BigInt>] long value, [JSMarshalAs<JSType.String>] string name);
+ [JSExport]
+ [return: JSMarshalAs<JSType.BigInt>]
+ public static long EchoBigInt64([JSMarshalAs<JSType.BigInt>] long arg1)
+ {
+ return arg1;
+ }
+ #endregion BigInt64
+
+ #region Double
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial double echo1_Double([JSMarshalAs<JSType.Number>] double value);
+ [JSImport("store1", "JavaScriptTestHelper")]
+ internal static partial void store1_Double([JSMarshalAs<JSType.Number>] double value);
+ [JSImport("retrieve1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial double retrieve1_Double();
+ [JSImport("identity1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool identity1_Double([JSMarshalAs<JSType.Number>] double value);
+ [JSImport("throw1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial double throw1_Double([JSMarshalAs<JSType.Number>] double value);
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial double invoke1_Double([JSMarshalAs<JSType.Number>] double value, [JSMarshalAs<JSType.String>] string name);
+ [JSExport]
+ [return: JSMarshalAs<JSType.Number>]
+ public static double EchoDouble([JSMarshalAs<JSType.Number>] double arg1)
+ {
+ return arg1;
+ }
+ #endregion Double
+
+ #region Single
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial float echo1_Single([JSMarshalAs<JSType.Number>] float value);
+ [JSImport("store1", "JavaScriptTestHelper")]
+ internal static partial void store1_Single([JSMarshalAs<JSType.Number>] float value);
+ [JSImport("retrieve1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial float retrieve1_Single();
+ [JSImport("identity1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool identity1_Single([JSMarshalAs<JSType.Number>] float value);
+ [JSImport("throw1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial float throw1_Single([JSMarshalAs<JSType.Number>] float value);
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial float invoke1_Single([JSMarshalAs<JSType.Number>] float value, [JSMarshalAs<JSType.String>] string name);
+ [JSExport]
+ [return: JSMarshalAs<JSType.Number>]
+ public static float EchoSingle([JSMarshalAs<JSType.Number>] float arg1)
+ {
+ return arg1;
+ }
+ #endregion Single
+
+ #region IntPtr
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial IntPtr echo1_IntPtr([JSMarshalAs<JSType.Number>] IntPtr value);
+ [JSImport("store1", "JavaScriptTestHelper")]
+ internal static partial void store1_IntPtr([JSMarshalAs<JSType.Number>] IntPtr value);
+ [JSImport("retrieve1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial IntPtr retrieve1_IntPtr();
+ [JSImport("identity1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool identity1_IntPtr([JSMarshalAs<JSType.Number>] IntPtr value);
+ [JSImport("throw1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial IntPtr throw1_IntPtr([JSMarshalAs<JSType.Number>] IntPtr value);
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial IntPtr invoke1_IntPtr([JSMarshalAs<JSType.Number>] IntPtr value, [JSMarshalAs<JSType.String>] string name);
+ [JSExport]
+ [return: JSMarshalAs<JSType.Number>]
+ public static IntPtr EchoIntPtr([JSMarshalAs<JSType.Number>] IntPtr arg1)
+ {
+ return arg1;
+ }
+ #endregion IntPtr
+
+ #region VoidPtr
+
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal unsafe static partial void* echo1_VoidPtr([JSMarshalAs<JSType.Number>] void* value);
+ [JSImport("store1", "JavaScriptTestHelper")]
+ internal unsafe static partial void store1_VoidPtr([JSMarshalAs<JSType.Number>] void* value);
+ [JSImport("retrieve1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal unsafe static partial void* retrieve1_VoidPtr();
+ [JSImport("identity1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal unsafe static partial bool identity1_VoidPtr([JSMarshalAs<JSType.Number>] void* value);
+ [JSImport("throw1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal unsafe static partial void* throw1_VoidPtr([JSMarshalAs<JSType.Number>] void* value);
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal unsafe static partial void* invoke1_VoidPtr([JSMarshalAs<JSType.Number>] void* value, [JSMarshalAs<JSType.String>] string name);
+ [JSExport]
+ [return: JSMarshalAs<JSType.Number>]
+ public unsafe static void* EchoVoidPtr([JSMarshalAs<JSType.Number>] void* arg1)
+ {
+ return arg1;
+ }
+ #endregion VoidPtr
+
+ #region DateTime
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Date>]
+ internal static partial DateTime echo1_DateTime([JSMarshalAs<JSType.Date>] DateTime value);
+ [JSImport("store1", "JavaScriptTestHelper")]
+ internal static partial void store1_DateTime([JSMarshalAs<JSType.Date>] DateTime value);
+ [JSImport("retrieve1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Date>]
+ internal static partial DateTime retrieve1_DateTime();
+ [JSImport("identity1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool identity1_DateTime([JSMarshalAs<JSType.Date>] DateTime value);
+ [JSImport("throw1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Date>]
+ internal static partial DateTime throw1_DateTime([JSMarshalAs<JSType.Date>] DateTime value);
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Date>]
+ internal static partial DateTime invoke1_DateTime([JSMarshalAs<JSType.Date>] DateTime value, [JSMarshalAs<JSType.String>] string name);
+ [JSExport]
+ [return: JSMarshalAs<JSType.Date>]
+ public static DateTime EchoDateTime([JSMarshalAs<JSType.Date>] DateTime arg1)
+ {
+ return arg1;
+ }
+ #endregion DateTime
+
+ #region DateTimeOffset
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Date>]
+ internal static partial DateTimeOffset echo1_DateTimeOffset([JSMarshalAs<JSType.Date>] DateTimeOffset value);
+ [JSImport("store1", "JavaScriptTestHelper")]
+ internal static partial void store1_DateTimeOffset([JSMarshalAs<JSType.Date>] DateTimeOffset value);
+ [JSImport("retrieve1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Date>]
+ internal static partial DateTimeOffset retrieve1_DateTimeOffset();
+ [JSImport("identity1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool identity1_DateTimeOffset([JSMarshalAs<JSType.Date>] DateTimeOffset value);
+ [JSImport("throw1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Date>]
+ internal static partial DateTimeOffset throw1_DateTimeOffset([JSMarshalAs<JSType.Date>] DateTimeOffset value);
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Date>]
+ internal static partial DateTimeOffset invoke1_DateTimeOffset([JSMarshalAs<JSType.Date>] DateTimeOffset value, [JSMarshalAs<JSType.String>] string name);
+ [JSExport]
+ [return: JSMarshalAs<JSType.Date>]
+ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs<JSType.Date>] DateTimeOffset arg1)
+ {
+ return arg1;
+ }
+ #endregion DateTimeOffset
+
+ #region NullableBoolean
+
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool? echo1_NullableBoolean([JSMarshalAs<JSType.Boolean>] bool? value);
+ [JSImport("store1", "JavaScriptTestHelper")]
+ internal static partial void store1_NullableBoolean([JSMarshalAs<JSType.Boolean>] bool? value);
+ [JSImport("retrieve1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool? retrieve1_NullableBoolean();
+ [JSImport("identity1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool identity1_NullableBoolean([JSMarshalAs<JSType.Boolean>] bool? value);
+ [JSImport("throw1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool? throw1_NullableBoolean([JSMarshalAs<JSType.Boolean>] bool? value);
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool? invoke1_NullableBoolean([JSMarshalAs<JSType.Boolean>] bool? value, [JSMarshalAs<JSType.String>] string name);
+ [JSExport]
+ [return: JSMarshalAs<JSType.Boolean>]
+ public static bool? EchoNullableBoolean([JSMarshalAs<JSType.Boolean>] bool? arg1)
+ {
+ return arg1;
+ }
+ #endregion NullableBoolean
+
+ #region NullableInt32
+
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial int? echo1_NullableInt32([JSMarshalAs<JSType.Number>] int? value);
+ [JSImport("store1", "JavaScriptTestHelper")]
+ internal static partial void store1_NullableInt32([JSMarshalAs<JSType.Number>] int? value);
+ [JSImport("retrieve1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial int? retrieve1_NullableInt32();
+ [JSImport("identity1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool identity1_NullableInt32([JSMarshalAs<JSType.Number>] int? value);
+ [JSImport("throw1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial int? throw1_NullableInt32([JSMarshalAs<JSType.Number>] int? value);
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial int? invoke1_NullableInt32([JSMarshalAs<JSType.Number>] int? value, [JSMarshalAs<JSType.String>] string name);
+ [JSExport]
+ [return: JSMarshalAs<JSType.Number>]
+ public static int? EchoNullableInt32([JSMarshalAs<JSType.Number>] int? arg1)
+ {
+ return arg1;
+ }
+ #endregion NullableInt32
+
+ #region NullableBigInt64
+
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.BigInt>]
+ internal static partial long? echo1_NullableBigInt64([JSMarshalAs<JSType.BigInt>] long? value);
+ [JSImport("store1", "JavaScriptTestHelper")]
+ internal static partial void store1_NullableBigInt64([JSMarshalAs<JSType.BigInt>] long? value);
+ [JSImport("retrieve1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.BigInt>]
+ internal static partial long? retrieve1_NullableBigInt64();
+ [JSImport("identity1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool identity1_NullableBigInt64([JSMarshalAs<JSType.BigInt>] long? value);
+ [JSImport("throw1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.BigInt>]
+ internal static partial long? throw1_NullableBigInt64([JSMarshalAs<JSType.BigInt>] long? value);
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.BigInt>]
+ internal static partial long? invoke1_NullableBigInt64([JSMarshalAs<JSType.BigInt>] long? value, [JSMarshalAs<JSType.String>] string name);
+ [JSExport]
+ [return: JSMarshalAs<JSType.BigInt>]
+ public static long? EchoNullableBigInt64([JSMarshalAs<JSType.BigInt>] long? arg1)
+ {
+ return arg1;
+ }
+ #endregion NullableBigInt64
+
+ #region NullableIntPtr
+
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial IntPtr? echo1_NullableIntPtr([JSMarshalAs<JSType.Number>] IntPtr? value);
+ [JSImport("store1", "JavaScriptTestHelper")]
+ internal static partial void store1_NullableIntPtr([JSMarshalAs<JSType.Number>] IntPtr? value);
+ [JSImport("retrieve1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial IntPtr? retrieve1_NullableIntPtr();
+ [JSImport("identity1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool identity1_NullableIntPtr([JSMarshalAs<JSType.Number>] IntPtr? value);
+ [JSImport("throw1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial IntPtr? throw1_NullableIntPtr([JSMarshalAs<JSType.Number>] IntPtr? value);
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial IntPtr? invoke1_NullableIntPtr([JSMarshalAs<JSType.Number>] IntPtr? value, [JSMarshalAs<JSType.String>] string name);
+ [JSExport]
+ [return: JSMarshalAs<JSType.Number>]
+ public static IntPtr? EchoNullableIntPtr([JSMarshalAs<JSType.Number>] IntPtr? arg1)
+ {
+ return arg1;
+ }
+ #endregion NullableIntPtr
+
+ #region NullableDouble
+
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial double? echo1_NullableDouble([JSMarshalAs<JSType.Number>] double? value);
+ [JSImport("store1", "JavaScriptTestHelper")]
+ internal static partial void store1_NullableDouble([JSMarshalAs<JSType.Number>] double? value);
+ [JSImport("retrieve1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial double? retrieve1_NullableDouble();
+ [JSImport("identity1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool identity1_NullableDouble([JSMarshalAs<JSType.Number>] double? value);
+ [JSImport("throw1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial double? throw1_NullableDouble([JSMarshalAs<JSType.Number>] double? value);
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Number>]
+ internal static partial double? invoke1_NullableDouble([JSMarshalAs<JSType.Number>] double? value, [JSMarshalAs<JSType.String>] string name);
+ [JSExport]
+ [return: JSMarshalAs<JSType.Number>]
+ public static double? EchoNullableDouble([JSMarshalAs<JSType.Number>] double? arg1)
+ {
+ return arg1;
+ }
+ #endregion NullableDouble
+
+ #region NullableDateTime
+
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Date>]
+ internal static partial DateTime? echo1_NullableDateTime([JSMarshalAs<JSType.Date>] DateTime? value);
+ [JSImport("store1", "JavaScriptTestHelper")]
+ internal static partial void store1_NullableDateTime([JSMarshalAs<JSType.Date>] DateTime? value);
+ [JSImport("retrieve1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Date>]
+ internal static partial DateTime? retrieve1_NullableDateTime();
+ [JSImport("identity1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool identity1_NullableDateTime([JSMarshalAs<JSType.Date>] DateTime? value);
+ [JSImport("throw1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Date>]
+ internal static partial DateTime? throw1_NullableDateTime([JSMarshalAs<JSType.Date>] DateTime? value);
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Date>]
+ internal static partial DateTime? invoke1_NullableDateTime([JSMarshalAs<JSType.Date>] DateTime? value, [JSMarshalAs<JSType.String>] string name);
+ [JSExport]
+ [return: JSMarshalAs<JSType.Date>]
+ public static DateTime? EchoNullableDateTime([JSMarshalAs<JSType.Date>] DateTime? arg1)
+ {
+ return arg1;
+ }
+ #endregion NullableDateTime
+
+ #region JSObject
+ [JSImport("echo1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Object>]
+ internal static partial JSObject echo1_JSObject([JSMarshalAs<JSType.Object>] JSObject value);
+ [JSImport("store1", "JavaScriptTestHelper")]
+ internal static partial void store1_JSObject([JSMarshalAs<JSType.Object>] JSObject value);
+ [JSImport("retrieve1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Object>]
+ internal static partial JSObject retrieve1_JSObject();
+ [JSImport("identity1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Boolean>]
+ internal static partial bool identity1_JSObject([JSMarshalAs<JSType.Object>] JSObject value);
+ [JSImport("throw1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Object>]
+ internal static partial JSObject throw1_JSObject([JSMarshalAs<JSType.Object>] JSObject value);
+ [JSImport("invoke1", "JavaScriptTestHelper")]
+ [return: JSMarshalAs<JSType.Object>]
+ internal static partial JSObject invoke1_JSObject([JSMarshalAs<JSType.Object>] JSObject value, [JSMarshalAs<JSType.String>] string name);
+ [JSExport]
+ [return: JSMarshalAs<JSType.Object>]
+ public static JSObject EchoIJSObject([JSMarshalAs<JSType.Object>] JSObject arg1)
+ {
+ return arg1;
+ }
+ #endregion JSObject
+
+ static JSObject _module;
+ public static async Task InitializeAsync()
+ {
+ if (_module == null)
+ {
+ Log("JavaScriptTestHelper.mjs importing");
+ _module = await JSHost.ImportAsync("JavaScriptTestHelper", "./JavaScriptTestHelper.mjs");
+ Log("JavaScriptTestHelper.mjs imported");
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs
new file mode 100644
index 00000000000..f91a9661620
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs
@@ -0,0 +1,254 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+class JSData {
+ constructor(name) {
+ this.name = name;
+ }
+ toString() {
+ return `JSData("${this.name}")`;
+ }
+}
+
+class JSTestError extends Error {
+ constructor(message) {
+ super(message)
+ }
+}
+
+export function createData(name) {
+ //console.log(`createData(name:"${name ? name : '<null>'}")`)
+ return new JSData(name);
+}
+
+export function createException(name) {
+ //console.log(`createException(name:"${name ? name : '<null>'}")`)
+ return new JSTestError(name);
+}
+
+export function echo1(arg1) {
+ if (globalThis.gc) {
+ // console.log('globalThis.gc');
+ globalThis.gc();
+ }
+
+ //console.log(`echo1(arg1:${arg1 !== null ? JSON.stringify(arg1): '<null>'})`)
+ return arg1;
+}
+
+export function echo1view(arg1, edit) {
+ if (globalThis.gc) {
+ // console.log('globalThis.gc');
+ globalThis.gc();
+ }
+
+ // console.log(`echo1view(arg1:${arg1 !== null ? arg1 : '<null>'})`)
+ // console.log(`echo1view(arg1:${arg1 !== null ? typeof arg1 : '<null>'})`)
+ const cpy = arg1.slice();
+ if (edit) {
+ cpy[1] = cpy[0]
+ arg1.set(cpy);
+ }
+ return arg1;
+}
+
+export function echo1array(arg1, edit) {
+ if (globalThis.gc) {
+ // console.log('globalThis.gc');
+ globalThis.gc();
+ }
+
+ // console.log(`echo1view(arg1:${arg1 !== null ? arg1 : '<null>'})`)
+ // console.log(`echo1view(arg1:${arg1 !== null ? typeof arg1 : '<null>'})`)
+ if (edit) {
+ arg1[1] = arg1[0]
+ }
+ return arg1;
+}
+
+export function echo1large(arg1) {
+ try {
+ arg1._large = new Uint8Array(10000000);
+ }
+ catch (ex) {
+ console.log("echo1large " + ex)
+ }
+ if (globalThis.gc) {
+ // console.log('globalThis.gc');
+ globalThis.gc();
+ }
+
+ //console.log(`echo1large(arg1:${arg1 !== null ? typeof arg1: '<null>'})`)
+ return () => {
+ console.log("don't call me");
+ };
+}
+
+export function store1(arg1) {
+ //console.log(`store1(arg1:${arg1 !== null ? arg1 : '<null>'})`)
+ globalThis.javaScriptTestHelper.store1val = arg1;
+}
+
+export function storeAt(arg1, arg2) {
+ //console.log(`storeAt(arg1:${arg1 !== null ? arg1 : '<null>'})`)
+ //console.log(`storeAt(arg2:${arg2 !== null ? arg2 : '<null>'})`)
+ globalThis.javaScriptTestHelper.store1val = arg1[arg2];
+ return arg1[arg2];
+}
+
+export function retrieve1() {
+ const val = globalThis.javaScriptTestHelper.store1val;
+ //console.log(`retrieve1(arg1:${val !== null ? val : '<null>'})`)
+ return val;
+}
+
+export function throw0() {
+ //console.log(`throw0()`)
+ throw new Error('throw-0-msg');
+}
+
+export function throw1(arg1) {
+ //console.log(`throw1(arg1:${arg1 !== null ? arg1 : '<null>'})`)
+ throw new Error('throw1-msg ' + arg1);
+}
+
+export function throwretrieve1() {
+ const val = globalThis.javaScriptTestHelper.store1val;
+ //console.log(`retrieve1(arg1:${val !== null ? val : '<null>'})`)
+ throw new Error('throwretrieve1 ' + val);
+}
+
+export function identity1(arg1) {
+ const val = globalThis.javaScriptTestHelper.store1val;
+ //console.log(`compare1(arg1:${arg1 !== null ? arg1 : '<null>'}) with ${val !== null ? val : '<null>'}`)
+ if (val instanceof Date) {
+ return arg1.valueOf() == val.valueOf();
+ }
+ if (Number.isNaN(val)) {
+ return Number.isNaN(arg1);
+ }
+ return arg1 === val;
+}
+
+export function getType1() {
+ const val = globalThis.javaScriptTestHelper.store1val;
+ const vtype = typeof (val);
+ // console.log(`getType1(arg1:${vtype !== null ? vtype : '<null>'})`)
+ return vtype;
+}
+
+export function getClass1() {
+ const val = globalThis.javaScriptTestHelper.store1val;
+ const cname = val.constructor.name;
+ // console.log(`getClass1(arg1:${cname !== null ? cname : '<null>'})`)
+ return cname;
+}
+
+export function invoke1(arg1, name) {
+ if (globalThis.gc) {
+ // console.log('globalThis.gc');
+ globalThis.gc();
+ }
+ // console.log(`invoke1: ${name}(arg1:${arg1 !== null ? typeof arg1 : '<null>'})`)
+ const JavaScriptTestHelper = globalThis.App.EXPORTS.System.Runtime.InteropServices.JavaScript.Tests.JavaScriptTestHelper
+ const fn = JavaScriptTestHelper[name];
+
+ //console.log("invoke1:" + fn.toString());
+ const res = fn(arg1);
+ // console.log(`invoke1: res ${res !== null ? typeof res : '<null>'}`)
+ return res;
+}
+
+export async function awaitvoid(arg1) {
+ // console.log("awaitvoid:" + typeof arg1);
+ await arg1;
+ // console.log("awaitvoid done");
+}
+
+export function thenvoid(arg1) {
+ //console.log("thenvoid:" + typeof arg1);
+ arg1.then(() => {
+ // console.log("thenvoid then done");
+ });
+ //console.log("thenvoid done");
+}
+
+export async function await1(arg1) {
+ try {
+ // console.log("await1:" + typeof arg1);
+ const value = await arg1;
+ // console.log("await1 value:" + value);
+ return value;
+ } catch (ex) {
+ // console.log("await1 ex:" + ex);
+ throw ex;
+ }
+}
+
+export async function await2(arg1) {
+ //console.log("await2-1:" + typeof arg1);
+ await arg1;
+ //console.log("await2-2:" + typeof arg1);
+}
+
+export async function sleep(ms) {
+ if (globalThis.gc) {
+ // console.log('globalThis.gc');
+ globalThis.gc();
+ }
+ // console.log("sleep:" + ms);
+ await new Promise(resolve => setTimeout(resolve, ms));
+ // console.log("sleep2:" + ms);
+ return ms;
+}
+
+export async function forever() {
+ if (globalThis.gc) {
+ // console.log('globalThis.gc');
+ globalThis.gc();
+ }
+ // console.log("forever:" + ms);
+ await new Promise(() => { });
+}
+
+export function back3(arg1, arg2, arg3) {
+ if (globalThis.gc) {
+ // console.log('globalThis.gc');
+ globalThis.gc();
+ }
+ try {
+ if (!(arg1 instanceof Function)) throw new Error('expecting Function!')
+ //console.log(`back3(arg1:${arg1 !== null ? typeof (arg1) : '<null>'})`)
+ //console.log(`back3(arg2:${arg2 !== null ? arg2 : '<null>'})`)
+ //console.log(`back3(arg3:${arg3 !== null ? arg3 : '<null>'})`)
+
+ // call it twice, to make sure it's persistent
+ arg1(arg2, arg3);
+ //console.log(`back3(arg2:${arg2 !== null ? arg2 : '<null>'})`)
+ //console.log(`back3(arg3:${arg3 !== null ? arg3 : '<null>'})`)
+
+ return arg1(arg2, arg3);
+ }
+ catch (ex) {
+ // console.log(`back1 - catch`)
+ throw ex;
+ }
+}
+
+export function backback(arg1, arg2, arg3) {
+ if (globalThis.gc) {
+ // console.log('globalThis.gc');
+ globalThis.gc();
+ }
+ // console.log('backback A')
+ return (brg1, brg2) => {
+ // console.log('backback B')
+ return arg1(brg1 + arg2, brg2 + arg3);
+ }
+}
+
+export const instance = {}
+
+globalThis.javaScriptTestHelper = instance;
+
+// console.log('JavaScriptTestHelper:' Object.keys(globalThis.JavaScriptTestHelper));
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/Utils.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/Utils.cs
new file mode 100644
index 00000000000..ad9be6d83d5
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/Utils.cs
@@ -0,0 +1,145 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace System.Runtime.InteropServices.JavaScript
+{
+ public static unsafe partial class Utils
+ {
+ [JSImport("INTERNAL.set_property")]
+ public static partial void SetProperty(JSObject self, string propertyName,
+ [JSMarshalAs<JSType.Function<JSType.Object>>] Action<JSObject> value);
+
+ [JSImport("INTERNAL.get_property")]
+ [return: JSMarshalAs<JSType.Function<JSType.Object>>]
+ public static partial Action<JSObject> GetActionOfJSObjectProperty(JSObject self, string propertyName);
+
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function>]
+ public static partial Action CreateAction([JSMarshalAs<JSType.String>] string code);
+
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.Boolean>>]
+ public static partial Func<bool> CreateFunctionBool([JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.Number>>]
+ public static partial Func<int> CreateFunctionInt([JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.Number>>]
+ public static partial Func<long> CreateFunctionLong([JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.Number>>]
+ public static partial Func<double> CreateFunctionDouble([JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.String>>]
+ public static partial Func<string> CreateFunctionString([JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.Object>>]
+ public static partial Func<JSObject> CreateFunctionJSObject([JSMarshalAs<JSType.String>] string code);
+
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.Boolean>>]
+ public static partial Action<bool> CreateActionBool([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.Number>>]
+ public static partial Action<int> CreateActionInt([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.Number>>]
+ public static partial Action<long> CreateActionLong([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.Number>>]
+ public static partial Action<double> CreateActionDouble([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.String>>]
+ public static partial Action<string> CreateActionString([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.Object>>]
+ public static partial Action<JSObject> CreateActionJSObject([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string code);
+
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.Boolean, JSType.Boolean>>]
+ public static partial Func<bool, bool> CreateFunctionBoolBool([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.Number, JSType.Number>>]
+ public static partial Func<int, int> CreateFunctionIntInt([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.Number, JSType.Number>>]
+ public static partial Func<long, long> CreateFunctionLongLong([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.Number, JSType.Number>>]
+ public static partial Func<double, double> CreateFunctionDoubleDouble([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.String, JSType.String>>]
+ public static partial Func<string, string> CreateFunctionStringString([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.Object, JSType.Object>>]
+ public static partial Func<JSObject, JSObject> CreateFunctionJSObjectJSObject([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string code);
+
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.Boolean, JSType.Object>>]
+ public static partial Func<bool, JSObject> CreateFunctionBoolJSObject([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.Number, JSType.Object>>]
+ public static partial Func<int, JSObject> CreateFunctionIntJSObject([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.Number, JSType.Object>>]
+ public static partial Func<long, JSObject> CreateFunctionLongJSObject([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.Number, JSType.Object>>]
+ public static partial Func<double, JSObject> CreateFunctionDoubleJSObject([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.String, JSType.Object>>]
+ public static partial Func<string, JSObject> CreateFunctionStringJSObject([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string code);
+
+ /* TODO
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs(JSType.Function, JSType.Boolean, JSType.Promise)]
+ public static partial Func<bool, Task<object>> CreateFunctionBoolTask([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs(JSType.Function, JSType.Number, JSType.Promise)]
+ public static partial Func<int, Task<object>> CreateFunctionIntTask([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs(JSType.Function, JSType.Number, JSType.Promise)]
+ public static partial Func<long, Task<object>> CreateFunctionLongTask([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs(JSType.Function, JSType.Number, JSType.Promise)]
+ public static partial Func<double, Task<object>> CreateFunctionDoubleJSTask([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs(JSType.Function, JSType.String, JSType.Promise)]
+ public static partial Func<string, Task<object>> CreateFunctionStringTask([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string code);
+ [return: JSMarshalAs(JSType.Function, JSType.Promise)]
+ public static partial Func<Task<object>> CreateFunctionTask([JSMarshalAs<JSType.String>] string code);
+ */
+
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.Number, JSType.Number>>]
+ public static partial Action<int, int> CreateActionIntInt([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string arg2Name, [JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.Number, JSType.Number>>]
+ public static partial Action<long, long> CreateActionLongLong([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string arg2Name, [JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.Number, JSType.Number>>]
+ public static partial Action<double, double> CreateActionDoubleDouble([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string arg2Name, [JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.String, JSType.String>>]
+ public static partial Action<string, string> CreateActionStringString([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string arg2Name, [JSMarshalAs<JSType.String>] string code);
+
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.Number, JSType.Number, JSType.Number>>]
+ public static partial Func<int, int, int> CreateFunctionIntIntInt([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string arg2Name, [JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.Number, JSType.Number, JSType.Number>>]
+ public static partial Func<long, long, long> CreateFunctionLongLongLong([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string arg2Name, [JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.Number, JSType.Number, JSType.Number>>]
+ public static partial Func<double, double, double> CreateFunctionDoubleDoubleDouble([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string arg2Name, [JSMarshalAs<JSType.String>] string code);
+ [JSImport("globalThis.App.create_function")]
+ [return: JSMarshalAs<JSType.Function<JSType.String, JSType.String, JSType.String>>]
+ public static partial Func<string, string, string> CreateFunctionStringStringString([JSMarshalAs<JSType.String>] string arg1Name, [JSMarshalAs<JSType.String>] string arg2Name, [JSMarshalAs<JSType.String>] string code);
+ }
+}
diff --git a/src/mono/System.Private.CoreLib/src/System/ValueType.cs b/src/mono/System.Private.CoreLib/src/System/ValueType.cs
index fb97a52cae8..262b30baff0 100644
--- a/src/mono/System.Private.CoreLib/src/System/ValueType.cs
+++ b/src/mono/System.Private.CoreLib/src/System/ValueType.cs
@@ -12,7 +12,7 @@ namespace System
{
#if TARGET_BROWSER
// Tracking issue https://github.com/dotnet/runtime/issues/47909
- [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, "System.Runtime.InteropServices.JavaScript.Runtime", "System.Private.Runtime.InteropServices.JavaScript")]
+ [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, "System.Runtime.InteropServices.JavaScript.JSFunctionBinding", "System.Runtime.InteropServices.JavaScript")]
#endif
protected ValueType()
{
diff --git a/src/mono/sample/wasm/browser/Program.cs b/src/mono/sample/wasm/browser/Program.cs
index 66ff814d0ef..eb2506e0f21 100644
--- a/src/mono/sample/wasm/browser/Program.cs
+++ b/src/mono/sample/wasm/browser/Program.cs
@@ -3,10 +3,11 @@
using System;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices.JavaScript;
namespace Sample
{
- public class Test
+ public partial class Test
{
public static int Main(string[] args)
{
@@ -14,10 +15,33 @@ namespace Sample
return 0;
}
- [MethodImpl(MethodImplOptions.NoInlining)]
- public static int TestMeaning()
+ [JSImport("Sample.Test.add")]
+ internal static partial int Add(int a, int b);
+
+ [JSImport("Sample.Test.sub")]
+ internal static partial int Sub(int a, int b);
+
+ [JSExport]
+ internal static int TestMeaning()
{
- return 42;
+ // call back to JS via imports
+ return Add(Sub(80, 40), 2);
}
+
+ [JSExport]
+ internal static bool IsPrime(int number)
+ {
+ if (number <= 1) return false;
+ if (number == 2) return true;
+ if (number % 2 == 0) return false;
+
+ var boundary = (int)Math.Floor(Math.Sqrt(number));
+
+ for (int i = 3; i <= boundary; i += 2)
+ if (number % i == 0)
+ return false;
+
+ return true;
+ }
}
}
diff --git a/src/mono/sample/wasm/browser/Wasm.Browser.ES6.Sample.csproj b/src/mono/sample/wasm/browser/Wasm.Browser.ES6.Sample.csproj
index 7c5e62b94c9..772c4add1c5 100644
--- a/src/mono/sample/wasm/browser/Wasm.Browser.ES6.Sample.csproj
+++ b/src/mono/sample/wasm/browser/Wasm.Browser.ES6.Sample.csproj
@@ -5,18 +5,15 @@
<DebugSymbols>true</DebugSymbols>
<DebugType>embedded</DebugType>
<WasmDebugLevel>1</WasmDebugLevel>
-
<GenerateRunScriptForSample Condition="'$(ArchiveTests)' == 'true'">true</GenerateRunScriptForSample>
<RunScriptCommand>$(ExecXHarnessCmd) wasm test-browser --app=. --browser=Chrome $(XHarnessBrowserPathArg) --html-file=index.html --output-directory=$(XHarnessOutput) -- $(MSBuildProjectName).dll</RunScriptCommand>
</PropertyGroup>
-
<ItemGroup>
<WasmExtraFilesToDeploy Include="index.html" />
+ <ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\gen\JSImportGenerator\JSImportGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
-
<PropertyGroup>
<_SampleProject>Wasm.Browser.ES6.Sample.csproj</_SampleProject>
</PropertyGroup>
-
<Target Name="RunSample" DependsOnTargets="RunSampleWithBrowser" />
</Project>
diff --git a/src/mono/sample/wasm/browser/main.js b/src/mono/sample/wasm/browser/main.js
index faba0f1ebee..06f216259f5 100644
--- a/src/mono/sample/wasm/browser/main.js
+++ b/src/mono/sample/wasm/browser/main.js
@@ -12,8 +12,16 @@ function wasm_exit(exit_code, reason) {
console.log(`WASM EXIT ${exit_code}`);
}
+function add(a, b) {
+ return a + b;
+}
+
+function sub(a, b) {
+ return a - b;
+}
+
try {
- const { MONO, BINDING, Module, RuntimeBuildInfo } = await createDotnetRuntime(() => {
+ const { MONO, RuntimeBuildInfo, IMPORTS } = await createDotnetRuntime(() => {
console.log('user code in createDotnetRuntime');
return {
configSrc: "./mono-config.json",
@@ -24,11 +32,20 @@ try {
}
});
console.log('after createDotnetRuntime');
+ IMPORTS.Sample = {
+ Test: {
+ add,
+ sub
+ }
+ };
- const testMeaning = BINDING.bind_static_method("[Wasm.Browser.ES6.Sample] Sample.Test:TestMeaning");
- const ret = testMeaning();
- document.getElementById("out").innerHTML = `${ret} as computed on dotnet ver ${RuntimeBuildInfo.ProductVersion}`;
- console.debug(`ret: ${ret}`);
+ const exports = await MONO.mono_wasm_get_assembly_exports("Wasm.Browser.ES6.Sample.dll");
+ const meaning = exports.Sample.Test.TestMeaning();
+ console.debug(`meaning: ${meaning}`);
+ if (!exports.Sample.Test.IsPrime(meaning)) {
+ document.getElementById("out").innerHTML = `${meaning} as computed on dotnet ver ${RuntimeBuildInfo.ProductVersion}`;
+ console.debug(`ret: ${meaning}`);
+ }
let exit_code = await MONO.mono_run_main("Wasm.Browser.ES6.Sample.dll", []);
wasm_exit(exit_code);
diff --git a/src/mono/wasm/runtime/cancelable-promise.ts b/src/mono/wasm/runtime/cancelable-promise.ts
index ebd01a86e5b..422d3b1ff66 100644
--- a/src/mono/wasm/runtime/cancelable-promise.ts
+++ b/src/mono/wasm/runtime/cancelable-promise.ts
@@ -1,11 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-import { mono_wasm_get_jsobj_from_js_handle } from "./gc-handles";
-import { wrap_error_root } from "./method-calls";
-import { mono_wasm_new_external_root } from "./roots";
-import { JSHandle, MonoObject, MonoObjectRef, MonoString } from "./types";
-import { Int32Ptr } from "./types/emscripten";
+import { _lookup_js_owned_object } from "./gc-handles";
+import { TaskCallbackHolder } from "./marshal-to-cs";
+import { mono_assert, GCHandle } from "./types";
export const _are_promises_supported = ((typeof Promise === "object") || (typeof Promise === "function")) && (typeof Promise.resolve === "function");
export const promise_control_symbol = Symbol.for("wasm promise_control");
@@ -18,34 +16,20 @@ export function isThenable(js_obj: any): boolean {
((typeof js_obj === "object" || typeof js_obj === "function") && typeof js_obj.then === "function");
}
-export function mono_wasm_cancel_promise_ref(thenable_js_handle: JSHandle, is_exception: Int32Ptr, result_address: MonoObjectRef): void | MonoString {
- const resultRoot = mono_wasm_new_external_root<MonoObject>(result_address);
- try {
- const promise = mono_wasm_get_jsobj_from_js_handle(thenable_js_handle);
- const promise_control = promise[promise_control_symbol];
- promise_control.reject("OperationCanceledException");
- }
- catch (ex) {
- wrap_error_root(is_exception, ex, resultRoot);
- return;
- }
- finally {
- resultRoot.release();
- }
-}
-
export interface PromiseControl {
isDone: boolean;
resolve: (data?: any) => void;
reject: (reason: any) => void;
+ promise: Promise<any>;
}
-export function _create_cancelable_promise(afterResolve?: () => void, afterReject?: () => void): {
+export function create_cancelable_promise(afterResolve?: () => void, afterReject?: () => void): {
promise: Promise<any>, promise_control: PromiseControl
} {
- let promise_control: PromiseControl | null = null;
+ let promise_control: PromiseControl = <any>null;
const promise = new Promise(function (resolve, reject) {
promise_control = {
+ promise: <any>null,
isDone: false,
resolve: (data: any) => {
if (!promise_control!.isDone) {
@@ -67,6 +51,26 @@ export function _create_cancelable_promise(afterResolve?: () => void, afterRejec
}
};
});
+ promise_control.promise = promise;
(<any>promise)[promise_control_symbol] = promise_control;
return { promise, promise_control: promise_control! };
-} \ No newline at end of file
+}
+
+export function wrap_as_cancelable_promise<T>(fn: () => Promise<T>): Promise<T> {
+ const { promise, promise_control } = create_cancelable_promise();
+ const inner = fn();
+ inner.then((data) => promise_control.resolve(data)).catch((reason) => promise_control.reject(reason));
+ return promise;
+}
+
+export function mono_wasm_cancel_promise(task_holder_gc_handle: GCHandle): void {
+ const holder = _lookup_js_owned_object(task_holder_gc_handle) as TaskCallbackHolder;
+ if (!holder) return; // probably already GC collected
+
+ const promise = holder.promise;
+ mono_assert(!!promise, () => `Expected Promise for GCHandle ${task_holder_gc_handle}`);
+ const promise_control = (<any>promise)[promise_control_symbol];
+ mono_assert(!!promise_control, () => `Expected promise_control for GCHandle ${task_holder_gc_handle}`);
+ promise_control.reject("OperationCanceledException");
+}
+
diff --git a/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js b/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js
index 3f8a0c56cda..93aaa3a2e24 100644
--- a/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js
+++ b/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js
@@ -74,13 +74,11 @@ const linked_functions = [
"mono_wasm_typed_array_copy_to_ref",
"mono_wasm_typed_array_from_ref",
"mono_wasm_typed_array_copy_from_ref",
- "mono_wasm_cancel_promise_ref",
- "mono_wasm_web_socket_open_ref",
- "mono_wasm_web_socket_send",
- "mono_wasm_web_socket_receive",
- "mono_wasm_web_socket_close_ref",
- "mono_wasm_web_socket_abort",
"mono_wasm_compile_function_ref",
+ "mono_wasm_bind_js_function",
+ "mono_wasm_invoke_bound_function",
+ "mono_wasm_bind_cs_function",
+ "mono_wasm_marshal_promise",
// pal_icushim_static.c
"mono_wasm_load_icu_data",
diff --git a/src/mono/wasm/runtime/corebindings.c b/src/mono/wasm/runtime/corebindings.c
index 2313924d9b9..11bd47ee8a3 100644
--- a/src/mono/wasm/runtime/corebindings.c
+++ b/src/mono/wasm/runtime/corebindings.c
@@ -29,12 +29,12 @@ extern void mono_wasm_typed_array_to_array_ref (int js_handle, int *is_exception
extern void mono_wasm_typed_array_copy_to_ref (int js_handle, int ptr, int begin, int end, int bytes_per_element, int *is_exception, MonoObject** result);
extern void mono_wasm_typed_array_from_ref (int ptr, int begin, int end, int bytes_per_element, int type, int *is_exception, MonoObject** result);
extern void mono_wasm_typed_array_copy_from_ref (int js_handle, int ptr, int begin, int end, int bytes_per_element, int *is_exception, MonoObject** result);
-extern void mono_wasm_cancel_promise_ref (int thenable_js_handle, int *is_exception, MonoString** result);
-extern void mono_wasm_web_socket_open_ref (MonoString **uri, MonoArray **subProtocols, MonoDelegate **on_close, int *web_socket_js_handle, int *thenable_js_handle, int *is_exception, MonoObject **result);
-extern void mono_wasm_web_socket_send (int webSocket_js_handle, void* buffer_ptr, int offset, int length, int message_type, int end_of_message, int *thenable_js_handle, int *is_exception, MonoObject **result);
-extern void mono_wasm_web_socket_receive (int webSocket_js_handle, void* buffer_ptr, int offset, int length, void* response_ptr, int *thenable_js_handle, int *is_exception, MonoObject **result);
-extern void mono_wasm_web_socket_close_ref (int webSocket_js_handle, int code, MonoString **reason, int wait_for_close_received, int *thenable_js_handle, int *is_exception, MonoObject **result);
-extern void mono_wasm_web_socket_abort (int webSocket_js_handle, int *is_exception, MonoString **result);
+
+extern void mono_wasm_bind_js_function(MonoString **function_name, MonoString **module_name, void *signature, int* function_js_handle, int *is_exception, MonoObject **result);
+extern void mono_wasm_invoke_bound_function(int function_js_handle, void *data);
+extern void mono_wasm_bind_cs_function(MonoString **fully_qualified_name, int signature_hash, void* signatures, int *is_exception, MonoObject **result);
+extern void mono_wasm_marshal_promise(void *data);
+
void core_initialize_internals ()
{
@@ -50,12 +50,13 @@ void core_initialize_internals ()
mono_add_internal_call ("Interop/Runtime::TypedArrayCopyToRef", mono_wasm_typed_array_copy_to_ref);
mono_add_internal_call ("Interop/Runtime::TypedArrayFromRef", mono_wasm_typed_array_from_ref);
mono_add_internal_call ("Interop/Runtime::TypedArrayCopyFromRef", mono_wasm_typed_array_copy_from_ref);
- mono_add_internal_call ("Interop/Runtime::WebSocketOpenRef", mono_wasm_web_socket_open_ref);
- mono_add_internal_call ("Interop/Runtime::WebSocketSend", mono_wasm_web_socket_send);
- mono_add_internal_call ("Interop/Runtime::WebSocketReceive", mono_wasm_web_socket_receive);
- mono_add_internal_call ("Interop/Runtime::WebSocketCloseRef", mono_wasm_web_socket_close_ref);
- mono_add_internal_call ("Interop/Runtime::WebSocketAbort", mono_wasm_web_socket_abort);
- mono_add_internal_call ("Interop/Runtime::CancelPromiseRef", mono_wasm_cancel_promise_ref);
+
+ mono_add_internal_call ("Interop/Runtime::BindJSFunction", mono_wasm_bind_js_function);
+ mono_add_internal_call ("Interop/Runtime::InvokeJSFunction", mono_wasm_invoke_bound_function);
+ mono_add_internal_call ("Interop/Runtime::BindCSFunction", mono_wasm_bind_cs_function);
+ mono_add_internal_call ("Interop/Runtime::MarshalPromise", mono_wasm_marshal_promise);
+ mono_add_internal_call ("Interop/Runtime::RegisterGCRoot", mono_wasm_register_root);
+ mono_add_internal_call ("Interop/Runtime::DeregisterGCRoot", mono_wasm_deregister_root);
}
// Int8Array | int8_t | byte or SByte (signed byte)
diff --git a/src/mono/wasm/runtime/corebindings.ts b/src/mono/wasm/runtime/corebindings.ts
index 82be21ce2a2..bd9c0fb12eb 100644
--- a/src/mono/wasm/runtime/corebindings.ts
+++ b/src/mono/wasm/runtime/corebindings.ts
@@ -5,6 +5,7 @@ import { JSHandle, GCHandle, MonoObjectRef } from "./types";
import { PromiseControl } from "./cancelable-promise";
import { runtimeHelpers } from "./imports";
+// TODO replace all of this with [JSExport]
const fn_signatures: [jsname: string, csname: string, signature: string/*ArgsMarshalString*/][] = [
["_get_cs_owned_object_by_js_handle_ref", "GetCSOwnedObjectByJSHandleRef", "iim"],
["_get_cs_owned_object_js_handle_ref", "GetCSOwnedObjectJSHandleRef", "mi"],
@@ -16,6 +17,7 @@ const fn_signatures: [jsname: string, csname: string, signature: string/*ArgsMar
["_release_js_owned_object_by_gc_handle", "ReleaseJSOwnedObjectByGCHandle", "i"],
["_create_tcs", "CreateTaskSource", ""],
+ ["_create_task_callback", "CreateTaskCallback", ""],
["_set_tcs_result_ref", "SetTaskSourceResultRef", "iR"],
["_set_tcs_failure", "SetTaskSourceFailure", "is"],
["_get_tcs_task_ref", "GetTaskSourceTaskRef", "im"],
@@ -53,6 +55,8 @@ export interface t_CSwraps {
_create_date_time_ref(ticks: number, result: MonoObjectRef): void;
_create_uri_ref(uri: string, result: MonoObjectRef): void;
_is_simple_array_ref(obj: MonoObjectRef): boolean;
+
+ _create_task_callback(): GCHandle;
}
const wrapped_cs_functions: t_CSwraps = <any>{};
diff --git a/src/mono/wasm/runtime/cs-to-js.ts b/src/mono/wasm/runtime/cs-to-js.ts
index 9f5e583dbdb..0f5392ea880 100644
--- a/src/mono/wasm/runtime/cs-to-js.ts
+++ b/src/mono/wasm/runtime/cs-to-js.ts
@@ -14,9 +14,10 @@ import cwraps from "./cwraps";
import { get_js_owned_object_by_gc_handle_ref, js_owned_gc_handle_symbol, mono_wasm_get_jsobj_from_js_handle, mono_wasm_get_js_handle, setup_managed_proxy, teardown_managed_proxy, _lookup_js_owned_object } from "./gc-handles";
import { mono_method_get_call_signature_ref, call_method_ref, wrap_error_root } from "./method-calls";
import { js_to_mono_obj_root } from "./js-to-cs";
-import { _are_promises_supported, _create_cancelable_promise } from "./cancelable-promise";
+import { _are_promises_supported, create_cancelable_promise } from "./cancelable-promise";
import { getU32, getI32, getF32, getF64 } from "./memory";
import { Int32Ptr, VoidPtr } from "./types/emscripten";
+import { ManagedObject } from "./marshal";
const delegate_invoke_symbol = Symbol.for("wasm delegate_invoke");
const delegate_invoke_signature_symbol = Symbol.for("wasm delegate_invoke_signature");
@@ -296,7 +297,7 @@ function _unbox_task_root_as_promise(root: WasmRoot<MonoObject>) {
if (!result) {
const explicitFinalization = () => teardown_managed_proxy(result, gc_handle);
- const { promise, promise_control } = _create_cancelable_promise(explicitFinalization, explicitFinalization);
+ const { promise, promise_control } = create_cancelable_promise(explicitFinalization, explicitFinalization);
// note that we do not implement promise/task roundtrip
// With more complexity we could recover original instance when this promise is marshaled back to C#.
@@ -335,7 +336,7 @@ export function _unbox_ref_type_root_as_js_object(root: WasmRoot<MonoObject>): a
// If the JS object for this gc_handle was already collected (or was never created)
if (is_nullish(result)) {
- result = {};
+ result = new ManagedObject();
// keep the gc_handle so that we could easily convert it back to original C# object for roundtrip
result[js_owned_gc_handle_symbol] = gc_handle;
diff --git a/src/mono/wasm/runtime/cwraps.ts b/src/mono/wasm/runtime/cwraps.ts
index c4892f33dbf..b2963790bd2 100644
--- a/src/mono/wasm/runtime/cwraps.ts
+++ b/src/mono/wasm/runtime/cwraps.ts
@@ -8,6 +8,7 @@ import {
MonoType, MonoObjectRef, MonoStringRef
} from "./types";
import { Module } from "./imports";
+import { JSMarshalerArguments } from "./marshal";
import { VoidPtr, CharPtrPtr, Int32Ptr, CharPtr, ManagedPointer } from "./types/emscripten";
const fn_signatures: [ident: string, returnType: string | null, argTypes?: string[], opts?: any][] = [
@@ -37,6 +38,7 @@ const fn_signatures: [ident: string, returnType: string | null, argTypes?: strin
["mono_wasm_assembly_load", "number", ["string"]],
["mono_wasm_find_corlib_class", "number", ["string", "string"]],
["mono_wasm_assembly_find_class", "number", ["number", "string", "string"]],
+ ["mono_wasm_runtime_run_module_cctor", "void", ["number"]],
["mono_wasm_find_corlib_type", "number", ["string", "string"]],
["mono_wasm_assembly_find_type", "number", ["number", "string", "string"]],
["mono_wasm_assembly_find_method", "number", ["number", "string", "number"]],
@@ -79,6 +81,7 @@ const fn_signatures: [ident: string, returnType: string | null, argTypes?: strin
["mono_wasm_enable_on_demand_gc", "void", ["number"]],
["mono_profiler_init_aot", "void", ["number"]],
["mono_wasm_exec_regression", "number", ["number", "string"]],
+ ["mono_wasm_invoke_method_bound", "number", ["number", "number"]],
["mono_wasm_write_managed_pointer_unsafe", "void", ["number", "number"]],
["mono_wasm_copy_managed_pointer", "void", ["number", "number"]],
["mono_wasm_i52_to_f64", "number", ["number", "number"]],
@@ -182,12 +185,14 @@ export interface t_Cwraps {
mono_wasm_set_main_args(argc: number, argv: VoidPtr): void;
mono_profiler_init_aot(desc: string): void;
mono_wasm_exec_regression(verbose_level: number, image: string): number;
+ mono_wasm_invoke_method_bound(method: MonoMethod, args: JSMarshalerArguments): MonoString;
mono_wasm_write_managed_pointer_unsafe(destination: VoidPtr | MonoObjectRef, pointer: ManagedPointer): void;
mono_wasm_copy_managed_pointer(destination: VoidPtr | MonoObjectRef, source: VoidPtr | MonoObjectRef): void;
- mono_wasm_i52_to_f64 (source: VoidPtr, error: Int32Ptr) : number;
- mono_wasm_u52_to_f64 (source: VoidPtr, error: Int32Ptr) : number;
- mono_wasm_f64_to_i52 (destination: VoidPtr, value: number) : I52Error;
- mono_wasm_f64_to_u52 (destination: VoidPtr, value: number) : I52Error;
+ mono_wasm_i52_to_f64(source: VoidPtr, error: Int32Ptr): number;
+ mono_wasm_u52_to_f64(source: VoidPtr, error: Int32Ptr): number;
+ mono_wasm_f64_to_i52(destination: VoidPtr, value: number): I52Error;
+ mono_wasm_f64_to_u52(destination: VoidPtr, value: number): I52Error;
+ mono_wasm_runtime_run_module_cctor(assembly: MonoAssembly): void;
}
const wrapped_c_functions: t_Cwraps = <any>{};
diff --git a/src/mono/wasm/runtime/dotnet.d.ts b/src/mono/wasm/runtime/dotnet.d.ts
index 0f2110f53d0..dd58eb3e3f2 100644
--- a/src/mono/wasm/runtime/dotnet.d.ts
+++ b/src/mono/wasm/runtime/dotnet.d.ts
@@ -376,6 +376,49 @@ declare function getF64(offset: _MemOffset): number;
declare function mono_run_main_and_exit(main_assembly_name: string, args: string[]): Promise<void>;
declare function mono_run_main(main_assembly_name: string, args: string[]): Promise<number>;
+interface IDisposable {
+ dispose(): void;
+ get isDisposed(): boolean;
+}
+declare class ManagedObject implements IDisposable {
+ dispose(): void;
+ get isDisposed(): boolean;
+ toString(): string;
+}
+declare class ManagedError extends Error implements IDisposable {
+ constructor(message: string);
+ get stack(): string | undefined;
+ dispose(): void;
+ get isDisposed(): boolean;
+ toString(): string;
+}
+declare enum MemoryViewType {
+ Byte = 0,
+ Int32 = 1,
+ Double = 2
+}
+interface IMemoryView {
+ /**
+ * copies elements from provided source to the wasm memory.
+ * target has to have the elements of the same type as the underlying C# array.
+ * same as TypedArray.set()
+ */
+ set(source: TypedArray, targetOffset?: number): void;
+ /**
+ * copies elements from wasm memory to provided target.
+ * target has to have the elements of the same type as the underlying C# array.
+ */
+ copyTo(target: TypedArray, sourceOffset?: number): void;
+ /**
+ * same as TypedArray.slice()
+ */
+ slice(start?: number, end?: number): TypedArray;
+ get length(): number;
+ get byteLength(): number;
+}
+
+declare function mono_wasm_get_assembly_exports(assembly: string): Promise<any>;
+
declare const MONO: {
mono_wasm_setenv: typeof mono_wasm_setenv;
mono_wasm_load_bytes_into_heap: typeof mono_wasm_load_bytes_into_heap;
@@ -390,6 +433,7 @@ declare const MONO: {
mono_wasm_release_roots: typeof mono_wasm_release_roots;
mono_run_main: typeof mono_run_main;
mono_run_main_and_exit: typeof mono_run_main_and_exit;
+ mono_wasm_get_assembly_exports: typeof mono_wasm_get_assembly_exports;
mono_wasm_add_assembly: (name: string, data: VoidPtr, size: number) => number;
mono_wasm_load_runtime: (unused: string, debug_level: number) => void;
config: MonoConfig | MonoConfigError;
@@ -490,4 +534,33 @@ declare global {
function getDotnetRuntime(runtimeId: number): DotnetPublicAPI | undefined;
}
-export { BINDINGType, CreateDotnetRuntimeType, DotnetModuleConfig, DotnetPublicAPI, EmscriptenModule, MONOType, MonoArray, MonoObject, MonoString, VoidPtr, createDotnetRuntime as default };
+/**
+ * Span class is JS wrapper for System.Span<T>. This view doesn't own the memory, nor pin the underlying array.
+ * It's ideal to be used on call from C# with the buffer pinned there or with unmanaged memory.
+ * It is disposed at the end of the call to JS.
+ */
+declare class Span implements IMemoryView, IDisposable {
+ dispose(): void;
+ get isDisposed(): boolean;
+ set(source: TypedArray, targetOffset?: number | undefined): void;
+ copyTo(target: TypedArray, sourceOffset?: number | undefined): void;
+ slice(start?: number | undefined, end?: number | undefined): TypedArray;
+ get length(): number;
+ get byteLength(): number;
+}
+/**
+ * ArraySegment class is JS wrapper for System.ArraySegment<T>.
+ * This wrapper would also pin the underlying array and hold GCHandleType.Pinned until this JS instance is collected.
+ * User could dispose it manualy.
+ */
+declare class ArraySegment implements IMemoryView, IDisposable {
+ dispose(): void;
+ get isDisposed(): boolean;
+ set(source: TypedArray, targetOffset?: number | undefined): void;
+ copyTo(target: TypedArray, sourceOffset?: number | undefined): void;
+ slice(start?: number | undefined, end?: number | undefined): TypedArray;
+ get length(): number;
+ get byteLength(): number;
+}
+
+export { ArraySegment, BINDINGType, CreateDotnetRuntimeType, DotnetModuleConfig, DotnetPublicAPI, EmscriptenModule, IMemoryView, MONOType, ManagedError, ManagedObject, MemoryViewType, MonoArray, MonoObject, MonoString, Span, VoidPtr, createDotnetRuntime as default };
diff --git a/src/mono/wasm/runtime/driver.c b/src/mono/wasm/runtime/driver.c
index fb0b15101fd..c8ea3e445d5 100644
--- a/src/mono/wasm/runtime/driver.c
+++ b/src/mono/wasm/runtime/driver.c
@@ -447,7 +447,6 @@ get_native_to_interp (MonoMethod *method, void *extra_arg)
void mono_initialize_internals ()
{
mono_add_internal_call ("Interop/Runtime::InvokeJS", mono_wasm_invoke_js);
- // TODO: what happens when two types in different assemblies have the same FQN?
// Blazor specific custom routines - see dotnet_support.js for backing code
mono_add_internal_call ("WebAssembly.JSInterop.InternalCalls::InvokeJS", mono_wasm_invoke_js_blazor);
@@ -655,6 +654,22 @@ mono_wasm_assembly_find_class (MonoAssembly *assembly, const char *namespace, co
return result;
}
+extern int mono_runtime_run_module_cctor (MonoImage *image, MonoError *error);
+
+EMSCRIPTEN_KEEPALIVE void
+mono_wasm_runtime_run_module_cctor (MonoAssembly *assembly)
+{
+ assert (assembly);
+ MonoError error;
+ MONO_ENTER_GC_UNSAFE;
+ MonoImage *image = mono_assembly_get_image (assembly);
+ if (!mono_runtime_run_module_cctor(image, &error)) {
+ //g_print ("Failed to run module constructor due to %s\n", mono_error_get_message (error));
+ }
+ MONO_EXIT_GC_UNSAFE;
+}
+
+
EMSCRIPTEN_KEEPALIVE MonoMethod*
mono_wasm_assembly_find_method (MonoClass *klass, const char *name, int arguments)
{
@@ -742,6 +757,25 @@ mono_wasm_invoke_method (MonoMethod *method, MonoObject *this_arg, void *params[
return result;
}
+EMSCRIPTEN_KEEPALIVE MonoObject*
+mono_wasm_invoke_method_bound (MonoMethod *method, void* args)// JSMarshalerArguments
+{
+ MonoObject *exc = NULL;
+ MonoObject *res;
+
+ void *invoke_args[1] = { args };
+
+ mono_runtime_invoke (method, NULL, invoke_args, &exc);
+ if (exc) {
+ MonoObject *exc2 = NULL;
+ res = (MonoObject*)mono_object_to_string (exc, &exc2);
+ if (exc2)
+ res = (MonoObject*) mono_string_new (root_domain, "Exception Double Fault");
+ return res;
+ }
+ return NULL;
+}
+
EMSCRIPTEN_KEEPALIVE MonoMethod*
mono_wasm_assembly_get_entry_point (MonoAssembly *assembly, int auto_insert_breakpoint)
{
diff --git a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js
index 19fd04fecd5..36e8a8162ae 100644
--- a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js
+++ b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js
@@ -111,13 +111,11 @@ const linked_functions = [
"mono_wasm_typed_array_copy_to_ref",
"mono_wasm_typed_array_from_ref",
"mono_wasm_typed_array_copy_from_ref",
- "mono_wasm_cancel_promise_ref",
- "mono_wasm_web_socket_open_ref",
- "mono_wasm_web_socket_send",
- "mono_wasm_web_socket_receive",
- "mono_wasm_web_socket_close_ref",
- "mono_wasm_web_socket_abort",
"mono_wasm_compile_function_ref",
+ "mono_wasm_bind_js_function",
+ "mono_wasm_invoke_bound_function",
+ "mono_wasm_bind_cs_function",
+ "mono_wasm_marshal_promise",
// pal_icushim_static.c
"mono_wasm_load_icu_data",
diff --git a/src/mono/wasm/runtime/export-types.ts b/src/mono/wasm/runtime/export-types.ts
index 3cb8c0df090..057ed837709 100644
--- a/src/mono/wasm/runtime/export-types.ts
+++ b/src/mono/wasm/runtime/export-types.ts
@@ -2,8 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license.
import { BINDINGType, DotnetPublicAPI, MONOType } from "./exports";
+import { IDisposable, IMemoryView, ManagedError, ManagedObject, MemoryViewType } from "./marshal";
import { DotnetModuleConfig, MonoArray, MonoObject, MonoString } from "./types";
-import { EmscriptenModule, VoidPtr } from "./types/emscripten";
+import { EmscriptenModule, TypedArray, VoidPtr } from "./types/emscripten";
// -----------------------------------------------------------
// this files has all public exports from the dotnet.js module
@@ -20,10 +21,41 @@ declare global {
export default createDotnetRuntime;
+/**
+ * Span class is JS wrapper for System.Span<T>. This view doesn't own the memory, nor pin the underlying array.
+ * It's ideal to be used on call from C# with the buffer pinned there or with unmanaged memory.
+ * It is disposed at the end of the call to JS.
+ */
+declare class Span implements IMemoryView, IDisposable {
+ dispose(): void;
+ get isDisposed(): boolean;
+ set(source: TypedArray, targetOffset?: number | undefined): void;
+ copyTo(target: TypedArray, sourceOffset?: number | undefined): void;
+ slice(start?: number | undefined, end?: number | undefined): TypedArray;
+ get length(): number;
+ get byteLength(): number;
+}
+
+/**
+ * ArraySegment class is JS wrapper for System.ArraySegment<T>.
+ * This wrapper would also pin the underlying array and hold GCHandleType.Pinned until this JS instance is collected.
+ * User could dispose it manualy.
+ */
+declare class ArraySegment implements IMemoryView, IDisposable {
+ dispose(): void;
+ get isDisposed(): boolean;
+ set(source: TypedArray, targetOffset?: number | undefined): void;
+ copyTo(target: TypedArray, sourceOffset?: number | undefined): void;
+ slice(start?: number | undefined, end?: number | undefined): TypedArray;
+ get length(): number;
+ get byteLength(): number;
+}
+
export {
VoidPtr,
MonoObject, MonoString, MonoArray,
BINDINGType, MONOType, EmscriptenModule,
- DotnetPublicAPI, DotnetModuleConfig, CreateDotnetRuntimeType
+ DotnetPublicAPI, DotnetModuleConfig, CreateDotnetRuntimeType,
+ IMemoryView, MemoryViewType, ManagedObject, ManagedError, Span, ArraySegment
};
diff --git a/src/mono/wasm/runtime/exports.ts b/src/mono/wasm/runtime/exports.ts
index 34da483b5ec..34646ca213b 100644
--- a/src/mono/wasm/runtime/exports.ts
+++ b/src/mono/wasm/runtime/exports.ts
@@ -67,7 +67,13 @@ import { create_weak_ref } from "./weak-ref";
import { fetch_like, readAsync_like } from "./polyfills";
import { EmscriptenModule } from "./types/emscripten";
import { mono_run_main, mono_run_main_and_exit } from "./run";
+import { dynamic_import, get_global_this, get_property, get_typeof_property, has_property, mono_wasm_bind_js_function, mono_wasm_invoke_bound_function, set_property } from "./invoke-js";
+import { mono_wasm_bind_cs_function, mono_wasm_get_assembly_exports } from "./invoke-cs";
+import { mono_wasm_marshal_promise } from "./marshal-to-js";
+import { ws_wasm_abort, ws_wasm_close, ws_wasm_create, ws_wasm_open, ws_wasm_receive, ws_wasm_send } from "./web-socket";
+import { http_wasm_abort_request, http_wasm_abort_response, http_wasm_create_abort_controler, http_wasm_fetch, http_wasm_fetch_bytes, http_wasm_get_response_bytes, http_wasm_get_response_header_names, http_wasm_get_response_header_values, http_wasm_get_response_length, http_wasm_get_streamed_response_bytes, http_wasm_supports_streaming_response } from "./http";
import { diagnostics } from "./diagnostics";
+import { mono_wasm_cancel_promise } from "./cancelable-promise";
import {
dotnet_browser_can_use_subtle_crypto_impl,
dotnet_browser_simple_digest_hash,
@@ -75,8 +81,6 @@ import {
dotnet_browser_encrypt_decrypt,
dotnet_browser_derive_bits,
} from "./crypto-worker";
-import { mono_wasm_cancel_promise_ref } from "./cancelable-promise";
-import { mono_wasm_web_socket_open_ref, mono_wasm_web_socket_send, mono_wasm_web_socket_receive, mono_wasm_web_socket_close_ref, mono_wasm_web_socket_abort } from "./web-socket";
import { mono_wasm_pthread_on_pthread_attached, afterThreadInitTLS } from "./pthreads/worker";
import { afterLoadWasmModuleToWorker } from "./pthreads/browser";
@@ -95,6 +99,7 @@ const MONO = {
mono_wasm_release_roots,
mono_run_main,
mono_run_main_and_exit,
+ mono_wasm_get_assembly_exports,
// for Blazor's future!
mono_wasm_add_assembly: cwraps.mono_wasm_add_assembly,
@@ -393,12 +398,10 @@ export const __linker_exports: any = {
mono_wasm_typed_array_copy_to_ref,
mono_wasm_typed_array_from_ref,
mono_wasm_typed_array_copy_from_ref,
- mono_wasm_cancel_promise_ref,
- mono_wasm_web_socket_open_ref,
- mono_wasm_web_socket_send,
- mono_wasm_web_socket_receive,
- mono_wasm_web_socket_close_ref,
- mono_wasm_web_socket_abort,
+ mono_wasm_bind_js_function,
+ mono_wasm_invoke_bound_function,
+ mono_wasm_bind_cs_function,
+ mono_wasm_marshal_promise,
// also keep in sync with pal_icushim_static.c
mono_wasm_load_icu_data,
@@ -451,6 +454,37 @@ const INTERNAL: any = {
mono_wasm_change_debugger_log_level,
mono_wasm_debugger_attached,
mono_wasm_runtime_is_ready: <boolean>runtimeHelpers.mono_wasm_runtime_is_ready,
+
+ // interop
+ get_property,
+ set_property,
+ has_property,
+ get_typeof_property,
+ get_global_this,
+ get_dotnet_instance,
+ dynamic_import,
+
+ // BrowserWebSocket
+ mono_wasm_cancel_promise,
+ ws_wasm_create,
+ ws_wasm_open,
+ ws_wasm_send,
+ ws_wasm_receive,
+ ws_wasm_close,
+ ws_wasm_abort,
+
+ // BrowserHttpHandler
+ http_wasm_supports_streaming_response,
+ http_wasm_create_abort_controler,
+ http_wasm_abort_request,
+ http_wasm_abort_response,
+ http_wasm_fetch,
+ http_wasm_fetch_bytes,
+ http_wasm_get_response_header_names,
+ http_wasm_get_response_header_values,
+ http_wasm_get_response_bytes,
+ http_wasm_get_response_length,
+ http_wasm_get_streamed_response_bytes,
};
// this represents visibility in the javascript
diff --git a/src/mono/wasm/runtime/gc-common.h b/src/mono/wasm/runtime/gc-common.h
index 4ea2bb06797..92789e641a3 100644
--- a/src/mono/wasm/runtime/gc-common.h
+++ b/src/mono/wasm/runtime/gc-common.h
@@ -61,3 +61,9 @@ static void
copy_volatile (PPVOLATILE(MonoObject) destination, PPVOLATILE(MonoObject) source) {
mono_gc_wbarrier_generic_store_atomic((void*)destination, (MonoObject*)(*source));
}
+
+EMSCRIPTEN_KEEPALIVE int
+mono_wasm_register_root (char *start, size_t size, const char *name);
+
+EMSCRIPTEN_KEEPALIVE void
+mono_wasm_deregister_root (char *addr); \ No newline at end of file
diff --git a/src/mono/wasm/runtime/http.ts b/src/mono/wasm/runtime/http.ts
new file mode 100644
index 00000000000..d8249fad0b9
--- /dev/null
+++ b/src/mono/wasm/runtime/http.ts
@@ -0,0 +1,150 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+import { wrap_as_cancelable_promise } from "./cancelable-promise";
+import { MemoryViewType, Span } from "./marshal";
+import { mono_assert } from "./types";
+import { VoidPtr } from "./types/emscripten";
+
+export function http_wasm_supports_streaming_response(): boolean {
+ return typeof Response !== "undefined" && "body" in Response.prototype && typeof ReadableStream === "function";
+}
+
+export function http_wasm_create_abort_controler(): AbortController {
+ return new AbortController();
+}
+
+export function http_wasm_abort_request(abort_controller: AbortController): void {
+ abort_controller.abort();
+}
+
+export function http_wasm_abort_response(res: ResponseExtension): void {
+ res.__abort_controller.abort();
+ if (res.__reader) {
+ res.__reader.cancel();
+ }
+}
+
+export function http_wasm_fetch_bytes(url: string, header_names: string[], header_values: string[], option_names: string[], option_values: any[], abort_controller: AbortController, bodyPtr: VoidPtr, bodyLength: number): Promise<ResponseExtension> {
+ // the bufferPtr is pinned by the caller
+ const view = new Span(bodyPtr, bodyLength, MemoryViewType.Byte);
+ const copy = view.slice() as Uint8Array;
+ return http_wasm_fetch(url, header_names, header_values, option_names, option_values, abort_controller, copy);
+}
+
+export function http_wasm_fetch(url: string, header_names: string[], header_values: string[], option_names: string[], option_values: any[], abort_controller: AbortController, body: string | Uint8Array | null): Promise<ResponseExtension> {
+ mono_assert(url && typeof url === "string", "expected url string");
+ mono_assert(header_names && header_values && Array.isArray(header_names) && Array.isArray(header_values) && header_names.length === header_values.length, "expected headerNames and headerValues arrays");
+ mono_assert(option_names && option_values && Array.isArray(option_names) && Array.isArray(option_values) && option_names.length === option_values.length, "expected headerNames and headerValues arrays");
+ const headers = new Headers();
+ for (let i = 0; i < header_names.length; i++) {
+ headers.append(header_names[i], header_values[i]);
+ }
+ const options: any = {
+ body,
+ headers,
+ signal: abort_controller.signal
+ };
+ for (let i = 0; i < option_names.length; i++) {
+ options[option_names[i]] = option_values[i];
+ }
+
+ return wrap_as_cancelable_promise(async () => {
+ const res = await fetch(url, options) as ResponseExtension;
+ res.__abort_controller = abort_controller;
+ return res;
+ });
+}
+
+function get_response_headers(res: ResponseExtension): void {
+ if (!res.__headerNames) {
+ res.__headerNames = [];
+ res.__headerValues = [];
+ const entries: Iterable<string[]> = (<any>res.headers).entries();
+
+ for (const pair of entries) {
+ res.__headerNames.push(pair[0]);
+ res.__headerValues.push(pair[1]);
+ }
+ }
+}
+
+export function http_wasm_get_response_header_names(res: ResponseExtension): string[] {
+ get_response_headers(res);
+ return res.__headerNames;
+}
+
+export function http_wasm_get_response_header_values(res: ResponseExtension): string[] {
+ get_response_headers(res);
+ return res.__headerValues;
+}
+
+export function http_wasm_get_response_length(res: ResponseExtension): Promise<number> {
+ return wrap_as_cancelable_promise(async () => {
+ const buffer = await res.arrayBuffer();
+ res.__buffer = buffer;
+ res.__source_offset = 0;
+ return buffer.byteLength;
+ });
+}
+
+export function http_wasm_get_response_bytes(res: ResponseExtension, view: Span): number {
+ mono_assert(res.__buffer, "expected resoved arrayBuffer");
+ if (res.__source_offset == res.__buffer!.byteLength) {
+ return 0;
+ }
+ const source_view = new Uint8Array(res.__buffer!, res.__source_offset);
+ view.set(source_view, 0);
+ const bytes_read = Math.min(view.byteLength, source_view.byteLength);
+ res.__source_offset += bytes_read;
+ return bytes_read;
+}
+
+export async function http_wasm_get_streamed_response_bytes(res: ResponseExtension, bufferPtr: VoidPtr, bufferLength: number): Promise<number> {
+ // the bufferPtr is pinned by the caller
+ const view = new Span(bufferPtr, bufferLength, MemoryViewType.Byte);
+ return wrap_as_cancelable_promise(async () => {
+ if (!res.__chunk && res.body) {
+ res.__reader = res.body.getReader();
+ res.__chunk = await res.__reader.read();
+ res.__source_offset = 0;
+ }
+
+ let target_offset = 0;
+ let bytes_read = 0;
+ // loop until end of browser stream or end of C# buffer
+ while (res.__reader && res.__chunk && !res.__chunk.done) {
+ const remaining_source = res.__chunk.value.byteLength - res.__source_offset;
+ if (remaining_source === 0) {
+ res.__chunk = await res.__reader.read();
+ res.__source_offset = 0;
+ continue;// are we done yet
+ }
+
+ const remaining_target = view.byteLength - target_offset;
+ const bytes_copied = Math.min(remaining_source, remaining_target);
+ const source_view = res.__chunk.value.subarray(res.__source_offset, res.__source_offset + bytes_copied);
+
+ // copy available bytes
+ view.set(source_view, target_offset);
+ target_offset += bytes_copied;
+ bytes_read += bytes_copied;
+ res.__source_offset += bytes_copied;
+
+ if (target_offset == view.byteLength) {
+ return bytes_read;
+ }
+ }
+ return bytes_read;
+ });
+}
+
+interface ResponseExtension extends Response {
+ __buffer?: ArrayBuffer
+ __reader?: ReadableStreamDefaultReader<Uint8Array>
+ __chunk?: ReadableStreamDefaultReadResult<Uint8Array>
+ __source_offset: number
+ __abort_controller: AbortController
+ __headerNames: string[];
+ __headerValues: string[];
+} \ No newline at end of file
diff --git a/src/mono/wasm/runtime/invoke-cs.ts b/src/mono/wasm/runtime/invoke-cs.ts
new file mode 100644
index 00000000000..0201be0d2d9
--- /dev/null
+++ b/src/mono/wasm/runtime/invoke-cs.ts
@@ -0,0 +1,206 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+import { MonoObject, MonoString } from "./export-types";
+import { EXPORTS, Module, runtimeHelpers } from "./imports";
+import { generate_arg_marshal_to_cs } from "./marshal-to-cs";
+import { marshal_exception_to_js, generate_arg_marshal_to_js } from "./marshal-to-js";
+import {
+ JSMarshalerArguments, JavaScriptMarshalerArgSize, JSFunctionSignature,
+ JSMarshalerTypeSize, JSMarshalerSignatureHeaderSize,
+ get_arg, get_sig, set_arg_type,
+ get_signature_argument_count, is_args_exception, bound_cs_function_symbol, get_signature_version, MarshalerType,
+} from "./marshal";
+import { parseFQN, wrap_error_root } from "./method-calls";
+import { mono_wasm_new_external_root, mono_wasm_new_root } from "./roots";
+import { conv_string, conv_string_root } from "./strings";
+import { mono_assert, MonoObjectRef, MonoStringRef } from "./types";
+import { Int32Ptr } from "./types/emscripten";
+import cwraps, { wrap_c_function } from "./cwraps";
+import { find_method } from "./method-binding";
+import { assembly_load } from "./class-loader";
+
+const exportedMethods = new Map<string, Function>();
+
+export function mono_wasm_bind_cs_function(fully_qualified_name: MonoStringRef, signature_hash: number, signature: JSFunctionSignature, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
+ const fqn_root = mono_wasm_new_external_root<MonoString>(fully_qualified_name), resultRoot = mono_wasm_new_external_root<MonoObject>(result_address);
+ const anyModule = Module as any;
+ try {
+ const version = get_signature_version(signature);
+ mono_assert(version === 1, () => `Signature version ${version} mismatch.`);
+
+ const args_count = get_signature_argument_count(signature);
+ const js_fqn = conv_string_root(fqn_root)!;
+ mono_assert(js_fqn, "fully_qualified_name must be string");
+
+ if (runtimeHelpers.config.diagnostic_tracing) {
+ console.trace(`MONO_WASM: Binding [JSExport] ${js_fqn}`);
+ }
+
+ const { assembly, namespace, classname, methodname } = parseFQN(js_fqn);
+
+ const asm = assembly_load(assembly);
+ if (!asm)
+ throw new Error("Could not find assembly: " + assembly);
+
+ const klass = cwraps.mono_wasm_assembly_find_class(asm, namespace, classname);
+ if (!klass)
+ throw new Error("Could not find class: " + namespace + ":" + classname + " in assembly " + assembly);
+
+ const wrapper_name = `__Wrapper_${methodname}_${signature_hash}`;
+ const method = find_method(klass, wrapper_name, -1);
+ if (!method)
+ throw new Error(`Could not find method: ${wrapper_name} in ${klass} [${assembly}]`);
+
+ const closure: any = {
+ method, get_arg, signature,
+ stackSave: anyModule.stackSave, stackAlloc: anyModule.stackAlloc, stackRestore: anyModule.stackRestore,
+ conv_string,
+ mono_wasm_new_root, init_void, init_result, /*init_argument,*/ marshal_exception_to_js, is_args_exception,
+ mono_wasm_invoke_method_bound: wrap_c_function("mono_wasm_invoke_method_bound"),
+ };
+ const bound_js_function_name = "_bound_cs_" + `${namespace}_${classname}_${methodname}`.replace(/\./g, "_");
+ let body = `//# sourceURL=https://mono-wasm.invalid/${bound_js_function_name} \n`;
+ let bodyToCs = "";
+ let converter_names = "";
+
+ for (let index = 0; index < args_count; index++) {
+ const arg_offset = (index + 2) * JavaScriptMarshalerArgSize;
+ const sig_offset = (index + 2) * JSMarshalerTypeSize + JSMarshalerSignatureHeaderSize;
+ const sig = get_sig(signature, index + 2);
+ const { converters, call_body } = generate_arg_marshal_to_cs(sig, index + 2, arg_offset, sig_offset, `arguments[${index}]`, closure);
+ converter_names += converters;
+ bodyToCs += call_body;
+ }
+ const { converters: res_converters, call_body: res_call_body, marshaler_type: res_marshaler_type } = generate_arg_marshal_to_js(get_sig(signature, 1), 1, JavaScriptMarshalerArgSize, JSMarshalerTypeSize + JSMarshalerSignatureHeaderSize, "js_result", closure);
+ converter_names += res_converters;
+
+ body += `const { method, get_arg, signature, stackSave, stackAlloc, stackRestore, mono_wasm_new_root, conv_string, init_void, init_result, init_argument, marshal_exception_to_js, is_args_exception, mono_wasm_invoke_method_bound ${converter_names} } = closure;\n`;
+ // TODO named arguments instead of arguments keyword
+ body += `return function ${bound_js_function_name} () {\n`;
+ if (res_marshaler_type === MarshalerType.String) {
+ body += "let root = null;\n";
+ }
+ body += "const sp = stackSave();\n";
+ body += "try {\n";
+ body += ` const args = stackAlloc(${(args_count + 2) * JavaScriptMarshalerArgSize});\n`;
+ if (res_marshaler_type !== MarshalerType.Void && res_marshaler_type !== MarshalerType.Discard) {
+ if (res_marshaler_type === MarshalerType.String) {
+ body += " root = mono_wasm_new_root(0);\n";
+ body += " init_result(args);\n";
+ }
+ else {
+ body += " init_result(args);\n";
+ }
+ } else {
+ body += " init_void(args);\n";
+ }
+
+ body += bodyToCs;
+
+ body += " const fail = mono_wasm_invoke_method_bound(method, args);\n";
+ body += " if (fail) throw new Error(\"ERR22: Unexpected error: \" + conv_string(fail));\n";
+ body += " if (is_args_exception(args)) throw marshal_exception_to_js(get_arg(args, 0));\n";
+ if (res_marshaler_type !== MarshalerType.Void && res_marshaler_type !== MarshalerType.Discard) {
+ body += res_call_body;
+ }
+
+ if (res_marshaler_type !== MarshalerType.Void && res_marshaler_type !== MarshalerType.Discard) {
+ body += "return js_result;\n";
+ }
+
+ body += "} finally {\n";
+ body += " stackRestore(sp);\n";
+ if (res_marshaler_type === MarshalerType.String) {
+ body += " if(root) root.release()\n";
+ }
+ body += "}}";
+ const factory = new Function("closure", body);
+ const bound_fn = factory(closure);
+ bound_fn[bound_cs_function_symbol] = true;
+
+ exportedMethods.set(js_fqn, bound_fn);
+ _walk_exports_to_set_function(assembly, namespace, classname, methodname, signature_hash, bound_fn);
+ }
+ catch (ex) {
+ wrap_error_root(is_exception, ex, resultRoot);
+ } finally {
+ resultRoot.release();
+ fqn_root.release();
+ }
+}
+
+function init_void(args: JSMarshalerArguments) {
+ mono_assert(args && (<any>args) % 8 == 0, "Arg alignment");
+ const exc = get_arg(args, 0);
+ set_arg_type(exc, MarshalerType.None);
+
+ const res = get_arg(args, 1);
+ set_arg_type(res, MarshalerType.None);
+}
+
+function init_result(args: JSMarshalerArguments) {
+ mono_assert(args && (<any>args) % 8 == 0, "Arg alignment");
+ const exc = get_arg(args, 0);
+ set_arg_type(exc, MarshalerType.None);
+
+ const res = get_arg(args, 1);
+ set_arg_type(res, MarshalerType.None);
+}
+
+export const exportsByAssembly: Map<string, any> = new Map();
+
+function _walk_exports_to_set_function(assembly: string, namespace: string, classname: string, methodname: string, signature_hash: number, fn: Function): void {
+ let scope: any = EXPORTS;
+ let parts = `${namespace}.${classname}`.split(".");
+
+ for (let i = 0; i < parts.length; i++) {
+ const part = parts[i];
+ let newscope = scope[part];
+ if (!newscope) {
+ newscope = {};
+ scope[part] = newscope;
+ }
+ mono_assert(newscope, () => `${part} not found while looking up ${classname}`);
+ scope = newscope;
+ }
+
+ if (!scope[methodname]) {
+ scope[methodname] = fn;
+ }
+ scope[`${methodname}.${signature_hash}`] = fn;
+
+ // do it again for per assemly scope
+ let assemblyScope = exportsByAssembly.get(assembly);
+ if (!assemblyScope) {
+ assemblyScope = {};
+ exportsByAssembly.set(assembly, assemblyScope);
+ exportsByAssembly.set(assembly + ".dll", assemblyScope);
+ }
+ scope = assemblyScope;
+ parts = `${namespace}.${classname}`.split(".");
+ for (let i = 0; i < parts.length; i++) {
+ const part = parts[i];
+ let newscope = scope[part];
+ if (!newscope) {
+ newscope = {};
+ scope[part] = newscope;
+ }
+ mono_assert(newscope, () => `${part} not found while looking up ${classname}`);
+ scope = newscope;
+ }
+
+ if (!scope[methodname]) {
+ scope[methodname] = fn;
+ }
+ scope[`${methodname}.${signature_hash}`] = fn;
+}
+
+export async function mono_wasm_get_assembly_exports(assembly: string): Promise<any> {
+ const asm = assembly_load(assembly);
+ if (!asm)
+ throw new Error("Could not find assembly: " + assembly);
+ cwraps.mono_wasm_runtime_run_module_cctor(asm);
+
+ return exportsByAssembly.get(assembly) || {};
+} \ No newline at end of file
diff --git a/src/mono/wasm/runtime/invoke-js.ts b/src/mono/wasm/runtime/invoke-js.ts
new file mode 100644
index 00000000000..0f6b64010b3
--- /dev/null
+++ b/src/mono/wasm/runtime/invoke-js.ts
@@ -0,0 +1,189 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+import { mono_wasm_get_jsobj_from_js_handle, mono_wasm_get_js_handle } from "./gc-handles";
+import { marshal_exception_to_cs, generate_arg_marshal_to_cs } from "./marshal-to-cs";
+import { get_signature_argument_count, JSMarshalerArguments as JSMarshalerArguments, JavaScriptMarshalerArgSize, JSFunctionSignature as JSFunctionSignature, bound_js_function_symbol, JSMarshalerTypeSize, get_sig, JSMarshalerSignatureHeaderSize, get_signature_version, MarshalerType, get_signature_type } from "./marshal";
+import { setI32 } from "./memory";
+import { wrap_error_root } from "./method-calls";
+import { conv_string_root } from "./strings";
+import { mono_assert, JSHandle, MonoObject, MonoObjectRef, MonoString, MonoStringRef } from "./types";
+import { Int32Ptr } from "./types/emscripten";
+import { IMPORTS, INTERNAL, runtimeHelpers } from "./imports";
+import { generate_arg_marshal_to_js } from "./marshal-to-js";
+import { mono_wasm_new_external_root } from "./roots";
+
+export function mono_wasm_bind_js_function(function_name: MonoStringRef, module_name: MonoStringRef, signature: JSFunctionSignature, function_js_handle: Int32Ptr, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
+ const function_name_root = mono_wasm_new_external_root<MonoString>(function_name),
+ module_name_root = mono_wasm_new_external_root<MonoString>(module_name),
+ resultRoot = mono_wasm_new_external_root<MonoObject>(result_address);
+ try {
+ const version = get_signature_version(signature);
+ mono_assert(version === 1, () => `Signature version ${version} mismatch.`);
+
+ const js_function_name = conv_string_root(function_name_root)!;
+ const js_module_name = conv_string_root(module_name_root)!;
+ if (runtimeHelpers.config.diagnostic_tracing) {
+ console.trace(`MONO_WASM: Binding [JSImport] ${js_function_name} from ${js_module_name}`);
+ }
+ const fn = mono_wasm_lookup_function(js_function_name, js_module_name);
+ const args_count = get_signature_argument_count(signature);
+
+ const closure: any = { fn, marshal_exception_to_cs, signature };
+ const bound_js_function_name = "_bound_js_" + js_function_name.replace(/\./g, "_");
+ let body = `//# sourceURL=https://mono-wasm.invalid/${bound_js_function_name} \n`;
+ let converter_names = "";
+
+
+ let bodyToJs = "";
+ let pass_args = "";
+ for (let index = 0; index < args_count; index++) {
+ const arg_offset = (index + 2) * JavaScriptMarshalerArgSize;
+ const sig_offset = (index + 2) * JSMarshalerTypeSize + JSMarshalerSignatureHeaderSize;
+ const arg_name = `arg${index}`;
+ const sig = get_sig(signature, index + 2);
+ const { converters, call_body } = generate_arg_marshal_to_js(sig, index + 2, arg_offset, sig_offset, arg_name, closure);
+ converter_names += converters;
+ bodyToJs += call_body;
+ if (pass_args === "") {
+ pass_args += arg_name;
+ } else {
+ pass_args += `, ${arg_name}`;
+ }
+ }
+ const { converters: res_converters, call_body: res_call_body, marshaler_type: res_marshaler_type } = generate_arg_marshal_to_cs(get_sig(signature, 1), 1, JavaScriptMarshalerArgSize, JSMarshalerTypeSize + JSMarshalerSignatureHeaderSize, "js_result", closure);
+ converter_names += res_converters;
+
+ body += `const { signature, fn, marshal_exception_to_cs ${converter_names} } = closure;\n`;
+ body += `return function ${bound_js_function_name} (args) { try {\n`;
+ // body += `console.log("${bound_js_function_name}")\n`;
+ body += bodyToJs;
+
+
+ if (res_marshaler_type === MarshalerType.Void) {
+ body += ` const js_result = fn(${pass_args});\n`;
+ body += ` if (js_result !== undefined) throw new Error('Function ${js_function_name} returned unexpected value, C# signature is void');\n`;
+ }
+ else if (res_marshaler_type === MarshalerType.Discard) {
+ body += ` fn(${pass_args});\n`;
+ }
+ else {
+ body += ` const js_result = fn(${pass_args});\n`;
+ body += res_call_body;
+ }
+
+ for (let index = 0; index < args_count; index++) {
+ const sig = get_sig(signature, index + 2);
+ const marshaler_type = get_signature_type(sig);
+ if (marshaler_type == MarshalerType.Span) {
+ const arg_name = `arg${index}`;
+ body += ` ${arg_name}.dispose();\n`;
+ }
+ }
+
+ body += "} catch (ex) {\n";
+ body += " marshal_exception_to_cs(args, ex);\n";
+ body += "}}";
+ const factory = new Function("closure", body);
+ const bound_fn = factory(closure);
+ bound_fn[bound_js_function_symbol] = true;
+ const bound_function_handle = mono_wasm_get_js_handle(bound_fn)!;
+ setI32(function_js_handle, <any>bound_function_handle);
+ } catch (ex) {
+ wrap_error_root(is_exception, ex, resultRoot);
+ } finally {
+ resultRoot.release();
+ function_name_root.release();
+ }
+}
+
+export function mono_wasm_invoke_bound_function(bound_function_js_handle: JSHandle, args: JSMarshalerArguments): void {
+ const bound_fn = mono_wasm_get_jsobj_from_js_handle(bound_function_js_handle);
+ mono_assert(bound_fn && typeof (bound_fn) === "function" && bound_fn[bound_js_function_symbol], () => `Bound function handle expected ${bound_function_js_handle}`);
+ bound_fn(args);
+}
+
+function mono_wasm_lookup_function(function_name: string, js_module_name: string): Function {
+ mono_assert(function_name && typeof function_name === "string", "function_name must be string");
+
+ let scope: any = IMPORTS;
+ const parts = function_name.split(".");
+ if (js_module_name) {
+ scope = importedModules.get(js_module_name);
+ mono_assert(scope, () => `ES6 module ${js_module_name} was not imported yet, please call JSHost.Import() first.`);
+ }
+ else if (parts[0] === "INTERNAL") {
+ scope = INTERNAL;
+ parts.shift();
+ }
+ else if (parts[0] === "globalThis") {
+ scope = globalThis;
+ parts.shift();
+ }
+
+ for (let i = 0; i < parts.length - 1; i++) {
+ const part = parts[i];
+ const newscope = scope[part];
+ mono_assert(newscope, () => `${part} not found while looking up ${function_name}`);
+ scope = newscope;
+ }
+
+ const fname = parts[parts.length - 1];
+ const fn = scope[fname];
+
+ mono_assert(typeof (fn) === "function", () => `${function_name} must be a Function but was ${typeof fn}`);
+ return fn;
+}
+
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+export function set_property(self: any, name: string, value: any): void {
+ mono_assert(self, "Null reference");
+ self[name] = value;
+}
+
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+export function get_property(self: any, name: string): any {
+ mono_assert(self, "Null reference");
+ return self[name];
+}
+
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+export function has_property(self: any, name: string): boolean {
+ mono_assert(self, "Null reference");
+ return name in self;
+}
+
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+export function get_typeof_property(self: any, name: string): string {
+ mono_assert(self, "Null reference");
+ return typeof self[name];
+}
+
+export function get_global_this(): any {
+ return globalThis;
+}
+
+export const importedModulesPromises: Map<string, Promise<any>> = new Map();
+export const importedModules: Map<string, Promise<any>> = new Map();
+
+export async function dynamic_import(module_name: string, module_url: string): Promise<any> {
+ mono_assert(module_name, "Invalid module_name");
+ mono_assert(module_url, "Invalid module_name");
+ let promise = importedModulesPromises.get(module_name);
+ const newPromise = !promise;
+ if (newPromise) {
+ if (runtimeHelpers.config.diagnostic_tracing)
+ console.trace(`MONO_WASM: importing ES6 module '${module_name}' from '${module_url}'`);
+ promise = import(module_url);
+ importedModulesPromises.set(module_name, promise);
+ }
+ const module = await promise;
+ if (newPromise) {
+ importedModules.set(module_name, module);
+ if (runtimeHelpers.config.diagnostic_tracing)
+ console.trace(`MONO_WASM: imported ES6 module '${module_name}' from '${module_url}'`);
+ }
+ return module;
+}
+
+
diff --git a/src/mono/wasm/runtime/marshal-to-cs.ts b/src/mono/wasm/runtime/marshal-to-cs.ts
new file mode 100644
index 00000000000..fff46ba6043
--- /dev/null
+++ b/src/mono/wasm/runtime/marshal-to-cs.ts
@@ -0,0 +1,651 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+import { isThenable } from "./cancelable-promise";
+import wrapped_cs_functions from "./corebindings";
+import cwraps from "./cwraps";
+import { assert_not_disposed, cs_owned_js_handle_symbol, js_owned_gc_handle_symbol, mono_wasm_get_js_handle, setup_managed_proxy, teardown_managed_proxy } from "./gc-handles";
+import { Module, runtimeHelpers } from "./imports";
+import {
+ JSMarshalerArgument, ManagedError,
+ set_gc_handle, set_js_handle, set_arg_type, set_arg_i32, set_arg_f64, set_arg_i52, set_arg_f32, set_arg_i16, set_arg_u8, set_arg_b8, set_arg_date,
+ set_arg_length, get_arg, is_args_exception, JavaScriptMarshalerArgSize, get_signature_type, get_signature_arg1_type, get_signature_arg2_type, cs_to_js_marshalers, js_to_cs_marshalers,
+ MarshalerToCs, MarshalerToJs, get_signature_res_type, JSMarshalerArguments, bound_js_function_symbol, set_arg_u16, JSMarshalerType, array_element_size, get_string_root, Span, ArraySegment, MemoryViewType, get_signature_arg3_type, MarshalerType, set_arg_i64_big, set_arg_intptr, IDisposable, set_arg_element_type, ManagedObject
+} from "./marshal";
+import { marshal_exception_to_js } from "./marshal-to-js";
+import { _zero_region } from "./memory";
+import { conv_string, js_string_to_mono_string_root } from "./strings";
+import { mono_assert, GCHandle, GCHandleNull } from "./types";
+import { TypedArray } from "./types/emscripten";
+
+export function initialize_marshalers_to_cs(): void {
+ if (js_to_cs_marshalers.size == 0) {
+ js_to_cs_marshalers.set(MarshalerType.Array, _marshal_array_to_cs);
+ js_to_cs_marshalers.set(MarshalerType.Span, _marshal_span_to_cs);
+ js_to_cs_marshalers.set(MarshalerType.ArraySegment, _marshal_array_segment_to_cs);
+ js_to_cs_marshalers.set(MarshalerType.Boolean, _marshal_bool_to_cs);
+ js_to_cs_marshalers.set(MarshalerType.Byte, _marshal_byte_to_cs);
+ js_to_cs_marshalers.set(MarshalerType.Char, _marshal_char_to_cs);
+ js_to_cs_marshalers.set(MarshalerType.Int16, _marshal_int16_to_cs);
+ js_to_cs_marshalers.set(MarshalerType.Int32, _marshal_int32_to_cs);
+ js_to_cs_marshalers.set(MarshalerType.Int52, _marshal_int52_to_cs);
+ js_to_cs_marshalers.set(MarshalerType.BigInt64, _marshal_bigint64_to_cs);
+ js_to_cs_marshalers.set(MarshalerType.Double, _marshal_double_to_cs);
+ js_to_cs_marshalers.set(MarshalerType.Single, _marshal_float_to_cs);
+ js_to_cs_marshalers.set(MarshalerType.IntPtr, _marshal_intptr_to_cs);
+ js_to_cs_marshalers.set(MarshalerType.DateTime, _marshal_date_time_to_cs);
+ js_to_cs_marshalers.set(MarshalerType.DateTimeOffset, _marshal_date_time_offset_to_cs);
+ js_to_cs_marshalers.set(MarshalerType.String, _marshal_string_to_cs);
+ js_to_cs_marshalers.set(MarshalerType.Exception, marshal_exception_to_cs);
+ js_to_cs_marshalers.set(MarshalerType.JSException, marshal_exception_to_cs);
+ js_to_cs_marshalers.set(MarshalerType.JSObject, _marshal_js_object_to_cs);
+ js_to_cs_marshalers.set(MarshalerType.Object, _marshal_cs_object_to_cs);
+ js_to_cs_marshalers.set(MarshalerType.Task, _marshal_task_to_cs);
+ js_to_cs_marshalers.set(MarshalerType.Action, _marshal_function_to_cs);
+ js_to_cs_marshalers.set(MarshalerType.Function, _marshal_function_to_cs);
+ js_to_cs_marshalers.set(MarshalerType.None, _marshal_null_to_cs);// also void
+ js_to_cs_marshalers.set(MarshalerType.Discard, _marshal_null_to_cs);// also void
+ js_to_cs_marshalers.set(MarshalerType.Void, _marshal_null_to_cs);// also void
+ }
+}
+
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+export function generate_arg_marshal_to_cs(sig: JSMarshalerType, index: number, arg_offset: number, sig_offset: number, jsname: string, closure: any): {
+ converters: string,
+ call_body: string,
+ marshaler_type: MarshalerType
+} {
+ let converters = "";
+ let converter_types = "";
+ let call_body = "";
+ const converter_name = "converter" + index;
+ let converter_name_arg1 = "null";
+ let converter_name_arg2 = "null";
+ let converter_name_arg3 = "null";
+ let converter_name_res = "null";
+
+ let marshaler_type = get_signature_type(sig);
+ if (marshaler_type === MarshalerType.None || marshaler_type === MarshalerType.Void) {
+ return {
+ converters,
+ call_body,
+ marshaler_type
+ };
+ }
+
+ const marshaler_type_res = get_signature_res_type(sig);
+ if (marshaler_type_res !== MarshalerType.None) {
+ const converter = js_to_cs_marshalers.get(marshaler_type_res);
+ mono_assert(converter && typeof converter === "function", () => `Unknow converter for type ${marshaler_type_res} at ${index}`);
+
+
+ if (marshaler_type != MarshalerType.Nullable) {
+ converter_name_res = "converter" + index + "_res";
+ converters += ", " + converter_name_res;
+ converter_types += " " + MarshalerType[marshaler_type_res];
+ closure[converter_name_res] = converter;
+ }
+ else {
+ marshaler_type = marshaler_type_res;
+ }
+ }
+
+ const marshaler_type_arg1 = get_signature_arg1_type(sig);
+ if (marshaler_type_arg1 !== MarshalerType.None) {
+ const converter = cs_to_js_marshalers.get(marshaler_type_arg1);
+ mono_assert(converter && typeof converter === "function", () => `Unknow converter for type ${marshaler_type_arg1} at ${index}`);
+
+ converter_name_arg1 = "converter" + index + "_arg1";
+ converters += ", " + converter_name_arg1;
+ converter_types += " " + MarshalerType[marshaler_type_arg1];
+ closure[converter_name_arg1] = converter;
+ }
+
+ const marshaler_type_arg2 = get_signature_arg2_type(sig);
+ if (marshaler_type_arg2 !== MarshalerType.None) {
+ const converter = cs_to_js_marshalers.get(marshaler_type_arg2);
+ mono_assert(converter && typeof converter === "function", () => `Unknow converter for type ${marshaler_type_arg2} at ${index}`);
+
+ converter_name_arg2 = "converter" + index + "_arg2";
+ converters += ", " + converter_name_arg2;
+ converter_types += " " + MarshalerType[marshaler_type_arg2];
+ closure[converter_name_arg2] = converter;
+ }
+
+ const marshaler_type_arg3 = get_signature_arg3_type(sig);
+ if (marshaler_type_arg3 !== MarshalerType.None) {
+ const converter = cs_to_js_marshalers.get(marshaler_type_arg3);
+ mono_assert(converter && typeof converter === "function", () => `Unknow converter for type ${marshaler_type_arg3} at ${index}`);
+
+ converter_name_arg3 = "converter" + index + "_arg3";
+ converters += ", " + converter_name_arg3;
+ converter_types += " " + MarshalerType[marshaler_type_arg3];
+ closure[converter_name_arg3] = converter;
+ }
+
+ const converter = js_to_cs_marshalers.get(marshaler_type);
+
+ const arg_type_name = MarshalerType[marshaler_type];
+ mono_assert(converter && typeof converter === "function", () => `Unknow converter for type ${arg_type_name} (${marshaler_type}) at ${index} `);
+
+ converters += ", " + converter_name;
+ converter_types += " " + arg_type_name;
+ closure[converter_name] = converter;
+
+
+ if (marshaler_type == MarshalerType.Task) {
+ call_body = ` ${converter_name}(args + ${arg_offset}, ${jsname}, signature + ${sig_offset}, ${converter_name_res}); // ${converter_types} \n`;
+ } else if (marshaler_type == MarshalerType.Action || marshaler_type == MarshalerType.Function) {
+ call_body = ` ${converter_name}(args + ${arg_offset}, ${jsname}, signature + ${sig_offset}, ${converter_name_res}, ${converter_name_arg1}, ${converter_name_arg2}, ${converter_name_arg2}); // ${converter_types} \n`;
+ } else {
+ call_body = ` ${converter_name}(args + ${arg_offset}, ${jsname}, signature + ${sig_offset}); // ${converter_types} \n`;
+ }
+
+ return {
+ converters,
+ call_body,
+ marshaler_type
+ };
+}
+
+function _marshal_bool_to_cs(arg: JSMarshalerArgument, value: any): void {
+ if (value === null || value === undefined) {
+ set_arg_type(arg, MarshalerType.None);
+ }
+ else {
+ set_arg_type(arg, MarshalerType.Boolean);
+ set_arg_b8(arg, value);
+ }
+}
+
+function _marshal_byte_to_cs(arg: JSMarshalerArgument, value: any): void {
+ if (value === null || value === undefined) {
+ set_arg_type(arg, MarshalerType.None);
+ }
+ else {
+ set_arg_type(arg, MarshalerType.Byte);
+ set_arg_u8(arg, value);
+ }
+}
+
+function _marshal_char_to_cs(arg: JSMarshalerArgument, value: any): void {
+ if (value === null || value === undefined) {
+ set_arg_type(arg, MarshalerType.None);
+ }
+ else {
+ set_arg_type(arg, MarshalerType.Char);
+ set_arg_u16(arg, value);
+ }
+}
+
+function _marshal_int16_to_cs(arg: JSMarshalerArgument, value: any): void {
+ if (value === null || value === undefined) {
+ set_arg_type(arg, MarshalerType.None);
+ }
+ else {
+ set_arg_type(arg, MarshalerType.Int16);
+ set_arg_i16(arg, value);
+ }
+}
+
+function _marshal_int32_to_cs(arg: JSMarshalerArgument, value: any): void {
+ if (value === null || value === undefined) {
+ set_arg_type(arg, MarshalerType.None);
+ }
+ else {
+ set_arg_type(arg, MarshalerType.Int32);
+ set_arg_i32(arg, value);
+ }
+}
+
+function _marshal_int52_to_cs(arg: JSMarshalerArgument, value: any): void {
+ if (value === null || value === undefined) {
+ set_arg_type(arg, MarshalerType.None);
+ }
+ else {
+ set_arg_type(arg, MarshalerType.Int52);
+ set_arg_i52(arg, value);
+ }
+}
+
+function _marshal_bigint64_to_cs(arg: JSMarshalerArgument, value: any): void {
+ if (value === null || value === undefined) {
+ set_arg_type(arg, MarshalerType.None);
+ }
+ else {
+ set_arg_type(arg, MarshalerType.BigInt64);
+ set_arg_i64_big(arg, value);
+ }
+}
+
+function _marshal_double_to_cs(arg: JSMarshalerArgument, value: any): void {
+ if (value === null || value === undefined) {
+ set_arg_type(arg, MarshalerType.None);
+ }
+ else {
+ set_arg_type(arg, MarshalerType.Double);
+ set_arg_f64(arg, value);
+ }
+}
+
+function _marshal_float_to_cs(arg: JSMarshalerArgument, value: any): void {
+ if (value === null || value === undefined) {
+ set_arg_type(arg, MarshalerType.None);
+ }
+ else {
+ set_arg_type(arg, MarshalerType.Single);
+ set_arg_f32(arg, value);
+ }
+}
+
+function _marshal_intptr_to_cs(arg: JSMarshalerArgument, value: any): void {
+ if (value === null || value === undefined) {
+ set_arg_type(arg, MarshalerType.None);
+ }
+ else {
+ set_arg_type(arg, MarshalerType.IntPtr);
+ set_arg_intptr(arg, value);
+ }
+}
+
+function _marshal_date_time_to_cs(arg: JSMarshalerArgument, value: Date): void {
+ if (value === null || value === undefined) {
+ set_arg_type(arg, MarshalerType.None);
+ }
+ else {
+ mono_assert(value instanceof Date, "Value is not a Date");
+ set_arg_type(arg, MarshalerType.DateTime);
+ set_arg_date(arg, value);
+ }
+}
+
+function _marshal_date_time_offset_to_cs(arg: JSMarshalerArgument, value: Date): void {
+ if (value === null || value === undefined) {
+ set_arg_type(arg, MarshalerType.None);
+ }
+ else {
+ mono_assert(value instanceof Date, "Value is not a Date");
+ set_arg_type(arg, MarshalerType.DateTimeOffset);
+ set_arg_date(arg, value);
+ }
+}
+
+function _marshal_string_to_cs(arg: JSMarshalerArgument, value: string) {
+ if (value === null || value === undefined) {
+ set_arg_type(arg, MarshalerType.None);
+ }
+ else {
+ set_arg_type(arg, MarshalerType.String);
+ mono_assert(typeof value === "string", "Value is not a String");
+ _marshal_string_to_cs_impl(arg, value);
+ }
+}
+
+function _marshal_string_to_cs_impl(arg: JSMarshalerArgument, value: string) {
+ const root = get_string_root(arg);
+ try {
+ js_string_to_mono_string_root(value, root);
+ }
+ finally {
+ root.release();
+ }
+}
+
+function _marshal_null_to_cs(arg: JSMarshalerArgument) {
+ set_arg_type(arg, MarshalerType.None);
+}
+
+function _marshal_function_to_cs(arg: JSMarshalerArgument, value: Function, _?: JSMarshalerType, res_converter?: MarshalerToCs, arg1_converter?: MarshalerToJs, arg2_converter?: MarshalerToJs, arg3_converter?: MarshalerToJs): void {
+ if (value === null || value === undefined) {
+ set_arg_type(arg, MarshalerType.None);
+ return;
+ }
+ mono_assert(value && value instanceof Function, "Value is not a Function");
+
+ // TODO: we could try to cache value -> exising JSHandle
+ const marshal_function_to_cs_wrapper: any = (args: JSMarshalerArguments) => {
+ const exc = get_arg(args, 0);
+ const res = get_arg(args, 1);
+ const arg1 = get_arg(args, 2);
+ const arg2 = get_arg(args, 3);
+ const arg3 = get_arg(args, 4);
+
+ try {
+ let arg1_js: any = undefined;
+ let arg2_js: any = undefined;
+ let arg3_js: any = undefined;
+ if (arg1_converter) {
+ arg1_js = arg1_converter(arg1);
+ }
+ if (arg2_converter) {
+ arg2_js = arg2_converter(arg2);
+ }
+ if (arg3_converter) {
+ arg3_js = arg3_converter(arg3);
+ }
+ const res_js = value(arg1_js, arg2_js, arg3_js);
+ if (res_converter) {
+ res_converter(res, res_js);
+ }
+
+ } catch (ex) {
+ marshal_exception_to_cs(exc, ex);
+ }
+ };
+
+ marshal_function_to_cs_wrapper[bound_js_function_symbol] = true;
+ const bound_function_handle = mono_wasm_get_js_handle(marshal_function_to_cs_wrapper)!;
+ set_js_handle(arg, bound_function_handle);
+ set_arg_type(arg, MarshalerType.Function);//TODO or action ?
+}
+
+export class TaskCallbackHolder implements IDisposable {
+ public promise: Promise<any>
+
+ public constructor(promise: Promise<any>) {
+ this.promise = promise;
+ }
+
+ dispose(): void {
+ teardown_managed_proxy(this, GCHandleNull);
+ }
+
+ get isDisposed(): boolean {
+ return (<any>this)[js_owned_gc_handle_symbol] === GCHandleNull;
+ }
+}
+
+function _marshal_task_to_cs(arg: JSMarshalerArgument, value: Promise<any>, _?: JSMarshalerType, res_converter?: MarshalerToCs) {
+ if (value === null || value === undefined) {
+ set_arg_type(arg, MarshalerType.None);
+ return;
+ }
+ mono_assert(isThenable(value), "Value is not a Promise");
+
+ const anyModule = Module as any;
+ const gc_handle: GCHandle = wrapped_cs_functions._create_task_callback();
+ set_gc_handle(arg, gc_handle);
+ set_arg_type(arg, MarshalerType.Task);
+ const holder = new TaskCallbackHolder(value);
+ setup_managed_proxy(holder, gc_handle);
+
+ value.then(data => {
+ const sp = anyModule.stackSave();
+ try {
+ const args = anyModule.stackAlloc(JavaScriptMarshalerArgSize * 3);
+ const exc = get_arg(args, 0);
+ set_arg_type(exc, MarshalerType.None);
+ const res = get_arg(args, 1);
+ set_arg_type(res, MarshalerType.None);
+ set_gc_handle(res, <any>gc_handle);
+ const arg1 = get_arg(args, 2);
+ if (!res_converter) {
+ _marshal_cs_object_to_cs(arg1, data);
+ } else {
+ res_converter(arg1, data);
+ }
+ const fail = cwraps.mono_wasm_invoke_method_bound(runtimeHelpers.complete_task_method, args);
+ if (fail) throw new Error("ERR22: Unexpected error: " + conv_string(fail));
+ if (is_args_exception(args)) throw marshal_exception_to_js(exc);
+ } finally {
+ anyModule.stackRestore(sp);
+ }
+ teardown_managed_proxy(holder, gc_handle); // this holds holder alive for finalizer, until the promise is freed, (holding promise instead would not work)
+ }).catch(reason => {
+ const sp = anyModule.stackSave();
+ try {
+ const args = anyModule.stackAlloc(JavaScriptMarshalerArgSize * 3);
+ const res = get_arg(args, 1);
+ set_arg_type(res, MarshalerType.None);
+ set_gc_handle(res, gc_handle);
+ const exc = get_arg(args, 0);
+ if (typeof reason === "string" || reason === null || reason === undefined) {
+ reason = new Error(reason || "");
+ }
+ marshal_exception_to_cs(exc, reason);
+ const fail = cwraps.mono_wasm_invoke_method_bound(runtimeHelpers.complete_task_method, args);
+ if (fail) throw new Error("ERR24: Unexpected error: " + conv_string(fail));
+ if (is_args_exception(args)) throw marshal_exception_to_js(exc);
+ } finally {
+ anyModule.stackRestore(sp);
+ }
+ teardown_managed_proxy(holder, gc_handle); // this holds holder alive for finalizer, until the promise is freed
+ });
+}
+
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+export function marshal_exception_to_cs(arg: JSMarshalerArgument, value: any): void {
+ if (value === null || value === undefined) {
+ set_arg_type(arg, MarshalerType.None);
+ }
+ else if (value instanceof ManagedError) {
+ set_arg_type(arg, MarshalerType.Exception);
+ // this is managed exception round-trip
+ const gc_handle = assert_not_disposed(value);
+ set_gc_handle(arg, gc_handle);
+ }
+ else {
+ mono_assert(typeof value === "object" || typeof value === "string", () => `Value is not an Error ${typeof value}`);
+ set_arg_type(arg, MarshalerType.JSException);
+ const message = value.toString();
+ _marshal_string_to_cs_impl(arg, message);
+
+ const known_js_handle = value[cs_owned_js_handle_symbol];
+ if (known_js_handle) {
+ set_js_handle(arg, known_js_handle);
+ }
+ else {
+ const js_handle = mono_wasm_get_js_handle(value)!;
+ set_js_handle(arg, js_handle);
+ }
+ }
+}
+
+function _marshal_js_object_to_cs(arg: JSMarshalerArgument, value: any): void {
+ if (value === undefined || value === null) {
+ set_arg_type(arg, MarshalerType.None);
+ }
+ else {
+ // if value was ManagedObject, it would be double proxied, but the C# signature requires that
+ mono_assert(value[js_owned_gc_handle_symbol] === undefined, "JSObject proxy of ManagedObject proxy is not supported");
+ mono_assert(typeof value === "function" || typeof value === "object", () => `JSObject proxy of ${typeof value} is not supported`);
+
+ set_arg_type(arg, MarshalerType.JSObject);
+ const js_handle = mono_wasm_get_js_handle(value)!;
+ set_js_handle(arg, js_handle);
+ }
+}
+
+function _marshal_cs_object_to_cs(arg: JSMarshalerArgument, value: any): void {
+ if (value === undefined || value === null) {
+ set_arg_type(arg, MarshalerType.None);
+ }
+ else {
+ const gc_handle = value[js_owned_gc_handle_symbol];
+ const js_type = typeof (value);
+ if (gc_handle === undefined) {
+ if (js_type === "string" || js_type === "symbol") {
+ set_arg_type(arg, MarshalerType.String);
+ _marshal_string_to_cs_impl(arg, value);
+ }
+ else if (js_type === "number") {
+ set_arg_type(arg, MarshalerType.Double);
+ set_arg_f64(arg, value);
+ }
+ else if (js_type === "bigint") {
+ // we do it because not all bigint values could fit into Int64
+ throw new Error("NotImplementedException: bigint");
+ }
+ else if (js_type === "boolean") {
+ set_arg_type(arg, MarshalerType.Boolean);
+ set_arg_b8(arg, value);
+ }
+ else if (value instanceof Date) {
+ set_arg_type(arg, MarshalerType.DateTime);
+ set_arg_date(arg, value);
+ }
+ else if (value instanceof Error) {
+ set_arg_type(arg, MarshalerType.JSException);
+ const js_handle = mono_wasm_get_js_handle(value);
+ set_js_handle(arg, js_handle);
+ }
+ else if (value instanceof Uint8Array) {
+ _marshal_array_to_cs_impl(arg, value, MarshalerType.Byte);
+ }
+ else if (value instanceof Float64Array) {
+ _marshal_array_to_cs_impl(arg, value, MarshalerType.Double);
+ }
+ else if (value instanceof Int32Array) {
+ _marshal_array_to_cs_impl(arg, value, MarshalerType.Int32);
+ }
+ else if (Array.isArray(value)) {
+ _marshal_array_to_cs_impl(arg, value, MarshalerType.Object);
+ }
+ else if (value instanceof Int16Array
+ || value instanceof Int8Array
+ || value instanceof Uint8ClampedArray
+ || value instanceof Uint16Array
+ || value instanceof Uint32Array
+ || value instanceof Float32Array
+ ) {
+ throw new Error("NotImplementedException: TypedArray");
+ }
+ else if (isThenable(value)) {
+ _marshal_task_to_cs(arg, value);
+ }
+ else if (value instanceof Span) {
+ throw new Error("NotImplementedException: Span");
+ }
+ else if (js_type == "object") {
+ const js_handle = mono_wasm_get_js_handle(value);
+ set_arg_type(arg, MarshalerType.JSObject);
+ set_js_handle(arg, js_handle);
+ }
+ else {
+ throw new Error(`JSObject proxy is not supported for ${js_type} ${value}`);
+ }
+ }
+ else {
+ assert_not_disposed(value);
+ if (value instanceof ArraySegment) {
+ throw new Error("NotImplementedException: ArraySegment");
+ }
+ else if (value instanceof ManagedError) {
+ set_arg_type(arg, MarshalerType.Exception);
+ set_gc_handle(arg, gc_handle);
+ }
+ else if (value instanceof ManagedObject) {
+ set_arg_type(arg, MarshalerType.Object);
+ set_gc_handle(arg, gc_handle);
+ } else {
+ throw new Error("NotImplementedException " + js_type);
+ }
+ }
+ }
+}
+
+function _marshal_array_to_cs(arg: JSMarshalerArgument, value: Array<any> | TypedArray, sig?: JSMarshalerType): void {
+ mono_assert(!!sig, "Expected valid sig paramater");
+ const element_type = get_signature_arg1_type(sig);
+ _marshal_array_to_cs_impl(arg, value, element_type);
+}
+
+function _marshal_array_to_cs_impl(arg: JSMarshalerArgument, value: Array<any> | TypedArray, element_type: MarshalerType): void {
+ if (value === null || value === undefined) {
+ set_arg_type(arg, MarshalerType.None);
+ }
+ else {
+ const element_size = array_element_size(element_type);
+ mono_assert(element_size != -1, () => `Element type ${MarshalerType[element_type]} not supported`);
+ const length = value.length;
+ const buffer_length = element_size * length;
+ const buffer_ptr = <any>Module._malloc(buffer_length);
+ if (element_type == MarshalerType.String) {
+ mono_assert(Array.isArray(value), "Value is not an Array");
+ _zero_region(buffer_ptr, buffer_length);
+ cwraps.mono_wasm_register_root(buffer_ptr, buffer_length, "marshal_array_to_cs");
+ for (let index = 0; index < length; index++) {
+ const element_arg = get_arg(<any>buffer_ptr, index);
+ _marshal_string_to_cs(element_arg, value[index]);
+ }
+ }
+ else if (element_type == MarshalerType.Object) {
+ mono_assert(Array.isArray(value), "Value is not an Array");
+ _zero_region(buffer_ptr, buffer_length);
+ cwraps.mono_wasm_register_root(buffer_ptr, buffer_length, "marshal_array_to_cs");
+ for (let index = 0; index < length; index++) {
+ const element_arg = get_arg(<any>buffer_ptr, index);
+ _marshal_cs_object_to_cs(element_arg, value[index]);
+ }
+ }
+ else if (element_type == MarshalerType.JSObject) {
+ mono_assert(Array.isArray(value), "Value is not an Array");
+ _zero_region(buffer_ptr, buffer_length);
+ for (let index = 0; index < length; index++) {
+ const element_arg = get_arg(buffer_ptr, index);
+ _marshal_js_object_to_cs(element_arg, value[index]);
+ }
+ }
+ else if (element_type == MarshalerType.Byte) {
+ mono_assert(Array.isArray(value) || value instanceof Uint8Array, "Value is not an Array or Uint8Array");
+ const targetView = Module.HEAPU8.subarray(<any>buffer_ptr, buffer_ptr + length);
+ targetView.set(value);
+ }
+ else if (element_type == MarshalerType.Int32) {
+ mono_assert(Array.isArray(value) || value instanceof Int32Array, "Value is not an Array or Int32Array");
+ const targetView = Module.HEAP32.subarray(<any>buffer_ptr >> 2, (buffer_ptr >> 2) + length);
+ targetView.set(value);
+ }
+ else if (element_type == MarshalerType.Double) {
+ mono_assert(Array.isArray(value) || value instanceof Float64Array, "Value is not an Array or Float64Array");
+ const targetView = Module.HEAPF64.subarray(<any>buffer_ptr >> 3, (buffer_ptr >> 3) + length);
+ targetView.set(value);
+ }
+ else {
+ throw new Error("not implemented");
+ }
+ set_arg_intptr(arg, buffer_ptr);
+ set_arg_type(arg, MarshalerType.Array);
+ set_arg_element_type(arg, element_type);
+ set_arg_length(arg, value.length);
+ }
+}
+
+function _marshal_span_to_cs(arg: JSMarshalerArgument, value: Span, sig?: JSMarshalerType): void {
+ mono_assert(!!sig, "Expected valid sig paramater");
+ mono_assert(!value.isDisposed, "ObjectDisposedException");
+ checkViewType(sig, value._viewType);
+
+ set_arg_type(arg, MarshalerType.Span);
+ set_arg_intptr(arg, value._pointer);
+ set_arg_length(arg, value.length);
+}
+
+// this only supports round-trip
+function _marshal_array_segment_to_cs(arg: JSMarshalerArgument, value: ArraySegment, sig?: JSMarshalerType): void {
+ mono_assert(!!sig, "Expected valid sig paramater");
+ const gc_handle = assert_not_disposed(value);
+ mono_assert(gc_handle, "Only roundtrip of ArraySegment instance created by C#");
+ checkViewType(sig, value._viewType);
+ set_arg_type(arg, MarshalerType.ArraySegment);
+ set_arg_intptr(arg, value._pointer);
+ set_arg_length(arg, value.length);
+ set_gc_handle(arg, gc_handle);
+}
+
+function checkViewType(sig: JSMarshalerType, viewType: MemoryViewType) {
+ const element_type = get_signature_arg1_type(sig);
+ if (element_type == MarshalerType.Byte) {
+ mono_assert(MemoryViewType.Byte == viewType, "Expected MemoryViewType.Byte");
+ }
+ else if (element_type == MarshalerType.Int32) {
+ mono_assert(MemoryViewType.Int32 == viewType, "Expected MemoryViewType.Int32");
+ }
+ else if (element_type == MarshalerType.Double) {
+ mono_assert(MemoryViewType.Double == viewType, "Expected MemoryViewType.Double");
+ }
+ else {
+ throw new Error(`NotImplementedException ${MarshalerType[element_type]} `);
+ }
+}
+
diff --git a/src/mono/wasm/runtime/marshal-to-js.ts b/src/mono/wasm/runtime/marshal-to-js.ts
new file mode 100644
index 00000000000..47d240f1f13
--- /dev/null
+++ b/src/mono/wasm/runtime/marshal-to-js.ts
@@ -0,0 +1,581 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+import { PromiseControl, promise_control_symbol, create_cancelable_promise } from "./cancelable-promise";
+import cwraps from "./cwraps";
+import { _lookup_js_owned_object, mono_wasm_get_jsobj_from_js_handle, mono_wasm_get_js_handle, setup_managed_proxy } from "./gc-handles";
+import { Module, runtimeHelpers } from "./imports";
+import {
+ ManagedObject, JSMarshalerArgument, ManagedError, JSMarshalerArguments, MarshalerToCs, MarshalerToJs, JSMarshalerType,
+ get_arg_gc_handle, get_arg_js_handle, get_arg_type, get_arg_i32, get_arg_f64, get_arg_i52, get_arg_i16, get_arg_u8, get_arg_f32,
+ get_arg_b8, get_arg_date, get_arg_length, set_js_handle, get_arg, set_arg_type,
+ get_signature_arg2_type, get_signature_arg1_type, get_signature_type, cs_to_js_marshalers, js_to_cs_marshalers,
+ get_signature_res_type, JavaScriptMarshalerArgSize, set_gc_handle, is_args_exception, get_arg_u16, array_element_size, get_string_root, ArraySegment, Span, MemoryViewType, get_signature_arg3_type, MarshalerType, get_arg_i64_big, get_arg_intptr, get_arg_element_type
+} from "./marshal";
+import { conv_string, conv_string_root } from "./strings";
+import { mono_assert, JSHandleNull, GCHandleNull } from "./types";
+import { TypedArray } from "./types/emscripten";
+
+export function initialize_marshalers_to_js(): void {
+ if (cs_to_js_marshalers.size == 0) {
+ cs_to_js_marshalers.set(MarshalerType.Array, _marshal_array_to_js);
+ cs_to_js_marshalers.set(MarshalerType.Span, _marshal_span_to_js);
+ cs_to_js_marshalers.set(MarshalerType.ArraySegment, _marshal_array_segment_to_js);
+ cs_to_js_marshalers.set(MarshalerType.Boolean, _marshal_bool_to_js);
+ cs_to_js_marshalers.set(MarshalerType.Byte, _marshal_byte_to_js);
+ cs_to_js_marshalers.set(MarshalerType.Char, _marshal_char_to_js);
+ cs_to_js_marshalers.set(MarshalerType.Int16, _marshal_int16_to_js);
+ cs_to_js_marshalers.set(MarshalerType.Int32, _marshal_int32_to_js);
+ cs_to_js_marshalers.set(MarshalerType.Int52, _marshal_int52_to_js);
+ cs_to_js_marshalers.set(MarshalerType.BigInt64, _marshal_bigint64_to_js);
+ cs_to_js_marshalers.set(MarshalerType.Single, _marshal_float_to_js);
+ cs_to_js_marshalers.set(MarshalerType.IntPtr, _marshal_intptr_to_js);
+ cs_to_js_marshalers.set(MarshalerType.Double, _marshal_double_to_js);
+ cs_to_js_marshalers.set(MarshalerType.String, _marshal_string_to_js);
+ cs_to_js_marshalers.set(MarshalerType.Exception, marshal_exception_to_js);
+ cs_to_js_marshalers.set(MarshalerType.JSException, marshal_exception_to_js);
+ cs_to_js_marshalers.set(MarshalerType.JSObject, _marshal_js_object_to_js);
+ cs_to_js_marshalers.set(MarshalerType.Object, _marshal_cs_object_to_js);
+ cs_to_js_marshalers.set(MarshalerType.DateTime, _marshal_datetime_to_js);
+ cs_to_js_marshalers.set(MarshalerType.DateTimeOffset, _marshal_datetime_to_js);
+ cs_to_js_marshalers.set(MarshalerType.Task, _marshal_task_to_js);
+ cs_to_js_marshalers.set(MarshalerType.Action, _marshal_delegate_to_js);
+ cs_to_js_marshalers.set(MarshalerType.Function, _marshal_delegate_to_js);
+ cs_to_js_marshalers.set(MarshalerType.None, _marshal_null_to_js);
+ cs_to_js_marshalers.set(MarshalerType.Void, _marshal_null_to_js);
+ cs_to_js_marshalers.set(MarshalerType.Discard, _marshal_null_to_js);
+ }
+}
+
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+export function generate_arg_marshal_to_js(sig: JSMarshalerType, index: number, arg_offset: number, sig_offset: number, jsname: string, closure: any): {
+ converters: string,
+ call_body: string,
+ marshaler_type: MarshalerType
+} {
+ let converters = "";
+ let converter_types = "";
+ let call_body = "";
+ const converter_name = "converter" + index;
+ let converter_name_arg1 = "null";
+ let converter_name_arg2 = "null";
+ let converter_name_arg3 = "null";
+ let converter_name_res = "null";
+
+ let marshaler_type = get_signature_type(sig);
+ if (marshaler_type === MarshalerType.None || marshaler_type === MarshalerType.Void) {
+ return {
+ converters,
+ call_body,
+ marshaler_type
+ };
+ }
+
+ const marshaler_type_res = get_signature_res_type(sig);
+ if (marshaler_type_res !== MarshalerType.None) {
+ const converter = cs_to_js_marshalers.get(marshaler_type_res);
+ mono_assert(converter && typeof converter === "function", () => `Unknow converter for type ${marshaler_type_res} at ${index}`);
+
+ if (marshaler_type != MarshalerType.Nullable) {
+ converter_name_res = "converter" + index + "_res";
+ converters += ", " + converter_name_res;
+ converter_types += " " + MarshalerType[marshaler_type_res];
+ closure[converter_name_res] = converter;
+ } else {
+ marshaler_type = marshaler_type_res;
+ }
+ }
+
+ const marshaler_type_arg1 = get_signature_arg1_type(sig);
+ if (marshaler_type_arg1 !== MarshalerType.None) {
+ const converter = js_to_cs_marshalers.get(marshaler_type_arg1);
+ mono_assert(converter && typeof converter === "function", () => `Unknow converter for type ${marshaler_type_arg1} at ${index}`);
+
+ converter_name_arg1 = "converter" + index + "_arg1";
+ converters += ", " + converter_name_arg1;
+ converter_types += " " + MarshalerType[marshaler_type_arg1];
+ closure[converter_name_arg1] = converter;
+ }
+
+ const marshaler_type_arg2 = get_signature_arg2_type(sig);
+ if (marshaler_type_arg2 !== MarshalerType.None) {
+ const converter = js_to_cs_marshalers.get(marshaler_type_arg2);
+ mono_assert(converter && typeof converter === "function", () => `Unknow converter for type ${marshaler_type_arg2} at ${index}`);
+
+ converter_name_arg2 = "converter" + index + "_arg2";
+ converters += ", " + converter_name_arg2;
+ converter_types += " " + MarshalerType[marshaler_type_arg2];
+ closure[converter_name_arg2] = converter;
+ }
+
+ const marshaler_type_arg3 = get_signature_arg3_type(sig);
+ if (marshaler_type_arg3 !== MarshalerType.None) {
+ const converter = js_to_cs_marshalers.get(marshaler_type_arg3);
+ mono_assert(converter && typeof converter === "function", () => `Unknow converter for type ${marshaler_type_arg3} at ${index}`);
+
+ converter_name_arg3 = "converter" + index + "_arg3";
+ converters += ", " + converter_name_arg3;
+ converter_types += " " + MarshalerType[marshaler_type_arg3];
+ closure[converter_name_arg3] = converter;
+ }
+
+ const converter = cs_to_js_marshalers.get(marshaler_type);
+ mono_assert(converter && typeof converter === "function", () => `Unknow converter for type ${marshaler_type} at ${index} `);
+
+ converters += ", " + converter_name;
+ converter_types += " " + MarshalerType[marshaler_type];
+ closure[converter_name] = converter;
+
+ if (marshaler_type == MarshalerType.Task) {
+ call_body = ` const ${jsname} = ${converter_name}(args + ${arg_offset}, signature + ${sig_offset}, ${converter_name_res}); // ${converter_types} \n`;
+ } else if (marshaler_type == MarshalerType.Action || marshaler_type == MarshalerType.Function) {
+ call_body = ` const ${jsname} = ${converter_name}(args + ${arg_offset}, signature + ${sig_offset}, ${converter_name_res}, ${converter_name_arg1}, ${converter_name_arg2}, ${converter_name_arg3}); // ${converter_types} \n`;
+ } else {
+ call_body = ` const ${jsname} = ${converter_name}(args + ${arg_offset}, signature + ${sig_offset}); // ${converter_types} \n`;
+ }
+
+ return {
+ converters,
+ call_body,
+ marshaler_type
+ };
+}
+
+function _marshal_bool_to_js(arg: JSMarshalerArgument): boolean | null {
+ const type = get_arg_type(arg);
+ if (type == MarshalerType.None) {
+ return null;
+ }
+ return get_arg_b8(arg);
+}
+
+function _marshal_byte_to_js(arg: JSMarshalerArgument): number | null {
+ const type = get_arg_type(arg);
+ if (type == MarshalerType.None) {
+ return null;
+ }
+ return get_arg_u8(arg);
+}
+
+function _marshal_char_to_js(arg: JSMarshalerArgument): number | null {
+ const type = get_arg_type(arg);
+ if (type == MarshalerType.None) {
+ return null;
+ }
+ return get_arg_u16(arg);
+}
+
+function _marshal_int16_to_js(arg: JSMarshalerArgument): number | null {
+ const type = get_arg_type(arg);
+ if (type == MarshalerType.None) {
+ return null;
+ }
+ return get_arg_i16(arg);
+}
+
+function _marshal_int32_to_js(arg: JSMarshalerArgument): number | null {
+ const type = get_arg_type(arg);
+ if (type == MarshalerType.None) {
+ return null;
+ }
+ return get_arg_i32(arg);
+}
+
+function _marshal_int52_to_js(arg: JSMarshalerArgument): number | null {
+ const type = get_arg_type(arg);
+ if (type == MarshalerType.None) {
+ return null;
+ }
+ return get_arg_i52(arg);
+}
+
+function _marshal_bigint64_to_js(arg: JSMarshalerArgument): bigint | null {
+ const type = get_arg_type(arg);
+ if (type == MarshalerType.None) {
+ return null;
+ }
+ return get_arg_i64_big(arg);
+}
+
+function _marshal_float_to_js(arg: JSMarshalerArgument): number | null {
+ const type = get_arg_type(arg);
+ if (type == MarshalerType.None) {
+ return null;
+ }
+ return get_arg_f32(arg);
+}
+
+function _marshal_double_to_js(arg: JSMarshalerArgument): number | null {
+ const type = get_arg_type(arg);
+ if (type == MarshalerType.None) {
+ return null;
+ }
+ return get_arg_f64(arg);
+}
+
+function _marshal_intptr_to_js(arg: JSMarshalerArgument): number | null {
+ const type = get_arg_type(arg);
+ if (type == MarshalerType.None) {
+ return null;
+ }
+ return get_arg_intptr(arg);
+}
+
+function _marshal_null_to_js(): null {
+ return null;
+}
+
+function _marshal_datetime_to_js(arg: JSMarshalerArgument): Date | null {
+ const type = get_arg_type(arg);
+ if (type === MarshalerType.None) {
+ return null;
+ }
+ return get_arg_date(arg);
+}
+
+function _marshal_delegate_to_js(arg: JSMarshalerArgument, _?: JSMarshalerType, res_converter?: MarshalerToJs, arg1_converter?: MarshalerToCs, arg2_converter?: MarshalerToCs, arg3_converter?: MarshalerToCs): Function | null {
+ const type = get_arg_type(arg);
+ if (type === MarshalerType.None) {
+ return null;
+ }
+
+ const anyModule = Module as any;
+ const gc_handle = get_arg_gc_handle(arg);
+ let result = _lookup_js_owned_object(gc_handle);
+ if (result === null || result === undefined) {
+ // this will create new Function for the C# delegate
+ result = (arg1_js: any, arg2_js: any, arg3_js: any) => {
+
+ const sp = anyModule.stackSave();
+ try {
+ const args = anyModule.stackAlloc(JavaScriptMarshalerArgSize * 5);
+ const exc = get_arg(args, 0);
+ set_arg_type(exc, MarshalerType.None);
+ const res = get_arg(args, 1);
+ set_arg_type(res, MarshalerType.None);
+ set_gc_handle(res, <any>gc_handle);
+ const arg1 = get_arg(args, 2);
+ const arg2 = get_arg(args, 3);
+ const arg3 = get_arg(args, 4);
+
+ if (arg1_converter) {
+ arg1_converter(arg1, arg1_js);
+ }
+ if (arg2_converter) {
+ arg2_converter(arg2, arg2_js);
+ }
+ if (arg3_converter) {
+ arg3_converter(arg3, arg3_js);
+ }
+
+ const fail = cwraps.mono_wasm_invoke_method_bound(runtimeHelpers.call_delegate, args);
+ if (fail) throw new Error("ERR23: Unexpected error: " + conv_string(fail));
+ if (is_args_exception(args)) throw marshal_exception_to_js(exc);
+
+ if (res_converter) {
+ return res_converter(res);
+ }
+
+ } finally {
+ anyModule.stackRestore(sp);
+ }
+ };
+
+ setup_managed_proxy(result, gc_handle);
+ }
+
+ return result;
+}
+
+function _marshal_task_to_js(arg: JSMarshalerArgument, _?: JSMarshalerType, res_converter?: MarshalerToJs): Promise<any> | null {
+ const type = get_arg_type(arg);
+ if (type === MarshalerType.None) {
+ return null;
+ }
+
+ if (type !== MarshalerType.Task) {
+
+ if (!res_converter) {
+ // when we arrived here from _marshal_cs_object_to_js
+ res_converter = cs_to_js_marshalers.get(type);
+ }
+ mono_assert(res_converter, () => `Unknow sub_converter for type ${MarshalerType[type]} `);
+
+ // this is already resolved
+ const val = res_converter(arg);
+ return new Promise((resolve) => resolve(val));
+ }
+
+ const js_handle = get_arg_js_handle(arg);
+ // console.log("_marshal_task_to_js A" + js_handle);
+ if (js_handle == JSHandleNull) {
+ // this is already resolved void
+ return new Promise((resolve) => resolve(undefined));
+ }
+ const promise = mono_wasm_get_jsobj_from_js_handle(js_handle);
+ mono_assert(!!promise, () => `ERR28: promise not found for js_handle: ${js_handle} `);
+ const promise_control = promise[promise_control_symbol] as PromiseControl;
+ mono_assert(!!promise_control, () => `ERR27: promise_control not found for js_handle: ${js_handle} `);
+
+ const orig_resolve = promise_control.resolve;
+ promise_control.resolve = (argInner: JSMarshalerArgument) => {
+ // console.log("_marshal_task_to_js R" + js_handle);
+ const type = get_arg_type(argInner);
+ if (type === MarshalerType.None) {
+ orig_resolve(null);
+ return;
+ }
+
+ if (!res_converter) {
+ // when we arrived here from _marshal_cs_object_to_js
+ res_converter = cs_to_js_marshalers.get(type);
+ }
+ mono_assert(res_converter, () => `Unknow sub_converter for type ${MarshalerType[type]}`);
+
+ const js_value = res_converter!(argInner);
+ orig_resolve(js_value);
+ };
+
+ return promise;
+}
+
+export function mono_wasm_marshal_promise(args: JSMarshalerArguments): void {
+ const exc = get_arg(args, 0);
+ const res = get_arg(args, 1);
+ const arg_handle = get_arg(args, 2);
+ const arg_value = get_arg(args, 3);
+
+ const exc_type = get_arg_type(exc);
+ const value_type = get_arg_type(arg_value);
+ const js_handle = get_arg_js_handle(arg_handle);
+
+ if (js_handle === JSHandleNull) {
+ const { promise, promise_control } = create_cancelable_promise();
+ const js_handle = mono_wasm_get_js_handle(promise)!;
+ set_js_handle(res, js_handle);
+
+ if (exc_type !== MarshalerType.None) {
+ // this is already failed task
+ const reason = marshal_exception_to_js(exc);
+ promise_control.reject(reason);
+ }
+ else if (value_type !== MarshalerType.Task) {
+ // this is already resolved task
+ const sub_converter = cs_to_js_marshalers.get(value_type);
+ mono_assert(sub_converter, () => `Unknow sub_converter for type ${MarshalerType[value_type]} `);
+ const data = sub_converter(arg_value);
+ promise_control.resolve(data);
+ }
+ } else {
+ // resolve existing promise
+ const promise = mono_wasm_get_jsobj_from_js_handle(js_handle);
+ mono_assert(!!promise, () => `ERR25: promise not found for js_handle: ${js_handle} `);
+ const promise_control = promise[promise_control_symbol] as PromiseControl;
+ mono_assert(!!promise_control, () => `ERR26: promise_control not found for js_handle: ${js_handle} `);
+
+ if (exc_type !== MarshalerType.None) {
+ const reason = marshal_exception_to_js(exc);
+ promise_control.reject(reason);
+ }
+ else if (value_type !== MarshalerType.Task) {
+ // here we assume that resolve was wrapped with sub_converter inside _marshal_task_to_js
+ promise_control.resolve(arg_value);
+ }
+ }
+ set_arg_type(res, MarshalerType.Task);
+ set_arg_type(exc, MarshalerType.None);
+}
+
+function _marshal_string_to_js(arg: JSMarshalerArgument): string | null {
+ const type = get_arg_type(arg);
+ if (type == MarshalerType.None) {
+ return null;
+ }
+ const root = get_string_root(arg);
+ try {
+ const value = conv_string_root(root);
+ return value;
+ } finally {
+ root.release();
+ }
+}
+
+export function marshal_exception_to_js(arg: JSMarshalerArgument): Error | null {
+ const type = get_arg_type(arg);
+ if (type == MarshalerType.None) {
+ return null;
+ }
+ if (type == MarshalerType.JSException) {
+ // this is JSException roundtrip
+ const js_handle = get_arg_js_handle(arg);
+ const js_obj = mono_wasm_get_jsobj_from_js_handle(js_handle);
+ return js_obj;
+ }
+
+ const gc_handle = get_arg_gc_handle(arg);
+ let result = _lookup_js_owned_object(gc_handle);
+ if (result === null || result === undefined) {
+ // this will create new ManagedError
+ const message = _marshal_string_to_js(arg);
+ result = new ManagedError(message!);
+
+ setup_managed_proxy(result, gc_handle);
+ }
+
+ return result;
+}
+
+function _marshal_js_object_to_js(arg: JSMarshalerArgument): any {
+ const type = get_arg_type(arg);
+ if (type == MarshalerType.None) {
+ return null;
+ }
+ const js_handle = get_arg_js_handle(arg);
+ const js_obj = mono_wasm_get_jsobj_from_js_handle(js_handle);
+ return js_obj;
+}
+
+function _marshal_cs_object_to_js(arg: JSMarshalerArgument): any {
+ const marshaler_type = get_arg_type(arg);
+ if (marshaler_type == MarshalerType.None) {
+ return null;
+ }
+ if (marshaler_type == MarshalerType.JSObject) {
+ const js_handle = get_arg_js_handle(arg);
+ const js_obj = mono_wasm_get_jsobj_from_js_handle(js_handle);
+ return js_obj;
+ }
+
+ if (marshaler_type == MarshalerType.Array) {
+ const element_type = get_arg_element_type(arg);
+ return _marshal_array_to_js_impl(arg, element_type);
+ }
+
+ if (marshaler_type == MarshalerType.Object) {
+ const gc_handle = get_arg_gc_handle(arg);
+ if (gc_handle === GCHandleNull) {
+ return null;
+ }
+
+ // see if we have js owned instance for this gc_handle already
+ let result = _lookup_js_owned_object(gc_handle);
+
+ // If the JS object for this gc_handle was already collected (or was never created)
+ if (!result) {
+ result = new ManagedObject();
+ setup_managed_proxy(result, gc_handle);
+ }
+
+ return result;
+ }
+
+ // other types
+ const converter = cs_to_js_marshalers.get(marshaler_type);
+ mono_assert(converter, () => `Unknow converter for type ${MarshalerType[marshaler_type]}`);
+ return converter(arg);
+}
+
+function _marshal_array_to_js(arg: JSMarshalerArgument, sig?: JSMarshalerType): Array<any> | TypedArray | null {
+ mono_assert(!!sig, "Expected valid sig paramater");
+ const element_type = get_signature_arg1_type(sig);
+ return _marshal_array_to_js_impl(arg, element_type);
+}
+
+function _marshal_array_to_js_impl(arg: JSMarshalerArgument, element_type: MarshalerType): Array<any> | TypedArray | null {
+ const type = get_arg_type(arg);
+ if (type == MarshalerType.None) {
+ return null;
+ }
+ const elementSize = array_element_size(element_type);
+ mono_assert(elementSize != -1, () => `Element type ${MarshalerType[element_type]} not supported`);
+ const buffer_ptr = get_arg_intptr(arg);
+ const length = get_arg_length(arg);
+ let result: Array<any> | TypedArray | null = null;
+ if (element_type == MarshalerType.String) {
+ result = new Array(length);
+ for (let index = 0; index < length; index++) {
+ const element_arg = get_arg(<any>buffer_ptr, index);
+ result[index] = _marshal_string_to_js(element_arg);
+ }
+ cwraps.mono_wasm_deregister_root(<any>buffer_ptr);
+ }
+ else if (element_type == MarshalerType.Object) {
+ result = new Array(length);
+ for (let index = 0; index < length; index++) {
+ const element_arg = get_arg(<any>buffer_ptr, index);
+ result[index] = _marshal_cs_object_to_js(element_arg);
+ }
+ cwraps.mono_wasm_deregister_root(<any>buffer_ptr);
+ }
+ else if (element_type == MarshalerType.JSObject) {
+ result = new Array(length);
+ for (let index = 0; index < length; index++) {
+ const element_arg = get_arg(<any>buffer_ptr, index);
+ result[index] = _marshal_js_object_to_js(element_arg);
+ }
+ }
+ else if (element_type == MarshalerType.Byte) {
+ const sourceView = Module.HEAPU8.subarray(<any>buffer_ptr, buffer_ptr + length);
+ result = sourceView.slice();//copy
+ }
+ else if (element_type == MarshalerType.Int32) {
+ const sourceView = Module.HEAP32.subarray(buffer_ptr >> 2, (buffer_ptr >> 2) + length);
+ result = sourceView.slice();//copy
+ }
+ else if (element_type == MarshalerType.Double) {
+ const sourceView = Module.HEAPF64.subarray(buffer_ptr >> 3, (buffer_ptr >> 3) + length);
+ result = sourceView.slice();//copy
+ }
+ else {
+ throw new Error(`NotImplementedException ${MarshalerType[element_type]} `);
+ }
+ Module._free(<any>buffer_ptr);
+ return result;
+}
+
+function _marshal_span_to_js(arg: JSMarshalerArgument, sig?: JSMarshalerType): Span {
+ mono_assert(!!sig, "Expected valid sig paramater");
+
+ const element_type = get_signature_arg1_type(sig);
+ const buffer_ptr = get_arg_intptr(arg);
+ const length = get_arg_length(arg);
+ let result: Span | null = null;
+ if (element_type == MarshalerType.Byte) {
+ result = new Span(<any>buffer_ptr, length, MemoryViewType.Byte);
+ }
+ else if (element_type == MarshalerType.Int32) {
+ result = new Span(<any>buffer_ptr, length, MemoryViewType.Int32);
+ }
+ else if (element_type == MarshalerType.Double) {
+ result = new Span(<any>buffer_ptr, length, MemoryViewType.Double);
+ }
+ else {
+ throw new Error(`NotImplementedException ${MarshalerType[element_type]} `);
+ }
+ return result;
+}
+
+function _marshal_array_segment_to_js(arg: JSMarshalerArgument, sig?: JSMarshalerType): ArraySegment {
+ mono_assert(!!sig, "Expected valid sig paramater");
+
+ const element_type = get_signature_arg1_type(sig);
+ const buffer_ptr = get_arg_intptr(arg);
+ const length = get_arg_length(arg);
+ let result: ArraySegment | null = null;
+ if (element_type == MarshalerType.Byte) {
+ result = new ArraySegment(<any>buffer_ptr, length, MemoryViewType.Byte);
+ }
+ else if (element_type == MarshalerType.Int32) {
+ result = new ArraySegment(<any>buffer_ptr, length, MemoryViewType.Int32);
+ }
+ else if (element_type == MarshalerType.Double) {
+ result = new ArraySegment(<any>buffer_ptr, length, MemoryViewType.Double);
+ }
+ else {
+ throw new Error(`NotImplementedException ${MarshalerType[element_type]} `);
+ }
+ const gc_handle = get_arg_gc_handle(arg);
+ setup_managed_proxy(result, gc_handle);
+
+ return result;
+}
diff --git a/src/mono/wasm/runtime/marshal.ts b/src/mono/wasm/runtime/marshal.ts
new file mode 100644
index 00000000000..9a5e6a6dd63
--- /dev/null
+++ b/src/mono/wasm/runtime/marshal.ts
@@ -0,0 +1,509 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+import { js_owned_gc_handle_symbol, teardown_managed_proxy } from "./gc-handles";
+import { Module } from "./imports";
+import { getF32, getF64, getI16, getI32, getI64Big, getU16, getU32, getU8, setF32, setF64, setI16, setI32, setI64Big, setU16, setU32, setU8 } from "./memory";
+import { mono_wasm_new_external_root, WasmRoot } from "./roots";
+import { mono_assert, GCHandle, JSHandle, MonoObject, MonoString, GCHandleNull } from "./types";
+import { CharPtr, NativePointer, TypedArray, VoidPtr } from "./types/emscripten";
+
+export const cs_to_js_marshalers = new Map<MarshalerType, MarshalerToJs>();
+export const js_to_cs_marshalers = new Map<MarshalerType, MarshalerToCs>();
+export const bound_cs_function_symbol = Symbol.for("wasm bound_cs_function");
+export const bound_js_function_symbol = Symbol.for("wasm bound_js_function");
+
+/**
+ * JSFunctionSignature is pointer to [
+ * Version: number,
+ * ArgumentCount: number,
+ * exc: { jsType: JsTypeFlags, type:MarshalerType, restype:MarshalerType, arg1type:MarshalerType, arg2type:MarshalerType, arg3type:MarshalerType}
+ * res: { jsType: JsTypeFlags, type:MarshalerType, restype:MarshalerType, arg1type:MarshalerType, arg2type:MarshalerType, arg3type:MarshalerType}
+ * arg1: { jsType: JsTypeFlags, type:MarshalerType, restype:MarshalerType, arg1type:MarshalerType, arg2type:MarshalerType, arg3type:MarshalerType}
+ * arg2: { jsType: JsTypeFlags, type:MarshalerType, restype:MarshalerType, arg1type:MarshalerType, arg2type:MarshalerType, arg3type:MarshalerType}
+ * ...
+ * ]
+ *
+ * Layout of the call stack frame buffers is array of JSMarshalerArgument
+ * JSMarshalerArguments is pointer to [
+ * exc: {type:MarshalerType, handle: IntPtr, data: Int64|Ref*|Void* },
+ * res: {type:MarshalerType, handle: IntPtr, data: Int64|Ref*|Void* },
+ * arg1: {type:MarshalerType, handle: IntPtr, data: Int64|Ref*|Void* },
+ * arg2: {type:MarshalerType, handle: IntPtr, data: Int64|Ref*|Void* },
+ * ...
+ * ]
+ */
+
+
+export const JavaScriptMarshalerArgSize = 16;
+export const JSMarshalerTypeSize = 32;
+export const JSMarshalerSignatureHeaderSize = 4 + 4; // without Exception and Result
+
+export interface JSMarshalerArguments extends NativePointer {
+ __brand: "JSMarshalerArguments"
+}
+
+export interface JSFunctionSignature extends NativePointer {
+ __brand: "JSFunctionSignatures"
+}
+
+export interface JSMarshalerType extends NativePointer {
+ __brand: "JSMarshalerType"
+}
+
+export interface JSMarshalerArgument extends NativePointer {
+ __brand: "JSMarshalerArgument"
+}
+
+export function get_arg(args: JSMarshalerArguments, index: number): JSMarshalerArgument {
+ mono_assert(args, "Null args");
+ return <any>args + (index * JavaScriptMarshalerArgSize);
+}
+
+export function is_args_exception(args: JSMarshalerArguments): boolean {
+ mono_assert(args, "Null args");
+ const exceptionType = get_arg_type(<any>args);
+ return exceptionType !== MarshalerType.None;
+}
+
+export function get_sig(signature: JSFunctionSignature, index: number): JSMarshalerType {
+ mono_assert(signature, "Null signatures");
+ return <any>signature + (index * JSMarshalerTypeSize) + JSMarshalerSignatureHeaderSize;
+}
+
+export function get_signature_type(sig: JSMarshalerType): MarshalerType {
+ mono_assert(sig, "Null sig");
+ return <any>getU32(sig);
+}
+
+export function get_signature_res_type(sig: JSMarshalerType): MarshalerType {
+ mono_assert(sig, "Null sig");
+ return <any>getU32(<any>sig + 16);
+}
+
+export function get_signature_custom_code(sig: JSMarshalerType): CharPtr {
+ mono_assert(sig, "Null sig");
+ return <any>getU32(<any>sig + 8);
+}
+
+export function get_signature_custom_code_len(sig: JSMarshalerType): number {
+ mono_assert(sig, "Null sig");
+ return <any>getU32(<any>sig + 12);
+}
+
+export function get_signature_arg1_type(sig: JSMarshalerType): MarshalerType {
+ mono_assert(sig, "Null sig");
+ return <any>getU32(<any>sig + 20);
+}
+
+export function get_signature_arg2_type(sig: JSMarshalerType): MarshalerType {
+ mono_assert(sig, "Null sig");
+ return <any>getU32(<any>sig + 24);
+}
+
+export function get_signature_arg3_type(sig: JSMarshalerType): MarshalerType {
+ mono_assert(sig, "Null sig");
+ return <any>getU32(<any>sig + 28);
+}
+
+export function get_signature_argument_count(signature: JSFunctionSignature): number {
+ mono_assert(signature, "Null signatures");
+ return <any>getI32(<any>signature + 4);
+}
+
+export function get_signature_version(signature: JSFunctionSignature): number {
+ mono_assert(signature, "Null signatures");
+ return <any>getI32(signature);
+}
+
+export function get_sig_type(sig: JSMarshalerType): MarshalerType {
+ mono_assert(sig, "Null signatures");
+ return <any>getU32(sig);
+}
+
+export function get_arg_type(arg: JSMarshalerArgument): MarshalerType {
+ mono_assert(arg, "Null arg");
+ const type = getU32(<any>arg + 12);
+ return <any>type;
+}
+
+export function get_arg_element_type(arg: JSMarshalerArgument): MarshalerType {
+ mono_assert(arg, "Null arg");
+ const type = getU32(<any>arg + 4);
+ return <any>type;
+}
+
+export function set_arg_type(arg: JSMarshalerArgument, type: MarshalerType): void {
+ mono_assert(arg, "Null arg");
+ setU32(<any>arg + 12, type);
+}
+
+export function set_arg_element_type(arg: JSMarshalerArgument, type: MarshalerType): void {
+ mono_assert(arg, "Null arg");
+ setU32(<any>arg + 4, type);
+}
+
+export function get_arg_b8(arg: JSMarshalerArgument): boolean {
+ mono_assert(arg, "Null arg");
+ return !!getU8(<any>arg);
+}
+
+export function get_arg_u8(arg: JSMarshalerArgument): number {
+ mono_assert(arg, "Null arg");
+ return getU8(<any>arg);
+}
+
+export function get_arg_u16(arg: JSMarshalerArgument): number {
+ mono_assert(arg, "Null arg");
+ return getU16(<any>arg);
+}
+
+export function get_arg_i16(arg: JSMarshalerArgument): number {
+ mono_assert(arg, "Null arg");
+ return getI16(<any>arg);
+}
+
+export function get_arg_i32(arg: JSMarshalerArgument): number {
+ mono_assert(arg, "Null arg");
+ return getI32(<any>arg);
+}
+
+export function get_arg_intptr(arg: JSMarshalerArgument): number {
+ mono_assert(arg, "Null arg");
+ return getU32(<any>arg);
+}
+
+export function get_arg_i52(arg: JSMarshalerArgument): number {
+ mono_assert(arg, "Null arg");
+ // we know that the range check and conversion from Int64 was be done on C# side
+ return getF64(<any>arg);
+}
+
+export function get_arg_i64_big(arg: JSMarshalerArgument): bigint {
+ mono_assert(arg, "Null arg");
+ return getI64Big(<any>arg);
+}
+
+export function get_arg_date(arg: JSMarshalerArgument): Date {
+ mono_assert(arg, "Null arg");
+ const unixTime = getF64(<any>arg);
+ const date = new Date(unixTime);
+ return date;
+}
+
+export function get_arg_f32(arg: JSMarshalerArgument): number {
+ mono_assert(arg, "Null arg");
+ return getF32(<any>arg);
+}
+
+export function get_arg_f64(arg: JSMarshalerArgument): number {
+ mono_assert(arg, "Null arg");
+ return getF64(<any>arg);
+}
+
+export function set_arg_b8(arg: JSMarshalerArgument, value: boolean): void {
+ mono_assert(arg, "Null arg");
+ mono_assert(typeof value === "boolean", () => `Value is not a Boolean: ${value} (${typeof (value)})`);
+ setU8(<any>arg, value ? 1 : 0);
+}
+
+export function set_arg_u8(arg: JSMarshalerArgument, value: number): void {
+ mono_assert(arg, "Null arg");
+ setU8(<any>arg, value);
+}
+
+export function set_arg_u16(arg: JSMarshalerArgument, value: number): void {
+ mono_assert(arg, "Null arg");
+ setU16(<any>arg, value);
+}
+
+export function set_arg_i16(arg: JSMarshalerArgument, value: number): void {
+ mono_assert(arg, "Null arg");
+ setI16(<any>arg, value);
+}
+
+export function set_arg_i32(arg: JSMarshalerArgument, value: number): void {
+ mono_assert(arg, "Null arg");
+ setI32(<any>arg, value);
+}
+
+export function set_arg_intptr(arg: JSMarshalerArgument, value: VoidPtr): void {
+ mono_assert(arg, "Null arg");
+ setU32(<any>arg, <any>value);
+}
+
+export function set_arg_i52(arg: JSMarshalerArgument, value: number): void {
+ mono_assert(arg, "Null arg");
+ mono_assert(Number.isSafeInteger(value), () => `Value is not an integer: ${value} (${typeof (value)})`);
+ // we know that conversion to Int64 would be done on C# side
+ setF64(<any>arg, value);
+}
+
+export function set_arg_i64_big(arg: JSMarshalerArgument, value: bigint): void {
+ mono_assert(arg, "Null arg");
+ setI64Big(<any>arg, value);
+}
+
+export function set_arg_date(arg: JSMarshalerArgument, value: Date): void {
+ mono_assert(arg, "Null arg");
+ // getTime() is always UTC
+ const unixTime = value.getTime();
+ setF64(<any>arg, unixTime);
+}
+
+export function set_arg_f64(arg: JSMarshalerArgument, value: number): void {
+ mono_assert(arg, "Null arg");
+ setF64(<any>arg, value);
+}
+
+export function set_arg_f32(arg: JSMarshalerArgument, value: number): void {
+ mono_assert(arg, "Null arg");
+ setF32(<any>arg, value);
+}
+
+export function get_arg_js_handle(arg: JSMarshalerArgument): JSHandle {
+ mono_assert(arg, "Null arg");
+ return <any>getU32(<any>arg + 4);
+}
+
+export function set_js_handle(arg: JSMarshalerArgument, jsHandle: JSHandle): void {
+ mono_assert(arg, "Null arg");
+ setU32(<any>arg + 4, <any>jsHandle);
+}
+
+export function get_arg_gc_handle(arg: JSMarshalerArgument): GCHandle {
+ mono_assert(arg, "Null arg");
+ return <any>getU32(<any>arg + 4);
+}
+
+export function set_gc_handle(arg: JSMarshalerArgument, gcHandle: GCHandle): void {
+ mono_assert(arg, "Null arg");
+ setU32(<any>arg + 4, <any>gcHandle);
+}
+
+export function get_string_root(arg: JSMarshalerArgument): WasmRoot<MonoString> {
+ mono_assert(arg, "Null arg");
+ return mono_wasm_new_external_root<MonoString>(<any>arg);
+}
+
+export function get_arg_length(arg: JSMarshalerArgument): number {
+ mono_assert(arg, "Null arg");
+ return <any>getI32(<any>arg + 8);
+}
+
+export function set_arg_length(arg: JSMarshalerArgument, size: number): void {
+ mono_assert(arg, "Null arg");
+ setI32(<any>arg + 8, size);
+}
+
+export function set_root(arg: JSMarshalerArgument, root: WasmRoot<MonoObject>): void {
+ mono_assert(arg, "Null arg");
+ setU32(<any>arg + 0, root.get_address());
+}
+
+export interface IDisposable {
+ dispose(): void;
+ get isDisposed(): boolean;
+}
+
+export class ManagedObject implements IDisposable {
+ dispose(): void {
+ teardown_managed_proxy(this, GCHandleNull);
+ }
+
+ get isDisposed(): boolean {
+ return (<any>this)[js_owned_gc_handle_symbol] === GCHandleNull;
+ }
+
+ toString(): string {
+ return `CsObject(gc_handle: ${(<any>this)[js_owned_gc_handle_symbol]})`;
+ }
+}
+
+export class ManagedError extends Error implements IDisposable {
+ constructor(message: string) {
+ super(message);
+ }
+
+ get stack(): string | undefined {
+ //todo implement lazy managed stack strace from this[js_owned_gc_handle_symbol]!
+ return super.stack;
+ }
+
+ dispose(): void {
+ teardown_managed_proxy(this, GCHandleNull);
+ }
+
+ get isDisposed(): boolean {
+ return (<any>this)[js_owned_gc_handle_symbol] === GCHandleNull;
+ }
+
+ toString(): string {
+ return `ManagedError(gc_handle: ${(<any>this)[js_owned_gc_handle_symbol]})`;
+ }
+}
+
+export type MarshalerToJs = (arg: JSMarshalerArgument, sig?: JSMarshalerType, res_converter?: MarshalerToJs, arg1_converter?: MarshalerToCs, arg2_converter?: MarshalerToCs) => any;
+export type MarshalerToCs = (arg: JSMarshalerArgument, value: any, sig?: JSMarshalerType, res_converter?: MarshalerToCs, arg1_converter?: MarshalerToJs, arg2_converter?: MarshalerToJs) => void;
+
+export function get_signature_marshaler(signature: JSFunctionSignature, index: number): JSHandle {
+ mono_assert(signature, "Null signatures");
+ const sig = get_sig(signature, index);
+ return <any>getU32(<any>sig + 8);
+}
+
+
+export function array_element_size(element_type: MarshalerType): number {
+ return element_type == MarshalerType.Byte ? 1
+ : element_type == MarshalerType.Int32 ? 4
+ : element_type == MarshalerType.Int52 ? 8
+ : element_type == MarshalerType.Double ? 8
+ : element_type == MarshalerType.String ? JavaScriptMarshalerArgSize
+ : element_type == MarshalerType.Object ? JavaScriptMarshalerArgSize
+ : element_type == MarshalerType.JSObject ? JavaScriptMarshalerArgSize
+ : -1;
+}
+
+export enum MemoryViewType {
+ Byte,
+ Int32,
+ Double,
+}
+
+abstract class MemoryView implements IMemoryView, IDisposable {
+ protected constructor(public _pointer: VoidPtr, public _length: number, public _viewType: MemoryViewType) {
+ }
+
+ abstract dispose(): void;
+ abstract get isDisposed(): boolean;
+
+ _unsafe_create_view(): TypedArray {
+ // this view must be short lived so that it doesn't fail after wasm memory growth
+ // for that reason we also don't give the view out to end user and provide set/slice/copyTo API instead
+ const view = this._viewType == MemoryViewType.Byte ? new Uint8Array(Module.HEAPU8.buffer, <any>this._pointer, this._length)
+ : this._viewType == MemoryViewType.Int32 ? new Int32Array(Module.HEAP32.buffer, <any>this._pointer, this._length)
+ : this._viewType == MemoryViewType.Double ? new Float64Array(Module.HEAPF64.buffer, <any>this._pointer, this._length)
+ : null;
+ if (!view) throw new Error("NotImplementedException");
+ return view;
+ }
+
+ set(source: TypedArray, targetOffset?: number): void {
+ mono_assert(!this.isDisposed, "ObjectDisposedException");
+ const targetView = this._unsafe_create_view();
+ mono_assert(source && targetView && source.constructor === targetView.constructor, () => `Expected ${targetView.constructor}`);
+ targetView.set(source, targetOffset);
+ // TODO consider memory write barrier
+ }
+
+ copyTo(target: TypedArray, sourceOffset?: number): void {
+ mono_assert(!this.isDisposed, "ObjectDisposedException");
+ const sourceView = this._unsafe_create_view();
+ mono_assert(target && sourceView && target.constructor === sourceView.constructor, () => `Expected ${sourceView.constructor}`);
+ const trimmedSource = sourceView.subarray(sourceOffset);
+ // TODO consider memory read barrier
+ target.set(trimmedSource);
+ }
+
+ slice(start?: number, end?: number): TypedArray {
+ mono_assert(!this.isDisposed, "ObjectDisposedException");
+ const sourceView = this._unsafe_create_view();
+ // TODO consider memory read barrier
+ return sourceView.slice(start, end);
+ }
+
+ get length(): number {
+ mono_assert(!this.isDisposed, "ObjectDisposedException");
+ return this._length;
+ }
+
+ get byteLength(): number {
+ mono_assert(!this.isDisposed, "ObjectDisposedException");
+ return this._viewType == MemoryViewType.Byte ? this._length
+ : this._viewType == MemoryViewType.Int32 ? this._length << 2
+ : this._viewType == MemoryViewType.Double ? this._length << 3
+ : 0;
+ }
+}
+
+export interface IMemoryView {
+ /**
+ * copies elements from provided source to the wasm memory.
+ * target has to have the elements of the same type as the underlying C# array.
+ * same as TypedArray.set()
+ */
+ set(source: TypedArray, targetOffset?: number): void;
+ /**
+ * copies elements from wasm memory to provided target.
+ * target has to have the elements of the same type as the underlying C# array.
+ */
+ copyTo(target: TypedArray, sourceOffset?: number): void;
+ /**
+ * same as TypedArray.slice()
+ */
+ slice(start?: number, end?: number): TypedArray;
+
+ get length(): number;
+ get byteLength(): number;
+}
+
+export class Span extends MemoryView implements IDisposable {
+ private is_disposed = false;
+ public constructor(pointer: VoidPtr, length: number, viewType: MemoryViewType) {
+ super(pointer, length, viewType);
+ }
+ dispose(): void {
+ this.is_disposed = true;
+ }
+ get isDisposed(): boolean {
+ return this.is_disposed;
+ }
+}
+
+export class ArraySegment extends MemoryView {
+ public constructor(pointer: VoidPtr, length: number, viewType: MemoryViewType) {
+ super(pointer, length, viewType);
+ }
+
+ dispose(): void {
+ teardown_managed_proxy(this, GCHandleNull);
+ }
+
+ get isDisposed(): boolean {
+ return (<any>this)[js_owned_gc_handle_symbol] === GCHandleNull;
+ }
+}
+
+// please keep in sync with src\libraries\System.Runtime.InteropServices.JavaScript\src\System\Runtime\InteropServices\JavaScript\MarshalerType.cs
+export enum MarshalerType {
+ None = 0,
+ Void = 1,
+ Discard,
+ Boolean,
+ Byte,
+ Char,
+ Int16,
+ Int32,
+ Int52,
+ BigInt64,
+ Double,
+ Single,
+ IntPtr,
+ JSObject,
+ Object,
+ String,
+ Exception,
+ DateTime,
+ DateTimeOffset,
+
+ Nullable,
+ Task,
+ Array,
+ ArraySegment,
+ Span,
+ Action,
+ Function,
+
+ // only on runtime
+ JSException,
+} \ No newline at end of file
diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts
index 776a8eef168..3b3ec8f1486 100644
--- a/src/mono/wasm/runtime/startup.ts
+++ b/src/mono/wasm/runtime/startup.ts
@@ -16,6 +16,8 @@ import { find_corlib_class } from "./class-loader";
import { VoidPtr, CharPtr } from "./types/emscripten";
import { DotnetPublicAPI } from "./exports";
import { mono_on_abort } from "./run";
+import { initialize_marshalers_to_cs } from "./marshal-to-cs";
+import { initialize_marshalers_to_js } from "./marshal-to-js";
import { mono_wasm_new_root } from "./roots";
import { init_crypto } from "./crypto-worker";
import { init_polyfills } from "./polyfills";
@@ -452,6 +454,21 @@ export function bindings_lazy_init(): void {
if (!runtimeHelpers.get_call_sig_ref)
throw "Can't find GetCallSignatureRef method";
+ runtimeHelpers.complete_task_method = get_method("CompleteTask");
+ if (!runtimeHelpers.complete_task_method)
+ throw "Can't find CompleteTask method";
+
+ runtimeHelpers.create_task_method = get_method("CreateTaskCallback");
+ if (!runtimeHelpers.create_task_method)
+ throw "Can't find CreateTaskCallback method";
+
+ runtimeHelpers.call_delegate = get_method("CallDelegate");
+ if (!runtimeHelpers.call_delegate)
+ throw "Can't find CallDelegate method";
+
+ initialize_marshalers_to_js();
+ initialize_marshalers_to_cs();
+
_create_primitive_converters();
runtimeHelpers._box_root = mono_wasm_new_root<MonoObject>();
diff --git a/src/mono/wasm/runtime/types.ts b/src/mono/wasm/runtime/types.ts
index 649de856d70..8e3970722aa 100644
--- a/src/mono/wasm/runtime/types.ts
+++ b/src/mono/wasm/runtime/types.ts
@@ -132,6 +132,9 @@ export const enum AssetBehaviours {
export type RuntimeHelpers = {
get_call_sig_ref: MonoMethod;
+ complete_task_method: MonoMethod;
+ create_task_method: MonoMethod;
+ call_delegate: MonoMethod;
runtime_interop_namespace: string;
runtime_interop_exports_classname: string;
runtime_interop_exports_class: MonoClass;
diff --git a/src/mono/wasm/runtime/web-socket.ts b/src/mono/wasm/runtime/web-socket.ts
index caaa9bd3930..feeebf0f449 100644
--- a/src/mono/wasm/runtime/web-socket.ts
+++ b/src/mono/wasm/runtime/web-socket.ts
@@ -1,19 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-import { mono_wasm_new_root, mono_wasm_new_external_root } from "./roots";
-import { setI32 } from "./memory";
import { prevent_timer_throttling } from "./scheduling";
import { Queue } from "./queue";
-import { PromiseControl, _create_cancelable_promise } from "./cancelable-promise";
-import { mono_array_root_to_js_array, _wrap_delegate_root_as_function } from "./cs-to-js";
-import { mono_wasm_get_jsobj_from_js_handle, mono_wasm_get_js_handle } from "./gc-handles";
-import { _wrap_js_thenable_as_task_root } from "./js-to-cs";
-import { wrap_error_root } from "./method-calls";
-import { conv_string_root } from "./strings";
-import { JSHandle, MonoArray, MonoObject, MonoString, MonoObjectRef } from "./types";
-import { Module } from "./imports";
-import { Int32Ptr, VoidPtr } from "./types/emscripten";
+import { PromiseControl, create_cancelable_promise } from "./cancelable-promise";
+import { mono_assert } from "./types";
+import { ArraySegment, IDisposable } from "./marshal";
const wasm_ws_pending_send_buffer = Symbol.for("wasm ws_pending_send_buffer");
const wasm_ws_pending_send_buffer_offset = Symbol.for("wasm ws_pending_send_buffer_offset");
@@ -30,302 +22,210 @@ let _text_encoder_utf8: TextEncoder | undefined = undefined;
const ws_send_buffer_blocking_threshold = 65536;
const emptyBuffer = new Uint8Array();
-export function mono_wasm_web_socket_open_ref(uri_address: MonoObjectRef, subProtocols: MonoObjectRef, on_close: MonoObjectRef, web_socket_js_handle: Int32Ptr, thenable_js_handle: Int32Ptr, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
- const result_root = mono_wasm_new_external_root<MonoObject>(result_address);
- const uri_root = mono_wasm_new_external_root<MonoString>(uri_address);
- const sub_root = mono_wasm_new_external_root<MonoArray>(subProtocols);
- const on_close_root = mono_wasm_new_root();
- on_close_root.copy_from_address(on_close);
- try {
- const js_uri = conv_string_root(uri_root);
- if (!js_uri) {
- wrap_error_root(is_exception, "ERR12: Invalid uri '" + uri_root.value + "'", result_root);
- return;
- }
+export function ws_wasm_create(uri: string, sub_protocols: string[] | null, onClosed: (code: number, reason: string) => void): WebSocketExtension {
+ mono_assert(uri && typeof uri === "string", () => `ERR12: Invalid uri ${typeof uri}`);
+
+ const ws = new globalThis.WebSocket(uri, sub_protocols || undefined) as WebSocketExtension;
+ const { promise_control: open_promise_control } = create_cancelable_promise();
+
+ ws[wasm_ws_pending_receive_event_queue] = new Queue();
+ ws[wasm_ws_pending_receive_promise_queue] = new Queue();
+ ws[wasm_ws_pending_open_promise] = open_promise_control;
+ ws[wasm_ws_pending_send_promises] = [];
+ ws[wasm_ws_pending_close_promises] = [];
+ ws.binaryType = "arraybuffer";
+ const local_on_open = () => {
+ if (ws[wasm_ws_is_aborted]) return;
+ open_promise_control.resolve(ws);
+ prevent_timer_throttling();
+ };
+ const local_on_message = (ev: MessageEvent) => {
+ if (ws[wasm_ws_is_aborted]) return;
+ _mono_wasm_web_socket_on_message(ws, ev);
+ prevent_timer_throttling();
+ };
+ const local_on_close = (ev: CloseEvent) => {
+ ws.removeEventListener("message", local_on_message);
+ if (ws[wasm_ws_is_aborted]) return;
+ if (onClosed) onClosed(ev.code, ev.reason);
+
+ // this reject would not do anything if there was already "open" before it.
+ open_promise_control.reject(ev.reason);
- const js_subs = mono_array_root_to_js_array(sub_root);
-
- const js_on_close = _wrap_delegate_root_as_function(on_close_root)!;
-
- const ws = new globalThis.WebSocket(js_uri, <any>js_subs) as WebSocketExtension;
- const { promise, promise_control: open_promise_control } = _create_cancelable_promise();
-
- ws[wasm_ws_pending_receive_event_queue] = new Queue();
- ws[wasm_ws_pending_receive_promise_queue] = new Queue();
- ws[wasm_ws_pending_open_promise] = open_promise_control;
- ws[wasm_ws_pending_send_promises] = [];
- ws[wasm_ws_pending_close_promises] = [];
- ws.binaryType = "arraybuffer";
- const local_on_open = () => {
- if (ws[wasm_ws_is_aborted]) return;
- open_promise_control.resolve(null);
- prevent_timer_throttling();
- };
- const local_on_message = (ev: MessageEvent) => {
- if (ws[wasm_ws_is_aborted]) return;
- _mono_wasm_web_socket_on_message(ws, ev);
- prevent_timer_throttling();
- };
- const local_on_close = (ev: CloseEvent) => {
- ws.removeEventListener("message", local_on_message);
- if (ws[wasm_ws_is_aborted]) return;
- js_on_close(ev.code, ev.reason);
-
- // this reject would not do anything if there was already "open" before it.
- open_promise_control.reject(ev.reason);
-
- for (const close_promise_control of ws[wasm_ws_pending_close_promises]) {
- close_promise_control.resolve();
- }
+ for (const close_promise_control of ws[wasm_ws_pending_close_promises]) {
+ close_promise_control.resolve();
+ }
- // send close to any pending receivers, to wake them
- const receive_promise_queue = ws[wasm_ws_pending_receive_promise_queue];
- receive_promise_queue.drain((receive_promise_control) => {
- const response_ptr = receive_promise_control.response_ptr;
- setI32(<any>response_ptr + 0, 0);// count
- setI32(<any>response_ptr + 4, 2);// type:close
- setI32(<any>response_ptr + 8, 1);// end_of_message: true
- receive_promise_control.resolve(null);
- });
- };
- const local_on_error = (ev: any) => {
- open_promise_control.reject(ev.message);
- };
- ws.addEventListener("message", local_on_message);
- ws.addEventListener("open", local_on_open, { once: true });
- ws.addEventListener("close", local_on_close, { once: true });
- ws.addEventListener("error", local_on_error, { once: true });
- const ws_js_handle = mono_wasm_get_js_handle(ws);
- Module.setValue(web_socket_js_handle, <any>ws_js_handle, "i32");
-
- const { then_js_handle } = _wrap_js_thenable_as_task_root(promise, result_root);
- // task_ptr above is not rooted, we need to return it to mono without any intermediate mono call which could cause GC
- Module.setValue(thenable_js_handle, <any>then_js_handle, "i32");
- }
- catch (ex) {
- wrap_error_root(is_exception, ex, result_root);
- }
- finally {
- result_root.release();
- uri_root.release();
- sub_root.release();
- on_close_root.release();
- }
+ // send close to any pending receivers, to wake them
+ const receive_promise_queue = ws[wasm_ws_pending_receive_promise_queue];
+ receive_promise_queue.drain((receive_promise_control) => {
+
+ const response = new Int32Array([
+ 0,// count
+ 2, // type:close
+ 1]);// end_of_message: true
+ receive_promise_control.responseView.set(response);
+ receive_promise_control.resolve(null);
+ });
+ };
+ const local_on_error = (ev: any) => {
+ open_promise_control.reject(ev.message || "WebSocket error");
+ };
+ ws.addEventListener("message", local_on_message);
+ ws.addEventListener("open", local_on_open, { once: true });
+ ws.addEventListener("close", local_on_close, { once: true });
+ ws.addEventListener("error", local_on_error, { once: true });
+
+ return ws;
}
-export function mono_wasm_web_socket_send(webSocket_js_handle: JSHandle, buffer_ptr: VoidPtr, offset: number, length: number, message_type: number, end_of_message: boolean, thenable_js_handle: Int32Ptr, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
- const result_root = mono_wasm_new_external_root<MonoObject>(result_address);
- try {
- const ws = mono_wasm_get_jsobj_from_js_handle(webSocket_js_handle);
- if (!ws)
- throw new Error("ERR17: Invalid JS object handle " + webSocket_js_handle);
+export function ws_wasm_open(ws: WebSocketExtension): Promise<void> | null {
+ mono_assert(!!ws, "ERR17: expected ws instance");
+ const open_promise_control = ws[wasm_ws_pending_open_promise];
+ return open_promise_control.promise;
+}
- if (ws.readyState != WebSocket.OPEN) {
- throw new Error("InvalidState: The WebSocket is not connected.");
- }
+export function ws_wasm_send(ws: WebSocketExtension, bufferView: ArraySegment, message_type: number, end_of_message: boolean): Promise<void> | null {
+ mono_assert(!!ws, "ERR17: expected ws instance");
- const whole_buffer = _mono_wasm_web_socket_send_buffering(ws, buffer_ptr, offset, length, message_type, end_of_message);
+ const whole_buffer = _mono_wasm_web_socket_send_buffering(ws, bufferView, message_type, end_of_message);
- if (!end_of_message || !whole_buffer) {
- result_root.clear(); // we are done buffering synchronously, no promise
- return;
- }
- _mono_wasm_web_socket_send_and_wait(ws, whole_buffer, thenable_js_handle, result_address);
- }
- catch (ex) {
- wrap_error_root(is_exception, ex, result_root);
- }
- finally {
- result_root.release();
+ if (!end_of_message || !whole_buffer) {
+ return null;
}
+
+ return _mono_wasm_web_socket_send_and_wait(ws, whole_buffer, bufferView);
}
-export function mono_wasm_web_socket_receive(webSocket_js_handle: JSHandle, buffer_ptr: VoidPtr, offset: number, length: number, response_ptr: VoidPtr, thenable_js_handle: Int32Ptr, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
- const result_root = mono_wasm_new_external_root<MonoObject>(result_address);
+export function ws_wasm_receive(ws: WebSocketExtension, bufferView: ArraySegment, responseView: ArraySegment): Promise<void> | null {
+ mono_assert(!!ws, "ERR18: expected ws instance");
- try {
- const ws = mono_wasm_get_jsobj_from_js_handle(webSocket_js_handle);
- if (!ws)
- throw new Error("ERR18: Invalid JS object handle " + webSocket_js_handle);
- const receive_event_queue = ws[wasm_ws_pending_receive_event_queue];
- const receive_promise_queue = ws[wasm_ws_pending_receive_promise_queue];
+ const receive_event_queue = ws[wasm_ws_pending_receive_event_queue];
+ const receive_promise_queue = ws[wasm_ws_pending_receive_promise_queue];
- const readyState = ws.readyState;
- if (readyState != WebSocket.OPEN && readyState != WebSocket.CLOSING) {
- throw new Error("InvalidState: The WebSocket is not connected.");
- }
+ const readyState = ws.readyState;
+ if (readyState != WebSocket.OPEN && readyState != WebSocket.CLOSING) {
+ throw new Error("InvalidState: The WebSocket is not connected.");
+ }
- if (receive_event_queue.getLength()) {
- if (receive_promise_queue.getLength() != 0) {
- throw new Error("ERR20: Invalid WS state");// assert
- }
- // finish synchronously
- _mono_wasm_web_socket_receive_buffering(receive_event_queue, buffer_ptr, offset, length, response_ptr);
+ if (receive_event_queue.getLength()) {
+ mono_assert(receive_promise_queue.getLength() == 0, "ERR20: Invalid WS state");
- Module.setValue(thenable_js_handle, 0, "i32");
- result_root.clear();
- return;
- }
- const { promise, promise_control } = _create_cancelable_promise(undefined, undefined);
- const receive_promise_control = promise_control as ReceivePromiseControl;
- receive_promise_control.buffer_ptr = buffer_ptr;
- receive_promise_control.buffer_offset = offset;
- receive_promise_control.buffer_length = length;
- receive_promise_control.response_ptr = response_ptr;
- receive_promise_queue.enqueue(receive_promise_control);
-
- const { then_js_handle } = _wrap_js_thenable_as_task_root(promise, result_root);
- // task_ptr above is not rooted, we need to return it to mono without any intermediate mono call which could cause GC
- Module.setValue(thenable_js_handle, <any>then_js_handle, "i32");
- }
- catch (ex) {
- wrap_error_root(is_exception, ex, result_root);
- }
- finally {
- result_root.release();
+ // finish synchronously
+ _mono_wasm_web_socket_receive_buffering(receive_event_queue, bufferView, responseView);
+
+ return null;
}
-}
+ const { promise, promise_control } = create_cancelable_promise(undefined, undefined);
+ const receive_promise_control = promise_control as ReceivePromiseControl;
+ receive_promise_control.bufferView = bufferView;
+ receive_promise_control.responseView = responseView;
+ receive_promise_queue.enqueue(receive_promise_control);
-export function mono_wasm_web_socket_close_ref(webSocket_js_handle: JSHandle, code: number, reason: MonoObjectRef, wait_for_close_received: boolean, thenable_js_handle: Int32Ptr, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
- const result_root = mono_wasm_new_external_root<MonoObject>(result_address);
- const reason_root = mono_wasm_new_external_root<MonoString>(reason);
- try {
- const ws = mono_wasm_get_jsobj_from_js_handle(webSocket_js_handle);
- if (!ws)
- throw new Error("ERR19: Invalid JS object handle " + webSocket_js_handle);
+ return promise;
+}
- if (ws.readyState == WebSocket.CLOSED) {
- result_root.clear();
- return;
- }
+export function ws_wasm_close(ws: WebSocketExtension, code: number, reason: string | null, wait_for_close_received: boolean): Promise<void> | null {
+ mono_assert(!!ws, "ERR19: expected ws instance");
- const js_reason = conv_string_root(reason_root);
- if (wait_for_close_received) {
- const { promise, promise_control } = _create_cancelable_promise();
- ws[wasm_ws_pending_close_promises].push(promise_control);
+ if (ws.readyState == WebSocket.CLOSED) {
+ return null;
+ }
- if (typeof (js_reason) === "string") {
- ws.close(code, js_reason);
- } else {
- ws.close(code);
- }
+ if (wait_for_close_received) {
+ const { promise, promise_control } = create_cancelable_promise();
+ ws[wasm_ws_pending_close_promises].push(promise_control);
- const { then_js_handle } = _wrap_js_thenable_as_task_root(promise, result_root);
- // task_ptr above is not rooted, we need to return it to mono without any intermediate mono call which could cause GC
- Module.setValue(thenable_js_handle, <any>then_js_handle, "i32");
- }
- else {
- if (!mono_wasm_web_socket_close_warning) {
- mono_wasm_web_socket_close_warning = true;
- console.warn("WARNING: Web browsers do not support closing the output side of a WebSocket. CloseOutputAsync has closed the socket and discarded any incoming messages.");
- }
- if (typeof (js_reason) === "string") {
- ws.close(code, js_reason);
- } else {
- ws.close(code);
- }
- Module.setValue(thenable_js_handle, 0, "i32");
- result_root.clear();
+ if (typeof reason === "string") {
+ ws.close(code, reason);
+ } else {
+ ws.close(code);
}
+ return promise;
}
- catch (ex) {
- wrap_error_root(is_exception, ex, result_root);
- }
- finally {
- result_root.release();
- reason_root.release();
- }
-}
-
-export function mono_wasm_web_socket_abort(webSocket_js_handle: JSHandle, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
- const result_root = mono_wasm_new_external_root<MonoObject>(result_address);
- try {
- const ws = mono_wasm_get_jsobj_from_js_handle(webSocket_js_handle) as WebSocketExtension;
- if (!ws)
- throw new Error("ERR18: Invalid JS object handle " + webSocket_js_handle);
-
- ws[wasm_ws_is_aborted] = true;
- const open_promise_control = ws[wasm_ws_pending_open_promise];
- if (open_promise_control) {
- open_promise_control.reject("OperationCanceledException");
- }
- for (const close_promise_control of ws[wasm_ws_pending_close_promises]) {
- close_promise_control.reject("OperationCanceledException");
+ else {
+ if (!mono_wasm_web_socket_close_warning) {
+ mono_wasm_web_socket_close_warning = true;
+ console.warn("WARNING: Web browsers do not support closing the output side of a WebSocket. CloseOutputAsync has closed the socket and discarded any incoming messages.");
}
- for (const send_promise_control of ws[wasm_ws_pending_send_promises]) {
- send_promise_control.reject("OperationCanceledException");
+ if (typeof reason === "string") {
+ ws.close(code, reason);
+ } else {
+ ws.close(code);
}
+ return null;
+ }
+}
- ws[wasm_ws_pending_receive_promise_queue].drain(receive_promise_control => {
- receive_promise_control.reject("OperationCanceledException");
- });
-
- // this is different from Managed implementation
- ws.close(1000, "Connection was aborted.");
+export function ws_wasm_abort(ws: WebSocketExtension): void {
+ mono_assert(!!ws, "ERR18: expected ws instance");
- result_root.clear();
+ ws[wasm_ws_is_aborted] = true;
+ const open_promise_control = ws[wasm_ws_pending_open_promise];
+ if (open_promise_control) {
+ open_promise_control.reject("OperationCanceledException");
}
- catch (ex) {
- wrap_error_root(is_exception, ex, result_root);
+ for (const close_promise_control of ws[wasm_ws_pending_close_promises]) {
+ close_promise_control.reject("OperationCanceledException");
}
- finally {
- result_root.release();
+ for (const send_promise_control of ws[wasm_ws_pending_send_promises]) {
+ send_promise_control.reject("OperationCanceledException");
}
+
+ ws[wasm_ws_pending_receive_promise_queue].drain(receive_promise_control => {
+ receive_promise_control.reject("OperationCanceledException");
+ });
+
+ // this is different from Managed implementation
+ ws.close(1000, "Connection was aborted.");
}
-function _mono_wasm_web_socket_send_and_wait(ws: WebSocketExtension, buffer: Uint8Array | string, thenable_js_handle: Int32Ptr, result_address: MonoObjectRef): void {
- const result_root = mono_wasm_new_external_root<MonoObject>(result_address);
- try {
- // send and return promise
- ws.send(buffer);
- ws[wasm_ws_pending_send_buffer] = null;
-
- // if the remaining send buffer is small, we don't block so that the throughput doesn't suffer.
- // Otherwise we block so that we apply some backpresure to the application sending large data.
- // this is different from Managed implementation
- if (ws.bufferedAmount < ws_send_buffer_blocking_threshold) {
- result_root.clear();
- return;
- }
+function _mono_wasm_web_socket_send_and_wait(ws: WebSocketExtension, buffer: Uint8Array | string, managedBuffer: IDisposable): Promise<void> | null {
+ // send and return promise
+ ws.send(buffer);
+ managedBuffer.dispose();
+ ws[wasm_ws_pending_send_buffer] = null;
+
+ // if the remaining send buffer is small, we don't block so that the throughput doesn't suffer.
+ // Otherwise we block so that we apply some backpresure to the application sending large data.
+ // this is different from Managed implementation
+ if (ws.bufferedAmount < ws_send_buffer_blocking_threshold) {
+ return null;
+ }
- // block the promise/task until the browser passed the buffer to OS
- const { promise, promise_control } = _create_cancelable_promise();
- const pending = ws[wasm_ws_pending_send_promises];
- pending.push(promise_control);
+ // block the promise/task until the browser passed the buffer to OS
+ const { promise, promise_control } = create_cancelable_promise();
+ const pending = ws[wasm_ws_pending_send_promises];
+ pending.push(promise_control);
- let nextDelay = 1;
- const polling_check = () => {
- // was it all sent yet ?
- if (ws.bufferedAmount === 0) {
- promise_control.resolve(null);
- }
- else if (ws.readyState != WebSocket.OPEN) {
- // only reject if the data were not sent
- // bufferedAmount does not reset to zero once the connection closes
- promise_control.reject("InvalidState: The WebSocket is not connected.");
- }
- else if (!promise_control.isDone) {
- globalThis.setTimeout(polling_check, nextDelay);
- // exponentially longer delays, up to 1000ms
- nextDelay = Math.min(nextDelay * 1.5, 1000);
- return;
- }
- // remove from pending
- const index = pending.indexOf(promise_control);
- if (index > -1) {
- pending.splice(index, 1);
- }
- };
+ let nextDelay = 1;
+ const polling_check = () => {
+ // was it all sent yet ?
+ if (ws.bufferedAmount === 0) {
+ promise_control.resolve(null);
+ }
+ else if (ws.readyState != WebSocket.OPEN) {
+ // only reject if the data were not sent
+ // bufferedAmount does not reset to zero once the connection closes
+ promise_control.reject("InvalidState: The WebSocket is not connected.");
+ }
+ else if (!promise_control.isDone) {
+ globalThis.setTimeout(polling_check, nextDelay);
+ // exponentially longer delays, up to 1000ms
+ nextDelay = Math.min(nextDelay * 1.5, 1000);
+ return;
+ }
+ // remove from pending
+ const index = pending.indexOf(promise_control);
+ if (index > -1) {
+ pending.splice(index, 1);
+ }
+ };
- globalThis.setTimeout(polling_check, 0);
+ globalThis.setTimeout(polling_check, 0);
- const { then_js_handle } = _wrap_js_thenable_as_task_root(promise, result_root);
- // task_ptr above is not rooted, we need to return it to mono without any intermediate mono call which could cause GC
- Module.setValue(thenable_js_handle, <any>then_js_handle, "i32");
- } finally {
- result_root.release();
- }
+ return promise;
}
function _mono_wasm_web_socket_on_message(ws: WebSocketExtension, event: MessageEvent) {
@@ -361,36 +261,37 @@ function _mono_wasm_web_socket_on_message(ws: WebSocketExtension, event: Message
while (promise_queue.getLength() && event_queue.getLength()) {
const promise_control = promise_queue.dequeue()!;
_mono_wasm_web_socket_receive_buffering(event_queue,
- promise_control.buffer_ptr, promise_control.buffer_offset, promise_control.buffer_length,
- promise_control.response_ptr);
+ promise_control.bufferView, promise_control.responseView);
promise_control.resolve(null);
}
prevent_timer_throttling();
}
-function _mono_wasm_web_socket_receive_buffering(event_queue: Queue<any>, buffer_ptr: VoidPtr, buffer_offset: number, buffer_length: number, response_ptr: VoidPtr) {
+function _mono_wasm_web_socket_receive_buffering(event_queue: Queue<any>, bufferView: ArraySegment, responseView: ArraySegment) {
const event = event_queue.peek();
- const count = Math.min(buffer_length, event.data.length - event.offset);
+ const count = Math.min(bufferView.length, event.data.length - event.offset);
if (count > 0) {
- const targetView = Module.HEAPU8.subarray(<any>buffer_ptr + buffer_offset, <any>buffer_ptr + buffer_offset + buffer_length);
const sourceView = event.data.subarray(event.offset, event.offset + count);
- targetView.set(sourceView, 0);
+ bufferView.set(sourceView, 0);
event.offset += count;
}
const end_of_message = event.data.length === event.offset ? 1 : 0;
if (end_of_message) {
event_queue.dequeue();
}
- setI32(<any>response_ptr + 0, count);
- setI32(<any>response_ptr + 4, event.type);
- setI32(<any>response_ptr + 8, end_of_message);
+
+ const response = new Int32Array([count, event.type, end_of_message]);
+ responseView.set(response);
+
+ bufferView.dispose();
+ responseView.dispose();
}
-function _mono_wasm_web_socket_send_buffering(ws: WebSocketExtension, buffer_ptr: VoidPtr, buffer_offset: number, length: number, message_type: number, end_of_message: boolean): Uint8Array | string | null {
+function _mono_wasm_web_socket_send_buffering(ws: WebSocketExtension, bufferView: ArraySegment, message_type: number, end_of_message: boolean): Uint8Array | string | null {
let buffer = ws[wasm_ws_pending_send_buffer];
let offset = 0;
- const message_ptr = <any>buffer_ptr + buffer_offset;
+ const length = bufferView.length;
if (buffer) {
offset = ws[wasm_ws_pending_send_buffer_offset];
@@ -398,15 +299,14 @@ function _mono_wasm_web_socket_send_buffering(ws: WebSocketExtension, buffer_ptr
message_type = ws[wasm_ws_pending_send_buffer_type];
// if not empty message, append to existing buffer
if (length !== 0) {
- const view = Module.HEAPU8.subarray(message_ptr, message_ptr + length);
if (offset + length > buffer.length) {
const newbuffer = new Uint8Array((offset + length + 50) * 1.5); // exponential growth
newbuffer.set(buffer, 0);// copy previous buffer
- newbuffer.set(view, offset);// append copy at the end
+ bufferView.copyTo(newbuffer.subarray(offset));// append copy at the end
ws[wasm_ws_pending_send_buffer] = buffer = newbuffer;
}
else {
- buffer.set(view, offset);// append copy at the end
+ bufferView.copyTo(buffer.subarray(offset));// append copy at the end
}
offset += length;
ws[wasm_ws_pending_send_buffer_offset] = offset;
@@ -415,8 +315,7 @@ function _mono_wasm_web_socket_send_buffering(ws: WebSocketExtension, buffer_ptr
else if (!end_of_message) {
// create new buffer
if (length !== 0) {
- const view = Module.HEAPU8.subarray(message_ptr, message_ptr + length);
- buffer = new Uint8Array(view); // copy
+ buffer = <Uint8Array>bufferView.slice(); // copy
offset = length;
ws[wasm_ws_pending_send_buffer_offset] = offset;
ws[wasm_ws_pending_send_buffer] = buffer;
@@ -424,10 +323,9 @@ function _mono_wasm_web_socket_send_buffering(ws: WebSocketExtension, buffer_ptr
ws[wasm_ws_pending_send_buffer_type] = message_type;
}
else {
- // use the buffer only localy
if (length !== 0) {
- const memoryView = Module.HEAPU8.subarray(message_ptr, message_ptr + length);
- buffer = memoryView; // send will make a copy
+ // we could use the unsafe view, because it will be immediately used in ws.send()
+ buffer = <Uint8Array>bufferView._unsafe_create_view();
offset = length;
}
}
@@ -469,10 +367,8 @@ type WebSocketExtension = WebSocket & {
}
type ReceivePromiseControl = PromiseControl & {
- response_ptr: VoidPtr
- buffer_ptr: VoidPtr
- buffer_offset: number
- buffer_length: number
+ bufferView: ArraySegment,
+ responseView: ArraySegment
}
type Message = {