From d0bb0dfdc4e3674d43ac7278b6fc3544c71a8a65 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Thu, 14 Jul 2022 22:23:03 +0200 Subject: [PATCH] cleanup --- .../src/Interop/Browser/Interop.Runtime.cs | 4 - .../JavaScript/JavaScriptTests.cs | 1 + .../JavaScript/MarshalTests.cs | 61 ++- ....Runtime.InteropServices.JavaScript.csproj | 1 + .../JavaScript/Interop/JavaScriptExports.cs | 317 +++----------- .../JavaScript/Interop/LegacyExports.cs | 255 +++++++++++ .../JavaScript/JSFunctionBinding.cs | 4 +- .../JavaScript/JSHostImplementation.cs | 27 +- .../JavaScript/Legacy/Runtime.cs | 1 + .../Marshaling/JSMarshalerArgument.Byte.cs | 2 +- .../Marshaling/JSMarshalerArgument.Double.cs | 2 +- .../JSMarshalerArgument.Exception.cs | 4 +- .../Marshaling/JSMarshalerArgument.Func.cs | 32 +- .../Marshaling/JSMarshalerArgument.Int32.cs | 2 +- .../JSMarshalerArgument.JSObject.cs | 2 +- .../Marshaling/JSMarshalerArgument.Object.cs | 2 +- .../Marshaling/JSMarshalerArgument.Task.cs | 6 +- .../JavaScript/JSImportExportTest.cs | 4 +- .../sample/wasm/browser-profile/README.md | 2 +- .../tests/debugger-test/debugger-driver.html | 20 +- src/mono/wasm/runtime/buffers.ts | 205 --------- src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js | 2 - src/mono/wasm/runtime/corebindings.c | 4 - src/mono/wasm/runtime/corebindings.ts | 73 ---- src/mono/wasm/runtime/cwraps.ts | 174 ++++---- src/mono/wasm/runtime/dotnet.d.ts | 397 +++++++++--------- src/mono/wasm/runtime/es6/dotnet.es6.lib.js | 2 - src/mono/wasm/runtime/export-types.ts | 4 +- src/mono/wasm/runtime/exports-internal.ts | 87 ++++ src/mono/wasm/runtime/exports-linker.ts | 76 ++++ src/mono/wasm/runtime/exports.ts | 344 +-------------- src/mono/wasm/runtime/gc-handles.ts | 26 +- src/mono/wasm/runtime/imports.ts | 7 +- src/mono/wasm/runtime/invoke-cs.ts | 113 +++-- src/mono/wasm/runtime/invoke-js.ts | 36 +- src/mono/wasm/runtime/legacy/buffers.ts | 110 +++++ src/mono/wasm/runtime/legacy/corebindings.ts | 97 +++++ .../wasm/runtime/{ => legacy}/cs-to-js.ts | 99 +++-- .../wasm/runtime/legacy/exports-legacy.ts | 207 +++++++++ .../wasm/runtime/{ => legacy}/js-to-cs.ts | 61 +-- .../runtime/{ => legacy}/method-binding.ts | 156 ++++--- .../wasm/runtime/{ => legacy}/method-calls.ts | 291 ++----------- src/mono/wasm/runtime/managed-exports.ts | 83 ++++ src/mono/wasm/runtime/marshal-to-cs.ts | 29 +- src/mono/wasm/runtime/marshal-to-js.ts | 16 +- src/mono/wasm/runtime/marshal.ts | 11 + src/mono/wasm/runtime/memory.ts | 8 + src/mono/wasm/runtime/run.ts | 2 +- src/mono/wasm/runtime/startup.ts | 64 +-- src/mono/wasm/runtime/types.ts | 32 +- src/mono/wasm/test-main.js | 3 +- src/mono/wasm/wasm.proj | 1 + 52 files changed, 1757 insertions(+), 1812 deletions(-) create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/LegacyExports.cs delete mode 100644 src/mono/wasm/runtime/buffers.ts delete mode 100644 src/mono/wasm/runtime/corebindings.ts create mode 100644 src/mono/wasm/runtime/exports-internal.ts create mode 100644 src/mono/wasm/runtime/exports-linker.ts create mode 100644 src/mono/wasm/runtime/legacy/buffers.ts create mode 100644 src/mono/wasm/runtime/legacy/corebindings.ts rename src/mono/wasm/runtime/{ => legacy}/cs-to-js.ts (78%) create mode 100644 src/mono/wasm/runtime/legacy/exports-legacy.ts rename src/mono/wasm/runtime/{ => legacy}/js-to-cs.ts (80%) rename src/mono/wasm/runtime/{ => legacy}/method-binding.ts (83%) rename src/mono/wasm/runtime/{ => legacy}/method-calls.ts (51%) create mode 100644 src/mono/wasm/runtime/managed-exports.ts diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs index 16535859613b1..693a88b8e9ff9 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs @@ -43,11 +43,7 @@ internal static unsafe partial class Runtime [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); - [MethodImplAttribute(MethodImplOptions.InternalCall)] internal static extern void TypedArrayFromRef(int arrayPtr, int begin, int end, int bytesPerElement, int type, out int exceptionalResult, out object result); - [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern void TypedArrayCopyFromRef(IntPtr jsHandle, int arrayPtr, int begin, int end, int bytesPerElement, out int exceptionalResult, out object result); #endregion diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JavaScriptTests.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JavaScriptTests.cs index dd2351ae2093a..c78c422cc1b6d 100644 --- a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JavaScriptTests.cs +++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JavaScriptTests.cs @@ -153,6 +153,7 @@ public static async Task Iterator() { throw new Exception($"At attempt={attempt}, index={index}: {ex.Message}", ex); } + await Task.Yield(); } } diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/MarshalTests.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/MarshalTests.cs index cdc79e556a23a..1f207e6e5e6fb 100644 --- a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/MarshalTests.cs +++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/MarshalTests.cs @@ -38,9 +38,6 @@ public static void MarshalArrayBuffer() Assert.Equal(16, HelperMarshal._byteBuffer.Length); } - - - [Fact] public static void MarshalStringToCS() { @@ -279,7 +276,7 @@ public static void BindStaticMethod() { HelperMarshal._intValue = 0; Utils.InvokeJS(@$" - var invoke_int = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); + var invoke_int = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); invoke_int (200); "); @@ -291,7 +288,7 @@ public static void BindIntPtrStaticMethod() { HelperMarshal._intPtrValue = IntPtr.Zero; Utils.InvokeJS(@$" - var invoke_int_ptr = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeIntPtr""); + var invoke_int_ptr = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeIntPtr""); invoke_int_ptr (42); "); Assert.Equal(42, (int)HelperMarshal._intPtrValue); @@ -302,7 +299,7 @@ public static void MarshalIntPtrToJS() { HelperMarshal._marshaledIntPtrValue = IntPtr.Zero; Utils.InvokeJS(@$" - var invokeMarshalIntPtr = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeMarshalIntPtr""); + var invokeMarshalIntPtr = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeMarshalIntPtr""); var r = invokeMarshalIntPtr (); if (r != 42) throw `Invalid int_ptr value`; @@ -310,17 +307,6 @@ public static void MarshalIntPtrToJS() Assert.Equal(42, (int)HelperMarshal._marshaledIntPtrValue); } - [Fact] - public static void InvokeStaticMethod() - { - HelperMarshal._intValue = 0; - Utils.InvokeJS(@$" - INTERNAL.call_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt"", [ 300 ]); - "); - - Assert.Equal(300, HelperMarshal._intValue); - } - [Fact] public static void ResolveMethod() { @@ -407,6 +393,17 @@ public static void MarshalTypedArray() Assert.Equal(16, HelperMarshal._byteBuffer.Length); } + [Fact] + public static void MarshalUri() + { + HelperMarshal._blobURI = null; + Utils.InvokeJS(@" + App.call_test_method (""SetBlobAsUri"", [ ""https://dotnet.microsoft.com/en-us/"" ]); + "); + + Assert.NotNull(HelperMarshal._blobURI); + } + private static void RunMarshalTypedArrayJS(string type) { Utils.InvokeJS(@" @@ -426,8 +423,6 @@ public static void MarshalTypedArrayByte() Assert.Equal("hic sunt dracones", System.Text.Encoding.Default.GetString(HelperMarshal._taByte)); } - - [Fact] public static void TestFunctionSum() { @@ -455,7 +450,7 @@ public static void BoundStaticMethodMissingArgs() { HelperMarshal._intValue = 1; var ex = Assert.Throws(() => Utils.InvokeJS(@$" - var invoke_int = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); + var invoke_int = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); invoke_int (); ")); Assert.Contains("Value is not an integer: undefined (undefined)", ex.Message); @@ -467,7 +462,7 @@ public static void BoundStaticMethodExtraArgs() { HelperMarshal._intValue = 0; Utils.InvokeJS(@$" - var invoke_int = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); + var invoke_int = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); invoke_int (200, 400); "); Assert.Equal(200, HelperMarshal._intValue); @@ -479,7 +474,7 @@ public static void RangeCheckInt() HelperMarshal._intValue = 0; // no numbers bigger than 32 bits var ex = Assert.Throws(() => Utils.InvokeJS(@$" - var invoke_int = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); + var invoke_int = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); invoke_int (Number.MAX_SAFE_INTEGER); ")); Assert.Contains("Overflow: value 9007199254740991 is out of -2147483648 2147483647 range", ex.Message); @@ -492,7 +487,7 @@ public static void IntegerCheckInt() HelperMarshal._intValue = 0; // no floating point rounding var ex = Assert.Throws(() => Utils.InvokeJS(@$" - var invoke_int = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); + var invoke_int = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); invoke_int (3.14); ")); Assert.Contains("Value is not an integer: 3.14 (number)", ex.Message); @@ -505,7 +500,7 @@ public static void TypeCheckInt() HelperMarshal._intValue = 0; // no string conversion var ex = Assert.Throws(() => Utils.InvokeJS(@$" - var invoke_int = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); + var invoke_int = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); invoke_int (""200""); ")); Assert.Contains("Value is not an integer: 200 (string)", ex.Message); @@ -517,7 +512,7 @@ public static void PassUintArgument() { HelperMarshal._uintValue = 0; Utils.InvokeJS(@$" - var invoke_uint = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeUInt""); + var invoke_uint = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeUInt""); invoke_uint (0xFFFFFFFE); "); @@ -530,9 +525,9 @@ public static void ReturnUintEnum() HelperMarshal._uintValue = 0; HelperMarshal._enumValue = TestEnum.BigValue; Utils.InvokeJS(@$" - var get_value = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}GetEnumValue""); + var get_value = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}GetEnumValue""); var e = get_value (); - var invoke_uint = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeUInt""); + var invoke_uint = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeUInt""); invoke_uint (e); "); Assert.Equal((uint)TestEnum.BigValue, HelperMarshal._uintValue); @@ -543,7 +538,7 @@ public static void PassUintEnumByValue() { HelperMarshal._enumValue = TestEnum.Zero; Utils.InvokeJS(@$" - var set_enum = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}SetEnumValue"", ""j""); + var set_enum = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}SetEnumValue"", ""j""); set_enum (0xFFFFFFFE); "); Assert.Equal(TestEnum.BigValue, HelperMarshal._enumValue); @@ -555,7 +550,7 @@ public static void PassUintEnumByNameIsNotImplemented() HelperMarshal._enumValue = TestEnum.Zero; var exc = Assert.Throws(() => Utils.InvokeJS(@$" - var set_enum = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}SetEnumValue"", ""j""); + var set_enum = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}SetEnumValue"", ""j""); set_enum (""BigValue""); ") ); @@ -567,7 +562,7 @@ public static void CannotUnboxUint64() { var exc = Assert.Throws(() => Utils.InvokeJS(@$" - var get_u64 = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}GetUInt64"", """"); + var get_u64 = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}GetUInt64"", """"); var u64 = get_u64(); ") ); @@ -679,8 +674,8 @@ public static void InternedStringReturnValuesWork() HelperMarshal._stringResource = HelperMarshal._stringResource2 = null; var fqn = "[System.Private.Runtime.InteropServices.JavaScript.Tests]System.Runtime.InteropServices.JavaScript.Tests.HelperMarshal:StoreArgumentAndReturnLiteral"; Utils.InvokeJS( - $"var a = INTERNAL.mono_bind_static_method('{fqn}')('test');\r\n" + - $"var b = INTERNAL.mono_bind_static_method('{fqn}')(a);\r\n" + + $"var a = BINDING.bind_static_method('{fqn}')('test');\r\n" + + $"var b = BINDING.bind_static_method('{fqn}')(a);\r\n" + "App.call_test_method ('InvokeString2', [ b ]);" ); Assert.Equal("s: 1 length: 1", HelperMarshal._stringResource); @@ -721,7 +716,7 @@ private static async Task MarshalTask(string helperMethodName, string help @"globalThis.__test_promise_completed = false; " + @"globalThis.__test_promise_resolved = false; " + @"globalThis.__test_promise_failed = false; " + - $@"var t = App.call_test_method ('{helperMethodName}', [ {helperMethodArgs} ], 'i'); " + + $@"var t = App.call_test_method ('{helperMethodName}', [ {helperMethodArgs} ]); " + "t.then(result => { globalThis.__test_promise_resolved = true; " + resolvedBody + " })" + " .catch(e => { globalThis.__test_promise_failed = true; })" + " .finally(result => { globalThis.__test_promise_completed = true; }); " + 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 e9c87d0295532..b5e2c58d25a6a 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 @@ -19,6 +19,7 @@ + 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 ea24f8e1bf74a..7650f750d9c51 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,270 +1,101 @@ // 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 + // this maps to src\mono\wasm\runtime\corebindings.ts + // the methods are protected from trimming by DynamicDependency on JSFunctionBinding.BindJSFunction internal static unsafe partial class JavaScriptExports { + + // 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 GetCSOwnedObjectByJSHandleRef(IntPtr jsHandle, int shouldAddInflight, out JSObject? result) + public static void ReleaseJSOwnedObjectByGCHandle(JSMarshalerArgument* arguments_buffer) { - lock (JSHostImplementation.s_csOwnedObjects) + ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; + ref JSMarshalerArgument arg_return = ref arguments_buffer[1]; + ref JSMarshalerArgument arg_1 = ref arguments_buffer[2]; + try { - if (JSHostImplementation.s_csOwnedObjects.TryGetValue((int)jsHandle, out WeakReference? reference)) + GCHandle handle = (GCHandle)arg_1.slot.GCHandle; + + lock (JSHostImplementation.s_gcHandleFromJSOwnedObject) { - reference.TryGetTarget(out JSObject? jsObject); - if (shouldAddInflight != 0) - { - jsObject?.AddInFlight(); - } - result = jsObject; - return; + JSHostImplementation.s_gcHandleFromJSOwnedObject.Remove(handle.Target!); + handle.Free(); } + arg_exc.Initialize(); } - result = null; - } - - [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 - public static IntPtr GetCSOwnedObjectJSHandleRef(in JSObject jsObject, int shouldAddInflight) - { - jsObject.AssertNotDisposed(); - - if (shouldAddInflight != 0) + catch (Exception ex) { - jsObject.AddInFlight(); + arg_exc.ToJS(ex); } - return jsObject.JSHandle; } [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 - public static IntPtr TryGetCSOwnedObjectJSHandleRef(in object rawObj, int shouldAddInflight) + public static void CreateTaskCallback(JSMarshalerArgument* arguments_buffer) { - JSObject? jsObject = rawObj as JSObject; - if (jsObject != null && shouldAddInflight != 0) - { - jsObject.AddInFlight(); - } - return jsObject?.JSHandle ?? IntPtr.Zero; - } - - [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? res = null; - - lock (JSHostImplementation.s_csOwnedObjects) + ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; + ref JSMarshalerArgument arg_return = ref arguments_buffer[1]; + try { - if (!JSHostImplementation.s_csOwnedObjects.TryGetValue((int)jsHandle, out WeakReference? reference) || - !reference.TryGetTarget(out res) || - res.IsDisposed) - { -#pragma warning disable CS0612 // Type or member is obsolete - res = mappedType switch - { - JSHostImplementation.MappedType.JSObject => new JSObject(jsHandle), - JSHostImplementation.MappedType.Array => new Array(jsHandle), - JSHostImplementation.MappedType.ArrayBuffer => new ArrayBuffer(jsHandle), - JSHostImplementation.MappedType.DataView => new DataView(jsHandle), - JSHostImplementation.MappedType.Function => new Function(jsHandle), - JSHostImplementation.MappedType.Uint8Array => new Uint8Array(jsHandle), - _ => throw new ArgumentOutOfRangeException(nameof(mappedType)) - }; -#pragma warning restore CS0612 // Type or member is obsolete - JSHostImplementation.s_csOwnedObjects[(int)jsHandle] = new WeakReference(res, trackResurrection: true); - } + JSHostImplementation.TaskCallback holder = new JSHostImplementation.TaskCallback(); + arg_return.slot.Type = MarshalerType.Object; + arg_return.slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(holder); } - if (shouldAddInflight != 0) + catch (Exception ex) { - res.AddInFlight(); + arg_exc.ToJS(ex); } - jsObject = res; } [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 - public static void GetJSOwnedObjectByGCHandleRef(int gcHandle, out object result) + public static void CallDelegate(JSMarshalerArgument* arguments_buffer) { - GCHandle h = (GCHandle)(IntPtr)gcHandle; - result = h.Target!; - } + ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; + ref JSMarshalerArgument arg_return = ref arguments_buffer[1]; + try + { + GCHandle callback_gc_handle = (GCHandle)arg_return.slot.GCHandle; - [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 - public static IntPtr GetJSOwnedObjectGCHandleRef(in object obj) - { - return JSHostImplementation.GetJSOwnedObjectGCHandleRef(obj, GCHandleType.Normal); - } + JSHostImplementation.ToManagedCallback? cb = (JSHostImplementation.ToManagedCallback?)callback_gc_handle.Target; + if (cb == null) + { + throw new InvalidOperationException("ToManagedCallback is null"); + } - // 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; - lock (JSHostImplementation.s_gcHandleFromJSOwnedObject) + cb(arguments_buffer); + } + catch (Exception ex) { - JSHostImplementation.s_gcHandleFromJSOwnedObject.Remove(handle.Target!); - handle.Free(); + arg_exc.ToJS(ex); } } [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 - public static IntPtr CreateTaskSource() - { - var tcs = new TaskCompletionSource(); - 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; - // this is JS owned Normal handle. We always have a Target - TaskCompletionSource tcs = (TaskCompletionSource)handle.Target!; - 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; - // this is JS owned Normal handle. We always have a Target - TaskCompletionSource tcs = (TaskCompletionSource)handle.Target!; - 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; - // this is JS owned Normal handle. We always have a Target - TaskCompletionSource tcs = (TaskCompletionSource)handle.Target!; - 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) + public static void CompleteTask(JSMarshalerArgument* arguments_buffer) { - // HACK: Attempting to use the in-param will produce CS1628, so we make a temporary copy - // on the stack that can be captured by our local functions below - var task = _task; - - if (task.IsCompleted) - Complete(); - else - task.GetAwaiter().OnCompleted(Complete); - - void Complete() + ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; + ref JSMarshalerArgument arg_return = ref arguments_buffer[1]; + try { - try - { - if (task.Exception == null) - { - object? result; - Type task_type = task.GetType(); - if (task_type == typeof(Task)) - { - result = System.Array.Empty(); - } - else - { - result = JSHostImplementation.GetTaskResultMethodInfo(task_type)?.Invoke(task, null); - } + GCHandle callback_gc_handle = (GCHandle)arg_return.slot.GCHandle; - continuationObj.Invoke("resolve", result); - } - else - { - continuationObj.Invoke("reject", task.Exception.ToString()); - } - } - catch (Exception e) - { - continuationObj.Invoke("reject", e.ToString()); - } - finally + JSHostImplementation.TaskCallback? holder = (JSHostImplementation.TaskCallback?)callback_gc_handle.Target; + if (holder == null || holder.Callback == null) { - continuationObj.Dispose(); + throw new InvalidOperationException("TaskCallback is null"); } - } - } - - [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); - - if (!(dtv is DateTime dt)) - throw new InvalidCastException(SR.Format(SR.UnableCastObjectToType, dtv.GetType(), typeof(DateTime))); - if (dt.Kind == DateTimeKind.Local) - dt = dt.ToUniversalTime(); - else if (dt.Kind == DateTimeKind.Unspecified) - dt = new DateTime(dt.Ticks, DateTimeKind.Utc); - return new DateTimeOffset(dt).ToUnixTimeMilliseconds(); - } - - // 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); - - MethodBase? mb = objForRuntimeType is null ? MethodBase.GetMethodFromHandle(methodHandle) : MethodBase.GetMethodFromHandle(methodHandle, Type.GetTypeHandle(objForRuntimeType)); - if (mb is null) - return string.Empty; - ParameterInfo[] parms = mb.GetParameters(); - int parmsLength = parms.Length; - if (parmsLength == 0) - return string.Empty; - - var result = new char[parmsLength]; - for (int i = 0; i < parmsLength; i++) + holder.Callback(arguments_buffer); + } + catch (Exception ex) { - Type t = parms[i].ParameterType; - var mt = JSHostImplementation.GetMarshalTypeFromType(t); - result[i] = JSHostImplementation.GetCallSignatureCharacterForMarshalType(mt, null); + arg_exc.ToJS(ex); } - - return new string(result); } [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 @@ -291,45 +122,5 @@ public static unsafe void DumpAotProfileData(ref byte buf, int len, string extra 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/LegacyExports.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/LegacyExports.cs new file mode 100644 index 0000000000000..faf196db950d2 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/LegacyExports.cs @@ -0,0 +1,255 @@ +// 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\legacy\corebindings.ts + // the methods are protected from trimming by DynamicDependency on JSFunctionBinding.BindJSFunction + internal static unsafe partial class LegacyExports + { + [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) + { + if (JSHostImplementation.s_csOwnedObjects.TryGetValue((int)jsHandle, out WeakReference? reference)) + { + reference.TryGetTarget(out JSObject? jsObject); + if (shouldAddInflight != 0) + { + jsObject?.AddInFlight(); + } + result = jsObject; + return; + } + } + result = null; + } + + [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 + public static IntPtr GetCSOwnedObjectJSHandleRef(in JSObject jsObject, int shouldAddInflight) + { + jsObject.AssertNotDisposed(); + + if (shouldAddInflight != 0) + { + jsObject.AddInFlight(); + } + return jsObject.JSHandle; + } + + [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 + public static IntPtr TryGetCSOwnedObjectJSHandleRef(in object rawObj, int shouldAddInflight) + { + JSObject? jsObject = rawObj as JSObject; + if (jsObject != null && shouldAddInflight != 0) + { + jsObject.AddInFlight(); + } + return jsObject?.JSHandle ?? IntPtr.Zero; + } + + [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? res = null; + + lock (JSHostImplementation.s_csOwnedObjects) + { + if (!JSHostImplementation.s_csOwnedObjects.TryGetValue((int)jsHandle, out WeakReference? reference) || + !reference.TryGetTarget(out res) || + res.IsDisposed) + { +#pragma warning disable CS0612 // Type or member is obsolete + res = mappedType switch + { + JSHostImplementation.MappedType.JSObject => new JSObject(jsHandle), + JSHostImplementation.MappedType.Array => new Array(jsHandle), + JSHostImplementation.MappedType.ArrayBuffer => new ArrayBuffer(jsHandle), + JSHostImplementation.MappedType.DataView => new DataView(jsHandle), + JSHostImplementation.MappedType.Function => new Function(jsHandle), + JSHostImplementation.MappedType.Uint8Array => new Uint8Array(jsHandle), + _ => throw new ArgumentOutOfRangeException(nameof(mappedType)) + }; +#pragma warning restore CS0612 // Type or member is obsolete + JSHostImplementation.s_csOwnedObjects[(int)jsHandle] = new WeakReference(res, trackResurrection: true); + } + } + if (shouldAddInflight != 0) + { + 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.GetJSOwnedObjectGCHandle(obj, GCHandleType.Normal); + } + + [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 + public static IntPtr CreateTaskSource() + { + var tcs = new TaskCompletionSource(); + 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; + // this is JS owned Normal handle. We always have a Target + TaskCompletionSource tcs = (TaskCompletionSource)handle.Target!; + 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; + // this is JS owned Normal handle. We always have a Target + TaskCompletionSource tcs = (TaskCompletionSource)handle.Target!; + 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; + // this is JS owned Normal handle. We always have a Target + TaskCompletionSource tcs = (TaskCompletionSource)handle.Target!; + result = tcs.Task; + } + + [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 + // on the stack that can be captured by our local functions below + var task = _task; + + if (task.IsCompleted) + Complete(); + else + task.GetAwaiter().OnCompleted(Complete); + + void Complete() + { + try + { + if (task.Exception == null) + { + object? result; + Type task_type = task.GetType(); + if (task_type == typeof(Task)) + { + result = System.Array.Empty(); + } + else + { + result = JSHostImplementation.GetTaskResultMethodInfo(task_type)?.Invoke(task, null); + } + + continuationObj.Invoke("resolve", result); + } + else + { + continuationObj.Invoke("reject", task.Exception.ToString()); + } + } + catch (Exception e) + { + continuationObj.Invoke("reject", e.ToString()); + } + finally + { + continuationObj.Dispose(); + } + } + } + + [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); + + if (!(dtv is DateTime dt)) + throw new InvalidCastException(SR.Format(SR.UnableCastObjectToType, dtv.GetType(), typeof(DateTime))); + if (dt.Kind == DateTimeKind.Local) + dt = dt.ToUniversalTime(); + else if (dt.Kind == DateTimeKind.Unspecified) + dt = new DateTime(dt.Ticks, DateTimeKind.Utc); + return new DateTimeOffset(dt).ToUnixTimeMilliseconds(); + } + + // 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; + } + + [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 + public static void CreateUriRef(string uri, out object? result) + { + // we do this via reflection to allow linker to trim dependency on URI and it's assembly + // if the user code has methods with Uri signature, this should work too + // System.Private.Uri is large assembly so it's worth trimming + var uriType = Type.GetType("System.Uri, System.Private.Uri"); + if (uriType == null) throw new InvalidProgramException(); + result = Activator.CreateInstance(uriType, 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); + + MethodBase? mb = objForRuntimeType is null ? MethodBase.GetMethodFromHandle(methodHandle) : MethodBase.GetMethodFromHandle(methodHandle, Type.GetTypeHandle(objForRuntimeType)); + if (mb is null) + return string.Empty; + + ParameterInfo[] parms = mb.GetParameters(); + int parmsLength = parms.Length; + if (parmsLength == 0) + return string.Empty; + + var result = new char[parmsLength]; + for (int i = 0; i < parmsLength; i++) + { + Type t = parms[i].ParameterType; + var mt = JSHostImplementation.GetMarshalTypeFromType(t); + result[i] = JSHostImplementation.GetCallSignatureCharacterForMarshalType(mt, null); + } + + return new string(result); + } + } +} 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 f73515faaea81..66287d12f6cea 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 @@ -130,6 +130,8 @@ public static void InvokeJS(JSFunctionBinding signature, Span // 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")] + // TODO make this DynamicDependency conditional + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, "System.Runtime.InteropServices.JavaScript.LegacyExports", "System.Runtime.InteropServices.JavaScript")] public static JSFunctionBinding BindJSFunction(string functionName, string moduleName, ReadOnlySpan signatures) { if (RuntimeInformation.OSArchitecture != Architecture.Wasm) @@ -174,7 +176,7 @@ internal static unsafe JSFunctionBinding BindJSFunctionImpl(string functionName, if (isException != 0) throw new JSException((string)exceptionMessage); - signature.JSFunction = JavaScriptExports.CreateCSOwnedProxy(jsFunctionHandle); + signature.JSFunction = JSHostImplementation.CreateCSOwnedProxy(jsFunctionHandle); return signature; } 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 aa1ad66a34c48..f42902e3d6150 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 @@ -66,7 +66,7 @@ public static void ReleaseInFlight(object obj) // strong references, allowing the managed object to be collected. // This ensures that things like delegates and promises will never 'go away' while JS // is expecting to be able to invoke or await them. - public static IntPtr GetJSOwnedObjectGCHandleRef(object obj, GCHandleType handleType) + public static IntPtr GetJSOwnedObjectGCHandle(object obj, GCHandleType handleType = GCHandleType.Normal) { if (obj == null) return IntPtr.Zero; @@ -176,6 +176,8 @@ public static MarshalType GetMarshalTypeFromType(Type type) return MarshalType.DELEGATE; else if ((type == typeof(Task)) || typeof(Task).IsAssignableFrom(type)) return MarshalType.TASK; + else if (type.FullName == "System.Uri") + return MarshalType.URI; else if (type.IsPointer) return MarshalType.POINTER; @@ -256,7 +258,7 @@ public static MethodInfo GetTaskResultMethodInfo(Type taskType) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void ThrowException(ref JSMarshalerArgument arg) + public static void ThrowException(ref JSMarshalerArgument arg) { arg.ToManaged(out Exception? ex); @@ -275,7 +277,7 @@ public static async Task ImportAsync(string moduleName, string moduleU return await wrappedTask.ConfigureAwait(true); } - private static async Task CancelationHelper(Task jsTask, CancellationToken cancellationToken) + public static async Task CancelationHelper(Task jsTask, CancellationToken cancellationToken) { if (jsTask.IsCompletedSuccessfully) { @@ -291,7 +293,7 @@ private static async Task CancelationHelper(Task jsTask, Can } // res type is first argument - internal static unsafe JSFunctionBinding GetMethodSignature(ReadOnlySpan types) + public static unsafe JSFunctionBinding GetMethodSignature(ReadOnlySpan types) { int argsCount = types.Length - 1; int size = JSFunctionBinding.JSBindingHeader.JSMarshalerSignatureHeaderSize + ((argsCount + 2) * sizeof(JSFunctionBinding.JSBindingType)); @@ -315,5 +317,22 @@ internal static unsafe JSFunctionBinding GetMethodSignature(ReadOnlySpan? reference) || + !reference.TryGetTarget(out res) || + res.IsDisposed) + { + res = new JSObject(jsHandle); + s_csOwnedObjects[(int)jsHandle] = new WeakReference(res, trackResurrection: true); + } + } + return res; + } } } 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 15160244e529c..469f71c42392b 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 @@ -10,6 +10,7 @@ namespace System.Runtime.InteropServices.JavaScript public static class Runtime { [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, "System.Runtime.InteropServices.JavaScript.JavaScriptExports", "System.Runtime.InteropServices.JavaScript")] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, "System.Runtime.InteropServices.JavaScript.LegacyExports", "System.Runtime.InteropServices.JavaScript")] public static object GetGlobalObject(string str) => JavaScriptImports.GetGlobalObject(str); 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 218b40336f66f..0c97341aac2af 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 @@ -125,7 +125,7 @@ public unsafe void ToJS(ArraySegment value) return; } slot.Type = MarshalerType.ArraySegment; - slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandleRef(value.Array, GCHandleType.Pinned); + slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(value.Array, GCHandleType.Pinned); var refPtr = (IntPtr)Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(value.Array)); slot.IntPtrValue = refPtr + value.Offset; slot.Length = value.Count; 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 2dcb1d445d569..43c48bec9dd73 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 @@ -127,7 +127,7 @@ public unsafe void ToJS(ArraySegment value) return; } slot.Type = MarshalerType.ArraySegment; - slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandleRef(value.Array, GCHandleType.Pinned); + slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(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; 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 674b5899ceba0..8c2ec3fbddc89 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 @@ -32,7 +32,7 @@ public unsafe void ToManaged(out Exception? value) if (slot.JSHandle != IntPtr.Zero) { // this is JSException round-trip - jsException = JavaScriptExports.CreateCSOwnedProxy(slot.JSHandle); + jsException = JSHostImplementation.CreateCSOwnedProxy(slot.JSHandle); } string? message; @@ -75,7 +75,7 @@ public unsafe void ToJS(Exception? value) { ToJS(cpy.Message); slot.Type = MarshalerType.Exception; - slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(cpy); + slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(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 b4cd56423b9a4..9a7f16e6869b7 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 @@ -11,7 +11,7 @@ private sealed class ActionJS public ActionJS(IntPtr jsHandle) { - JSObject = JavaScriptExports.CreateCSOwnedProxy(jsHandle); + JSObject = JSHostImplementation.CreateCSOwnedProxy(jsHandle); } public void InvokeJS() @@ -37,7 +37,7 @@ private sealed class ActionJS public ActionJS(IntPtr jsHandle, ArgumentToJSCallback arg1Marshaler) { - JSObject = JavaScriptExports.CreateCSOwnedProxy(jsHandle); + JSObject = JSHostImplementation.CreateCSOwnedProxy(jsHandle); Arg1Marshaler = arg1Marshaler; } @@ -64,7 +64,7 @@ private sealed class ActionJS public ActionJS(IntPtr jsHandle, ArgumentToJSCallback arg1Marshaler, ArgumentToJSCallback arg2Marshaler) { - JSObject = JavaScriptExports.CreateCSOwnedProxy(jsHandle); + JSObject = JSHostImplementation.CreateCSOwnedProxy(jsHandle); Arg1Marshaler = arg1Marshaler; Arg2Marshaler = arg2Marshaler; } @@ -95,7 +95,7 @@ private sealed class ActionJS public ActionJS(IntPtr jsHandle, ArgumentToJSCallback arg1Marshaler, ArgumentToJSCallback arg2Marshaler, ArgumentToJSCallback arg3Marshaler) { - JSObject = JavaScriptExports.CreateCSOwnedProxy(jsHandle); + JSObject = JSHostImplementation.CreateCSOwnedProxy(jsHandle); Arg1Marshaler = arg1Marshaler; Arg2Marshaler = arg2Marshaler; Arg3Marshaler = arg3Marshaler; @@ -187,7 +187,7 @@ private sealed class FuncJS public FuncJS(IntPtr jsHandle, ArgumentToManagedCallback resMarshaler) { - JSObject = JavaScriptExports.CreateCSOwnedProxy(jsHandle); + JSObject = JSHostImplementation.CreateCSOwnedProxy(jsHandle); ResMarshaler = resMarshaler; } @@ -218,7 +218,7 @@ private sealed class FuncJS public FuncJS(IntPtr jsHandle, ArgumentToJSCallback arg1Marshaler, ArgumentToManagedCallback resMarshaler) { - JSObject = JavaScriptExports.CreateCSOwnedProxy(jsHandle); + JSObject = JSHostImplementation.CreateCSOwnedProxy(jsHandle); Arg1Marshaler = arg1Marshaler; ResMarshaler = resMarshaler; } @@ -250,7 +250,7 @@ private sealed class FuncJS public FuncJS(IntPtr jsHandle, ArgumentToJSCallback arg1Marshaler, ArgumentToJSCallback arg2Marshaler, ArgumentToManagedCallback resMarshaler) { - JSObject = JavaScriptExports.CreateCSOwnedProxy(jsHandle); + JSObject = JSHostImplementation.CreateCSOwnedProxy(jsHandle); Arg1Marshaler = arg1Marshaler; Arg2Marshaler = arg2Marshaler; ResMarshaler = resMarshaler; @@ -286,7 +286,7 @@ private sealed class FuncJS public FuncJS(IntPtr jsHandle, ArgumentToJSCallback arg1Marshaler, ArgumentToJSCallback arg2Marshaler, ArgumentToJSCallback arg3Marshaler, ArgumentToManagedCallback resMarshaler) { - JSObject = JavaScriptExports.CreateCSOwnedProxy(jsHandle); + JSObject = JSHostImplementation.CreateCSOwnedProxy(jsHandle); Arg1Marshaler = arg1Marshaler; Arg2Marshaler = arg2Marshaler; Arg3Marshaler = arg3Marshaler; @@ -397,7 +397,7 @@ public unsafe void ToJS(Action value) } }; slot.Type = MarshalerType.Function; - slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(cb); + slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(cb); } /// @@ -422,7 +422,7 @@ public unsafe void ToJS(Action value, ArgumentToManagedCallback arg1Mar } }; slot.Type = MarshalerType.Action; - slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(cb); + slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(cb); } /// @@ -449,7 +449,7 @@ public unsafe void ToJS(Action value, ArgumentToManagedCallback< } }; slot.Type = MarshalerType.Action; - slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(cb); + slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(cb); } /// @@ -478,7 +478,7 @@ public unsafe void ToJS(Action value, ArgumentToManagedC } }; slot.Type = MarshalerType.Action; - slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(cb); + slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(cb); } /// @@ -503,7 +503,7 @@ public unsafe void ToJS(Func value, ArgumentToJSCallback @@ -530,7 +530,7 @@ public unsafe void ToJS(Func value, ArgumentToManagedCal } }; slot.Type = MarshalerType.Function; - slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(cb); + slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(cb); } /// @@ -561,7 +561,7 @@ public unsafe void ToJS(Func value, ArgumentTo slot.Type = MarshalerType.Function; - slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(cb); + slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(cb); } /// @@ -594,7 +594,7 @@ public unsafe void ToJS(Func value, Ar slot.Type = MarshalerType.Function; - slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(cb); + slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(cb); } } } 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 05ddab050c9f9..75b25496398da 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 @@ -125,7 +125,7 @@ public unsafe void ToJS(ArraySegment value) return; } slot.Type = MarshalerType.ArraySegment; - slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandleRef(value.Array, GCHandleType.Pinned); + slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(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; 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 ea632d02db3b9..58522b4622ae7 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 @@ -19,7 +19,7 @@ public unsafe void ToManaged(out JSObject? value) value = null; return; } - value = JavaScriptExports.CreateCSOwnedProxy(slot.JSHandle); + value = JSHostImplementation.CreateCSOwnedProxy(slot.JSHandle); } /// 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 46ff369eecc97..d9b48229c223d 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 @@ -316,7 +316,7 @@ public void ToJS(object? value) else { slot.Type = MarshalerType.Object; - slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(value); + slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(value); } } 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 608443cc45cf3..babd0b418d0a4 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 @@ -138,7 +138,7 @@ internal void ToJSDynamic(Task? value) IntPtr jsHandle = CreatePendingPromise(); slot.JSHandle = jsHandle; - JSObject promise = JavaScriptExports.CreateCSOwnedProxy(jsHandle); + JSObject promise = JSHostImplementation.CreateCSOwnedProxy(jsHandle); task.GetAwaiter().OnCompleted(Complete); @@ -216,7 +216,7 @@ public void ToJS(Task value) IntPtr jsHandle = CreatePendingPromise(); slot.JSHandle = jsHandle; - JSObject promise = JavaScriptExports.CreateCSOwnedProxy(jsHandle); + JSObject promise = JSHostImplementation.CreateCSOwnedProxy(jsHandle); task.GetAwaiter().OnCompleted(Complete); @@ -289,7 +289,7 @@ public void ToJS(Task? value, ArgumentToJSCallback marshaler) IntPtr jsHandle = CreatePendingPromise(); slot.JSHandle = jsHandle; - JSObject promise = JavaScriptExports.CreateCSOwnedProxy(jsHandle); + JSObject promise = JSHostImplementation.CreateCSOwnedProxy(jsHandle); task.GetAwaiter().OnCompleted(Complete); 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 index e5bcf479b39fe..71a795545f331 100644 --- 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 @@ -150,8 +150,8 @@ public unsafe void CreateFunctionString() [Fact] public unsafe void CreateFunctionInternal() { - Func internals = Utils.CreateFunctionString("return INTERNAL.BINDING_ASM"); - Assert.Equal("[System.Runtime.InteropServices.JavaScript]System.Runtime.InteropServices.JavaScript.JavaScriptExports", internals()); + Func internals = Utils.CreateFunctionBool("return INTERNAL.mono_wasm_runtime_is_ready"); + Assert.True(internals()); } #endregion diff --git a/src/mono/sample/wasm/browser-profile/README.md b/src/mono/sample/wasm/browser-profile/README.md index 83a3fd35c234a..08bedff4756ae 100644 --- a/src/mono/sample/wasm/browser-profile/README.md +++ b/src/mono/sample/wasm/browser-profile/README.md @@ -26,7 +26,7 @@ await createDotnetRuntime(() => ({ 3. Call the `write_at` method at the end of the app, either in C# or in JS. To call the `write_at` method in JS, make use of bindings: -`INTERNAL.call_static_method("<[ProjectName] Namespace.Class::StopProfile">, []);` +`BINDING.bind_static_method("<[ProjectName] Namespace.Class::StopProfile">)();` When the `write_at` method is called, the `send_to` method `DumpAotProfileData` stores the profile data into `INTERNAL.aot_profile_data` diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-driver.html b/src/mono/wasm/debugger/tests/debugger-test/debugger-driver.html index 2513830fddd28..04bcb7efc12fa 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-driver.html +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-driver.html @@ -7,14 +7,14 @@ var App = { static_method_table: {}, init: function () { - this.int_add = getDotnetRuntime(0).INTERNAL.mono_bind_static_method ("[debugger-test] Math:IntAdd"); - this.use_complex = getDotnetRuntime(0).INTERNAL.mono_bind_static_method ("[debugger-test] Math:UseComplex"); - this.delegates_test = getDotnetRuntime(0).INTERNAL.mono_bind_static_method ("[debugger-test] Math:DelegatesTest"); - this.generic_types_test = getDotnetRuntime(0).INTERNAL.mono_bind_static_method ("[debugger-test] Math:GenericTypesTest"); - this.outer_method = getDotnetRuntime(0).INTERNAL.mono_bind_static_method ("[debugger-test] Math:OuterMethod"); - this.async_method = getDotnetRuntime(0).INTERNAL.mono_bind_static_method ("[debugger-test] Math/NestedInMath:AsyncTest"); - this.method_with_structs = getDotnetRuntime(0).INTERNAL.mono_bind_static_method ("[debugger-test] DebuggerTests.ValueTypesTest:MethodWithLocalStructs"); - this.run_all = getDotnetRuntime(0).INTERNAL.mono_bind_static_method ("[debugger-test] DebuggerTest:run_all"); + this.int_add = getDotnetRuntime(0).BINDING.bind_static_method ("[debugger-test] Math:IntAdd"); + this.use_complex = getDotnetRuntime(0).BINDING.bind_static_method ("[debugger-test] Math:UseComplex"); + this.delegates_test = getDotnetRuntime(0).BINDING.bind_static_method ("[debugger-test] Math:DelegatesTest"); + this.generic_types_test = getDotnetRuntime(0).BINDING.bind_static_method ("[debugger-test] Math:GenericTypesTest"); + this.outer_method = getDotnetRuntime(0).BINDING.bind_static_method ("[debugger-test] Math:OuterMethod"); + this.async_method = getDotnetRuntime(0).BINDING.bind_static_method ("[debugger-test] Math/NestedInMath:AsyncTest"); + this.method_with_structs = getDotnetRuntime(0).BINDING.bind_static_method ("[debugger-test] DebuggerTests.ValueTypesTest:MethodWithLocalStructs"); + this.run_all = getDotnetRuntime(0).BINDING.bind_static_method ("[debugger-test] DebuggerTest:run_all"); this.static_method_table = {}; console.log ("ready"); }, @@ -22,7 +22,7 @@ function invoke_static_method (method_name, ...args) { var method = App.static_method_table [method_name]; if (method == undefined) - method = App.static_method_table [method_name] = getDotnetRuntime(0).INTERNAL.mono_bind_static_method (method_name); + method = App.static_method_table [method_name] = getDotnetRuntime(0).BINDING.bind_static_method (method_name); return method (...args); } @@ -30,7 +30,7 @@ async function invoke_static_method_async (method_name, ...args) { var method = App.static_method_table [method_name]; if (method == undefined) { - method = App.static_method_table [method_name] = getDotnetRuntime(0).INTERNAL.mono_bind_static_method (method_name); + method = App.static_method_table [method_name] = getDotnetRuntime(0).BINDING.bind_static_method (method_name); } return await method (...args); diff --git a/src/mono/wasm/runtime/buffers.ts b/src/mono/wasm/runtime/buffers.ts deleted file mode 100644 index cf23c6baca9c7..0000000000000 --- a/src/mono/wasm/runtime/buffers.ts +++ /dev/null @@ -1,205 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -import { JSHandle, MonoArray, MonoObject, MonoObjectRef, is_nullish } from "./types"; -import { Module } from "./imports"; -import { mono_wasm_get_jsobj_from_js_handle } from "./gc-handles"; -import { wrap_error_root } from "./method-calls"; -import { js_to_mono_obj_root } from "./js-to-cs"; -import { Int32Ptr, TypedArray, VoidPtr } from "./types/emscripten"; -import { mono_wasm_new_external_root } from "./roots"; - -// Creates a new typed array from pinned array address from pinned_array allocated on the heap to the typed array. -// adress of managed pinned array -> copy from heap -> typed array memory -function typed_array_from(pinned_array: MonoArray, begin: number, end: number, bytes_per_element: number, type: number) { - - // typed array - let newTypedArray: TypedArray | null = null; - - switch (type) { - case 5: - newTypedArray = new Int8Array(end - begin); - break; - case 6: - newTypedArray = new Uint8Array(end - begin); - break; - case 7: - newTypedArray = new Int16Array(end - begin); - break; - case 8: - newTypedArray = new Uint16Array(end - begin); - break; - case 9: - newTypedArray = new Int32Array(end - begin); - break; - case 10: - newTypedArray = new Uint32Array(end - begin); - break; - case 13: - newTypedArray = new Float32Array(end - begin); - break; - case 14: - newTypedArray = new Float64Array(end - begin); - break; - case 15: // This is a special case because the typed array is also byte[] - newTypedArray = new Uint8ClampedArray(end - begin); - break; - default: - throw new Error("Unknown array type " + type); - } - - typedarray_copy_from(newTypedArray, pinned_array, begin, end, bytes_per_element); - return newTypedArray; -} - -// Copy the existing typed array to the heap pointed to by the pinned array address -// typed array memory -> copy to heap -> address of managed pinned array -function typedarray_copy_to(typed_array: TypedArray, pinned_array: MonoArray, begin: number, end: number, bytes_per_element: number) { - - // JavaScript typed arrays are array-like objects and provide a mechanism for accessing - // raw binary data. (...) To achieve maximum flexibility and efficiency, JavaScript typed arrays - // split the implementation into buffers and views. A buffer (implemented by the ArrayBuffer object) - // is an object representing a chunk of data; it has no format to speak of, and offers no - // mechanism for accessing its contents. In order to access the memory contained in a buffer, - // you need to use a view. A view provides a context - that is, a data type, starting offset, - // and number of elements - that turns the data into an actual typed array. - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays - if (has_backing_array_buffer(typed_array) && typed_array.BYTES_PER_ELEMENT) { - // Some sanity checks of what is being asked of us - // lets play it safe and throw an error here instead of assuming to much. - // Better safe than sorry later - if (bytes_per_element !== typed_array.BYTES_PER_ELEMENT) - throw new Error("Inconsistent element sizes: TypedArray.BYTES_PER_ELEMENT '" + typed_array.BYTES_PER_ELEMENT + "' sizeof managed element: '" + bytes_per_element + "'"); - - // how much space we have to work with - let num_of_bytes = (end - begin) * bytes_per_element; - // how much typed buffer space are we talking about - const view_bytes = typed_array.length * typed_array.BYTES_PER_ELEMENT; - // only use what is needed. - if (num_of_bytes > view_bytes) - num_of_bytes = view_bytes; - - // offset index into the view - const offset = begin * bytes_per_element; - - // Create a view over the heap pointed to by the pinned array address - const heapBytes = new Uint8Array(Module.HEAPU8.buffer, pinned_array + offset, num_of_bytes); - // Copy the bytes of the typed array to the heap. - heapBytes.set(new Uint8Array(typed_array.buffer, typed_array.byteOffset, num_of_bytes)); - - return num_of_bytes; - } - else { - throw new Error("Object '" + typed_array + "' is not a typed array"); - } - -} - -// Copy the pinned array address from pinned_array allocated on the heap to the typed array. -// adress of managed pinned array -> copy from heap -> typed array memory -function typedarray_copy_from(typed_array: TypedArray, pinned_array: MonoArray, begin: number, end: number, bytes_per_element: number) { - - // JavaScript typed arrays are array-like objects and provide a mechanism for accessing - // raw binary data. (...) To achieve maximum flexibility and efficiency, JavaScript typed arrays - // split the implementation into buffers and views. A buffer (implemented by the ArrayBuffer object) - // is an object representing a chunk of data; it has no format to speak of, and offers no - // mechanism for accessing its contents. In order to access the memory contained in a buffer, - // you need to use a view. A view provides a context - that is, a data type, starting offset, - // and number of elements - that turns the data into an actual typed array. - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays - if (has_backing_array_buffer(typed_array) && typed_array.BYTES_PER_ELEMENT) { - // Some sanity checks of what is being asked of us - // lets play it safe and throw an error here instead of assuming to much. - // Better safe than sorry later - if (bytes_per_element !== typed_array.BYTES_PER_ELEMENT) - throw new Error("Inconsistent element sizes: TypedArray.BYTES_PER_ELEMENT '" + typed_array.BYTES_PER_ELEMENT + "' sizeof managed element: '" + bytes_per_element + "'"); - - // how much space we have to work with - let num_of_bytes = (end - begin) * bytes_per_element; - // how much typed buffer space are we talking about - const view_bytes = typed_array.length * typed_array.BYTES_PER_ELEMENT; - // only use what is needed. - if (num_of_bytes > view_bytes) - num_of_bytes = view_bytes; - - // Create a new view for mapping - const typedarrayBytes = new Uint8Array(typed_array.buffer, 0, num_of_bytes); - // offset index into the view - const offset = begin * bytes_per_element; - // Set view bytes to value from HEAPU8 - typedarrayBytes.set(Module.HEAPU8.subarray(pinned_array + offset, pinned_array + offset + num_of_bytes)); - return num_of_bytes; - } - else { - throw new Error("Object '" + typed_array + "' is not a typed array"); - } -} - -export function mono_wasm_typed_array_copy_to_ref(js_handle: JSHandle, pinned_array: MonoArray, begin: number, end: number, bytes_per_element: number, is_exception: Int32Ptr, result_address: MonoObjectRef): void { - const resultRoot = mono_wasm_new_external_root(result_address); - try { - const js_obj = mono_wasm_get_jsobj_from_js_handle(js_handle); - if (is_nullish(js_obj)) { - wrap_error_root(is_exception, "ERR07: Invalid JS object handle '" + js_handle + "'", resultRoot); - return; - } - - const res = typedarray_copy_to(js_obj, pinned_array, begin, end, bytes_per_element); - // FIXME: We should just return an int - // returns num_of_bytes boxed - js_to_mono_obj_root(res, resultRoot, false); - } catch (exc) { - wrap_error_root(is_exception, String(exc), resultRoot); - } finally { - resultRoot.release(); - } -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export function mono_wasm_typed_array_from_ref(pinned_array: MonoArray, begin: number, end: number, bytes_per_element: number, type: number, is_exception: Int32Ptr, result_address: MonoObjectRef): void { - const resultRoot = mono_wasm_new_external_root(result_address); - try { - const res = typed_array_from(pinned_array, begin, end, bytes_per_element, type); - // returns JS typed array like Int8Array, to be wraped with JSObject proxy - js_to_mono_obj_root(res, resultRoot, true); - } catch (exc) { - wrap_error_root(is_exception, String(exc), resultRoot); - } finally { - resultRoot.release(); - } -} - -export function mono_wasm_typed_array_copy_from_ref(js_handle: JSHandle, pinned_array: MonoArray, begin: number, end: number, bytes_per_element: number, is_exception: Int32Ptr, result_address: MonoObjectRef): void { - const resultRoot = mono_wasm_new_external_root(result_address); - try { - const js_obj = mono_wasm_get_jsobj_from_js_handle(js_handle); - if (is_nullish(js_obj)) { - wrap_error_root(is_exception, "ERR08: Invalid JS object handle '" + js_handle + "'", resultRoot); - return; - } - - const res = typedarray_copy_from(js_obj, pinned_array, begin, end, bytes_per_element); - // FIXME: We should just return an int - // returns num_of_bytes boxed - js_to_mono_obj_root(res, resultRoot, false); - } catch (exc) { - wrap_error_root(is_exception, String(exc), resultRoot); - } finally { - resultRoot.release(); - } -} - -export function has_backing_array_buffer(js_obj: TypedArray): boolean { - return typeof SharedArrayBuffer !== "undefined" - ? js_obj.buffer instanceof ArrayBuffer || js_obj.buffer instanceof SharedArrayBuffer - : js_obj.buffer instanceof ArrayBuffer; -} - -// @bytes must be a typed array. space is allocated for it in the native heap -// and it is copied to that location. returns the address of the allocation. -export function mono_wasm_load_bytes_into_heap(bytes: Uint8Array): VoidPtr { - const memoryOffset = Module._malloc(bytes.length); - const heapBytes = new Uint8Array(Module.HEAPU8.buffer, memoryOffset, bytes.length); - heapBytes.set(bytes); - return memoryOffset; -} diff --git a/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js b/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js index 4aca26762484d..2e134be95bd97 100644 --- a/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js +++ b/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js @@ -70,9 +70,7 @@ const linked_functions = [ "mono_wasm_create_cs_owned_object_ref", "mono_wasm_release_cs_owned_object", "mono_wasm_typed_array_to_array_ref", - "mono_wasm_typed_array_copy_to_ref", "mono_wasm_typed_array_from_ref", - "mono_wasm_typed_array_copy_from_ref", "mono_wasm_compile_function_ref", "mono_wasm_bind_js_function", "mono_wasm_invoke_bound_function", diff --git a/src/mono/wasm/runtime/corebindings.c b/src/mono/wasm/runtime/corebindings.c index 11bd47ee8a335..1d4267a6fb8db 100644 --- a/src/mono/wasm/runtime/corebindings.c +++ b/src/mono/wasm/runtime/corebindings.c @@ -26,9 +26,7 @@ extern void mono_wasm_get_global_object_ref (MonoString **global_name, int *is_e extern void mono_wasm_release_cs_owned_object (int js_handle); extern void mono_wasm_create_cs_owned_object_ref (MonoString **core_name, MonoArray **args, int *is_exception, MonoObject** result); extern void mono_wasm_typed_array_to_array_ref (int js_handle, int *is_exception, MonoObject **result); -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_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); @@ -47,9 +45,7 @@ void core_initialize_internals () mono_add_internal_call ("Interop/Runtime::CreateCSOwnedObjectRef", mono_wasm_create_cs_owned_object_ref); mono_add_internal_call ("Interop/Runtime::ReleaseCSOwnedObject", mono_wasm_release_cs_owned_object); mono_add_internal_call ("Interop/Runtime::TypedArrayToArrayRef", mono_wasm_typed_array_to_array_ref); - 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::BindJSFunction", mono_wasm_bind_js_function); mono_add_internal_call ("Interop/Runtime::InvokeJSFunction", mono_wasm_invoke_bound_function); diff --git a/src/mono/wasm/runtime/corebindings.ts b/src/mono/wasm/runtime/corebindings.ts deleted file mode 100644 index bd9c0fb12ebff..0000000000000 --- a/src/mono/wasm/runtime/corebindings.ts +++ /dev/null @@ -1,73 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -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"], - ["_try_get_cs_owned_object_js_handle_ref", "TryGetCSOwnedObjectJSHandleRef", "mi"], - ["_create_cs_owned_proxy_ref", "CreateCSOwnedProxyRef", "iiim"], - - ["_get_js_owned_object_by_gc_handle_ref", "GetJSOwnedObjectByGCHandleRef", "im"], - ["_get_js_owned_object_gc_handle_ref", "GetJSOwnedObjectGCHandleRef", "m"], - ["_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"], - ["_task_from_result_ref", "TaskFromResultRef", "Rm"], - ["_setup_js_cont_ref", "SetupJSContinuationRef", "mo"], - - ["_object_to_string_ref", "ObjectToStringRef", "m"], - ["_get_date_value_ref", "GetDateValueRef", "m"], - ["_create_date_time_ref", "CreateDateTimeRef", "dm"], - ["_create_uri_ref", "CreateUriRef", "sm"], - ["_is_simple_array_ref", "IsSimpleArrayRef", "m"], -]; - -export interface t_CSwraps { - // BINDING - _get_cs_owned_object_by_js_handle_ref(jsHandle: JSHandle, shouldAddInflight: 0 | 1, result: MonoObjectRef): void; - _get_cs_owned_object_js_handle_ref(obj: MonoObjectRef, shouldAddInflight: 0 | 1): JSHandle; - _try_get_cs_owned_object_js_handle_ref(obj: MonoObjectRef, shouldAddInflight: 0 | 1): JSHandle; - _create_cs_owned_proxy_ref(jsHandle: JSHandle, mappedType: number, shouldAddInflight: 0 | 1, result: MonoObjectRef): void; - - _get_js_owned_object_by_gc_handle_ref(gcHandle: GCHandle, result: MonoObjectRef): void; - _get_js_owned_object_gc_handle_ref(obj: MonoObjectRef): GCHandle - _release_js_owned_object_by_gc_handle(gcHandle: GCHandle): void; - - _create_tcs(): GCHandle; - _set_tcs_result_ref(gcHandle: GCHandle, result: any): void - _set_tcs_failure(gcHandle: GCHandle, result: string): void - _get_tcs_task_ref(gcHandle: GCHandle, result: MonoObjectRef): void; - _task_from_result_ref(value: any, result: MonoObjectRef): void; - // FIXME: PromiseControl is a JS object so we can't pass an address directly - _setup_js_cont_ref(task: MonoObjectRef, continuation: PromiseControl): void; - - _object_to_string_ref(obj: MonoObjectRef): string; - _get_date_value_ref(obj: MonoObjectRef): number; - _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 = {}; -for (const sig of fn_signatures) { - const wf: any = wrapped_cs_functions; - // lazy init on first run - wf[sig[0]] = function (...args: any[]) { - const fce = runtimeHelpers.bind_runtime_method(sig[1], sig[2]); - wf[sig[0]] = fce; - return fce(...args); - }; -} - -export default wrapped_cs_functions; diff --git a/src/mono/wasm/runtime/cwraps.ts b/src/mono/wasm/runtime/cwraps.ts index b2963790bd20f..758265adc7656 100644 --- a/src/mono/wasm/runtime/cwraps.ts +++ b/src/mono/wasm/runtime/cwraps.ts @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. import { - mono_assert, MonoArray, MonoAssembly, MonoClass, MonoMethod, MonoObject, MonoString, MonoType, MonoObjectRef, MonoStringRef @@ -11,83 +10,85 @@ 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][] = [ +type SigLine = [lazy: boolean, name: string, returnType: string | null, argTypes?: string[], opts?: any]; + +// when the method is assigned/cached at usage, instead of being invoked directly from cwraps, it can't be marked lazy, because it would be re-bound on each call +const fn_signatures: SigLine[] = [ // MONO - ["mono_wasm_register_root", "number", ["number", "number", "string"]], - ["mono_wasm_deregister_root", null, ["number"]], - ["mono_wasm_string_get_data", null, ["number", "number", "number", "number"]], - ["mono_wasm_string_get_data_ref", null, ["number", "number", "number", "number"]], - ["mono_wasm_set_is_debugger_attached", "void", ["bool"]], - ["mono_wasm_send_dbg_command", "bool", ["number", "number", "number", "number", "number"]], - ["mono_wasm_send_dbg_command_with_parms", "bool", ["number", "number", "number", "number", "number", "number", "string"]], - ["mono_wasm_setenv", null, ["string", "string"]], - ["mono_wasm_parse_runtime_options", null, ["number", "number"]], - ["mono_wasm_strdup", "number", ["string"]], - ["mono_background_exec", null, []], - ["mono_set_timeout_exec", null, []], - ["mono_wasm_load_icu_data", "number", ["number"]], - ["mono_wasm_get_icudt_name", "string", ["string"]], - ["mono_wasm_add_assembly", "number", ["string", "number", "number"]], - ["mono_wasm_add_satellite_assembly", "void", ["string", "string", "number", "number"]], - ["mono_wasm_load_runtime", null, ["string", "number"]], - ["mono_wasm_exit", null, ["number"]], - ["mono_wasm_change_debugger_log_level", "void", ["number"]], + [true, "mono_wasm_register_root", "number", ["number", "number", "string"]], + [true, "mono_wasm_deregister_root", null, ["number"]], + [true, "mono_wasm_string_get_data", null, ["number", "number", "number", "number"]], + [true, "mono_wasm_string_get_data_ref", null, ["number", "number", "number", "number"]], + [true, "mono_wasm_set_is_debugger_attached", "void", ["bool"]], + [true, "mono_wasm_send_dbg_command", "bool", ["number", "number", "number", "number", "number"]], + [true, "mono_wasm_send_dbg_command_with_parms", "bool", ["number", "number", "number", "number", "number", "number", "string"]], + [true, "mono_wasm_setenv", null, ["string", "string"]], + [true, "mono_wasm_parse_runtime_options", null, ["number", "number"]], + [true, "mono_wasm_strdup", "number", ["string"]], + [true, "mono_background_exec", null, []], + [true, "mono_set_timeout_exec", null, []], + [true, "mono_wasm_load_icu_data", "number", ["number"]], + [true, "mono_wasm_get_icudt_name", "string", ["string"]], + [false, "mono_wasm_add_assembly", "number", ["string", "number", "number"]], + [true, "mono_wasm_add_satellite_assembly", "void", ["string", "string", "number", "number"]], + [false, "mono_wasm_load_runtime", null, ["string", "number"]], + [true, "mono_wasm_change_debugger_log_level", "void", ["number"]], // BINDING - ["mono_wasm_get_corlib", "number", []], - ["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"]], - ["mono_wasm_invoke_method", "number", ["number", "number", "number", "number"]], - ["mono_wasm_invoke_method_ref", "void", ["number", "number", "number", "number", "number"]], - ["mono_wasm_string_get_utf8", "number", ["number"]], - ["mono_wasm_string_from_utf16_ref", "void", ["number", "number", "number"]], - ["mono_wasm_get_obj_type", "number", ["number"]], - ["mono_wasm_array_length", "number", ["number"]], - ["mono_wasm_array_get", "number", ["number", "number"]], - ["mono_wasm_array_get_ref", "void", ["number", "number", "number"]], - ["mono_wasm_obj_array_new", "number", ["number"]], - ["mono_wasm_obj_array_new_ref", "void", ["number", "number"]], - ["mono_wasm_obj_array_set", "void", ["number", "number", "number"]], - ["mono_wasm_obj_array_set_ref", "void", ["number", "number", "number"]], - ["mono_wasm_register_bundled_satellite_assemblies", "void", []], - ["mono_wasm_try_unbox_primitive_and_get_type_ref", "number", ["number", "number", "number"]], - ["mono_wasm_box_primitive_ref", "void", ["number", "number", "number", "number"]], - ["mono_wasm_intern_string_ref", "void", ["number"]], - ["mono_wasm_assembly_get_entry_point", "number", ["number"]], - ["mono_wasm_get_delegate_invoke_ref", "number", ["number"]], - ["mono_wasm_string_array_new_ref", "void", ["number", "number"]], - ["mono_wasm_typed_array_new_ref", "void", ["number", "number", "number", "number", "number"]], - ["mono_wasm_class_get_type", "number", ["number"]], - ["mono_wasm_type_get_class", "number", ["number"]], - ["mono_wasm_get_type_name", "string", ["number"]], - ["mono_wasm_get_type_aqn", "string", ["number"]], + [true, "mono_wasm_get_corlib", "number", []], + [true, "mono_wasm_assembly_load", "number", ["string"]], + [true, "mono_wasm_find_corlib_class", "number", ["string", "string"]], + [true, "mono_wasm_assembly_find_class", "number", ["number", "string", "string"]], + [true, "mono_wasm_runtime_run_module_cctor", "void", ["number"]], + [true, "mono_wasm_find_corlib_type", "number", ["string", "string"]], + [true, "mono_wasm_assembly_find_type", "number", ["number", "string", "string"]], + [true, "mono_wasm_assembly_find_method", "number", ["number", "string", "number"]], + [true, "mono_wasm_invoke_method", "number", ["number", "number", "number", "number"]], + [false, "mono_wasm_invoke_method_ref", "void", ["number", "number", "number", "number", "number"]], + [true, "mono_wasm_string_get_utf8", "number", ["number"]], + [true, "mono_wasm_string_from_utf16_ref", "void", ["number", "number", "number"]], + [true, "mono_wasm_get_obj_type", "number", ["number"]], + [true, "mono_wasm_array_length", "number", ["number"]], + [true, "mono_wasm_array_get", "number", ["number", "number"]], + [true, "mono_wasm_array_get_ref", "void", ["number", "number", "number"]], + [false, "mono_wasm_obj_array_new", "number", ["number"]], + [false, "mono_wasm_obj_array_new_ref", "void", ["number", "number"]], + [false, "mono_wasm_obj_array_set", "void", ["number", "number", "number"]], + [false, "mono_wasm_obj_array_set_ref", "void", ["number", "number", "number"]], + [true, "mono_wasm_register_bundled_satellite_assemblies", "void", []], + [false, "mono_wasm_try_unbox_primitive_and_get_type_ref", "number", ["number", "number", "number"]], + [true, "mono_wasm_box_primitive_ref", "void", ["number", "number", "number", "number"]], + [true, "mono_wasm_intern_string_ref", "void", ["number"]], + [true, "mono_wasm_assembly_get_entry_point", "number", ["number"]], + [true, "mono_wasm_get_delegate_invoke_ref", "number", ["number"]], + [true, "mono_wasm_string_array_new_ref", "void", ["number", "number"]], + [true, "mono_wasm_typed_array_new_ref", "void", ["number", "number", "number", "number", "number"]], + [true, "mono_wasm_class_get_type", "number", ["number"]], + [true, "mono_wasm_type_get_class", "number", ["number"]], + [true, "mono_wasm_get_type_name", "string", ["number"]], + [true, "mono_wasm_get_type_aqn", "string", ["number"]], // MONO.diagnostics - ["mono_wasm_event_pipe_enable", "bool", ["string", "number", "string", "bool", "number"]], - ["mono_wasm_event_pipe_session_start_streaming", "bool", ["number"]], - ["mono_wasm_event_pipe_session_disable", "bool", ["number"]], + [true, "mono_wasm_event_pipe_enable", "bool", ["string", "number", "string", "bool", "number"]], + [true, "mono_wasm_event_pipe_session_start_streaming", "bool", ["number"]], + [true, "mono_wasm_event_pipe_session_disable", "bool", ["number"]], //DOTNET - ["mono_wasm_string_from_js", "number", ["string"]], + [true, "mono_wasm_string_from_js", "number", ["string"]], //INTERNAL - ["mono_wasm_exit", "void", ["number"]], - ["mono_wasm_set_main_args", "void", ["number", "number"]], - ["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"]], - ["mono_wasm_u52_to_f64", "number", ["number", "number"]], - ["mono_wasm_f64_to_i52", "number", ["number", "number"]], - ["mono_wasm_f64_to_u52", "number", ["number", "number"]], + [false, "mono_wasm_exit", "void", ["number"]], + [true, "mono_wasm_set_main_args", "void", ["number", "number"]], + [false, "mono_wasm_enable_on_demand_gc", "void", ["number"]], + [false, "mono_profiler_init_aot", "void", ["number"]], + [false, "mono_wasm_exec_regression", "number", ["number", "string"]], + [false, "mono_wasm_invoke_method_bound", "number", ["number", "number"]], + [true, "mono_wasm_write_managed_pointer_unsafe", "void", ["number", "number"]], + [true, "mono_wasm_copy_managed_pointer", "void", ["number", "number"]], + [true, "mono_wasm_i52_to_f64", "number", ["number", "number"]], + [true, "mono_wasm_u52_to_f64", "number", ["number", "number"]], + [true, "mono_wasm_f64_to_i52", "number", ["number", "number"]], + [true, "mono_wasm_f64_to_u52", "number", ["number", "number"]], ]; export interface t_Cwraps { @@ -196,29 +197,30 @@ export interface t_Cwraps { } const wrapped_c_functions: t_Cwraps = {}; -for (const sig of fn_signatures) { - const wf: any = wrapped_c_functions; - // lazy init on first run - wf[sig[0]] = function (...args: any[]) { - const fce = Module.cwrap(sig[0], sig[1], sig[2], sig[3]); - wf[sig[0]] = fce; - return fce(...args); - }; -} export default wrapped_c_functions; -export function wrap_c_function(name: string): Function { - const wf: any = wrapped_c_functions; - const sig = fn_signatures.find(s => s[0] === name); - mono_assert(sig, () => `Function ${name} not found`); - const fce = Module.cwrap(sig[0], sig[1], sig[2], sig[3]); - wf[sig[0]] = fce; - return fce; -} // see src/mono/wasm/driver.c I52_ERROR_xxx export const enum I52Error { NONE = 0, NON_INTEGRAL = 1, OUT_OF_RANGE = 2, +} + +export function init_c_exports(): void { + for (const sig of fn_signatures) { + const wf: any = wrapped_c_functions; + const [lazy, name, returnType, argTypes, opts] = sig; + if (lazy) { + // lazy init on first run + wf[name] = function (...args: any[]) { + const fce = Module.cwrap(name, returnType, argTypes, opts); + wf[name] = fce; + return fce(...args); + }; + } else { + const fce = Module.cwrap(name, returnType, argTypes, opts); + wf[name] = fce; + } + } } \ No newline at end of file diff --git a/src/mono/wasm/runtime/dotnet.d.ts b/src/mono/wasm/runtime/dotnet.d.ts index 4b0d7826fde5e..44f72e60ac7fc 100644 --- a/src/mono/wasm/runtime/dotnet.d.ts +++ b/src/mono/wasm/runtime/dotnet.d.ts @@ -61,72 +61,7 @@ declare interface EmscriptenModule { } declare type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array; -/** - * Allocates a block of memory that can safely contain pointers into the managed heap. - * The result object has get(index) and set(index, value) methods that can be used to retrieve and store managed pointers. - * Once you are done using the root buffer, you must call its release() method. - * For small numbers of roots, it is preferable to use the mono_wasm_new_root and mono_wasm_new_roots APIs instead. - */ -declare function mono_wasm_new_root_buffer(capacity: number, name?: string): WasmRootBuffer; -/** - * Allocates a WasmRoot pointing to a root provided and controlled by external code. Typicaly on managed stack. - * Releasing this root will not de-allocate the root space. You still need to call .release(). - */ -declare function mono_wasm_new_external_root(address: VoidPtr | MonoObjectRef): WasmRoot; -/** - * Allocates temporary storage for a pointer into the managed heap. - * Pointers stored here will be visible to the GC, ensuring that the object they point to aren't moved or collected. - * If you already have a managed pointer you can pass it as an argument to initialize the temporary storage. - * The result object has get() and set(value) methods, along with a .value property. - * When you are done using the root you must call its .release() method. - */ -declare function mono_wasm_new_root(value?: T | undefined): WasmRoot; -/** - * Releases 1 or more root or root buffer objects. - * Multiple objects may be passed on the argument list. - * 'undefined' may be passed as an argument so it is safe to call this method from finally blocks - * even if you are not sure all of your roots have been created yet. - * @param {... WasmRoot} roots - */ -declare function mono_wasm_release_roots(...args: WasmRoot[]): void; -declare class WasmRootBuffer { - private __count; - private length; - private __offset; - private __offset32; - private __handle; - private __ownsAllocation; - constructor(offset: VoidPtr, capacity: number, ownsAllocation: boolean, name?: string); - _throw_index_out_of_range(): void; - _check_in_range(index: number): void; - get_address(index: number): MonoObjectRef; - get_address_32(index: number): number; - get(index: number): ManagedPointer; - set(index: number, value: ManagedPointer): ManagedPointer; - copy_value_from_address(index: number, sourceAddress: MonoObjectRef): void; - _unsafe_get(index: number): number; - _unsafe_set(index: number, value: ManagedPointer | NativePointer): void; - clear(): void; - release(): void; - toString(): string; -} -interface WasmRoot { - get_address(): MonoObjectRef; - get_address_32(): number; - get address(): MonoObjectRef; - get(): T; - set(value: T): T; - get value(): T; - set value(value: T); - copy_from_address(source: MonoObjectRef): void; - copy_to_address(destination: MonoObjectRef): void; - copy_from(source: WasmRoot): void; - copy_to(destination: WasmRoot): void; - valueOf(): T; - clear(): void; - release(): void; - toString(): string; -} +declare function mono_wasm_runtime_ready(): void; interface MonoObject extends ManagedPointer { __brandMonoObject: "MonoObject"; @@ -242,6 +177,19 @@ declare type DotnetModuleConfigImports = { }; url?: any; }; +interface DotnetPublicAPI { + MONO: MONOType; + BINDING: BINDINGType; + INTERNAL: any; + EXPORTS: any; + IMPORTS: any; + Module: EmscriptenModule; + RuntimeId: number; + RuntimeBuildInfo: { + ProductVersion: string; + Configuration: string; + }; +} declare type EventPipeSessionID = bigint; interface EventPipeSession { @@ -286,53 +234,117 @@ interface Diagnostics { createEventPipeSession(options?: EventPipeSessionOptions): EventPipeSession | null; } -declare function mono_wasm_runtime_ready(): void; - -declare function mono_wasm_setenv(name: string, value: string): void; -declare function mono_load_runtime_and_bcl_args(config: MonoConfig | MonoConfigError | undefined): Promise; -declare function mono_wasm_load_data_archive(data: Uint8Array, prefix: string): boolean; -/** - * Loads the mono config file (typically called mono-config.json) asynchroniously - * Note: the run dependencies are so emsdk actually awaits it in order. - * - * @param {string} configFilePath - relative path to the config file - * @throws Will throw an error if the config file loading fails - */ -declare function mono_wasm_load_config(configFilePath: string): Promise; - declare function mono_wasm_load_icu_data(offset: VoidPtr): boolean; /** - * @deprecated Not GC or thread safe + * Allocates a block of memory that can safely contain pointers into the managed heap. + * The result object has get(index) and set(index, value) methods that can be used to retrieve and store managed pointers. + * Once you are done using the root buffer, you must call its release() method. + * For small numbers of roots, it is preferable to use the mono_wasm_new_root and mono_wasm_new_roots APIs instead. */ -declare function conv_string(mono_obj: MonoString): string | null; -declare function conv_string_root(root: WasmRoot): string | null; -declare function js_string_to_mono_string_root(string: string, result: WasmRoot): void; +declare function mono_wasm_new_root_buffer(capacity: number, name?: string): WasmRootBuffer; /** - * @deprecated Not GC or thread safe + * Allocates a WasmRoot pointing to a root provided and controlled by external code. Typicaly on managed stack. + * Releasing this root will not de-allocate the root space. You still need to call .release(). */ -declare function js_string_to_mono_string(string: string): MonoString; - +declare function mono_wasm_new_external_root(address: VoidPtr | MonoObjectRef): WasmRoot; /** - * @deprecated Not GC or thread safe. For blazor use only + * Allocates temporary storage for a pointer into the managed heap. + * Pointers stored here will be visible to the GC, ensuring that the object they point to aren't moved or collected. + * If you already have a managed pointer you can pass it as an argument to initialize the temporary storage. + * The result object has get() and set(value) methods, along with a .value property. + * When you are done using the root you must call its .release() method. */ -declare function js_to_mono_obj(js_obj: any): MonoObject; -declare function js_to_mono_obj_root(js_obj: any, result: WasmRoot, should_add_in_flight: boolean): void; -declare function js_typed_array_to_array_root(js_obj: any, result: WasmRoot): void; +declare function mono_wasm_new_root(value?: T | undefined): WasmRoot; /** - * @deprecated Not GC or thread safe + * Releases 1 or more root or root buffer objects. + * Multiple objects may be passed on the argument list. + * 'undefined' may be passed as an argument so it is safe to call this method from finally blocks + * even if you are not sure all of your roots have been created yet. + * @param {... WasmRoot} roots */ -declare function js_typed_array_to_array(js_obj: any): MonoArray; - -declare function unbox_mono_obj(mono_obj: MonoObject): any; -declare function unbox_mono_obj_root(root: WasmRoot): any; -declare function mono_array_to_js_array(mono_array: MonoArray): any[] | null; -declare function mono_array_root_to_js_array(arrayRoot: WasmRoot): any[] | null; +declare function mono_wasm_release_roots(...args: WasmRoot[]): void; +declare class WasmRootBuffer { + private __count; + private length; + private __offset; + private __offset32; + private __handle; + private __ownsAllocation; + constructor(offset: VoidPtr, capacity: number, ownsAllocation: boolean, name?: string); + _throw_index_out_of_range(): void; + _check_in_range(index: number): void; + get_address(index: number): MonoObjectRef; + get_address_32(index: number): number; + get(index: number): ManagedPointer; + set(index: number, value: ManagedPointer): ManagedPointer; + copy_value_from_address(index: number, sourceAddress: MonoObjectRef): void; + _unsafe_get(index: number): number; + _unsafe_set(index: number, value: ManagedPointer | NativePointer): void; + clear(): void; + release(): void; + toString(): string; +} +interface WasmRoot { + get_address(): MonoObjectRef; + get_address_32(): number; + get address(): MonoObjectRef; + get(): T; + set(value: T): T; + get value(): T; + set value(value: T); + copy_from_address(source: MonoObjectRef): void; + copy_to_address(destination: MonoObjectRef): void; + copy_from(source: WasmRoot): void; + copy_to(destination: WasmRoot): void; + valueOf(): T; + clear(): void; + release(): void; + toString(): string; +} -declare function mono_bind_static_method(fqn: string, signature?: string): Function; -declare function mono_call_assembly_entry_point(assembly: string, args?: any[], signature?: string): 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 const 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_load_bytes_into_heap(bytes: Uint8Array): VoidPtr; +declare function mono_wasm_get_assembly_exports(assembly: string): Promise; declare type _MemOffset = number | VoidPtr | NativePointer | ManagedPointer; declare type _NumberOrPointer = number | VoidPtr | NativePointer | ManagedPointer; @@ -372,54 +384,98 @@ declare function getU52(offset: _MemOffset): number; declare function getI64Big(offset: _MemOffset): bigint; declare function getF32(offset: _MemOffset): number; declare function getF64(offset: _MemOffset): number; +declare function mono_wasm_load_bytes_into_heap(bytes: Uint8Array): VoidPtr; declare function mono_run_main_and_exit(main_assembly_name: string, args: string[]): Promise; declare function mono_run_main(main_assembly_name: string, args: string[]): Promise; -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 const enum MemoryViewType { - Byte = 0, - Int32 = 1, - Double = 2 -} -interface IMemoryView { +declare function mono_wasm_setenv(name: string, value: string): void; +declare function mono_load_runtime_and_bcl_args(config: MonoConfig | MonoConfigError | undefined): Promise; +declare function mono_wasm_load_data_archive(data: Uint8Array, prefix: string): boolean; +/** + * Loads the mono config file (typically called mono-config.json) asynchroniously + * Note: the run dependencies are so emsdk actually awaits it in order. + * + * @param {string} configFilePath - relative path to the config file + * @throws Will throw an error if the config file loading fails + */ +declare function mono_wasm_load_config(configFilePath: string): Promise; + +/** + * @deprecated Not GC or thread safe + */ +declare function conv_string(mono_obj: MonoString): string | null; +declare function conv_string_root(root: WasmRoot): string | null; +declare function js_string_to_mono_string_root(string: string, result: WasmRoot): void; +/** + * @deprecated Not GC or thread safe + */ +declare function js_string_to_mono_string(string: string): MonoString; + +declare function unbox_mono_obj(mono_obj: MonoObject): any; +declare function unbox_mono_obj_root(root: WasmRoot): any; +declare function mono_array_to_js_array(mono_array: MonoArray): any[] | null; +declare function mono_array_root_to_js_array(arrayRoot: WasmRoot): any[] | null; + +/** + * @deprecated Not GC or thread safe. For blazor use only + */ +declare function js_to_mono_obj(js_obj: any): MonoObject; +declare function js_to_mono_obj_root(js_obj: any, result: WasmRoot, should_add_in_flight: boolean): void; +declare function js_typed_array_to_array_root(js_obj: any, result: WasmRoot): void; +/** + * @deprecated Not GC or thread safe + */ +declare function js_typed_array_to_array(js_obj: any): MonoArray; + +declare function mono_bind_static_method(fqn: string, signature?: string): Function; +declare function mono_call_assembly_entry_point(assembly: string, args?: any[], signature?: string): number; + +declare type BINDINGType = { + bind_static_method: typeof mono_bind_static_method; + call_assembly_entry_point: typeof mono_call_assembly_entry_point; /** - * 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() + * @deprecated Not GC or thread safe */ - set(source: TypedArray, targetOffset?: number): void; + mono_obj_array_new: (size: number) => MonoArray; /** - * copies elements from wasm memory to provided target. - * target has to have the elements of the same type as the underlying C# array. + * @deprecated Not GC or thread safe */ - copyTo(target: TypedArray, sourceOffset?: number): void; + mono_obj_array_set: (array: MonoArray, idx: number, obj: MonoObject) => void; /** - * same as TypedArray.slice() + * @deprecated Not GC or thread safe */ - slice(start?: number, end?: number): TypedArray; - get length(): number; - get byteLength(): number; -} - -declare function mono_wasm_get_assembly_exports(assembly: string): Promise; - -declare const MONO: { + js_string_to_mono_string: typeof js_string_to_mono_string; + /** + * @deprecated Not GC or thread safe + */ + js_typed_array_to_array: typeof js_typed_array_to_array; + /** + * @deprecated Not GC or thread safe + */ + mono_array_to_js_array: typeof mono_array_to_js_array; + /** + * @deprecated Not GC or thread safe + */ + js_to_mono_obj: typeof js_to_mono_obj; + /** + * @deprecated Not GC or thread safe + */ + conv_string: typeof conv_string; + /** + * @deprecated Not GC or thread safe + */ + unbox_mono_obj: typeof unbox_mono_obj; + mono_obj_array_new_ref: (size: number, result: MonoObjectRef) => void; + mono_obj_array_set_ref: (array: MonoObjectRef, idx: number, obj: MonoObjectRef) => void; + js_string_to_mono_string_root: typeof js_string_to_mono_string_root; + js_typed_array_to_array_root: typeof js_typed_array_to_array_root; + js_to_mono_obj_root: typeof js_to_mono_obj_root; + conv_string_root: typeof conv_string_root; + unbox_mono_obj_root: typeof unbox_mono_obj_root; + mono_array_root_to_js_array: typeof mono_array_root_to_js_array; +}; +declare type MONOType = { mono_wasm_setenv: typeof mono_wasm_setenv; mono_wasm_load_bytes_into_heap: typeof mono_wasm_load_bytes_into_heap; mono_wasm_load_icu_data: typeof mono_wasm_load_icu_data; @@ -464,69 +520,6 @@ declare const MONO: { getF64: typeof getF64; diagnostics: Diagnostics; }; -declare type MONOType = typeof MONO; -declare const BINDING: { - /** - * @deprecated Not GC or thread safe - */ - mono_obj_array_new: (size: number) => MonoArray; - /** - * @deprecated Not GC or thread safe - */ - mono_obj_array_set: (array: MonoArray, idx: number, obj: MonoObject) => void; - /** - * @deprecated Not GC or thread safe - */ - js_string_to_mono_string: typeof js_string_to_mono_string; - /** - * @deprecated Not GC or thread safe - */ - js_typed_array_to_array: typeof js_typed_array_to_array; - /** - * @deprecated Not GC or thread safe - */ - mono_array_to_js_array: typeof mono_array_to_js_array; - /** - * @deprecated Not GC or thread safe - */ - js_to_mono_obj: typeof js_to_mono_obj; - /** - * @deprecated Not GC or thread safe - */ - conv_string: typeof conv_string; - /** - * @deprecated Not GC or thread safe - */ - unbox_mono_obj: typeof unbox_mono_obj; - /** - * @deprecated Renamed to conv_string_root - */ - conv_string_rooted: typeof conv_string_root; - mono_obj_array_new_ref: (size: number, result: MonoObjectRef) => void; - mono_obj_array_set_ref: (array: MonoObjectRef, idx: number, obj: MonoObjectRef) => void; - js_string_to_mono_string_root: typeof js_string_to_mono_string_root; - js_typed_array_to_array_root: typeof js_typed_array_to_array_root; - js_to_mono_obj_root: typeof js_to_mono_obj_root; - conv_string_root: typeof conv_string_root; - unbox_mono_obj_root: typeof unbox_mono_obj_root; - mono_array_root_to_js_array: typeof mono_array_root_to_js_array; - bind_static_method: typeof mono_bind_static_method; - call_assembly_entry_point: typeof mono_call_assembly_entry_point; -}; -declare type BINDINGType = typeof BINDING; -interface DotnetPublicAPI { - MONO: typeof MONO; - BINDING: typeof BINDING; - INTERNAL: any; - EXPORTS: any; - IMPORTS: any; - Module: EmscriptenModule; - RuntimeId: number; - RuntimeBuildInfo: { - ProductVersion: string; - Configuration: string; - }; -} declare function createDotnetRuntime(moduleFactory: DotnetModuleConfig | ((api: DotnetPublicAPI) => DotnetModuleConfig)): Promise; declare type CreateDotnetRuntimeType = typeof createDotnetRuntime; diff --git a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js index fe431d319e1f7..dfba4c82406da 100644 --- a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js +++ b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js @@ -107,9 +107,7 @@ const linked_functions = [ "mono_wasm_create_cs_owned_object_ref", "mono_wasm_release_cs_owned_object", "mono_wasm_typed_array_to_array_ref", - "mono_wasm_typed_array_copy_to_ref", "mono_wasm_typed_array_from_ref", - "mono_wasm_typed_array_copy_from_ref", "mono_wasm_compile_function_ref", "mono_wasm_bind_js_function", "mono_wasm_invoke_bound_function", diff --git a/src/mono/wasm/runtime/export-types.ts b/src/mono/wasm/runtime/export-types.ts index 057ed83770920..374173baffe7c 100644 --- a/src/mono/wasm/runtime/export-types.ts +++ b/src/mono/wasm/runtime/export-types.ts @@ -1,9 +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 { BINDINGType, DotnetPublicAPI, MONOType } from "./exports"; +import { BINDINGType, MONOType } from "./legacy/exports-legacy"; import { IDisposable, IMemoryView, ManagedError, ManagedObject, MemoryViewType } from "./marshal"; -import { DotnetModuleConfig, MonoArray, MonoObject, MonoString } from "./types"; +import { DotnetModuleConfig, DotnetPublicAPI, MonoArray, MonoObject, MonoString } from "./types"; import { EmscriptenModule, TypedArray, VoidPtr } from "./types/emscripten"; // ----------------------------------------------------------- diff --git a/src/mono/wasm/runtime/exports-internal.ts b/src/mono/wasm/runtime/exports-internal.ts new file mode 100644 index 0000000000000..068af05eccf7a --- /dev/null +++ b/src/mono/wasm/runtime/exports-internal.ts @@ -0,0 +1,87 @@ +import { mono_wasm_cancel_promise } from "./cancelable-promise"; +import cwraps from "./cwraps"; +import { mono_wasm_symbolicate_string, mono_wasm_stringify_as_error_with_stack, mono_wasm_get_loaded_files, mono_wasm_send_dbg_command_with_parms, mono_wasm_send_dbg_command, mono_wasm_get_dbg_command_info, mono_wasm_get_details, mono_wasm_release_object, mono_wasm_call_function_on, mono_wasm_debugger_resume, mono_wasm_detach_debugger, mono_wasm_raise_debug_event, mono_wasm_change_debugger_log_level, mono_wasm_debugger_attached } from "./debug"; +import { get_dotnet_instance } from "./exports"; +import { 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 } from "./http"; +import { Module, runtimeHelpers } from "./imports"; +import { get_property, set_property, has_property, get_typeof_property, get_global_this, dynamic_import } from "./invoke-js"; +import { mono_method_resolve } from "./legacy/method-binding"; +import { mono_wasm_set_runtime_options } from "./startup"; +import { mono_intern_string } from "./strings"; +import { ws_wasm_create, ws_wasm_open, ws_wasm_send, ws_wasm_receive, ws_wasm_close, ws_wasm_abort } from "./web-socket"; + +export function export_internal(): any { + return { + // tests + mono_wasm_exit: (exit_code: number) => { Module.printErr("MONO_WASM: early exit " + exit_code); }, + mono_wasm_enable_on_demand_gc: cwraps.mono_wasm_enable_on_demand_gc, + mono_profiler_init_aot: cwraps.mono_profiler_init_aot, + mono_wasm_set_runtime_options, + mono_wasm_exec_regression: cwraps.mono_wasm_exec_regression, + mono_method_resolve,//MarshalTests.cs + mono_intern_string,// MarshalTests.cs + + // with mono_wasm_debugger_log and mono_wasm_trace_logger + logging: undefined, + + // + mono_wasm_symbolicate_string, + mono_wasm_stringify_as_error_with_stack, + + // used in debugger DevToolsHelper.cs + mono_wasm_get_loaded_files, + mono_wasm_send_dbg_command_with_parms, + mono_wasm_send_dbg_command, + mono_wasm_get_dbg_command_info, + mono_wasm_get_details, + mono_wasm_release_object, + mono_wasm_call_function_on, + mono_wasm_debugger_resume, + mono_wasm_detach_debugger, + mono_wasm_raise_debug_event, + mono_wasm_change_debugger_log_level, + mono_wasm_debugger_attached, + mono_wasm_runtime_is_ready: 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, + }; +} + +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export function cwraps_internal(internal: any): void { + Object.assign(internal, { + mono_wasm_exit: cwraps.mono_wasm_exit, + mono_wasm_enable_on_demand_gc: cwraps.mono_wasm_enable_on_demand_gc, + mono_profiler_init_aot: cwraps.mono_profiler_init_aot, + mono_wasm_exec_regression: cwraps.mono_wasm_exec_regression, + }); +} diff --git a/src/mono/wasm/runtime/exports-linker.ts b/src/mono/wasm/runtime/exports-linker.ts new file mode 100644 index 0000000000000..db349e70ce2ef --- /dev/null +++ b/src/mono/wasm/runtime/exports-linker.ts @@ -0,0 +1,76 @@ +import MonoWasmThreads from "consts:monoWasmThreads"; +import { dotnet_browser_can_use_subtle_crypto_impl, dotnet_browser_simple_digest_hash, dotnet_browser_sign, dotnet_browser_encrypt_decrypt, dotnet_browser_derive_bits } from "./crypto-worker"; +import { mono_wasm_fire_debugger_agent_message, mono_wasm_debugger_log, mono_wasm_add_dbg_command_received, mono_wasm_trace_logger, mono_wasm_set_entrypoint_breakpoint } from "./debug"; +import { mono_wasm_release_cs_owned_object } from "./gc-handles"; +import { mono_wasm_load_icu_data, mono_wasm_get_icudt_name } from "./icu"; +import { mono_wasm_bind_cs_function } from "./invoke-cs"; +import { mono_wasm_bind_js_function, mono_wasm_invoke_bound_function } from "./invoke-js"; +import { mono_wasm_typed_array_from_ref } from "./legacy/buffers"; +import { mono_wasm_create_cs_owned_object_ref } from "./legacy/cs-to-js"; +import { mono_wasm_typed_array_to_array_ref } from "./legacy/js-to-cs"; +import { mono_wasm_invoke_js_blazor, mono_wasm_invoke_js_with_args_ref, mono_wasm_get_object_property_ref, mono_wasm_set_object_property_ref, mono_wasm_get_by_index_ref, mono_wasm_set_by_index_ref, mono_wasm_get_global_object_ref } from "./legacy/method-calls"; +import { mono_wasm_marshal_promise } from "./marshal-to-js"; +import { mono_wasm_pthread_on_pthread_attached } from "./pthreads/worker"; +import { mono_set_timeout, schedule_background_exec } from "./scheduling"; +import { mono_wasm_asm_loaded } from "./startup"; + +// the methods would be visible to EMCC linker +// --- keep in sync with dotnet.cjs.lib.js --- +const mono_wasm_threads_exports = !MonoWasmThreads ? undefined : { + // mono-threads-wasm.c + mono_wasm_pthread_on_pthread_attached, +}; + +// the methods would be visible to EMCC linker +// --- keep in sync with dotnet.cjs.lib.js --- +// --- keep in sync with dotnet.es6.lib.js --- +export function export_linker(): any { + return { + // mini-wasm.c + mono_set_timeout, + + // mini-wasm-debugger.c + mono_wasm_asm_loaded, + mono_wasm_fire_debugger_agent_message, + mono_wasm_debugger_log, + mono_wasm_add_dbg_command_received, + + // mono-threads-wasm.c + schedule_background_exec, + + // also keep in sync with driver.c + mono_wasm_invoke_js_blazor, + mono_wasm_trace_logger, + mono_wasm_set_entrypoint_breakpoint, + + // also keep in sync with corebindings.c + mono_wasm_invoke_js_with_args_ref, + mono_wasm_get_object_property_ref, + mono_wasm_set_object_property_ref, + mono_wasm_get_by_index_ref, + mono_wasm_set_by_index_ref, + mono_wasm_get_global_object_ref, + mono_wasm_create_cs_owned_object_ref, + mono_wasm_release_cs_owned_object, + mono_wasm_typed_array_to_array_ref, + mono_wasm_typed_array_from_ref, + 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, + mono_wasm_get_icudt_name, + + // pal_crypto_webworker.c + dotnet_browser_can_use_subtle_crypto_impl, + dotnet_browser_simple_digest_hash, + dotnet_browser_sign, + dotnet_browser_encrypt_decrypt, + dotnet_browser_derive_bits, + + // threading exports, if threading is enabled + ...mono_wasm_threads_exports, + }; +} \ No newline at end of file diff --git a/src/mono/wasm/runtime/exports.ts b/src/mono/wasm/runtime/exports.ts index f12c35dd9d319..52e0852638ea2 100644 --- a/src/mono/wasm/runtime/exports.ts +++ b/src/mono/wasm/runtime/exports.ts @@ -3,201 +3,30 @@ import ProductVersion from "consts:productVersion"; import Configuration from "consts:configuration"; -import MonoWasmThreads from "consts:monoWasmThreads"; -import { - mono_wasm_new_root, mono_wasm_release_roots, mono_wasm_new_external_root, - mono_wasm_new_root_buffer -} from "./roots"; -import { - mono_wasm_send_dbg_command_with_parms, - mono_wasm_send_dbg_command, - mono_wasm_get_dbg_command_info, - mono_wasm_get_details, - mono_wasm_release_object, - mono_wasm_call_function_on, - mono_wasm_debugger_resume, - mono_wasm_detach_debugger, - mono_wasm_runtime_ready, - mono_wasm_get_loaded_files, - mono_wasm_raise_debug_event, - mono_wasm_fire_debugger_agent_message, - mono_wasm_debugger_log, - mono_wasm_trace_logger, - mono_wasm_add_dbg_command_received, - mono_wasm_change_debugger_log_level, - mono_wasm_symbolicate_string, - mono_wasm_stringify_as_error_with_stack, - mono_wasm_debugger_attached, - mono_wasm_set_entrypoint_breakpoint, -} from "./debug"; import { ENVIRONMENT_IS_WEB, ENVIRONMENT_IS_WORKER, ExitStatusError, runtimeHelpers, setImportsAndExports } from "./imports"; -import { DotnetModuleConfigImports, DotnetModule, is_nullish, MonoConfig, MonoConfigError } from "./types"; +import { DotnetModuleConfigImports, DotnetModule, is_nullish, DotnetPublicAPI, PThreadReplacements } from "./types"; import { - mono_load_runtime_and_bcl_args, mono_wasm_load_config, - mono_wasm_setenv, mono_wasm_set_runtime_options, - mono_wasm_load_data_archive, mono_wasm_asm_loaded, configure_emscripten_startup } from "./startup"; -import { mono_set_timeout, schedule_background_exec } from "./scheduling"; -import { mono_wasm_load_icu_data, mono_wasm_get_icudt_name } from "./icu"; -import { conv_string, conv_string_root, js_string_to_mono_string, js_string_to_mono_string_root, mono_intern_string } from "./strings"; -import { js_to_mono_obj, js_typed_array_to_array, mono_wasm_typed_array_to_array_ref, js_to_mono_obj_root, js_typed_array_to_array_root } from "./js-to-cs"; -import { - mono_array_to_js_array, mono_wasm_create_cs_owned_object_ref, unbox_mono_obj, unbox_mono_obj_root, mono_array_root_to_js_array -} from "./cs-to-js"; import { - call_static_method, mono_bind_static_method, mono_call_assembly_entry_point, - mono_method_resolve, - mono_wasm_get_by_index_ref, mono_wasm_get_global_object_ref, mono_wasm_get_object_property_ref, - mono_wasm_invoke_js_blazor, - mono_wasm_invoke_js_with_args_ref, mono_wasm_set_by_index_ref, mono_wasm_set_object_property_ref -} from "./method-calls"; -import { mono_wasm_typed_array_copy_to_ref, mono_wasm_typed_array_from_ref, mono_wasm_typed_array_copy_from_ref, mono_wasm_load_bytes_into_heap } from "./buffers"; -import { mono_wasm_release_cs_owned_object } from "./gc-handles"; -import cwraps from "./cwraps"; + mono_bind_static_method +} from "./legacy/method-calls"; import { - setI8, setI16, setI32, setI52, - setU8, setU16, setU32, setF32, setF64, - getI8, getI16, getI32, getI52, - getU8, getU16, getU32, getF32, getF64, afterUpdateGlobalBufferAndViews, getI64Big, setI64Big, getU52, setU52, setB32, getB32, + afterUpdateGlobalBufferAndViews } from "./memory"; 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, - dotnet_browser_sign, - dotnet_browser_encrypt_decrypt, - dotnet_browser_derive_bits, -} from "./crypto-worker"; -import { mono_wasm_pthread_on_pthread_attached, afterThreadInitTLS } from "./pthreads/worker"; +import { afterThreadInitTLS } from "./pthreads/worker"; import { afterLoadWasmModuleToWorker } from "./pthreads/browser"; +import { export_binding_api, export_mono_api } from "./legacy/exports-legacy"; +import { export_internal } from "./exports-internal"; +import { export_linker } from "./exports-linker"; -const MONO = { - // current "public" MONO API - mono_wasm_setenv, - mono_wasm_load_bytes_into_heap, - mono_wasm_load_icu_data, - mono_wasm_runtime_ready, - mono_wasm_load_data_archive, - mono_wasm_load_config, - mono_load_runtime_and_bcl_args, - mono_wasm_new_root_buffer, - mono_wasm_new_root, - mono_wasm_new_external_root, - 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, - mono_wasm_load_runtime: cwraps.mono_wasm_load_runtime, - - config: runtimeHelpers.config, - loaded_files: [], - - // memory accessors - setB32, - setI8, - setI16, - setI32, - setI52, - setU52, - setI64Big, - setU8, - setU16, - setU32, - setF32, - setF64, - getB32, - getI8, - getI16, - getI32, - getI52, - getU52, - getI64Big, - getU8, - getU16, - getU32, - getF32, - getF64, - - // Diagnostics - diagnostics -}; -export type MONOType = typeof MONO; - -const BINDING = { - //current "public" BINDING API - /** - * @deprecated Not GC or thread safe - */ - mono_obj_array_new: cwraps.mono_wasm_obj_array_new, - /** - * @deprecated Not GC or thread safe - */ - mono_obj_array_set: cwraps.mono_wasm_obj_array_set, - /** - * @deprecated Not GC or thread safe - */ - js_string_to_mono_string, - /** - * @deprecated Not GC or thread safe - */ - js_typed_array_to_array, - /** - * @deprecated Not GC or thread safe - */ - mono_array_to_js_array, - /** - * @deprecated Not GC or thread safe - */ - js_to_mono_obj, - /** - * @deprecated Not GC or thread safe - */ - conv_string, - /** - * @deprecated Not GC or thread safe - */ - unbox_mono_obj, - /** - * @deprecated Renamed to conv_string_root - */ - conv_string_rooted: conv_string_root, - - mono_obj_array_new_ref: cwraps.mono_wasm_obj_array_new_ref, - mono_obj_array_set_ref: cwraps.mono_wasm_obj_array_set_ref, - js_string_to_mono_string_root, - js_typed_array_to_array_root, - js_to_mono_obj_root, - conv_string_root, - unbox_mono_obj_root, - mono_array_root_to_js_array, - - bind_static_method: mono_bind_static_method, - call_assembly_entry_point: mono_call_assembly_entry_point, -}; -export type BINDINGType = typeof BINDING; - +export const __initializeImportsAndExports: any = initializeImportsAndExports; // don't want to export the type +export let __linker_exports: any = null; let exportedAPI: DotnetPublicAPI; -// We need to replace some of the methods in the Emscripten PThreads support with our own -type PThreadReplacements = { - loadWasmModuleToWorker: Function, - threadInitTLS: Function -} // this is executed early during load of emscripten runtime // it exports methods to global objects MONO, BINDING and Module in backward compatible way @@ -215,9 +44,10 @@ function initializeImportsAndExports( setImportsAndExports(imports, exports); // here we merge methods from the local objects into exported objects - Object.assign(exports.mono, MONO); - Object.assign(exports.binding, BINDING); - Object.assign(exports.internal, INTERNAL); + Object.assign(exports.mono, export_mono_api()); + Object.assign(exports.binding, export_binding_api()); + Object.assign(exports.internal, export_internal()); + __linker_exports = export_linker(); exportedAPI = { MONO: exports.mono, @@ -354,152 +184,6 @@ function initializeImportsAndExports( return exportedAPI; } -export const __initializeImportsAndExports: any = initializeImportsAndExports; // don't want to export the type - -// the methods would be visible to EMCC linker -// --- keep in sync with dotnet.cjs.lib.js --- -const mono_wasm_threads_exports = !MonoWasmThreads ? undefined : { - // mono-threads-wasm.c - mono_wasm_pthread_on_pthread_attached, -}; - -// the methods would be visible to EMCC linker -// --- keep in sync with dotnet.cjs.lib.js --- -export const __linker_exports: any = { - // mini-wasm.c - mono_set_timeout, - - // mini-wasm-debugger.c - mono_wasm_asm_loaded, - mono_wasm_fire_debugger_agent_message, - mono_wasm_debugger_log, - mono_wasm_add_dbg_command_received, - - // mono-threads-wasm.c - schedule_background_exec, - - // also keep in sync with driver.c - mono_wasm_invoke_js_blazor, - mono_wasm_trace_logger, - mono_wasm_set_entrypoint_breakpoint, - - // also keep in sync with corebindings.c - mono_wasm_invoke_js_with_args_ref, - mono_wasm_get_object_property_ref, - mono_wasm_set_object_property_ref, - mono_wasm_get_by_index_ref, - mono_wasm_set_by_index_ref, - mono_wasm_get_global_object_ref, - mono_wasm_create_cs_owned_object_ref, - mono_wasm_release_cs_owned_object, - mono_wasm_typed_array_to_array_ref, - mono_wasm_typed_array_copy_to_ref, - mono_wasm_typed_array_from_ref, - mono_wasm_typed_array_copy_from_ref, - 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, - mono_wasm_get_icudt_name, - - // pal_crypto_webworker.c - dotnet_browser_can_use_subtle_crypto_impl, - dotnet_browser_simple_digest_hash, - dotnet_browser_sign, - dotnet_browser_encrypt_decrypt, - dotnet_browser_derive_bits, - - // threading exports, if threading is enabled - ...mono_wasm_threads_exports, -}; - -const INTERNAL: any = { - // startup - BINDING_ASM: "[System.Runtime.InteropServices.JavaScript]System.Runtime.InteropServices.JavaScript.JavaScriptExports", - - // tests - call_static_method, - mono_wasm_exit: cwraps.mono_wasm_exit, - mono_wasm_enable_on_demand_gc: cwraps.mono_wasm_enable_on_demand_gc, - mono_profiler_init_aot: cwraps.mono_profiler_init_aot, - mono_wasm_set_runtime_options, - mono_wasm_exec_regression: cwraps.mono_wasm_exec_regression, - mono_method_resolve,//MarshalTests.cs - mono_bind_static_method,// MarshalTests.cs - mono_intern_string,// MarshalTests.cs - - // with mono_wasm_debugger_log and mono_wasm_trace_logger - logging: undefined, - - // - mono_wasm_symbolicate_string, - mono_wasm_stringify_as_error_with_stack, - - // used in debugger DevToolsHelper.cs - mono_wasm_get_loaded_files, - mono_wasm_send_dbg_command_with_parms, - mono_wasm_send_dbg_command, - mono_wasm_get_dbg_command_info, - mono_wasm_get_details, - mono_wasm_release_object, - mono_wasm_call_function_on, - mono_wasm_debugger_resume, - mono_wasm_detach_debugger, - mono_wasm_raise_debug_event, - mono_wasm_change_debugger_log_level, - mono_wasm_debugger_attached, - mono_wasm_runtime_is_ready: 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 -// like https://github.com/dotnet/aspnetcore/blob/main/src/Components/Web.JS/src/Platform/Mono/MonoTypes.ts -export interface DotnetPublicAPI { - MONO: typeof MONO, - BINDING: typeof BINDING, - INTERNAL: any, - EXPORTS: any, - IMPORTS: any, - Module: EmscriptenModule, - RuntimeId: number, - RuntimeBuildInfo: { - ProductVersion: string, - Configuration: string, - } -} class RuntimeList { private list: { [runtimeId: number]: WeakRef } = {}; diff --git a/src/mono/wasm/runtime/gc-handles.ts b/src/mono/wasm/runtime/gc-handles.ts index 39d94629123df..62287678498b3 100644 --- a/src/mono/wasm/runtime/gc-handles.ts +++ b/src/mono/wasm/runtime/gc-handles.ts @@ -1,10 +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 corebindings from "./corebindings"; -import { GCHandle, GCHandleNull, JSHandle, JSHandleDisposed, JSHandleNull, MonoObjectRef, mono_assert } from "./types"; -import { setI32_unchecked } from "./memory"; +import { GCHandle, GCHandleNull, JSHandle, JSHandleDisposed, JSHandleNull, mono_assert } from "./types"; import { create_weak_ref } from "./weak-ref"; +import { javaScriptExports } from "./managed-exports"; const _use_finalization_registry = typeof globalThis.FinalizationRegistry === "function"; let _js_owned_object_registry: FinalizationRegistry; @@ -31,16 +30,6 @@ export function mono_wasm_get_jsobj_from_js_handle(js_handle: JSHandle): any { return null; } -// when should_add_in_flight === true, the JSObject would be temporarily hold by Normal gc_handle, so that it would not get collected during transition to the managed stack. -// its InFlight gc_handle would be freed when the instance arrives to managed side via Interop.Runtime.ReleaseInFlight -export function get_cs_owned_object_by_js_handle_ref(js_handle: JSHandle, should_add_in_flight: boolean, result: MonoObjectRef): void { - if (js_handle === JSHandleNull || js_handle === JSHandleDisposed) { - setI32_unchecked(result, 0); - return; - } - corebindings._get_cs_owned_object_by_js_handle_ref(js_handle, should_add_in_flight ? 1 : 0, result); -} - export function get_js_obj(js_handle: JSHandle): any { if (js_handle !== JSHandleNull && js_handle !== JSHandleDisposed) return mono_wasm_get_jsobj_from_js_handle(js_handle); @@ -116,7 +105,7 @@ export function teardown_managed_proxy(result: any, gc_handle: GCHandle): void { } } if (gc_handle !== GCHandleNull && _js_owned_object_table.delete(gc_handle)) { - corebindings._release_js_owned_object_by_gc_handle(gc_handle); + javaScriptExports._release_js_owned_object_by_gc_handle(gc_handle); } } @@ -142,12 +131,3 @@ export function _lookup_js_owned_object(gc_handle: GCHandle): any { } return null; } - -export function get_js_owned_object_by_gc_handle_ref(gc_handle: GCHandle, result: MonoObjectRef): void { - if (!gc_handle) { - setI32_unchecked(result, 0); - return; - } - // this is always strong gc_handle - corebindings._get_js_owned_object_by_gc_handle_ref(gc_handle, result); -} diff --git a/src/mono/wasm/runtime/imports.ts b/src/mono/wasm/runtime/imports.ts index 37daf4d7de39e..7ab3f367d3ee5 100644 --- a/src/mono/wasm/runtime/imports.ts +++ b/src/mono/wasm/runtime/imports.ts @@ -4,13 +4,14 @@ /* eslint-disable @typescript-eslint/triple-slash-reference */ /// +import { BINDINGType, MONOType } from "./legacy/exports-legacy"; import { DotnetModule, MonoConfig, RuntimeHelpers } from "./types"; import { EmscriptenModule } from "./types/emscripten"; // these are our public API (except internal) export let Module: EmscriptenModule & DotnetModule; -export let MONO: any; -export let BINDING: any; +export let MONO: MONOType; +export let BINDING: BINDINGType; export let INTERNAL: any; export let EXPORTS: any; export let IMPORTS: any; @@ -61,8 +62,6 @@ let monoConfig: MonoConfig = {} as any; let runtime_is_ready = false; export const runtimeHelpers: RuntimeHelpers = { - namespace: "System.Runtime.InteropServices.JavaScript", - classname: "Runtime", get mono_wasm_runtime_is_ready() { return runtime_is_ready; }, diff --git a/src/mono/wasm/runtime/invoke-cs.ts b/src/mono/wasm/runtime/invoke-cs.ts index 7443716303dea..dafb5613afc8b 100644 --- a/src/mono/wasm/runtime/invoke-cs.ts +++ b/src/mono/wasm/runtime/invoke-cs.ts @@ -1,24 +1,22 @@ // 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, + get_arg, get_sig, + get_signature_argument_count, is_args_exception, bound_cs_function_symbol, get_signature_version, MarshalerType, alloc_stack_frame, } from "./marshal"; -import { parseFQN, wrap_error_root } from "./method-calls"; -import { mono_wasm_new_external_root, mono_wasm_new_root } from "./roots"; +import { mono_wasm_new_external_root } from "./roots"; import { conv_string, conv_string_root } from "./strings"; -import { mono_assert, MonoObjectRef, MonoStringRef } from "./types"; +import { mono_assert, MonoObjectRef, MonoStringRef, MonoString, MonoObject, MonoMethod } from "./types"; import { Int32Ptr } from "./types/emscripten"; -import cwraps, { wrap_c_function } from "./cwraps"; -import { find_method } from "./method-binding"; +import cwraps from "./cwraps"; import { assembly_load } from "./class-loader"; +import { wrap_error_root } from "./invoke-js"; const exportedMethods = new Map(); @@ -48,16 +46,15 @@ export function mono_wasm_bind_cs_function(fully_qualified_name: MonoStringRef, 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); + const method = cwraps.mono_wasm_assembly_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"), + method, signature, + stackSave: anyModule.stackSave, stackRestore: anyModule.stackRestore, + alloc_stack_frame, + invoke_method_and_handle_exception }; const bound_js_function_name = "_bound_cs_" + `${namespace}_${classname}_${methodname}`.replace(/\./g, "_").replace(/\//g, "_"); let body = `//# sourceURL=https://mono-wasm.invalid/${bound_js_function_name} \n`; @@ -75,45 +72,26 @@ export function mono_wasm_bind_cs_function(fully_qualified_name: MonoStringRef, 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`; + body += `const { method, signature, stackSave, stackRestore, alloc_stack_frame, invoke_method_and_handle_exception ${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 += ` const args = alloc_stack_frame(${(args_count + 2)});\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"; + body += " invoke_method_and_handle_exception(method, args);\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 += " 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); @@ -131,22 +109,13 @@ export function mono_wasm_bind_cs_function(fully_qualified_name: MonoStringRef, } } -function init_void(args: JSMarshalerArguments) { - mono_assert(args && (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 && (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 function invoke_method_and_handle_exception(method: MonoMethod, args: JSMarshalerArguments): void { + const fail = cwraps.mono_wasm_invoke_method_bound(method, args); + if (fail) throw new Error("ERR24: Unexpected error: " + conv_string(fail)); + if (is_args_exception(args)) { + const exc = get_arg(args, 0); + throw marshal_exception_to_js(exc); + } } export const exportsByAssembly: Map = new Map(); @@ -200,11 +169,39 @@ function _walk_exports_to_set_function(assembly: string, namespace: string, clas scope[`${methodname}.${signature_hash}`] = fn; } -export async function mono_wasm_get_assembly_exports(assembly: string): Promise { - const asm = assembly_load(assembly); - if (!asm) - throw new Error("Could not find assembly: " + assembly); - cwraps.mono_wasm_runtime_run_module_cctor(asm); +export function mono_wasm_get_assembly_exports(assembly: string): Promise { + const result = exportsByAssembly.get(assembly); + if (!result) { + 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) || {}; +} + +export function parseFQN(fqn: string) + : { assembly: string, namespace: string, classname: string, methodname: string } { + const assembly = fqn.substring(fqn.indexOf("[") + 1, fqn.indexOf("]")).trim(); + fqn = fqn.substring(fqn.indexOf("]") + 1).trim(); + + const methodname = fqn.substring(fqn.indexOf(":") + 1); + fqn = fqn.substring(0, fqn.indexOf(":")).trim(); + + let namespace = ""; + let classname = fqn; + if (fqn.indexOf(".") != -1) { + const idx = fqn.lastIndexOf("."); + namespace = fqn.substring(0, idx); + classname = fqn.substring(idx + 1); + } + + if (!assembly.trim()) + throw new Error("No assembly name specified " + fqn); + if (!classname.trim()) + throw new Error("No class name specified " + fqn); + if (!methodname.trim()) + throw new Error("No method name specified " + fqn); + return { assembly, namespace, classname, methodname }; } \ No newline at end of file diff --git a/src/mono/wasm/runtime/invoke-js.ts b/src/mono/wasm/runtime/invoke-js.ts index 814e0ae784fdf..609b0b07db90d 100644 --- a/src/mono/wasm/runtime/invoke-js.ts +++ b/src/mono/wasm/runtime/invoke-js.ts @@ -5,13 +5,13 @@ import { mono_wasm_get_jsobj_from_js_handle, mono_wasm_get_js_handle } from "./g 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 { conv_string_root, js_string_to_mono_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 { IMPORTS, INTERNAL, Module, runtimeHelpers } from "./imports"; import { generate_arg_marshal_to_js } from "./marshal-to-js"; -import { mono_wasm_new_external_root } from "./roots"; +import { mono_wasm_new_external_root, WasmRoot } from "./roots"; +import { mono_wasm_symbolicate_string } from "./debug"; 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(function_name), @@ -189,3 +189,31 @@ export async function dynamic_import(module_name: string, module_url: string): P } +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +function _wrap_error_flag(is_exception: Int32Ptr | null, ex: any): string { + let res = "unknown exception"; + if (ex) { + res = ex.toString(); + const stack = ex.stack; + if (stack) { + // 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 + if (stack.startsWith(res)) + res = stack; + else + res += "\n" + stack; + } + + res = mono_wasm_symbolicate_string(res); + } + if (is_exception) { + Module.setValue(is_exception, 1, "i32"); + } + return res; +} + +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export function wrap_error_root(is_exception: Int32Ptr | null, ex: any, result: WasmRoot): void { + const res = _wrap_error_flag(is_exception, ex); + js_string_to_mono_string_root(res, result); +} diff --git a/src/mono/wasm/runtime/legacy/buffers.ts b/src/mono/wasm/runtime/legacy/buffers.ts new file mode 100644 index 0000000000000..c65144ea2f649 --- /dev/null +++ b/src/mono/wasm/runtime/legacy/buffers.ts @@ -0,0 +1,110 @@ +import { Module } from "../imports"; +import { wrap_error_root } from "../invoke-js"; +import { mono_wasm_new_external_root } from "../roots"; +import { MonoArray, MonoObjectRef, MonoObject } from "../types"; +import { Int32Ptr, TypedArray } from "../types/emscripten"; +import { js_to_mono_obj_root } from "./js-to-cs"; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export function mono_wasm_typed_array_from_ref(pinned_array: MonoArray, begin: number, end: number, bytes_per_element: number, type: number, is_exception: Int32Ptr, result_address: MonoObjectRef): void { + const resultRoot = mono_wasm_new_external_root(result_address); + try { + const res = typed_array_from(pinned_array, begin, end, bytes_per_element, type); + // returns JS typed array like Int8Array, to be wraped with JSObject proxy + js_to_mono_obj_root(res, resultRoot, true); + } catch (exc) { + wrap_error_root(is_exception, String(exc), resultRoot); + } finally { + resultRoot.release(); + } +} + +// Creates a new typed array from pinned array address from pinned_array allocated on the heap to the typed array. +// adress of managed pinned array -> copy from heap -> typed array memory +function typed_array_from(pinned_array: MonoArray, begin: number, end: number, bytes_per_element: number, type: number) { + + // typed array + let newTypedArray: TypedArray | null = null; + + switch (type) { + case 5: + newTypedArray = new Int8Array(end - begin); + break; + case 6: + newTypedArray = new Uint8Array(end - begin); + break; + case 7: + newTypedArray = new Int16Array(end - begin); + break; + case 8: + newTypedArray = new Uint16Array(end - begin); + break; + case 9: + newTypedArray = new Int32Array(end - begin); + break; + case 10: + newTypedArray = new Uint32Array(end - begin); + break; + case 13: + newTypedArray = new Float32Array(end - begin); + break; + case 14: + newTypedArray = new Float64Array(end - begin); + break; + case 15: // This is a special case because the typed array is also byte[] + newTypedArray = new Uint8ClampedArray(end - begin); + break; + default: + throw new Error("Unknown array type " + type); + } + + typedarray_copy_from(newTypedArray, pinned_array, begin, end, bytes_per_element); + return newTypedArray; +} + +// Copy the pinned array address from pinned_array allocated on the heap to the typed array. +// adress of managed pinned array -> copy from heap -> typed array memory +function typedarray_copy_from(typed_array: TypedArray, pinned_array: MonoArray, begin: number, end: number, bytes_per_element: number) { + + // JavaScript typed arrays are array-like objects and provide a mechanism for accessing + // raw binary data. (...) To achieve maximum flexibility and efficiency, JavaScript typed arrays + // split the implementation into buffers and views. A buffer (implemented by the ArrayBuffer object) + // is an object representing a chunk of data; it has no format to speak of, and offers no + // mechanism for accessing its contents. In order to access the memory contained in a buffer, + // you need to use a view. A view provides a context - that is, a data type, starting offset, + // and number of elements - that turns the data into an actual typed array. + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays + if (has_backing_array_buffer(typed_array) && typed_array.BYTES_PER_ELEMENT) { + // Some sanity checks of what is being asked of us + // lets play it safe and throw an error here instead of assuming to much. + // Better safe than sorry later + if (bytes_per_element !== typed_array.BYTES_PER_ELEMENT) + throw new Error("Inconsistent element sizes: TypedArray.BYTES_PER_ELEMENT '" + typed_array.BYTES_PER_ELEMENT + "' sizeof managed element: '" + bytes_per_element + "'"); + + // how much space we have to work with + let num_of_bytes = (end - begin) * bytes_per_element; + // how much typed buffer space are we talking about + const view_bytes = typed_array.length * typed_array.BYTES_PER_ELEMENT; + // only use what is needed. + if (num_of_bytes > view_bytes) + num_of_bytes = view_bytes; + + // Create a new view for mapping + const typedarrayBytes = new Uint8Array(typed_array.buffer, 0, num_of_bytes); + // offset index into the view + const offset = begin * bytes_per_element; + // Set view bytes to value from HEAPU8 + typedarrayBytes.set(Module.HEAPU8.subarray(pinned_array + offset, pinned_array + offset + num_of_bytes)); + return num_of_bytes; + } + else { + throw new Error("Object '" + typed_array + "' is not a typed array"); + } +} + + +export function has_backing_array_buffer(js_obj: TypedArray): boolean { + return typeof SharedArrayBuffer !== "undefined" + ? js_obj.buffer instanceof ArrayBuffer || js_obj.buffer instanceof SharedArrayBuffer + : js_obj.buffer instanceof ArrayBuffer; +} \ No newline at end of file diff --git a/src/mono/wasm/runtime/legacy/corebindings.ts b/src/mono/wasm/runtime/legacy/corebindings.ts new file mode 100644 index 0000000000000..faf39d5343f27 --- /dev/null +++ b/src/mono/wasm/runtime/legacy/corebindings.ts @@ -0,0 +1,97 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import { JSHandle, GCHandle, MonoObjectRef, MonoMethod, MonoObject } from "../types"; +import { PromiseControl } from "../cancelable-promise"; +import { mono_bind_method, _create_primitive_converters } from "./method-binding"; +import { WasmRoot } from "../roots"; +import { runtimeHelpers } from "../imports"; +import cwraps from "../cwraps"; +type SigLine = [lazy: boolean, jsname: string, csname: string, signature: string/*ArgsMarshalString*/]; +const fn_signatures: SigLine[] = [ + [true, "_get_cs_owned_object_by_js_handle_ref", "GetCSOwnedObjectByJSHandleRef", "iim"], + [true, "_get_cs_owned_object_js_handle_ref", "GetCSOwnedObjectJSHandleRef", "mi"], + [true, "_try_get_cs_owned_object_js_handle_ref", "TryGetCSOwnedObjectJSHandleRef", "mi"], + [false, "_create_cs_owned_proxy_ref", "CreateCSOwnedProxyRef", "iiim"], + + [false, "_get_js_owned_object_by_gc_handle_ref", "GetJSOwnedObjectByGCHandleRef", "im"], + [true, "_get_js_owned_object_gc_handle_ref", "GetJSOwnedObjectGCHandleRef", "m"], + + [true, "_create_tcs", "CreateTaskSource", ""], + [true, "_set_tcs_result_ref", "SetTaskSourceResultRef", "iR"], + [true, "_set_tcs_failure", "SetTaskSourceFailure", "is"], + [true, "_get_tcs_task_ref", "GetTaskSourceTaskRef", "im"], + [true, "_setup_js_cont_ref", "SetupJSContinuationRef", "mo"], + + [true, "_object_to_string_ref", "ObjectToStringRef", "m"], + [true, "_get_date_value_ref", "GetDateValueRef", "m"], + [true, "_create_date_time_ref", "CreateDateTimeRef", "dm"], + [true, "_create_uri_ref", "CreateUriRef", "sm"], + [true, "_is_simple_array_ref", "IsSimpleArrayRef", "m"], + [false, "_get_call_sig_ref", "GetCallSignatureRef", "im"], +]; + +export interface LegacyExports { + // see src\libraries\System.Runtime.InteropServices.JavaScript\src\System\Runtime\InteropServices\JavaScript\Interop\LegacyExports.cs + _get_cs_owned_object_by_js_handle_ref(jsHandle: JSHandle, shouldAddInflight: 0 | 1, result: MonoObjectRef): void; + _get_cs_owned_object_js_handle_ref(obj: MonoObjectRef, shouldAddInflight: 0 | 1): JSHandle; + _try_get_cs_owned_object_js_handle_ref(obj: MonoObjectRef, shouldAddInflight: 0 | 1): JSHandle; + _create_cs_owned_proxy_ref(jsHandle: JSHandle, mappedType: number, shouldAddInflight: 0 | 1, result: MonoObjectRef): void; + + _get_js_owned_object_by_gc_handle_ref(gcHandle: GCHandle, result: MonoObjectRef): void; + _get_js_owned_object_gc_handle_ref(obj: MonoObjectRef): GCHandle + + _create_tcs(): GCHandle; + _set_tcs_result_ref(gcHandle: GCHandle, result: any): void + _set_tcs_failure(gcHandle: GCHandle, result: string): void + _get_tcs_task_ref(gcHandle: GCHandle, result: MonoObjectRef): void; + _setup_js_cont_ref(task: MonoObjectRef, continuation: PromiseControl): void; + + _object_to_string_ref(obj: MonoObjectRef): string; + _get_date_value_ref(obj: MonoObjectRef): number; + _create_date_time_ref(ticks: number, result: MonoObjectRef): void; + _create_uri_ref(uri: string, result: MonoObjectRef): void; + _is_simple_array_ref(obj: MonoObjectRef): boolean; + _get_call_sig_ref(method: MonoMethod, obj: WasmRoot): string; +} + +export const legacyManagedExports: LegacyExports = {}; + + +export function bind_runtime_method(method_name: string, signature: string): Function { + const method = get_method(method_name); + return mono_bind_method(method, signature, false, "BINDINGS_" + method_name); +} + +export function init_legacy_exports(): void { + _create_primitive_converters(); + + runtimeHelpers.runtime_legacy_exports_classname = "LegacyExports"; + runtimeHelpers.runtime_legacy_exports_class = cwraps.mono_wasm_assembly_find_class(runtimeHelpers.runtime_interop_module, runtimeHelpers.runtime_interop_namespace, runtimeHelpers.runtime_legacy_exports_classname); + if (!runtimeHelpers.runtime_legacy_exports_class) + throw "Can't find " + runtimeHelpers.runtime_interop_namespace + "." + runtimeHelpers.runtime_interop_exports_classname + " class"; + + for (const sig of fn_signatures) { + const wf: any = legacyManagedExports; + const [lazy, jsname, csname, signature] = sig; + if (lazy) { + // lazy init on first run + wf[jsname] = function (...args: any[]) { + const fce = bind_runtime_method(csname, signature); + wf[jsname] = fce; + return fce(...args); + }; + } + else { + const fce = bind_runtime_method(csname, signature); + wf[jsname] = fce; + } + } +} + +export function get_method(method_name: string): MonoMethod { + const res = cwraps.mono_wasm_assembly_find_method(runtimeHelpers.runtime_legacy_exports_class, method_name, -1); + if (!res) + throw "Can't find method " + runtimeHelpers.runtime_interop_namespace + "." + runtimeHelpers.runtime_legacy_exports_classname + "." + method_name; + return res; +} \ No newline at end of file diff --git a/src/mono/wasm/runtime/cs-to-js.ts b/src/mono/wasm/runtime/legacy/cs-to-js.ts similarity index 78% rename from src/mono/wasm/runtime/cs-to-js.ts rename to src/mono/wasm/runtime/legacy/cs-to-js.ts index 0f5392ea88086..343f79e5da9e4 100644 --- a/src/mono/wasm/runtime/cs-to-js.ts +++ b/src/mono/wasm/runtime/legacy/cs-to-js.ts @@ -1,26 +1,19 @@ -// 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, WasmRoot, mono_wasm_new_external_root } from "./roots"; -import { - GCHandle, JSHandleDisposed, MarshalError, MarshalType, MonoArray, - MonoArrayNull, MonoObject, MonoObjectNull, MonoString, - MonoType, MonoTypeNull, MonoObjectRef, MonoStringRef, is_nullish -} from "./types"; -import { runtimeHelpers } from "./imports"; -import { conv_string_root } from "./strings"; -import corebindings from "./corebindings"; -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 { _are_promises_supported, create_cancelable_promise } from "../cancelable-promise"; +import cwraps from "../cwraps"; +import { mono_wasm_get_jsobj_from_js_handle, _lookup_js_owned_object, setup_managed_proxy, mono_wasm_get_js_handle, teardown_managed_proxy, assert_not_disposed } from "../gc-handles"; +import { runtimeHelpers } from "../imports"; +import { wrap_error_root } from "../invoke-js"; +import { ManagedObject } from "../marshal"; +import { getU32, getI32, getF32, getF64, setI32_unchecked } from "../memory"; +import { WasmRoot, mono_wasm_new_root, mono_wasm_new_external_root } from "../roots"; +import { conv_string_root } from "../strings"; +import { MarshalType, MonoType, MarshalError, MonoTypeNull, MonoArray, MonoArrayNull, MonoObject, MonoObjectNull, GCHandle, MonoStringRef, MonoObjectRef, MonoString, JSHandleDisposed, is_nullish } from "../types"; +import { Int32Ptr, VoidPtr } from "../types/emscripten"; +import { legacyManagedExports } from "./corebindings"; import { js_to_mono_obj_root } from "./js-to-cs"; -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"; +import { mono_bind_method, mono_method_get_call_signature_ref } from "./method-binding"; const delegate_invoke_symbol = Symbol.for("wasm delegate_invoke"); -const delegate_invoke_signature_symbol = Symbol.for("wasm delegate_invoke_signature"); // this is only used from Blazor export function unbox_mono_obj(mono_obj: MonoObject): any { @@ -37,7 +30,7 @@ export function unbox_mono_obj(mono_obj: MonoObject): any { function _unbox_cs_owned_root_as_js_object(root: WasmRoot) { // we don't need in-flight reference as we already have it rooted here - const js_handle = corebindings._get_cs_owned_object_js_handle_ref(root.address, 0); + const js_handle = legacyManagedExports._get_cs_owned_object_js_handle_ref(root.address, 0); const js_obj = mono_wasm_get_jsobj_from_js_handle(js_handle); return js_obj; } @@ -74,11 +67,11 @@ function _unbox_mono_obj_root_with_known_nonprimitive_type_impl(root: WasmRoot20: // clr .NET DateTime - return new Date(corebindings._get_date_value_ref(root.address)); + return new Date(legacyManagedExports._get_date_value_ref(root.address)); case 21: // clr .NET DateTimeOffset - return corebindings._object_to_string_ref(root.address); + return legacyManagedExports._object_to_string_ref(root.address); case MarshalType.URI: - return corebindings._object_to_string_ref(root.address); + return legacyManagedExports._object_to_string_ref(root.address); case MarshalType.SAFEHANDLE: return _unbox_cs_owned_root_as_js_object(root); case MarshalType.VOID: @@ -146,7 +139,7 @@ export function mono_array_to_js_array(mono_array: MonoArray): any[] | null { } function is_nested_array_ref(ele: WasmRoot) { - return corebindings._is_simple_array_ref(ele.address); + return legacyManagedExports._is_simple_array_ref(ele.address); } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types @@ -181,30 +174,23 @@ export function _wrap_delegate_root_as_function(root: WasmRoot): Fun return null; // get strong reference to the Delegate - const gc_handle = corebindings._get_js_owned_object_gc_handle_ref(root.address); + const gc_handle = legacyManagedExports._get_js_owned_object_gc_handle_ref(root.address); return _wrap_delegate_gc_handle_as_function(gc_handle); } -export function _wrap_delegate_gc_handle_as_function(gc_handle: GCHandle, after_listener_callback?: () => void): Function { +export function _wrap_delegate_gc_handle_as_function(gc_handle: GCHandle): Function { // see if we have js owned instance for this gc_handle already let result = _lookup_js_owned_object(gc_handle); + // If the function for this gc_handle was already collected (or was never created) if (!result) { + // note that we do not implement function/delegate roundtrip result = function (...args: any[]) { - const delegateRoot = mono_wasm_new_root(); - get_js_owned_object_by_gc_handle_ref(gc_handle, delegateRoot.address); - try { - // FIXME: Pass delegateRoot by-ref - const res = call_method_ref(result[delegate_invoke_symbol], delegateRoot, result[delegate_invoke_signature_symbol], args); - if (after_listener_callback) { - after_listener_callback(); - } - return res; - } finally { - delegateRoot.release(); - } + assert_not_disposed(result); + const boundMethod = result[delegate_invoke_symbol]; + return boundMethod(...args); }; // bind the method @@ -212,20 +198,21 @@ export function _wrap_delegate_gc_handle_as_function(gc_handle: GCHandle, after_ get_js_owned_object_by_gc_handle_ref(gc_handle, delegateRoot.address); try { if (typeof result[delegate_invoke_symbol] === "undefined") { - result[delegate_invoke_symbol] = cwraps.mono_wasm_get_delegate_invoke_ref(delegateRoot.address); + const method = cwraps.mono_wasm_get_delegate_invoke_ref(delegateRoot.address); + const signature = mono_method_get_call_signature_ref(method, delegateRoot); + const js_method = mono_bind_method(method, signature, true); + result[delegate_invoke_symbol] = js_method.bind({ this_arg_gc_handle: gc_handle }); if (!result[delegate_invoke_symbol]) { throw new Error("System.Delegate Invoke method can not be resolved."); } } - - if (typeof result[delegate_invoke_signature_symbol] === "undefined") { - result[delegate_invoke_signature_symbol] = mono_method_get_call_signature_ref(result[delegate_invoke_symbol], delegateRoot); - } } finally { delegateRoot.release(); } setup_managed_proxy(result, gc_handle); + } else { + assert_not_disposed(result); } return result; @@ -288,7 +275,7 @@ function _unbox_task_root_as_promise(root: WasmRoot) { throw new Error("Promises are not supported thus 'System.Threading.Tasks.Task' can not work in this context."); // get strong reference to Task - const gc_handle = corebindings._get_js_owned_object_gc_handle_ref(root.address); + const gc_handle = legacyManagedExports._get_js_owned_object_gc_handle_ref(root.address); // see if we have js owned instance for this gc_handle already let result = _lookup_js_owned_object(gc_handle); @@ -304,7 +291,7 @@ function _unbox_task_root_as_promise(root: WasmRoot) { result = promise; // register C# side of the continuation - corebindings._setup_js_cont_ref(root.address, promise_control); + legacyManagedExports._setup_js_cont_ref(root.address, promise_control); setup_managed_proxy(result, gc_handle); } @@ -319,7 +306,7 @@ export function _unbox_ref_type_root_as_js_object(root: WasmRoot): a // this could be JSObject proxy of a js native object // we don't need in-flight reference as we already have it rooted here - const js_handle = corebindings._try_get_cs_owned_object_js_handle_ref(root.address, 0); + const js_handle = legacyManagedExports._try_get_cs_owned_object_js_handle_ref(root.address, 0); if (js_handle) { if (js_handle === JSHandleDisposed) { throw new Error("Cannot access a disposed JSObject at " + root.value); @@ -329,7 +316,7 @@ export function _unbox_ref_type_root_as_js_object(root: WasmRoot): a // otherwise this is C# only object // get strong reference to Object - const gc_handle = corebindings._get_js_owned_object_gc_handle_ref(root.address); + const gc_handle = legacyManagedExports._get_js_owned_object_gc_handle_ref(root.address); // see if we have js owned instance for this gc_handle already let result = _lookup_js_owned_object(gc_handle); @@ -338,11 +325,19 @@ export function _unbox_ref_type_root_as_js_object(root: WasmRoot): a if (is_nullish(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; - setup_managed_proxy(result, gc_handle); } return result; -} \ No newline at end of file +} + +export function get_js_owned_object_by_gc_handle_ref(gc_handle: GCHandle, result: MonoObjectRef): void { + if (!gc_handle) { + setI32_unchecked(result, 0); + return; + } + // this is always strong gc_handle + legacyManagedExports._get_js_owned_object_by_gc_handle_ref(gc_handle, result); +} + + diff --git a/src/mono/wasm/runtime/legacy/exports-legacy.ts b/src/mono/wasm/runtime/legacy/exports-legacy.ts new file mode 100644 index 0000000000000..ff49a0328ae86 --- /dev/null +++ b/src/mono/wasm/runtime/legacy/exports-legacy.ts @@ -0,0 +1,207 @@ +import cwraps from "../cwraps"; +import { mono_wasm_runtime_ready } from "../debug"; +import diagnostics, { Diagnostics } from "../diagnostics"; +import { mono_wasm_load_icu_data } from "../icu"; +import { runtimeHelpers } from "../imports"; +import { mono_wasm_get_assembly_exports } from "../invoke-cs"; +import { mono_wasm_load_bytes_into_heap, setB32, setI8, setI16, setI32, setI52, setU52, setI64Big, setU8, setU16, setU32, setF32, setF64, getB32, getI8, getI16, getI32, getI52, getU52, getI64Big, getU8, getU16, getU32, getF32, getF64 } from "../memory"; +import { mono_wasm_new_root_buffer, mono_wasm_new_root, mono_wasm_new_external_root, mono_wasm_release_roots } from "../roots"; +import { mono_run_main, mono_run_main_and_exit } from "../run"; +import { mono_wasm_setenv, mono_wasm_load_data_archive, mono_wasm_load_config, mono_load_runtime_and_bcl_args } from "../startup"; +import { js_string_to_mono_string, conv_string, js_string_to_mono_string_root, conv_string_root } from "../strings"; +import { MonoArray, MonoConfig, MonoConfigError, MonoObject, MonoObjectRef } from "../types"; +import { VoidPtr } from "../types/emscripten"; +import { mono_array_to_js_array, unbox_mono_obj, unbox_mono_obj_root, mono_array_root_to_js_array } from "./cs-to-js"; +import { js_typed_array_to_array, js_to_mono_obj, js_typed_array_to_array_root, js_to_mono_obj_root } from "./js-to-cs"; +import { mono_bind_static_method, mono_call_assembly_entry_point } from "./method-calls"; + +export function export_mono_api(): MONOType { + return { + // current "public" MONO API + mono_wasm_setenv, + mono_wasm_load_bytes_into_heap, + mono_wasm_load_icu_data, + mono_wasm_runtime_ready, + mono_wasm_load_data_archive, + mono_wasm_load_config, + mono_load_runtime_and_bcl_args, + mono_wasm_new_root_buffer, + mono_wasm_new_root, + mono_wasm_new_external_root, + 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: null, + mono_wasm_load_runtime: null, + + config: runtimeHelpers.config, + loaded_files: [], + + // memory accessors + setB32, + setI8, + setI16, + setI32, + setI52, + setU52, + setI64Big, + setU8, + setU16, + setU32, + setF32, + setF64, + getB32, + getI8, + getI16, + getI32, + getI52, + getU52, + getI64Big, + getU8, + getU16, + getU32, + getF32, + getF64, + + // Diagnostics + diagnostics + }; +} + +export function cwraps_mono_api(mono: MONOType): void { + Object.assign(mono, { + mono_wasm_add_assembly: cwraps.mono_wasm_add_assembly, + mono_wasm_load_runtime: cwraps.mono_wasm_load_runtime, + }); +} + +export function export_binding_api(): BINDINGType { + return { + //current "public" BINDING API + bind_static_method: mono_bind_static_method, + call_assembly_entry_point: mono_call_assembly_entry_point, + mono_obj_array_new: null, + mono_obj_array_set: null, + js_string_to_mono_string, + js_typed_array_to_array, + mono_array_to_js_array, + js_to_mono_obj, + conv_string, + unbox_mono_obj, + + mono_obj_array_new_ref: null, + mono_obj_array_set_ref: null, + js_string_to_mono_string_root, + js_typed_array_to_array_root, + js_to_mono_obj_root, + conv_string_root, + unbox_mono_obj_root, + mono_array_root_to_js_array, + }; +} + +export function cwraps_binding_api(binding: BINDINGType): void { + Object.assign(binding, { + mono_obj_array_new: cwraps.mono_wasm_obj_array_new, + mono_obj_array_set: cwraps.mono_wasm_obj_array_set, + mono_obj_array_new_ref: cwraps.mono_wasm_obj_array_new_ref, + mono_obj_array_set_ref: cwraps.mono_wasm_obj_array_set_ref, + }); +} + +export type BINDINGType = { + bind_static_method: typeof mono_bind_static_method; + call_assembly_entry_point: typeof mono_call_assembly_entry_point; + /** + * @deprecated Not GC or thread safe + */ + mono_obj_array_new: (size: number) => MonoArray; + /** + * @deprecated Not GC or thread safe + */ + mono_obj_array_set: (array: MonoArray, idx: number, obj: MonoObject) => void; + /** + * @deprecated Not GC or thread safe + */ + js_string_to_mono_string: typeof js_string_to_mono_string; + /** + * @deprecated Not GC or thread safe + */ + js_typed_array_to_array: typeof js_typed_array_to_array; + /** + * @deprecated Not GC or thread safe + */ + mono_array_to_js_array: typeof mono_array_to_js_array; + /** + * @deprecated Not GC or thread safe + */ + js_to_mono_obj: typeof js_to_mono_obj; + /** + * @deprecated Not GC or thread safe + */ + conv_string: typeof conv_string; + /** + * @deprecated Not GC or thread safe + */ + unbox_mono_obj: typeof unbox_mono_obj; + + // do we really want to advertize add these below ? + mono_obj_array_new_ref: (size: number, result: MonoObjectRef) => void; + mono_obj_array_set_ref: (array: MonoObjectRef, idx: number, obj: MonoObjectRef) => void; + js_string_to_mono_string_root: typeof js_string_to_mono_string_root; + js_typed_array_to_array_root: typeof js_typed_array_to_array_root; + js_to_mono_obj_root: typeof js_to_mono_obj_root; + conv_string_root: typeof conv_string_root; + unbox_mono_obj_root: typeof unbox_mono_obj_root; + mono_array_root_to_js_array: typeof mono_array_root_to_js_array; +} + +export type MONOType = { + mono_wasm_setenv: typeof mono_wasm_setenv; + mono_wasm_load_bytes_into_heap: typeof mono_wasm_load_bytes_into_heap; + mono_wasm_load_icu_data: typeof mono_wasm_load_icu_data; + mono_wasm_runtime_ready: typeof mono_wasm_runtime_ready; + mono_wasm_load_data_archive: typeof mono_wasm_load_data_archive; + mono_wasm_load_config: typeof mono_wasm_load_config; + mono_load_runtime_and_bcl_args: typeof mono_load_runtime_and_bcl_args; + mono_wasm_new_root_buffer: typeof mono_wasm_new_root_buffer; + mono_wasm_new_root: typeof mono_wasm_new_root; + mono_wasm_new_external_root: typeof mono_wasm_new_external_root; + 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; + loaded_files: string[]; + setB32: typeof setB32; + setI8: typeof setI8; + setI16: typeof setI16; + setI32: typeof setI32; + setI52: typeof setI52; + setU52: typeof setU52; + setI64Big: typeof setI64Big; + setU8: typeof setU8; + setU16: typeof setU16; + setU32: typeof setU32; + setF32: typeof setF32; + setF64: typeof setF64; + getB32: typeof getB32; + getI8: typeof getI8; + getI16: typeof getI16; + getI32: typeof getI32; + getI52: typeof getI52; + getU52: typeof getU52; + getI64Big: typeof getI64Big; + getU8: typeof getU8; + getU16: typeof getU16; + getU32: typeof getU32; + getF32: typeof getF32; + getF64: typeof getF64; + diagnostics: Diagnostics; + +} \ No newline at end of file diff --git a/src/mono/wasm/runtime/js-to-cs.ts b/src/mono/wasm/runtime/legacy/js-to-cs.ts similarity index 80% rename from src/mono/wasm/runtime/js-to-cs.ts rename to src/mono/wasm/runtime/legacy/js-to-cs.ts index 890febfd05c1f..438afbae9b63c 100644 --- a/src/mono/wasm/runtime/js-to-cs.ts +++ b/src/mono/wasm/runtime/legacy/js-to-cs.ts @@ -1,24 +1,16 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -import { Module, runtimeHelpers } from "./imports"; -import { - assert_not_disposed, - cs_owned_js_handle_symbol, get_cs_owned_object_by_js_handle_ref, - 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, - mono_wasm_release_cs_owned_object, setup_managed_proxy, teardown_managed_proxy -} from "./gc-handles"; -import corebindings from "./corebindings"; -import cwraps from "./cwraps"; -import { mono_wasm_new_root, mono_wasm_release_roots, WasmRoot, mono_wasm_new_external_root } from "./roots"; -import { wrap_error_root } from "./method-calls"; -import { js_string_to_mono_string_root, js_string_to_mono_string_interned_root } from "./strings"; -import { isThenable } from "./cancelable-promise"; +import { isThenable } from "../cancelable-promise"; +import cwraps from "../cwraps"; +import { js_owned_gc_handle_symbol, assert_not_disposed, cs_owned_js_handle_symbol, mono_wasm_get_js_handle, setup_managed_proxy, mono_wasm_release_cs_owned_object, teardown_managed_proxy, mono_wasm_get_jsobj_from_js_handle } from "../gc-handles"; +import { runtimeHelpers, Module } from "../imports"; +import { wrap_error_root } from "../invoke-js"; +import { setI32_unchecked, setU32_unchecked, setF64, setB32 } from "../memory"; +import { WasmRoot, mono_wasm_new_root, mono_wasm_release_roots, mono_wasm_new_external_root } from "../roots"; +import { js_string_to_mono_string_root, js_string_to_mono_string_interned_root } from "../strings"; +import { MonoObject, is_nullish, MonoClass, wasm_type_symbol, MonoArray, MonoMethod, MonoObjectNull, JSHandle, MonoObjectRef, JSHandleNull, JSHandleDisposed } from "../types"; +import { TypedArray, Int32Ptr } from "../types/emscripten"; import { has_backing_array_buffer } from "./buffers"; -import { JSHandle, MonoArray, MonoMethod, MonoObject, MonoObjectNull, wasm_type_symbol, MonoClass, MonoObjectRef, is_nullish } from "./types"; -import { setF64, setI32_unchecked, setU32_unchecked, setB32 } from "./memory"; -import { Int32Ptr, TypedArray } from "./types/emscripten"; +import { legacyManagedExports } from "./corebindings"; +import { get_js_owned_object_by_gc_handle_ref } from "./cs-to-js"; // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types export function _js_to_mono_uri_root(should_add_in_flight: boolean, js_obj: any, result: WasmRoot): void { @@ -29,7 +21,7 @@ export function _js_to_mono_uri_root(should_add_in_flight: boolean, js_obj: any, return; case typeof js_obj === "symbol": case typeof js_obj === "string": - corebindings._create_uri_ref(js_obj, result.address); + legacyManagedExports._create_uri_ref(js_obj, result.address); return; default: _extract_mono_obj_root(should_add_in_flight, js_obj, result); @@ -108,7 +100,7 @@ export function js_to_mono_obj_root(js_obj: any, result: WasmRoot, s } case js_obj.constructor.name === "Date": // getTime() is always UTC - corebindings._create_date_time_ref(js_obj.getTime(), result.address); + legacyManagedExports._create_date_time_ref(js_obj.getTime(), result.address); return; default: _extract_mono_obj_root(should_add_in_flight, js_obj, result); @@ -125,8 +117,8 @@ function _extract_mono_obj_root(should_add_in_flight: boolean, js_obj: any, resu if (js_obj[js_owned_gc_handle_symbol] !== undefined) { // for js_owned_gc_handle we don't want to create new proxy // since this is strong gc_handle we don't need to in-flight reference - assert_not_disposed(js_obj); - get_js_owned_object_by_gc_handle_ref(js_obj[js_owned_gc_handle_symbol], result.address); + const gc_handle = assert_not_disposed(js_obj); + get_js_owned_object_by_gc_handle_ref(gc_handle, result.address); return; } if (js_obj[cs_owned_js_handle_symbol]) { @@ -148,7 +140,7 @@ function _extract_mono_obj_root(should_add_in_flight: boolean, js_obj: any, resu const js_handle = mono_wasm_get_js_handle(js_obj); - corebindings._create_cs_owned_proxy_ref(js_handle, wasm_type_id, should_add_in_flight ? 1 : 0, result.address); + legacyManagedExports._create_cs_owned_proxy_ref(js_handle, wasm_type_id, should_add_in_flight ? 1 : 0, result.address); } } @@ -245,13 +237,13 @@ export function _wrap_js_thenable_as_task_root(thenable: Promise, resultRoo // Note that we do not implement promise/task roundtrip. // With more complexity we could recover original instance when this Task is marshaled back to JS. // TODO optimization: return the tcs.Task on this same call instead of _get_tcs_task - const tcs_gc_handle = corebindings._create_tcs(); + const tcs_gc_handle = legacyManagedExports._create_tcs(); const holder: any = { tcs_gc_handle }; setup_managed_proxy(holder, tcs_gc_handle); thenable.then((result) => { - corebindings._set_tcs_result_ref(tcs_gc_handle, result); + legacyManagedExports._set_tcs_result_ref(tcs_gc_handle, result); }, (reason) => { - corebindings._set_tcs_failure(tcs_gc_handle, reason ? reason.toString() : ""); + legacyManagedExports._set_tcs_failure(tcs_gc_handle, reason ? reason.toString() : ""); }).finally(() => { // let go of the thenable reference mono_wasm_release_cs_owned_object(thenable_js_handle); @@ -259,7 +251,7 @@ export function _wrap_js_thenable_as_task_root(thenable: Promise, resultRoo }); - corebindings._get_tcs_task_ref(tcs_gc_handle, resultRoot.address); + legacyManagedExports._get_tcs_task_ref(tcs_gc_handle, resultRoot.address); // returns raw pointer to tcs.Task return { @@ -284,3 +276,14 @@ export function mono_wasm_typed_array_to_array_ref(js_handle: JSHandle, is_excep resultRoot.release(); } } + +// when should_add_in_flight === true, the JSObject would be temporarily hold by Normal gc_handle, so that it would not get collected during transition to the managed stack. +// its InFlight gc_handle would be freed when the instance arrives to managed side via Interop.Runtime.ReleaseInFlight +export function get_cs_owned_object_by_js_handle_ref(js_handle: JSHandle, should_add_in_flight: boolean, result: MonoObjectRef): void { + if (js_handle === JSHandleNull || js_handle === JSHandleDisposed) { + setI32_unchecked(result, 0); + return; + } + legacyManagedExports._get_cs_owned_object_by_js_handle_ref(js_handle, should_add_in_flight ? 1 : 0, result); +} + diff --git a/src/mono/wasm/runtime/method-binding.ts b/src/mono/wasm/runtime/legacy/method-binding.ts similarity index 83% rename from src/mono/wasm/runtime/method-binding.ts rename to src/mono/wasm/runtime/legacy/method-binding.ts index 581f3ae5c74b4..d657254c691e1 100644 --- a/src/mono/wasm/runtime/method-binding.ts +++ b/src/mono/wasm/runtime/legacy/method-binding.ts @@ -1,27 +1,24 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -import { WasmRoot, WasmRootBuffer, mono_wasm_new_root, mono_wasm_new_external_root } from "./roots"; -import { MonoClass, MonoMethod, MonoObject, VoidPtrNull, MonoType, MarshalType, mono_assert } from "./types"; -import { BINDING, Module, runtimeHelpers } from "./imports"; -import { js_to_mono_enum, js_to_mono_obj_root, _js_to_mono_uri_root } from "./js-to-cs"; -import { js_string_to_mono_string_root, js_string_to_mono_string_interned_root } from "./strings"; -import { _unbox_mono_obj_root_with_known_nonprimitive_type } from "./cs-to-js"; -import { - _create_temp_frame, _zero_region, - getI32, getU32, getF32, getF64, - setI32, setU32, setF32, setF64, setI52, setU52, - setB32, getB32, setI32_unchecked, setU32_unchecked -} from "./memory"; -import { - _handle_exception_for_call, _teardown_after_call -} from "./method-calls"; -import cwraps, { wrap_c_function } from "./cwraps"; -import { VoidPtr } from "./types/emscripten"; - +import cwraps from "../cwraps"; +import { runtimeHelpers, BINDING, Module } from "../imports"; +import { parseFQN } from "../invoke-cs"; +import { setI32, setU32, setF32, setF64, setU52, setI52, setB32, setI32_unchecked, setU32_unchecked, _zero_region, _create_temp_frame, getB32, getI32, getU32, getF32, getF64 } from "../memory"; +import { WasmRoot, mono_wasm_new_external_root, mono_wasm_new_root, WasmRootBuffer } from "../roots"; +import { js_string_to_mono_string_root, js_string_to_mono_string_interned_root, conv_string_root } from "../strings"; +import { MonoMethod, MonoObject, MonoType, MonoClass, mono_assert, VoidPtrNull, MarshalType, MonoString, MonoObjectNull } from "../types"; +import { VoidPtr } from "../types/emscripten"; +import { legacyManagedExports } from "./corebindings"; +import { get_js_owned_object_by_gc_handle_ref, _unbox_mono_obj_root_with_known_nonprimitive_type } from "./cs-to-js"; +import { js_to_mono_obj_root, _js_to_mono_uri_root, js_to_mono_enum } from "./js-to-cs"; +import { _teardown_after_call } from "./method-calls"; + + +const escapeRE = /[^A-Za-z0-9_$]/g; const primitiveConverters = new Map(); const _signature_converters = new Map(); - +const boundMethodsByMethod: Map = new Map(); export function _get_type_name(typePtr: MonoType): string { if (!typePtr) @@ -41,23 +38,6 @@ export function _get_class_name(classPtr: MonoClass): string { return cwraps.mono_wasm_get_type_name(cwraps.mono_wasm_class_get_type(classPtr)); } -export function find_method(klass: MonoClass, name: string, n: number): MonoMethod { - return cwraps.mono_wasm_assembly_find_method(klass, name, n); -} - -export function get_method(method_name: string): MonoMethod { - const res = find_method(runtimeHelpers.runtime_interop_exports_class, method_name, -1); - if (!res) - throw "Can't find method " + runtimeHelpers.runtime_interop_namespace + "." + runtimeHelpers.runtime_interop_exports_classname + ":" + method_name; - return res; -} - -export function bind_runtime_method(method_name: string, signature: string): Function { - const method = get_method(method_name); - return mono_bind_method(method, null, signature, "BINDINGS_" + method_name); -} - - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types export function _create_named_function(name: string, argumentNames: string[], body: string, closure: any): Function { let result = null; @@ -399,10 +379,20 @@ export function _decide_if_result_is_marshaled(converter: Converter, argc: numbe } } -export function mono_bind_method(method: MonoMethod, this_arg: null, args_marshal: string/*ArgsMarshalString*/, friendly_name: string): Function { + +export function mono_bind_method(method: MonoMethod, args_marshal: string/*ArgsMarshalString*/, has_this_arg: boolean, friendly_name?: string): Function { if (typeof (args_marshal) !== "string") throw new Error("args_marshal argument invalid, expected string"); + const key = `managed_${method}_${args_marshal}`; + let result = boundMethodsByMethod.get(key); + if (result) { + return result; + } + if (!friendly_name) { + friendly_name = key; + } + let converter: Converter | null = null; if (typeof (args_marshal) === "string") { converter = _compile_converter_for_marshal_string(args_marshal); @@ -419,17 +409,19 @@ export function mono_bind_method(method: MonoMethod, this_arg: null, args_marsha scratchRootBuffer: null, scratchBuffer: VoidPtrNull, scratchResultRoot: mono_wasm_new_root(), - scratchExceptionRoot: mono_wasm_new_root() + scratchExceptionRoot: mono_wasm_new_root(), + scratchThisArgRoot: mono_wasm_new_root() }; const closure: any = { Module, mono_wasm_new_root, + get_js_owned_object_by_gc_handle_ref, _create_temp_frame, _handle_exception_for_call, _teardown_after_call, - mono_wasm_try_unbox_primitive_and_get_type_ref: wrap_c_function("mono_wasm_try_unbox_primitive_and_get_type_ref"), + mono_wasm_try_unbox_primitive_and_get_type_ref: cwraps.mono_wasm_try_unbox_primitive_and_get_type_ref, _unbox_mono_obj_root_with_known_nonprimitive_type, - invoke_method_ref: wrap_c_function("mono_wasm_invoke_method_ref"), + invoke_method_ref: cwraps.mono_wasm_invoke_method_ref, method, token, unbox_buffer, @@ -449,13 +441,16 @@ export function mono_bind_method(method: MonoMethod, this_arg: null, args_marsha const argumentNames = []; const body = [ "_create_temp_frame();", - "let resultRoot = token.scratchResultRoot, exceptionRoot = token.scratchExceptionRoot, sp = stackSave();", + "let resultRoot = token.scratchResultRoot, exceptionRoot = token.scratchExceptionRoot, thisArgRoot = token.scratchThisArgRoot , sp = stackSave();", "token.scratchResultRoot = null;", "token.scratchExceptionRoot = null;", + "token.scratchThisArgRoot = null;", "if (resultRoot === null)", " resultRoot = mono_wasm_new_root ();", "if (exceptionRoot === null)", " exceptionRoot = mono_wasm_new_root ();", + "if (thisArgRoot === null)", + " thisArgRoot = mono_wasm_new_root ();", "" ]; @@ -503,8 +498,18 @@ export function mono_bind_method(method: MonoMethod, this_arg: null, args_marsha // The end result is that bound method invocations don't always allocate, so no more nursery GCs. Yay! -kg body.push( "", - "invoke_method_ref (method, 0, buffer, exceptionRoot.address, resultRoot.address);", - `_handle_exception_for_call (${converterKey}, token, buffer, resultRoot, exceptionRoot, sp);`, + "", + "", + ); + if (has_this_arg) { + body.push("get_js_owned_object_by_gc_handle_ref(this.this_arg_gc_handle, thisArgRoot.address);"); + body.push("invoke_method_ref (method, thisArgRoot.address, buffer, exceptionRoot.address, resultRoot.address);"); + } else { + body.push("invoke_method_ref (method, 0, buffer, exceptionRoot.address, resultRoot.address);"); + } + + body.push( + `_handle_exception_for_call (${converterKey}, token, buffer, resultRoot, exceptionRoot, thisArgRoot, sp);`, "", "let resultPtr = resultRoot.value, result = undefined;" ); @@ -549,24 +554,20 @@ export function mono_bind_method(method: MonoMethod, this_arg: null, args_marsha throw new Error("No converter"); } - if (friendly_name) { - const escapeRE = /[^A-Za-z0-9_$]/g; - friendly_name = friendly_name.replace(escapeRE, "_"); - } - - let displayName = friendly_name || ("clr_" + method); + let displayName = friendly_name.replace(escapeRE, "_"); - if (this_arg) - displayName += "_this" + this_arg; + if (has_this_arg) + displayName += "_this"; body.push( - `_teardown_after_call (${converterKey}, token, buffer, resultRoot, exceptionRoot, sp);`, + `_teardown_after_call (${converterKey}, token, buffer, resultRoot, exceptionRoot, thisArgRoot, sp);`, "return result;" ); const bodyJs = body.join("\r\n"); - const result = _create_named_function(displayName, argumentNames, bodyJs, closure); + result = _create_named_function(displayName, argumentNames, bodyJs, closure); + boundMethodsByMethod.set(key, result); return result; } @@ -639,4 +640,51 @@ export type BoundMethodToken = { scratchBuffer: VoidPtr; scratchResultRoot: WasmRoot; scratchExceptionRoot: WasmRoot; -} \ No newline at end of file + scratchThisArgRoot: WasmRoot; +} + +function _handle_exception_for_call( + converter: Converter | undefined, token: BoundMethodToken | null, + buffer: VoidPtr, resultRoot: WasmRoot, + exceptionRoot: WasmRoot, + thisArgRoot: WasmRoot, + sp: VoidPtr +): void { + const exc = _convert_exception_for_method_call(resultRoot, exceptionRoot); + if (!exc) + return; + + _teardown_after_call(converter, token, buffer, resultRoot, exceptionRoot, thisArgRoot, sp); + throw exc; +} + +function _convert_exception_for_method_call(result: WasmRoot, exception: WasmRoot) { + if (exception.value === MonoObjectNull) + return null; + + const msg = conv_string_root(result); + const err = new Error(msg!); //the convention is that invoke_method ToString () any outgoing exception + // console.warn (`error ${msg} at location ${err.stack}); + return err; +} + +export function mono_method_resolve(fqn: string): MonoMethod { + const { assembly, namespace, classname, methodname } = parseFQN(fqn); + + const asm = cwraps.mono_wasm_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 method = cwraps.mono_wasm_assembly_find_method(klass, methodname, -1); + if (!method) + throw new Error("Could not find method: " + methodname); + return method; +} + +export function mono_method_get_call_signature_ref(method: MonoMethod, mono_obj?: WasmRoot): string/*ArgsMarshalString*/ { + return legacyManagedExports._get_call_sig_ref(method, mono_obj ? mono_obj.address : runtimeHelpers._null_root.address); +} diff --git a/src/mono/wasm/runtime/method-calls.ts b/src/mono/wasm/runtime/legacy/method-calls.ts similarity index 51% rename from src/mono/wasm/runtime/method-calls.ts rename to src/mono/wasm/runtime/legacy/method-calls.ts index 2b33a3477bb98..7d3fa206804d9 100644 --- a/src/mono/wasm/runtime/method-calls.ts +++ b/src/mono/wasm/runtime/legacy/method-calls.ts @@ -1,148 +1,27 @@ -// 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, WasmRoot, mono_wasm_new_external_root } from "./roots"; -import { - JSHandle, MonoArray, MonoMethod, MonoObject, - MonoObjectNull, MonoString, coerceNull as coerceNull, - VoidPtrNull, MonoObjectRef, - MonoStringRef, is_nullish -} from "./types"; -import { INTERNAL, Module, runtimeHelpers } from "./imports"; +import { assembly_load } from "../class-loader"; +import cwraps from "../cwraps"; +import { get_js_obj, mono_wasm_get_jsobj_from_js_handle } from "../gc-handles"; +import { Module, runtimeHelpers, INTERNAL } from "../imports"; +import { wrap_error_root } from "../invoke-js"; +import { _release_temp_frame } from "../memory"; +import { WasmRoot, mono_wasm_new_external_root, mono_wasm_new_root } from "../roots"; +import { bindings_lazy_init } from "../startup"; +import { conv_string_root, js_string_to_mono_string_root } from "../strings"; +import { JSHandle, MonoStringRef, MonoObjectRef, MonoArray, MonoString, MonoObject, is_nullish } from "../types"; +import { Int32Ptr, VoidPtr } from "../types/emscripten"; import { mono_array_root_to_js_array, unbox_mono_obj_root } from "./cs-to-js"; -import { get_js_obj, mono_wasm_get_jsobj_from_js_handle } from "./gc-handles"; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore used by unsafe export import { js_array_to_mono_array, js_to_mono_obj_root } from "./js-to-cs"; -import { - mono_bind_method, - Converter, _compile_converter_for_marshal_string, - _decide_if_result_is_marshaled, find_method, - BoundMethodToken -} from "./method-binding"; -import { conv_string_root, js_string_to_mono_string, js_string_to_mono_string_root } from "./strings"; -import cwraps from "./cwraps"; -import { bindings_lazy_init } from "./startup"; -import { _create_temp_frame, _release_temp_frame } from "./memory"; -import { VoidPtr, Int32Ptr } from "./types/emscripten"; -import { assembly_load } from "./class-loader"; - -function _verify_args_for_method_call(args_marshal: string/*ArgsMarshalString*/, args: any) { - const has_args = args && (typeof args === "object") && args.length > 0; - const has_args_marshal = typeof args_marshal === "string"; - - if (has_args) { - if (!has_args_marshal) - throw new Error("No signature provided for method call."); - else if (args.length > args_marshal.length) - throw new Error("Too many parameter values. Expected at most " + args_marshal.length + " value(s) for signature " + args_marshal); - } - - return has_args_marshal && has_args; -} - -function _convert_exception_for_method_call(result: WasmRoot, exception: WasmRoot) { - if (exception.value === MonoObjectNull) - return null; - - const msg = conv_string_root(result); - const err = new Error(msg!); //the convention is that invoke_method ToString () any outgoing exception - // console.warn (`error ${msg} at location ${err.stack}); - return err; -} - -/* -args_marshal is a string with one character per parameter that tells how to marshal it, here are the valid values: - -i: int32 -j: int32 - Enum with underlying type of int32 -l: int64 -k: int64 - Enum with underlying type of int64 -f: float -d: double -s: string -S: interned string -o: js object will be converted to a C# object (this will box numbers/bool/promises) -m: raw mono object. Don't use it unless you know what you're doing - -to suppress marshaling of the return value, place '!' at the end of args_marshal, i.e. 'ii!' instead of 'ii' -*/ -export function call_method_ref(method: MonoMethod, this_arg: WasmRoot | MonoObjectRef | undefined, args_marshal: string/*ArgsMarshalString*/, args: ArrayLike): any { - // HACK: Sometimes callers pass null or undefined, coerce it to 0 since that's what wasm expects - let this_arg_ref: MonoObjectRef | undefined = undefined; - if (typeof (this_arg) === "number") - this_arg_ref = this_arg; - else if (typeof (this_arg) === "object") - this_arg_ref = (this_arg).address; - else - this_arg_ref = coerceNull(this_arg); - - // Detect someone accidentally passing the wrong type of value to method - if (typeof method !== "number") - throw new Error(`method must be an address in the native heap, but was '${method}'`); - if (!method) - throw new Error("no method specified"); - if (typeof (this_arg_ref) !== "number") - throw new Error(`this_arg must be a root instance, the address of a root, or undefined, but was ${this_arg}`); - - const needs_converter = _verify_args_for_method_call(args_marshal, args); - - let buffer = VoidPtrNull, converter = undefined; - const sp = Module.stackSave(); - let is_result_marshaled = true; - - // TODO: Only do this if the signature needs marshaling - _create_temp_frame(); - - // check if the method signature needs argument mashalling - if (needs_converter) { - converter = _compile_converter_for_marshal_string(args_marshal); - - is_result_marshaled = _decide_if_result_is_marshaled(converter, args.length); - - buffer = converter.compiled_variadic_function!(method, args); - } - - return _call_method_with_converted_args(method, this_arg_ref, converter, null, buffer, is_result_marshaled, sp); -} - +import { Converter, BoundMethodToken, mono_method_resolve, mono_method_get_call_signature_ref, mono_bind_method } from "./method-binding"; -export function _handle_exception_for_call( - converter: Converter | undefined, token: BoundMethodToken | null, - buffer: VoidPtr, resultRoot: WasmRoot, - exceptionRoot: WasmRoot, sp: VoidPtr -): void { - const exc = _convert_exception_for_method_call(resultRoot, exceptionRoot); - if (!exc) - return; - - _teardown_after_call(converter, token, buffer, resultRoot, exceptionRoot, sp); - throw exc; -} - -function _handle_exception_and_produce_result_for_call( - converter: Converter | undefined, token: BoundMethodToken | null, - buffer: VoidPtr, resultRoot: WasmRoot, - exceptionRoot: WasmRoot, sp: VoidPtr, - is_result_marshaled: boolean -): any { - _handle_exception_for_call(converter, token, buffer, resultRoot, exceptionRoot, sp); - - let result: any; - - if (is_result_marshaled) - result = unbox_mono_obj_root(resultRoot); - else - result = resultRoot.value; - - _teardown_after_call(converter, token, buffer, resultRoot, exceptionRoot, sp); - return result; -} +const boundMethodsByFqn: Map = new Map(); export function _teardown_after_call( converter: Converter | undefined, token: BoundMethodToken | null, - buffer: VoidPtr, resultRoot: WasmRoot, - exceptionRoot: WasmRoot, sp: VoidPtr + buffer: VoidPtr, + resultRoot: WasmRoot, + exceptionRoot: WasmRoot, + thisArgRoot: WasmRoot, + sp: VoidPtr ): void { _release_temp_frame(); Module.stackRestore(sp); @@ -161,38 +40,29 @@ export function _teardown_after_call( else exceptionRoot.release(); } -} - -function _call_method_with_converted_args( - method: MonoMethod, this_arg_ref: MonoObjectRef, converter: Converter | undefined, - token: BoundMethodToken | null, buffer: VoidPtr, - is_result_marshaled: boolean, sp: VoidPtr -): any { - const resultRoot = mono_wasm_new_root(), exceptionRoot = mono_wasm_new_root(); - cwraps.mono_wasm_invoke_method_ref(method, this_arg_ref, buffer, exceptionRoot.address, resultRoot.address); - return _handle_exception_and_produce_result_for_call(converter, token, buffer, resultRoot, exceptionRoot, sp, is_result_marshaled); -} - -export function call_static_method(fqn: string, args: any[], signature: string/*ArgsMarshalString*/): any { - bindings_lazy_init();// TODO remove this once Blazor does better startup - - const method = mono_method_resolve(fqn); - - if (typeof signature === "undefined") - signature = mono_method_get_call_signature_ref(method, undefined); - - return call_method_ref(method, undefined, signature, args); + if (typeof (thisArgRoot) === "object") { + thisArgRoot.clear(); + if ((token !== null) && (token.scratchThisArgRoot === null)) + token.scratchThisArgRoot = thisArgRoot; + else + thisArgRoot.release(); + } } export function mono_bind_static_method(fqn: string, signature?: string/*ArgsMarshalString*/): Function { bindings_lazy_init();// TODO remove this once Blazor does better startup + const key = `${fqn}-${signature}`; + let js_method = boundMethodsByFqn.get(key); + if (js_method === undefined) { + const method = mono_method_resolve(fqn); - const method = mono_method_resolve(fqn); - - if (typeof signature === "undefined") - signature = mono_method_get_call_signature_ref(method, undefined); + if (typeof signature === "undefined") + signature = mono_method_get_call_signature_ref(method, undefined); - return mono_bind_method(method, null, signature!, fqn); + js_method = mono_bind_method(method, signature!, false, fqn); + boundMethodsByFqn.set(key, js_method); + } + return js_method; } export function mono_bind_assembly_entry_point(assembly: string, signature?: string/*ArgsMarshalString*/): Function { @@ -213,10 +83,12 @@ export function mono_bind_assembly_entry_point(assembly: string, signature?: str if (typeof (signature) !== "string") signature = mono_method_get_call_signature_ref(method, undefined); + const js_method = mono_bind_method(method, signature!, false, assembly + "__entrypoint"); + return async function (...args: any[]) { if (args.length > 0 && Array.isArray(args[0])) args[0] = js_array_to_mono_array(args[0], true, false); - return call_method_ref(method, undefined, signature!, args); + return js_method(...args); }; } @@ -419,93 +291,6 @@ export function mono_wasm_get_global_object_ref(global_name: MonoStringRef, is_e } } -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types -function _wrap_error_flag(is_exception: Int32Ptr | null, ex: any): string { - let res = "unknown exception"; - if (ex) { - res = ex.toString(); - const stack = ex.stack; - if (stack) { - // 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 - if (stack.startsWith(res)) - res = stack; - else - res += "\n" + stack; - } - - res = INTERNAL.mono_wasm_symbolicate_string(res); - } - if (is_exception) { - Module.setValue(is_exception, 1, "i32"); - } - return res; -} - -/** - * @deprecated Not GC or thread safe - */ -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types -export function wrap_error(is_exception: Int32Ptr | null, ex: any): MonoString { - const res = _wrap_error_flag(is_exception, ex); - return js_string_to_mono_string(res)!; -} - -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types -export function wrap_error_root(is_exception: Int32Ptr | null, ex: any, result: WasmRoot): void { - const res = _wrap_error_flag(is_exception, ex); - js_string_to_mono_string_root(res, result); -} - -export function mono_method_get_call_signature_ref(method: MonoMethod, mono_obj?: WasmRoot): string/*ArgsMarshalString*/ { - return call_method_ref( - runtimeHelpers.get_call_sig_ref, undefined, "im", - [method, mono_obj ? mono_obj.address : runtimeHelpers._null_root.address] - ); -} - -export function parseFQN(fqn: string) - : { assembly: string, namespace: string, classname: string, methodname: string } { - const assembly = fqn.substring(fqn.indexOf("[") + 1, fqn.indexOf("]")).trim(); - fqn = fqn.substring(fqn.indexOf("]") + 1).trim(); - - const methodname = fqn.substring(fqn.indexOf(":") + 1); - fqn = fqn.substring(0, fqn.indexOf(":")).trim(); - - let namespace = ""; - let classname = fqn; - if (fqn.indexOf(".") != -1) { - const idx = fqn.lastIndexOf("."); - namespace = fqn.substring(0, idx); - classname = fqn.substring(idx + 1); - } - - if (!assembly.trim()) - throw new Error("No assembly name specified " + fqn); - if (!classname.trim()) - throw new Error("No class name specified " + fqn); - if (!methodname.trim()) - throw new Error("No method name specified " + fqn); - return { assembly, namespace, classname, methodname }; -} - -export function mono_method_resolve(fqn: string): MonoMethod { - const { assembly, namespace, classname, methodname } = parseFQN(fqn); - - const asm = cwraps.mono_wasm_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 method = find_method(klass, methodname, -1); - if (!method) - throw new Error("Could not find method: " + methodname); - return method; -} - // Blazor specific custom routine // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types export function mono_wasm_invoke_js_blazor(exceptionMessage: Int32Ptr, callInfo: any, arg0: any, arg1: any, arg2: any): void | number { diff --git a/src/mono/wasm/runtime/managed-exports.ts b/src/mono/wasm/runtime/managed-exports.ts new file mode 100644 index 0000000000000..30305f31244b3 --- /dev/null +++ b/src/mono/wasm/runtime/managed-exports.ts @@ -0,0 +1,83 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import { GCHandle, MonoMethod, mono_assert } from "./types"; +import cwraps from "./cwraps"; +import { Module, runtimeHelpers } from "./imports"; +import { alloc_stack_frame, get_arg, get_arg_gc_handle, JSMarshalerArguments, set_gc_handle } from "./marshal"; +import { invoke_method_and_handle_exception } from "./invoke-cs"; + +// in all the exported internals methods, we use the same data structures for stack frame as normal full blow interop +export interface JavaScriptExports { + // see src\libraries\System.Runtime.InteropServices.JavaScript\src\System\Runtime\InteropServices\JavaScript\Interop\JavaScriptExports.cs + _release_js_owned_object_by_gc_handle(gc_handle: GCHandle): void;//ReleaseJSOwnedObjectByGCHandle + _create_task_callback(): GCHandle;// CreateTaskCallback + _complete_task(args: JSMarshalerArguments): void;// CompleteTask + _call_delegate(args: JSMarshalerArguments): void;// CallDelegate +} + +export const javaScriptExports: JavaScriptExports = {}; + +export function init_managed_exports(): void { + const anyModule = Module as any; + const exports_fqn_asm = "System.Runtime.InteropServices.JavaScript"; + runtimeHelpers.runtime_interop_module = cwraps.mono_wasm_assembly_load(exports_fqn_asm); + if (!runtimeHelpers.runtime_interop_module) + throw "Can't find bindings module assembly: " + exports_fqn_asm; + + runtimeHelpers.runtime_interop_namespace = "System.Runtime.InteropServices.JavaScript"; + runtimeHelpers.runtime_interop_exports_classname = "JavaScriptExports"; + runtimeHelpers.runtime_interop_exports_class = cwraps.mono_wasm_assembly_find_class(runtimeHelpers.runtime_interop_module, runtimeHelpers.runtime_interop_namespace, runtimeHelpers.runtime_interop_exports_classname); + if (!runtimeHelpers.runtime_interop_exports_class) + throw "Can't find " + runtimeHelpers.runtime_interop_namespace + "." + runtimeHelpers.runtime_interop_exports_classname + " class"; + + + const release_js_owned_object_by_gc_handle_method = get_method("ReleaseJSOwnedObjectByGCHandle"); + mono_assert(release_js_owned_object_by_gc_handle_method, "Can't find ReleaseJSOwnedObjectByGCHandle method"); + const create_task_callback_method = get_method("CreateTaskCallback"); + mono_assert(create_task_callback_method, "Can't find CreateTaskCallback method"); + const complete_task_method = get_method("CompleteTask"); + mono_assert(complete_task_method, "Can't find CompleteTask method"); + const call_delegate_method = get_method("CallDelegate"); + mono_assert(call_delegate_method, "Can't find CallDelegate method"); + + javaScriptExports._release_js_owned_object_by_gc_handle = (gc_handle: GCHandle) => { + if (!gc_handle) { + Module.printErr("Must be valid gc_handle"); + } + mono_assert(gc_handle, "Must be valid gc_handle"); + const sp = anyModule.stackSave(); + try { + const args = alloc_stack_frame(3); + const arg1 = get_arg(args, 2); + set_gc_handle(arg1, gc_handle); + invoke_method_and_handle_exception(release_js_owned_object_by_gc_handle_method, args); + } finally { + anyModule.stackRestore(sp); + } + }; + javaScriptExports._create_task_callback = () => { + const sp = anyModule.stackSave(); + try { + const args = alloc_stack_frame(3); + invoke_method_and_handle_exception(create_task_callback_method, args); + const res = get_arg(args, 1); + return get_arg_gc_handle(res); + } finally { + anyModule.stackRestore(sp); + } + }; + javaScriptExports._complete_task = (args: JSMarshalerArguments) => { + invoke_method_and_handle_exception(complete_task_method, args); + }; + javaScriptExports._call_delegate = (args: JSMarshalerArguments) => { + invoke_method_and_handle_exception(call_delegate_method, args); + }; +} + +export function get_method(method_name: string): MonoMethod { + const res = cwraps.mono_wasm_assembly_find_method(runtimeHelpers.runtime_interop_exports_class, method_name, -1); + if (!res) + throw "Can't find method " + runtimeHelpers.runtime_interop_namespace + "." + runtimeHelpers.runtime_interop_exports_classname + "." + method_name; + return res; +} \ No newline at end of file diff --git a/src/mono/wasm/runtime/marshal-to-cs.ts b/src/mono/wasm/runtime/marshal-to-cs.ts index fff46ba60437d..4d0319a6ba625 100644 --- a/src/mono/wasm/runtime/marshal-to-cs.ts +++ b/src/mono/wasm/runtime/marshal-to-cs.ts @@ -2,19 +2,18 @@ // The .NET Foundation licenses this file to you under the MIT license. import { isThenable } from "./cancelable-promise"; -import wrapped_cs_functions from "./corebindings"; +import { javaScriptExports } from "./managed-exports"; 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 { Module } 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 + set_arg_length, get_arg, 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, alloc_stack_frame } 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 { js_string_to_mono_string_root } from "./strings"; import { mono_assert, GCHandle, GCHandleNull } from "./types"; import { TypedArray } from "./types/emscripten"; @@ -363,7 +362,7 @@ function _marshal_task_to_cs(arg: JSMarshalerArgument, value: Promise, _?: mono_assert(isThenable(value), "Value is not a Promise"); const anyModule = Module as any; - const gc_handle: GCHandle = wrapped_cs_functions._create_task_callback(); + const gc_handle: GCHandle = javaScriptExports._create_task_callback(); set_gc_handle(arg, gc_handle); set_arg_type(arg, MarshalerType.Task); const holder = new TaskCallbackHolder(value); @@ -372,11 +371,8 @@ function _marshal_task_to_cs(arg: JSMarshalerArgument, value: Promise, _?: 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 args = alloc_stack_frame(3); const res = get_arg(args, 1); - set_arg_type(res, MarshalerType.None); set_gc_handle(res, gc_handle); const arg1 = get_arg(args, 2); if (!res_converter) { @@ -384,9 +380,7 @@ function _marshal_task_to_cs(arg: JSMarshalerArgument, value: Promise, _?: } 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); + javaScriptExports._complete_task(args); } finally { anyModule.stackRestore(sp); } @@ -394,18 +388,15 @@ function _marshal_task_to_cs(arg: JSMarshalerArgument, value: Promise, _?: }).catch(reason => { const sp = anyModule.stackSave(); try { - const args = anyModule.stackAlloc(JavaScriptMarshalerArgSize * 3); + const args = alloc_stack_frame(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); + javaScriptExports._complete_task(args); } finally { anyModule.stackRestore(sp); } diff --git a/src/mono/wasm/runtime/marshal-to-js.ts b/src/mono/wasm/runtime/marshal-to-js.ts index 47d240f1f1353..a06285d232437 100644 --- a/src/mono/wasm/runtime/marshal-to-js.ts +++ b/src/mono/wasm/runtime/marshal-to-js.ts @@ -4,15 +4,16 @@ 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 { Module } from "./imports"; +import { javaScriptExports } from "./managed-exports"; 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 + get_signature_res_type, set_gc_handle, 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, alloc_stack_frame } from "./marshal"; -import { conv_string, conv_string_root } from "./strings"; +import { conv_string_root } from "./strings"; import { mono_assert, JSHandleNull, GCHandleNull } from "./types"; import { TypedArray } from "./types/emscripten"; @@ -248,11 +249,8 @@ function _marshal_delegate_to_js(arg: JSMarshalerArgument, _?: JSMarshalerType, const sp = anyModule.stackSave(); try { - const args = anyModule.stackAlloc(JavaScriptMarshalerArgSize * 5); - const exc = get_arg(args, 0); - set_arg_type(exc, MarshalerType.None); + const args = alloc_stack_frame(5); const res = get_arg(args, 1); - set_arg_type(res, MarshalerType.None); set_gc_handle(res, gc_handle); const arg1 = get_arg(args, 2); const arg2 = get_arg(args, 3); @@ -268,9 +266,7 @@ function _marshal_delegate_to_js(arg: JSMarshalerArgument, _?: JSMarshalerType, 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); + javaScriptExports._call_delegate(args); if (res_converter) { return res_converter(res); diff --git a/src/mono/wasm/runtime/marshal.ts b/src/mono/wasm/runtime/marshal.ts index 7df0c232693be..f62415ec934d2 100644 --- a/src/mono/wasm/runtime/marshal.ts +++ b/src/mono/wasm/runtime/marshal.ts @@ -55,6 +55,17 @@ export interface JSMarshalerArgument extends NativePointer { __brand: "JSMarshalerArgument" } +export function alloc_stack_frame(size: number): JSMarshalerArguments { + const anyModule = Module as any; + const args = anyModule.stackAlloc(JavaScriptMarshalerArgSize * size); + mono_assert(args && (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); + return args; +} + export function get_arg(args: JSMarshalerArguments, index: number): JSMarshalerArgument { mono_assert(args, "Null args"); return args + (index * JavaScriptMarshalerArgSize); diff --git a/src/mono/wasm/runtime/memory.ts b/src/mono/wasm/runtime/memory.ts index 2f3a729358166..e2462e2207654 100644 --- a/src/mono/wasm/runtime/memory.ts +++ b/src/mono/wasm/runtime/memory.ts @@ -255,3 +255,11 @@ export function withStackAlloc(bytesWanted: number, f: (ptr } } +// @bytes must be a typed array. space is allocated for it in the native heap +// and it is copied to that location. returns the address of the allocation. +export function mono_wasm_load_bytes_into_heap(bytes: Uint8Array): VoidPtr { + const memoryOffset = Module._malloc(bytes.length); + const heapBytes = new Uint8Array(Module.HEAPU8.buffer, memoryOffset, bytes.length); + heapBytes.set(bytes); + return memoryOffset; +} \ No newline at end of file diff --git a/src/mono/wasm/runtime/run.ts b/src/mono/wasm/runtime/run.ts index ae7174c3f639d..409e5d7d443d2 100644 --- a/src/mono/wasm/runtime/run.ts +++ b/src/mono/wasm/runtime/run.ts @@ -1,7 +1,7 @@ import { ExitStatus, INTERNAL, Module, quit, runtimeHelpers } from "./imports"; -import { mono_call_assembly_entry_point } from "./method-calls"; import { mono_wasm_wait_for_debugger } from "./debug"; import { mono_wasm_set_main_args, runtime_is_initialized_reject } from "./startup"; +import { mono_call_assembly_entry_point } from "./legacy/method-calls"; export async function mono_run_main_and_exit(main_assembly_name: string, args: string[]): Promise { try { diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts index 3b3ec8f14869f..9c471b59a9050 100644 --- a/src/mono/wasm/runtime/startup.ts +++ b/src/mono/wasm/runtime/startup.ts @@ -2,19 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. import MonoWasmThreads from "consts:monoWasmThreads"; -import { AllAssetEntryTypes, mono_assert, AssetEntry, CharPtrNull, DotnetModule, GlobalizationMode, MonoConfig, MonoConfigError, wasm_type_symbol, MonoObject } from "./types"; -import { ENVIRONMENT_IS_ESM, ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_PTHREAD, ENVIRONMENT_IS_SHELL, INTERNAL, locateFile, Module, MONO, requirePromise, runtimeHelpers } from "./imports"; -import cwraps from "./cwraps"; +import { AllAssetEntryTypes, mono_assert, AssetEntry, CharPtrNull, DotnetModule, GlobalizationMode, MonoConfig, MonoConfigError, wasm_type_symbol, MonoObject, DotnetPublicAPI } from "./types"; +import { BINDING, ENVIRONMENT_IS_ESM, ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_PTHREAD, ENVIRONMENT_IS_SHELL, INTERNAL, locateFile, Module, MONO, requirePromise, runtimeHelpers } from "./imports"; +import cwraps, { init_c_exports } from "./cwraps"; import { mono_wasm_raise_debug_event, mono_wasm_runtime_ready } from "./debug"; import GuardedPromise from "./guarded-promise"; import { mono_wasm_globalization_init, mono_wasm_load_icu_data } from "./icu"; import { toBase64StringImpl } from "./base64"; import { mono_wasm_init_aot_profiler, mono_wasm_init_coverage_profiler } from "./profiler"; -import { mono_wasm_load_bytes_into_heap } from "./buffers"; -import { bind_runtime_method, get_method, _create_primitive_converters } from "./method-binding"; 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"; @@ -22,6 +19,11 @@ import { mono_wasm_new_root } from "./roots"; import { init_crypto } from "./crypto-worker"; import { init_polyfills } from "./polyfills"; import * as pthreads_worker from "./pthreads/worker"; +import { init_managed_exports as init_managed_exports } from "./managed-exports"; +import { mono_wasm_load_bytes_into_heap } from "./memory"; +import { init_legacy_exports } from "./legacy/corebindings"; +import { cwraps_binding_api, cwraps_mono_api } from "./legacy/exports-legacy"; +import { cwraps_internal } from "./exports-internal"; export let runtime_is_initialized_resolve: () => void; export let runtime_is_initialized_reject: (reason?: any) => void; @@ -132,6 +134,10 @@ async function mono_wasm_pre_init(): Promise { init_polyfills(); init_crypto(); + init_c_exports(); + cwraps_internal(INTERNAL); + cwraps_mono_api(MONO); + cwraps_binding_api(BINDING); if (moduleExt.configSrc) { try { @@ -357,8 +363,7 @@ function finalize_startup(config: MonoConfig | MonoConfigError | undefined): voi runtime_is_initialized_reject(err); if (ENVIRONMENT_IS_SHELL || ENVIRONMENT_IS_NODE) { - const wasm_exit = cwraps.mono_wasm_exit; - wasm_exit(1); + cwraps.mono_wasm_exit(1); } } @@ -427,49 +432,12 @@ export function bindings_lazy_init(): void { runtimeHelpers._class_uint32 = find_corlib_class("System", "UInt32"); runtimeHelpers._class_double = find_corlib_class("System", "Double"); runtimeHelpers._class_boolean = find_corlib_class("System", "Boolean"); - runtimeHelpers.bind_runtime_method = bind_runtime_method; - - const bindingAssembly = INTERNAL.BINDING_ASM; - const binding_fqn_asm = bindingAssembly.substring(bindingAssembly.indexOf("[") + 1, bindingAssembly.indexOf("]")).trim(); - const binding_fqn_class = bindingAssembly.substring(bindingAssembly.indexOf("]") + 1).trim(); - - const binding_module = cwraps.mono_wasm_assembly_load(binding_fqn_asm); - if (!binding_module) - throw "Can't find bindings module assembly: " + binding_fqn_asm; - - if (binding_fqn_class && binding_fqn_class.length) { - runtimeHelpers.runtime_interop_exports_classname = binding_fqn_class; - if (binding_fqn_class.indexOf(".") != -1) { - const idx = binding_fqn_class.lastIndexOf("."); - runtimeHelpers.runtime_interop_namespace = binding_fqn_class.substring(0, idx); - runtimeHelpers.runtime_interop_exports_classname = binding_fqn_class.substring(idx + 1); - } - } - - runtimeHelpers.runtime_interop_exports_class = cwraps.mono_wasm_assembly_find_class(binding_module, runtimeHelpers.runtime_interop_namespace, runtimeHelpers.runtime_interop_exports_classname); - if (!runtimeHelpers.runtime_interop_exports_class) - throw "Can't find " + binding_fqn_class + " class"; - - runtimeHelpers.get_call_sig_ref = get_method("GetCallSignatureRef"); - 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"; + init_managed_exports(); + init_legacy_exports(); initialize_marshalers_to_js(); initialize_marshalers_to_cs(); - _create_primitive_converters(); runtimeHelpers._box_root = mono_wasm_new_root(); runtimeHelpers._null_root = mono_wasm_new_root(); @@ -632,7 +600,7 @@ function finalize_assets(config: MonoConfig | MonoConfigError | undefined): void } } - ctx.loaded_files.forEach(value => MONO.loaded_files.push(value.url)); + ctx.loaded_files.forEach(value => { if (value.url) MONO.loaded_files.push(value.url); }); if (ctx.tracing) { console.trace("MONO_WASM: loaded_assets: " + JSON.stringify(ctx.loaded_assets)); console.trace("MONO_WASM: loaded_files: " + JSON.stringify(ctx.loaded_files)); diff --git a/src/mono/wasm/runtime/types.ts b/src/mono/wasm/runtime/types.ts index 8e3970722aa92..634549ba428f9 100644 --- a/src/mono/wasm/runtime/types.ts +++ b/src/mono/wasm/runtime/types.ts @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. import "node/buffer"; // we use the Buffer type to type some of Emscripten's APIs -import { bind_runtime_method } from "./method-binding"; +import { BINDINGType, MONOType } from "./legacy/exports-legacy"; import { CharPtr, EmscriptenModule, ManagedPointer, NativePointer, VoidPtr, Int32Ptr } from "./types/emscripten"; export type GCHandle = { @@ -131,14 +131,12 @@ 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_module: MonoAssembly; runtime_interop_namespace: string; runtime_interop_exports_classname: string; runtime_interop_exports_class: MonoClass; - bind_runtime_method: typeof bind_runtime_method; + runtime_legacy_exports_classname: string; + runtime_legacy_exports_class: MonoClass; _box_buffer_size: number; _unbox_buffer_size: number; @@ -286,3 +284,25 @@ export const enum MarshalError { export function is_nullish(value: T | null | undefined): value is null | undefined { return (value === undefined) || (value === null); } + +// this represents visibility in the javascript +// like https://github.com/dotnet/aspnetcore/blob/main/src/Components/Web.JS/src/Platform/Mono/MonoTypes.ts +export interface DotnetPublicAPI { + MONO: MONOType, + BINDING: BINDINGType, + INTERNAL: any, + EXPORTS: any, + IMPORTS: any, + Module: EmscriptenModule, + RuntimeId: number, + RuntimeBuildInfo: { + ProductVersion: string, + Configuration: string, + } +} + +// We need to replace some of the methods in the Emscripten PThreads support with our own +export type PThreadReplacements = { + loadWasmModuleToWorker: Function, + threadInitTLS: Function +} diff --git a/src/mono/wasm/test-main.js b/src/mono/wasm/test-main.js index a39f3e5ae5dca..65b8a6e43e8fd 100644 --- a/src/mono/wasm/test-main.js +++ b/src/mono/wasm/test-main.js @@ -514,7 +514,8 @@ const App = { const fqn = "[System.Private.Runtime.InteropServices.JavaScript.Tests]System.Runtime.InteropServices.JavaScript.Tests.HelperMarshal:" + method_name; try { - return App.INTERNAL.call_static_method(fqn, args || [], signature); + const method = App.BINDING.bind_static_method(fqn, signature); + return method.apply(null, args || []); } catch (exc) { console.error("exception thrown in", fqn); throw exc; diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj index 62ef5f58b45ab..2e068bd949f78 100644 --- a/src/mono/wasm/wasm.proj +++ b/src/mono/wasm/wasm.proj @@ -355,6 +355,7 @@ <_RollupInputs Include="$(MonoProjectRoot)wasm/runtime/workers/**/*.js"/> <_RollupInputs Include="$(MonoProjectRoot)wasm/runtime/workers/**/*.ts"/> <_RollupInputs Include="$(MonoProjectRoot)wasm/runtime/types/*.ts"/> + <_RollupInputs Include="$(MonoProjectRoot)wasm/runtime/legacy/*.ts"/> <_RollupInputs Include="$(MonoProjectRoot)wasm/runtimetypes/*.d.ts"/> <_RollupInputs Include="$(MonoProjectRoot)wasm/runtime/*.json"/> <_RollupInputs Include="$(MonoProjectRoot)wasm/runtime/*.js"/>