diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs index 16535859613b1f..693a88b8e9ff95 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 dd2351ae2093a0..c78c422cc1b6df 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 cdc79e556a23a3..1f207e6e5e6fb2 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 da7cac5cad5aea..0605e250b9cea1 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 ea24f8e1bf74ac..c0aa35fed8763d 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,279 +1,124 @@ // 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 { - [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) + + // The JS layer invokes this method when the JS wrapper for a JS owned object + // has been collected by the JS garbage collector + [MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 + // the marshaled signature is: + // void ReleaseJSOwnedObjectByGCHandle(GCHandle gcHandle) + public static void ReleaseJSOwnedObjectByGCHandle(JSMarshalerArgument* arguments_buffer) + { + ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame() + ref JSMarshalerArgument arg_1 = ref arguments_buffer[2]; // initialized and set by caller + 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(); } } - 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) + [MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 + // the marshaled signature is: + // GCHandle CreateTaskCallback() + public static void CreateTaskCallback(JSMarshalerArgument* arguments_buffer) { - JSObject? jsObject = rawObj as JSObject; - if (jsObject != null && shouldAddInflight != 0) + ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame() + ref JSMarshalerArgument arg_return = ref arguments_buffer[1]; // used as return vaule + try { - jsObject.AddInFlight(); + JSHostImplementation.TaskCallback holder = new JSHostImplementation.TaskCallback(); + arg_return.slot.Type = MarshalerType.Object; + arg_return.slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(holder); + } + catch (Exception ex) + { + arg_exc.ToJS(ex); } - 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) + [MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 + // the marshaled signature is: + // TRes? CallDelegate(GCHandle callback, T1? arg1, T2? arg2, T3? arg3) + public static void CallDelegate(JSMarshalerArgument* arguments_buffer) { - JSObject? res = null; - - lock (JSHostImplementation.s_csOwnedObjects) + ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by JS caller in alloc_stack_frame() + // arg_res is initialized by JS caller + ref JSMarshalerArgument arg_1 = ref arguments_buffer[2];// initialized and set by JS caller + // arg_2 set by JS caller when there are arguments + // arg_3 set by JS caller when there are arguments + // arg_4 set by JS caller when there are arguments + try { - if (!JSHostImplementation.s_csOwnedObjects.TryGetValue((int)jsHandle, out WeakReference? reference) || - !reference.TryGetTarget(out res) || - res.IsDisposed) + GCHandle callback_gc_handle = (GCHandle)arg_1.slot.GCHandle; + if (callback_gc_handle.Target is JSHostImplementation.ToManagedCallback callback) + { + // arg_2, arg_3, arg_4, arg_res are processed by the callback + callback(arguments_buffer); + } + else { -#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); + throw new InvalidOperationException("ToManagedCallback is null"); } } - 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) - { - GCHandle h = (GCHandle)(IntPtr)gcHandle; - result = h.Target!; } - [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 - public static IntPtr GetJSOwnedObjectGCHandleRef(in object obj) - { - return JSHostImplementation.GetJSOwnedObjectGCHandleRef(obj, GCHandleType.Normal); - } - - // 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) - { - JSHostImplementation.s_gcHandleFromJSOwnedObject.Remove(handle.Target!); - handle.Free(); - } - } - - [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) + [MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 + // the marshaled signature is: + // void CompleteTask(GCHandle holder, Exception? exceptionResult, T? result) + 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]; // initialized by caller in alloc_stack_frame() + ref JSMarshalerArgument arg_1 = ref arguments_buffer[2];// initialized and set by caller + // arg_2 set by caller when this is SetException call + // arg_3 set by caller when this is SetResult call + 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); - } - - continuationObj.Invoke("resolve", result); - } - else - { - continuationObj.Invoke("reject", task.Exception.ToString()); - } - } - catch (Exception e) + GCHandle callback_gc_handle = (GCHandle)arg_1.slot.GCHandle; + if (callback_gc_handle.Target is JSHostImplementation.TaskCallback holder && holder.Callback is not null) { - continuationObj.Invoke("reject", e.ToString()); + // arg_2, arg_3 are processed by the callback + holder.Callback(arguments_buffer); } - finally + else { - 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++) + 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 + [MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 public static void StopProfile() { } // Called by the AOT profiler to save profile data into INTERNAL.aot_profile_data - [MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 + [MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 public static unsafe void DumpAotProfileData(ref byte buf, int len, string extraArg) { if (len == 0) @@ -291,45 +136,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 00000000000000..faf196db950d20 --- /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 3455f53a0124ae..8ec27641bcaf03 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 aa1ad66a34c482..f42902e3d61508 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 15160244e529c3..469f71c42392b3 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 218b40336f66f8..0c97341aac2af2 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 2dcb1d445d5697..43c48bec9dd73d 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 674b5899ceba06..8c2ec3fbddc899 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 dd09fc25c40c39..e631f8994e4518 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; @@ -386,18 +386,11 @@ public unsafe void ToJS(Action value) // TODO: we could try to cache value -> existing GCHandle JSHostImplementation.ToManagedCallback cb = (JSMarshalerArgument* arguments) => { - ref JSMarshalerArgument exc = ref arguments[0]; - try - { - cpy.Invoke(); - } - catch (Exception ex) - { - exc.ToJS(ex); - } + cpy.Invoke(); + // eventual exception is handled by C# caller }; slot.Type = MarshalerType.Function; - slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(cb); + slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(cb); } /// @@ -409,20 +402,13 @@ public unsafe void ToJS(Action value, ArgumentToManagedCallback arg1Mar Action cpy = value; JSHostImplementation.ToManagedCallback cb = (JSMarshalerArgument* arguments) => { - ref JSMarshalerArgument exc = ref arguments[0]; - ref JSMarshalerArgument arg1 = ref arguments[2]; - try - { - arg1Marshaler(ref arg1, out T arg1cs); - cpy.Invoke(arg1cs); - } - catch (Exception ex) - { - exc.ToJS(ex); - } + ref JSMarshalerArgument arg2 = ref arguments[3]; // set by JS caller + arg1Marshaler(ref arg2, out T arg1cs); + cpy.Invoke(arg1cs); + // eventual exception is handled by C# caller }; slot.Type = MarshalerType.Action; - slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(cb); + slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(cb); } /// @@ -434,22 +420,15 @@ public unsafe void ToJS(Action value, ArgumentToManagedCallback< Action cpy = value; JSHostImplementation.ToManagedCallback cb = (JSMarshalerArgument* arguments) => { - ref JSMarshalerArgument exc = ref arguments[0]; - ref JSMarshalerArgument arg1 = ref arguments[2]; - ref JSMarshalerArgument arg2 = ref arguments[3]; - try - { - arg1Marshaler(ref arg1, out T1 arg1cs); - arg2Marshaler(ref arg2, out T2 arg2cs); - cpy.Invoke(arg1cs, arg2cs); - } - catch (Exception ex) - { - exc.ToJS(ex); - } + ref JSMarshalerArgument arg2 = ref arguments[3];// set by JS caller + ref JSMarshalerArgument arg3 = ref arguments[4];// set by JS caller + arg1Marshaler(ref arg2, out T1 arg1cs); + arg2Marshaler(ref arg3, out T2 arg2cs); + cpy.Invoke(arg1cs, arg2cs); + // eventual exception is handled by C# caller }; slot.Type = MarshalerType.Action; - slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(cb); + slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(cb); } /// @@ -461,24 +440,17 @@ public unsafe void ToJS(Action value, ArgumentToManagedC Action cpy = value; JSHostImplementation.ToManagedCallback cb = (JSMarshalerArgument* arguments) => { - ref JSMarshalerArgument exc = ref arguments[0]; - ref JSMarshalerArgument arg1 = ref arguments[2]; - ref JSMarshalerArgument arg2 = ref arguments[3]; - ref JSMarshalerArgument arg3 = ref arguments[4]; - try - { - arg1Marshaler(ref arg1, out T1 arg1cs); - arg2Marshaler(ref arg2, out T2 arg2cs); - arg3Marshaler(ref arg3, out T3 arg3cs); - cpy.Invoke(arg1cs, arg2cs, arg3cs); - } - catch (Exception ex) - { - exc.ToJS(ex); - } + ref JSMarshalerArgument arg2 = ref arguments[3];// set by JS caller + ref JSMarshalerArgument arg3 = ref arguments[4];// set by JS caller + ref JSMarshalerArgument arg4 = ref arguments[5];// set by JS caller + arg1Marshaler(ref arg2, out T1 arg1cs); + arg2Marshaler(ref arg3, out T2 arg2cs); + arg3Marshaler(ref arg4, out T3 arg3cs); + cpy.Invoke(arg1cs, arg2cs, arg3cs); + // eventual exception is handled by C# caller }; slot.Type = MarshalerType.Action; - slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(cb); + slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(cb); } /// @@ -490,20 +462,13 @@ public unsafe void ToJS(Func value, ArgumentToJSCallback cpy = value; JSHostImplementation.ToManagedCallback cb = (JSMarshalerArgument* arguments) => { - ref JSMarshalerArgument exc = ref arguments[0]; ref JSMarshalerArgument res = ref arguments[1]; - try - { - TResult resCs = cpy.Invoke(); - resMarshaler(ref res, resCs); - } - catch (Exception ex) - { - exc.ToJS(ex); - } + TResult resCs = cpy.Invoke(); + resMarshaler(ref res, resCs); + // eventual exception is handled by C# caller }; slot.Type = MarshalerType.Function; - slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(cb); + slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(cb); } /// @@ -515,22 +480,15 @@ public unsafe void ToJS(Func value, ArgumentToManagedCal Func cpy = value; JSHostImplementation.ToManagedCallback cb = (JSMarshalerArgument* arguments) => { - ref JSMarshalerArgument exc = ref arguments[0]; ref JSMarshalerArgument res = ref arguments[1]; - ref JSMarshalerArgument arg1 = ref arguments[2]; - try - { - arg1Marshaler(ref arg1, out T arg1cs); - TResult resCs = cpy.Invoke(arg1cs); - resMarshaler(ref res, resCs); - } - catch (Exception ex) - { - exc.ToJS(ex); - } + ref JSMarshalerArgument arg2 = ref arguments[3];// set by JS caller + arg1Marshaler(ref arg2, out T arg1cs); + TResult resCs = cpy.Invoke(arg1cs); + resMarshaler(ref res, resCs); + // eventual exception is handled by C# caller }; slot.Type = MarshalerType.Function; - slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(cb); + slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(cb); } /// @@ -542,26 +500,17 @@ public unsafe void ToJS(Func value, ArgumentTo Func cpy = value; JSHostImplementation.ToManagedCallback cb = (JSMarshalerArgument* arguments) => { - ref JSMarshalerArgument exc = ref arguments[0]; ref JSMarshalerArgument res = ref arguments[1]; - ref JSMarshalerArgument arg1 = ref arguments[2]; - ref JSMarshalerArgument arg2 = ref arguments[3]; - try - { - arg1Marshaler(ref arg1, out T1 arg1cs); - arg2Marshaler(ref arg2, out T2 arg2cs); - TResult resCs = cpy.Invoke(arg1cs, arg2cs); - resMarshaler(ref res, resCs); - } - catch (Exception ex) - { - exc.ToJS(ex); - } + ref JSMarshalerArgument arg2 = ref arguments[3];// set by JS caller + ref JSMarshalerArgument arg3 = ref arguments[4];// set by JS caller + arg1Marshaler(ref arg2, out T1 arg1cs); + arg2Marshaler(ref arg3, out T2 arg2cs); + TResult resCs = cpy.Invoke(arg1cs, arg2cs); + resMarshaler(ref res, resCs); + // eventual exception is handled by C# caller }; - - slot.Type = MarshalerType.Function; - slot.GCHandle = JavaScriptExports.GetJSOwnedObjectGCHandleRef(cb); + slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(cb); } /// @@ -573,28 +522,19 @@ public unsafe void ToJS(Func value, Ar Func cpy = value; JSHostImplementation.ToManagedCallback cb = (JSMarshalerArgument* arguments) => { - ref JSMarshalerArgument exc = ref arguments[0]; ref JSMarshalerArgument res = ref arguments[1]; - ref JSMarshalerArgument arg1 = ref arguments[2]; - ref JSMarshalerArgument arg2 = ref arguments[3]; - ref JSMarshalerArgument arg3 = ref arguments[4]; - try - { - arg1Marshaler(ref arg1, out T1 arg1cs); - arg2Marshaler(ref arg2, out T2 arg2cs); - arg3Marshaler(ref arg3, out T3 arg3cs); - TResult resCs = cpy.Invoke(arg1cs, arg2cs, arg3cs); - resMarshaler(ref res, resCs); - } - catch (Exception ex) - { - exc.ToJS(ex); - } + ref JSMarshalerArgument arg2 = ref arguments[3];// set by JS caller + ref JSMarshalerArgument arg3 = ref arguments[4];// set by JS caller + ref JSMarshalerArgument arg4 = ref arguments[5];// set by JS caller + arg1Marshaler(ref arg2, out T1 arg1cs); + arg2Marshaler(ref arg3, out T2 arg2cs); + arg3Marshaler(ref arg4, out T3 arg3cs); + TResult resCs = cpy.Invoke(arg1cs, arg2cs, arg3cs); + resMarshaler(ref res, resCs); + // eventual exception is handled by C# caller }; - - 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 05ddab050c9f95..75b25496398dad 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 ea632d02db3b96..58522b4622ae73 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 76fa8ab7817bfe..ac9395194a4bb7 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 608443cc45cf35..e504bb15522b9a 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 @@ -40,24 +40,18 @@ public unsafe void ToManaged(out Task? value) TaskCompletionSource tcs = new TaskCompletionSource(gcHandle); JSHostImplementation.ToManagedCallback callback = (JSMarshalerArgument* arguments_buffer) => { - ref JSMarshalerArgument arg_exception = ref arguments_buffer[0]; - try + ref JSMarshalerArgument arg_2 = ref arguments_buffer[3]; // set by caller when this is SetException call + // arg_3 set by caller when this is SetResult call, un-used here + if (arg_2.slot.Type != MarshalerType.None) { - if (arg_exception.slot.Type != MarshalerType.None) - { - arg_exception.ToManaged(out Exception? fail); - tcs.SetException(fail!); - } - else - { - tcs.SetResult(); - } - arg_exception.slot.Type = MarshalerType.None; + arg_2.ToManaged(out Exception? fail); + tcs.SetException(fail!); } - catch (Exception ex) + else { - arg_exception.ToJS(ex); + tcs.SetResult(); } + // eventual exception is handled by caller }; holder.Callback = callback; value = tcs.Task; @@ -82,27 +76,20 @@ public unsafe void ToManaged(out Task? value, ArgumentToManagedCallback TaskCompletionSource tcs = new TaskCompletionSource(gcHandle); JSHostImplementation.ToManagedCallback callback = (JSMarshalerArgument* arguments_buffer) => { - ref JSMarshalerArgument arg_exception = ref arguments_buffer[0]; - ref JSMarshalerArgument arg1 = ref arguments_buffer[2]; - try + ref JSMarshalerArgument arg_2 = ref arguments_buffer[3]; // set by caller when this is SetException call + ref JSMarshalerArgument arg_3 = ref arguments_buffer[4]; // set by caller when this is SetResult call + if (arg_2.slot.Type != MarshalerType.None) { - if (arg_exception.slot.Type != MarshalerType.None) - { - arg_exception.ToManaged(out Exception? fail); - if (fail == null) throw new NullReferenceException("Exception"); - tcs.SetException(fail); - } - else - { - marshaler(ref arg1, out T result); - tcs.SetResult(result); - } - arg_exception.slot.Type = MarshalerType.None; + arg_2.ToManaged(out Exception? fail); + if (fail == null) throw new NullReferenceException("Exception"); + tcs.SetException(fail); } - catch (Exception ex) + else { - arg_exception.ToJS(ex); + marshaler(ref arg_3, out T result); + tcs.SetResult(result); } + // eventual exception is handled by caller }; holder.Callback = callback; value = tcs.Task; @@ -138,7 +125,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 +203,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 +276,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 e5bcf479b39fe1..71a795545f3317 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/libraries/System.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs index 272e8548a4f15d..47501520abc726 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs @@ -923,9 +923,9 @@ public static async Task InitializeAsync() { if (_module == null) { - Log("JavaScriptTestHelper.mjs importing"); + // Log("JavaScriptTestHelper.mjs importing"); _module = await JSHost.ImportAsync("JavaScriptTestHelper", "./JavaScriptTestHelper.mjs"); - Log("JavaScriptTestHelper.mjs imported"); + // Log("JavaScriptTestHelper.mjs imported"); } } } diff --git a/src/mono/sample/wasm/browser-profile/README.md b/src/mono/sample/wasm/browser-profile/README.md index 83a3fd35c234ab..08bedff4756ae8 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/runtime/buffers.ts b/src/mono/wasm/runtime/buffers.ts deleted file mode 100644 index ccbc7dcbbd6b95..00000000000000 --- 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. -// address 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. -// address 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 new file mode 100644 index 00000000000000..2c815a38facb7c --- /dev/null +++ b/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js @@ -0,0 +1,117 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +/* eslint-disable no-undef */ + +"use strict"; + +#if USE_PTHREADS +const usePThreads = `true`; +const isPThread = `ENVIRONMENT_IS_PTHREAD`; +#else +const usePThreads = `false`; +const isPThread = `false`; +#endif + +const DotnetSupportLib = { + $DOTNET: {}, + // these lines will be placed early on emscripten runtime creation, passing import and export objects into __dotnet_runtime IFFE + // we replace implementation of fetch + // replacement of require is there for consistency with ES6 code + $DOTNET__postset: ` +let __dotnet_replacement_PThread = ${usePThreads} ? {} : undefined; +if (${usePThreads}) { + __dotnet_replacement_PThread.loadWasmModuleToWorker = PThread.loadWasmModuleToWorker; + __dotnet_replacement_PThread.threadInitTLS = PThread.threadInitTLS; +} +let __dotnet_replacements = {scriptUrl: undefined, fetch: globalThis.fetch, require, updateGlobalBufferAndViews, pthreadReplacements: __dotnet_replacement_PThread}; +if (ENVIRONMENT_IS_NODE) { + __dotnet_replacements.requirePromise = Promise.resolve(require); +} +let __dotnet_exportedAPI = __dotnet_runtime.__initializeImportsAndExports( + { isESM:false, isGlobal:ENVIRONMENT_IS_GLOBAL, isNode:ENVIRONMENT_IS_NODE, isWorker:ENVIRONMENT_IS_WORKER, isShell:ENVIRONMENT_IS_SHELL, isWeb:ENVIRONMENT_IS_WEB, isPThread:${isPThread}, quit_, ExitStatus, requirePromise:Promise.resolve(require)}, + { mono:MONO, binding:BINDING, internal:INTERNAL, module:Module, marshaled_exports: EXPORTS, marshaled_imports: IMPORTS }, + __dotnet_replacements); +updateGlobalBufferAndViews = __dotnet_replacements.updateGlobalBufferAndViews; +var fetch = __dotnet_replacements.fetch; +_scriptDir = __dirname = scriptDirectory = __dotnet_replacements.scriptDirectory; +if (ENVIRONMENT_IS_NODE) { + __dotnet_replacements.requirePromise.then(someRequire => { + require = someRequire; + }); +} +var noExitRuntime = __dotnet_replacements.noExitRuntime; +if (${usePThreads}) { + PThread.loadWasmModuleToWorker = __dotnet_replacements.pthreadReplacements.loadWasmModuleToWorker; + PThread.threadInitTLS = __dotnet_replacements.pthreadReplacements.threadInitTS; +} +`, +}; + +// the methods would be visible to EMCC linker +// --- keep in sync with exports.ts --- +const linked_functions = [ + // 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", + + // driver.c + "mono_wasm_invoke_js_blazor", + "mono_wasm_trace_logger", + "mono_wasm_set_entrypoint_breakpoint", + "mono_wasm_event_pipe_early_startup_callback", + + // 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_compile_function_ref", + "mono_wasm_bind_js_function", + "mono_wasm_invoke_bound_function", + "mono_wasm_bind_cs_function", + "mono_wasm_marshal_promise", + + // pal_icushim_static.c + "mono_wasm_load_icu_data", + "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", + + #if USE_PTHREADS + /// mono-threads-wasm.c + "mono_wasm_pthread_on_pthread_attached", + /// diagnostics_server.c + "mono_wasm_diagnostic_server_on_server_thread_created", + "mono_wasm_diagnostic_server_on_runtime_server_init", + "mono_wasm_diagnostic_server_stream_signal_work_available", + #endif +]; + +// -- this javascript file is evaluated by emcc during compilation! -- +// we generate simple proxy for each exported function so that emcc will include them in the final output +for (let linked_function of linked_functions) { + const fn_template = `return __dotnet_runtime.__linker_exports.${linked_function}.apply(__dotnet_runtime, arguments)`; + DotnetSupportLib[linked_function] = new Function(fn_template); +} + +autoAddDeps(DotnetSupportLib, "$DOTNET"); +mergeInto(LibraryManager.library, DotnetSupportLib); diff --git a/src/mono/wasm/runtime/corebindings.c b/src/mono/wasm/runtime/corebindings.c index 11bd47ee8a3351..1d4267a6fb8db7 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 0a843c59083efa..00000000000000 --- a/src/mono/wasm/runtime/corebindings.ts +++ /dev/null @@ -1,72 +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 { PromiseController } from "./promise-controller"; -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; - _setup_js_cont_ref(task: MonoObjectRef, continuation: PromiseController): 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 317ad12a3f1d59..2bfe35484baac7 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,87 +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", "number", "string", "bool", "number"]], - ["mono_wasm_event_pipe_session_start_streaming", "bool", ["number"]], - ["mono_wasm_event_pipe_session_disable", "bool", ["number"]], - ["mono_wasm_diagnostic_server_create_thread", "bool", ["string", "number"]], - ["mono_wasm_diagnostic_server_thread_attach_to_runtime", "void", []], - ["mono_wasm_diagnostic_server_post_resume_runtime", "void", []], - ["mono_wasm_diagnostic_server_create_stream", "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 { @@ -204,25 +201,8 @@ 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 { @@ -230,3 +210,21 @@ export const enum I52Error { 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 5e5c5a5db50947..f51c7a4d594d25 100644 --- a/src/mono/wasm/runtime/dotnet.d.ts +++ b/src/mono/wasm/runtime/dotnet.d.ts @@ -61,6 +61,8 @@ declare interface EmscriptenModule { } declare type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array; +declare function mono_wasm_runtime_ready(): void; + /** * 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. @@ -128,6 +130,47 @@ interface WasmRoot { toString(): string; } +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; +} + interface MonoObject extends ManagedPointer { __brandMonoObject: "MonoObject"; } @@ -245,6 +288,19 @@ interface LoadingResource { response: Promise; } declare type EventPipeSessionID = bigint; +interface DotnetPublicAPI { + MONO: MONOType; + BINDING: BINDINGType; + INTERNAL: any; + EXPORTS: any; + IMPORTS: any; + Module: EmscriptenModule; + RuntimeId: number; + RuntimeBuildInfo: { + ProductVersion: string; + Configuration: string; + }; +} declare const eventLevel: { readonly LogAlways: 0; @@ -291,57 +347,9 @@ interface Diagnostics { getStartupSessions(): (EventPipeSession | null)[]; } -declare function mono_wasm_runtime_ready(): void; - -declare function mono_wasm_setenv(name: string, value: string): void; -declare function mono_wasm_load_runtime(unused?: string, debug_level?: number): void; -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 -*/ -declare function mono_load_runtime_and_bcl_args(cfg?: MonoConfig | MonoConfigError | undefined): Promise; - declare function mono_wasm_load_icu_data(offset: VoidPtr): boolean; -/** - * @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; - -/** - * @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 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_bind_static_method(fqn: string, signature?: string): Function; -declare function mono_call_assembly_entry_point(assembly: string, args?: any[], signature?: string): 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; @@ -381,54 +389,101 @@ 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_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 +*/ +declare function mono_load_runtime_and_bcl_args(cfg?: MonoConfig | MonoConfigError | undefined): 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; @@ -444,7 +499,7 @@ declare const MONO: { 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: typeof mono_wasm_load_runtime; + mono_wasm_load_runtime: (unused: string, debug_level: number) => void; config: MonoConfig | MonoConfigError; loaded_files: string[]; setB32: typeof setB32; @@ -473,69 +528,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 7539fa8eaa7505..82fd6abba1b97b 100644 --- a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js +++ b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js @@ -82,9 +82,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 9f70faf8e32355..2087094ac723d1 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 "./net6-legacy/exports-legacy"; import { IDisposable, IMemoryView, ManagedError, ManagedObject, MemoryViewType } from "./marshal"; -import { AssetBehaviours, AssetEntry, DotnetModuleConfig, LoadingResource, MonoArray, MonoConfig, MonoObject, MonoString, ResourceRequest } from "./types"; +import { AssetBehaviours, AssetEntry, DotnetModuleConfig, DotnetPublicAPI, LoadingResource, MonoArray, MonoConfig, MonoObject, MonoString, ResourceRequest } 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 00000000000000..a5c9219b2933e3 --- /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 "./net6-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 00000000000000..a8d2c08453d75d --- /dev/null +++ b/src/mono/wasm/runtime/exports-linker.ts @@ -0,0 +1,87 @@ +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 "./net6-legacy/buffers"; +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 "./net6-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"; +import { mono_wasm_diagnostic_server_on_server_thread_created } from "./diagnostics/server_pthread"; +import { mono_wasm_diagnostic_server_on_runtime_server_init, mono_wasm_event_pipe_early_startup_callback } from "./diagnostics"; +import { mono_wasm_diagnostic_server_stream_signal_work_available } from "./diagnostics/server_pthread/stream-queue"; +import { mono_wasm_create_cs_owned_object_ref } from "./net6-legacy/cs-to-js"; +import { mono_wasm_typed_array_to_array_ref } from "./net6-legacy/js-to-cs"; + +// 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, + // diagnostics_server.c + mono_wasm_diagnostic_server_on_server_thread_created, + mono_wasm_diagnostic_server_on_runtime_server_init, + mono_wasm_diagnostic_server_stream_signal_work_available, +}; + +// 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, + mono_wasm_event_pipe_early_startup_callback, + + // 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 d9f679a71b4d43..e995f40ab17a64 100644 --- a/src/mono/wasm/runtime/exports.ts +++ b/src/mono/wasm/runtime/exports.ts @@ -3,205 +3,23 @@ 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_WORKER, runtimeHelpers, set_imports_exports } from "./imports"; -import { DotnetModule, is_nullish, MonoConfig, MonoConfigError, EarlyImports, EarlyExports, EarlyReplacements } 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, - mono_wasm_load_runtime, -} 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_event_pipe_early_startup_callback, - mono_wasm_diagnostic_server_on_runtime_server_init -} from "./diagnostics"; -import { - mono_wasm_diagnostic_server_on_server_thread_created, -} from "./diagnostics/server_pthread"; -import { - mono_wasm_diagnostic_server_stream_signal_work_available -} from "./diagnostics/server_pthread/stream-queue"; -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"; -import { - setI8, setI16, setI32, setI52, - setU8, setU16, setU32, setF32, setF64, - getI8, getI16, getI32, getI52, - getU8, getU16, getU32, getF32, getF64, getI64Big, setI64Big, getU52, setU52, setB32, getB32, -} from "./memory"; +import { ENVIRONMENT_IS_WORKER, set_imports_exports } from "./imports"; +import { DotnetModule, is_nullish, DotnetPublicAPI, EarlyImports, EarlyExports, EarlyReplacements } from "./types"; +import { configure_emscripten_startup } from "./startup"; +import { mono_bind_static_method } from "./net6-legacy/method-calls"; + import { create_weak_ref } from "./weak-ref"; -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 } from "./pthreads/worker"; +import { export_binding_api, export_mono_api } from "./net6-legacy/exports-legacy"; +import { export_internal } from "./exports-internal"; +import { export_linker } from "./exports-linker"; import { init_polyfills } from "./polyfills"; -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, - - mono_wasm_add_assembly: cwraps.mono_wasm_add_assembly, - 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; + // this is executed early during load of emscripten runtime // it exports methods to global objects MONO, BINDING and Module in backward compatible way // At runtime this will be referred to as 'createDotnetRuntime' @@ -219,9 +37,10 @@ function initializeImportsAndExports( init_polyfills(replacements); // 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, @@ -317,157 +136,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.es6.lib.js --- -const mono_wasm_threads_exports = !MonoWasmThreads ? undefined : { - // mono-threads-wasm.c - mono_wasm_pthread_on_pthread_attached, - // diagnostics_server.c - mono_wasm_diagnostic_server_on_server_thread_created, - mono_wasm_diagnostic_server_on_runtime_server_init, - mono_wasm_diagnostic_server_stream_signal_work_available, -}; - -// the methods would be visible to EMCC linker -// --- keep in sync with dotnet.es6.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, - mono_wasm_event_pipe_early_startup_callback, - - // 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 39d94629123df4..af0f4776daa850 100644 --- a/src/mono/wasm/runtime/gc-handles.ts +++ b/src/mono/wasm/runtime/gc-handles.ts @@ -1,9 +1,8 @@ // 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 { runtimeHelpers } from "./imports"; +import { GCHandle, GCHandleNull, JSHandle, JSHandleDisposed, JSHandleNull, mono_assert } from "./types"; import { create_weak_ref } from "./weak-ref"; const _use_finalization_registry = typeof globalThis.FinalizationRegistry === "function"; @@ -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); + runtimeHelpers.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 5bec05a72a10f0..29772c201287d4 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 "./net6-legacy/exports-legacy"; import { DotnetModule, EarlyExports, EarlyImports, 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; @@ -49,8 +50,7 @@ let monoConfig: MonoConfig = {} as any; let runtime_is_ready = false; export const runtimeHelpers: RuntimeHelpers = { - namespace: "System.Runtime.InteropServices.JavaScript", - classname: "Runtime", + javaScriptExports: {}, mono_wasm_load_runtime_done: false, mono_wasm_bindings_is_ready: false, get mono_wasm_runtime_is_ready() { diff --git a/src/mono/wasm/runtime/invoke-cs.ts b/src/mono/wasm/runtime/invoke-cs.ts index 9075100f805cff..d4e91aaeb65d7f 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(); @@ -202,10 +171,38 @@ function _walk_exports_to_set_function(assembly: string, namespace: string, clas export async function mono_wasm_get_assembly_exports(assembly: string): Promise { mono_assert(runtimeHelpers.mono_wasm_bindings_is_ready, "Expected binding to be initialized later during startup sequence."); - const asm = assembly_load(assembly); - if (!asm) - throw new Error("Could not find assembly: " + assembly); - cwraps.mono_wasm_runtime_run_module_cctor(asm); + 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 1eae1e83902c55..21c5bfa14150d5 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/managed-exports.ts b/src/mono/wasm/runtime/managed-exports.ts new file mode 100644 index 00000000000000..7b6e21ffe7e717 --- /dev/null +++ b/src/mono/wasm/runtime/managed-exports.ts @@ -0,0 +1,133 @@ +// 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, MarshalerToCs, MarshalerToJs, MarshalerType, set_arg_type, set_gc_handle } from "./marshal"; +import { invoke_method_and_handle_exception } from "./invoke-cs"; +import { marshal_exception_to_cs } from "./marshal-to-cs"; + +// in all the exported internals methods, we use the same data structures for stack frame as normal full blow interop +// see src\libraries\System.Runtime.InteropServices.JavaScript\src\System\Runtime\InteropServices\JavaScript\Interop\JavaScriptExports.cs +export interface JavaScriptExports { + // the marshaled signature is: void ReleaseJSOwnedObjectByGCHandle(GCHandle gcHandle) + _release_js_owned_object_by_gc_handle(gc_handle: GCHandle): void; + // the marshaled signature is: GCHandle CreateTaskCallback() + _create_task_callback(): GCHandle; + // the marshaled signature is: void CompleteTask(GCHandle holder, Exception? exceptionResult, T? result) + _complete_task(holder_gc_handle: GCHandle, error?: any, data?: any, res_converter?: MarshalerToCs): void; + // the marshaled signature is: TRes? CallDelegate(GCHandle callback, T1? arg1, T2? arg2, T3? arg3) + _call_delegate(callback_gc_handle: GCHandle, arg1_js: any, arg2_js: any, arg3_js: any, + res_converter?: MarshalerToJs, arg1_converter?: MarshalerToCs, arg2_converter?: MarshalerToCs, arg3_converter?: MarshalerToCs): any; +} + +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"); + + runtimeHelpers.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); + } + }; + runtimeHelpers.javaScriptExports._create_task_callback = () => { + const sp = anyModule.stackSave(); + try { + const args = alloc_stack_frame(2); + 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); + } + }; + runtimeHelpers.javaScriptExports._complete_task = (holder_gc_handle: GCHandle, error?: any, data?: any, res_converter?: MarshalerToCs) => { + const sp = anyModule.stackSave(); + try { + const args = alloc_stack_frame(5); + const arg1 = get_arg(args, 2); + set_gc_handle(arg1, holder_gc_handle); + const arg2 = get_arg(args, 3); + if (error) { + marshal_exception_to_cs(arg2, error); + } else { + set_arg_type(arg2, MarshalerType.None); + const arg3 = get_arg(args, 4); + mono_assert(res_converter, "res_converter missing"); + res_converter(arg3, data); + } + invoke_method_and_handle_exception(complete_task_method, args); + } finally { + anyModule.stackRestore(sp); + } + }; + runtimeHelpers.javaScriptExports._call_delegate = (callback_gc_handle: GCHandle, arg1_js: any, arg2_js: any, arg3_js: any, res_converter?: MarshalerToJs, arg1_converter?: MarshalerToCs, arg2_converter?: MarshalerToCs, arg3_converter?: MarshalerToCs) => { + const sp = anyModule.stackSave(); + try { + const args = alloc_stack_frame(6); + + const arg1 = get_arg(args, 2); + set_gc_handle(arg1, callback_gc_handle); + // payload arg numbers are shifted by one, the real first is a gc handle of the callback + + if (arg1_converter) { + const arg2 = get_arg(args, 3); + arg1_converter(arg2, arg1_js); + } + if (arg2_converter) { + const arg3 = get_arg(args, 4); + arg2_converter(arg3, arg2_js); + } + if (arg3_converter) { + const arg4 = get_arg(args, 5); + arg3_converter(arg4, arg3_js); + } + + invoke_method_and_handle_exception(call_delegate_method, args); + + if (res_converter) { + const res = get_arg(args, 1); + return res_converter(res); + } + } finally { + anyModule.stackRestore(sp); + } + }; +} + +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 a9fff713e9af14..ae2019fb574174 100644 --- a/src/mono/wasm/runtime/marshal-to-cs.ts +++ b/src/mono/wasm/runtime/marshal-to-cs.ts @@ -2,19 +2,19 @@ // The .NET Foundation licenses this file to you under the MIT license. import { isThenable } from "./cancelable-promise"; -import wrapped_cs_functions from "./corebindings"; import cwraps from "./cwraps"; import { assert_not_disposed, cs_owned_js_handle_symbol, js_owned_gc_handle_symbol, mono_wasm_get_js_handle, setup_managed_proxy, teardown_managed_proxy } from "./gc-handles"; import { Module, runtimeHelpers } from "./imports"; import { JSMarshalerArgument, ManagedError, set_gc_handle, set_js_handle, set_arg_type, set_arg_i32, set_arg_f64, set_arg_i52, set_arg_f32, set_arg_i16, set_arg_u8, set_arg_b8, set_arg_date, - set_arg_length, get_arg, is_args_exception, JavaScriptMarshalerArgSize, get_signature_type, get_signature_arg1_type, get_signature_arg2_type, cs_to_js_marshalers, js_to_cs_marshalers, - MarshalerToCs, MarshalerToJs, get_signature_res_type, JSMarshalerArguments, bound_js_function_symbol, set_arg_u16, JSMarshalerType, array_element_size, get_string_root, Span, ArraySegment, MemoryViewType, get_signature_arg3_type, MarshalerType, set_arg_i64_big, set_arg_intptr, IDisposable, set_arg_element_type, ManagedObject + 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 } 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"; @@ -362,53 +362,17 @@ 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 = runtimeHelpers.javaScriptExports._create_task_callback(); set_gc_handle(arg, gc_handle); set_arg_type(arg, MarshalerType.Task); const holder = new TaskCallbackHolder(value); setup_managed_proxy(holder, gc_handle); value.then(data => { - const sp = anyModule.stackSave(); - try { - const args = anyModule.stackAlloc(JavaScriptMarshalerArgSize * 3); - const exc = get_arg(args, 0); - set_arg_type(exc, MarshalerType.None); - const res = get_arg(args, 1); - set_arg_type(res, MarshalerType.None); - set_gc_handle(res, gc_handle); - const arg1 = get_arg(args, 2); - if (!res_converter) { - _marshal_cs_object_to_cs(arg1, data); - } else { - res_converter(arg1, data); - } - const fail = cwraps.mono_wasm_invoke_method_bound(runtimeHelpers.complete_task_method, args); - if (fail) throw new Error("ERR22: Unexpected error: " + conv_string(fail)); - if (is_args_exception(args)) throw marshal_exception_to_js(exc); - } finally { - anyModule.stackRestore(sp); - } + runtimeHelpers.javaScriptExports._complete_task(gc_handle, null, data, res_converter || _marshal_cs_object_to_cs); teardown_managed_proxy(holder, gc_handle); // this holds holder alive for finalizer, until the promise is freed, (holding promise instead would not work) }).catch(reason => { - const sp = anyModule.stackSave(); - try { - const args = anyModule.stackAlloc(JavaScriptMarshalerArgSize * 3); - const res = get_arg(args, 1); - set_arg_type(res, MarshalerType.None); - set_gc_handle(res, gc_handle); - const exc = get_arg(args, 0); - if (typeof reason === "string" || reason === null || reason === undefined) { - reason = new Error(reason || ""); - } - marshal_exception_to_cs(exc, reason); - const fail = cwraps.mono_wasm_invoke_method_bound(runtimeHelpers.complete_task_method, args); - if (fail) throw new Error("ERR24: Unexpected error: " + conv_string(fail)); - if (is_args_exception(args)) throw marshal_exception_to_js(exc); - } finally { - anyModule.stackRestore(sp); - } + runtimeHelpers.javaScriptExports._complete_task(gc_handle, reason, null, undefined); teardown_managed_proxy(holder, gc_handle); // this holds holder alive for finalizer, until the promise is freed }); } diff --git a/src/mono/wasm/runtime/marshal-to-js.ts b/src/mono/wasm/runtime/marshal-to-js.ts index 1cf0b43748784b..7555fdbff6cf2a 100644 --- a/src/mono/wasm/runtime/marshal-to-js.ts +++ b/src/mono/wasm/runtime/marshal-to-js.ts @@ -10,9 +10,10 @@ import { 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, get_arg_u16, array_element_size, get_string_root, + ArraySegment, Span, MemoryViewType, get_signature_arg3_type, MarshalerType, get_arg_i64_big, get_arg_intptr, get_arg_element_type } from "./marshal"; -import { conv_string, conv_string_root } from "./strings"; +import { conv_string_root } from "./strings"; import { mono_assert, JSHandleNull, GCHandleNull } from "./types"; import { TypedArray } from "./types/emscripten"; @@ -239,48 +240,14 @@ function _marshal_delegate_to_js(arg: JSMarshalerArgument, _?: JSMarshalerType, return null; } - const anyModule = Module as any; const gc_handle = get_arg_gc_handle(arg); let result = _lookup_js_owned_object(gc_handle); if (result === null || result === undefined) { // this will create new Function for the C# delegate - result = (arg1_js: any, arg2_js: any, arg3_js: any) => { - - const sp = anyModule.stackSave(); - try { - const args = anyModule.stackAlloc(JavaScriptMarshalerArgSize * 5); - const exc = get_arg(args, 0); - set_arg_type(exc, MarshalerType.None); - const res = get_arg(args, 1); - set_arg_type(res, MarshalerType.None); - set_gc_handle(res, gc_handle); - const arg1 = get_arg(args, 2); - const arg2 = get_arg(args, 3); - const arg3 = get_arg(args, 4); - - if (arg1_converter) { - arg1_converter(arg1, arg1_js); - } - if (arg2_converter) { - arg2_converter(arg2, arg2_js); - } - if (arg3_converter) { - arg3_converter(arg3, arg3_js); - } - - const fail = cwraps.mono_wasm_invoke_method_bound(runtimeHelpers.call_delegate, args); - if (fail) throw new Error("ERR23: Unexpected error: " + conv_string(fail)); - if (is_args_exception(args)) throw marshal_exception_to_js(exc); - - if (res_converter) { - return res_converter(res); - } - - } finally { - anyModule.stackRestore(sp); - } + result = (arg1_js: any, arg2_js: any, arg3_js: any): any => { + // arg numbers are shifted by one, the real first is a gc handle of the callback + return runtimeHelpers.javaScriptExports._call_delegate(gc_handle, arg1_js, arg2_js, arg3_js, res_converter, arg1_converter, arg2_converter, arg3_converter); }; - setup_managed_proxy(result, gc_handle); } diff --git a/src/mono/wasm/runtime/marshal.ts b/src/mono/wasm/runtime/marshal.ts index 7df0c232693be9..f62415ec934d27 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 ecc4275493d611..1653b94af2b72d 100644 --- a/src/mono/wasm/runtime/memory.ts +++ b/src/mono/wasm/runtime/memory.ts @@ -256,6 +256,15 @@ 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; +} + const BuiltinAtomics = globalThis.Atomics; export const Atomics = monoWasmThreads ? { @@ -269,4 +278,4 @@ export const Atomics = monoWasmThreads ? { } : { storeI32: setI32, notifyI32: () => { /*empty*/ } -}; +}; \ No newline at end of file diff --git a/src/mono/wasm/runtime/net6-legacy/buffers.ts b/src/mono/wasm/runtime/net6-legacy/buffers.ts new file mode 100644 index 00000000000000..e09b9ab4cab21b --- /dev/null +++ b/src/mono/wasm/runtime/net6-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. +// address 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. +// address 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/net6-legacy/corebindings.ts b/src/mono/wasm/runtime/net6-legacy/corebindings.ts new file mode 100644 index 00000000000000..4ac7b5b2263659 --- /dev/null +++ b/src/mono/wasm/runtime/net6-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 { mono_bind_method, _create_primitive_converters } from "./method-binding"; +import { WasmRoot } from "../roots"; +import { runtimeHelpers } from "../imports"; +import cwraps from "../cwraps"; +import { PromiseController } from "../promise-controller"; +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: PromiseController): 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/net6-legacy/cs-to-js.ts similarity index 77% rename from src/mono/wasm/runtime/cs-to-js.ts rename to src/mono/wasm/runtime/net6-legacy/cs-to-js.ts index 84f09852ce662e..51fc454cb82708 100644 --- a/src/mono/wasm/runtime/cs-to-js.ts +++ b/src/mono/wasm/runtime/net6-legacy/cs-to-js.ts @@ -1,27 +1,20 @@ -// 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 } 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 { createPromiseController } from "../promise-controller"; +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 } from "./cancelable-promise"; -import { getU32, getI32, getF32, getF64 } from "./memory"; -import { Int32Ptr, VoidPtr } from "./types/emscripten"; -import { ManagedObject } from "./marshal"; -import { createPromiseController } from "./promise-controller"; +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 { @@ -38,7 +31,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; } @@ -75,11 +68,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: @@ -147,7 +140,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 @@ -182,30 +175,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 @@ -213,20 +199,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; @@ -289,7 +276,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); @@ -305,7 +292,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); } @@ -320,7 +307,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); @@ -330,7 +317,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); @@ -339,11 +326,17 @@ 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; } + +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); +} \ No newline at end of file diff --git a/src/mono/wasm/runtime/net6-legacy/exports-legacy.ts b/src/mono/wasm/runtime/net6-legacy/exports-legacy.ts new file mode 100644 index 00000000000000..93b26e6f1e145e --- /dev/null +++ b/src/mono/wasm/runtime/net6-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"; +import { mono_wasm_load_runtime } from "../startup"; + +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, + + 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, + }); +} + +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/net6-legacy/js-to-cs.ts similarity index 80% rename from src/mono/wasm/runtime/js-to-cs.ts rename to src/mono/wasm/runtime/net6-legacy/js-to-cs.ts index 890febfd05c1fa..438afbae9b63cc 100644 --- a/src/mono/wasm/runtime/js-to-cs.ts +++ b/src/mono/wasm/runtime/net6-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/net6-legacy/method-binding.ts similarity index 83% rename from src/mono/wasm/runtime/method-binding.ts rename to src/mono/wasm/runtime/net6-legacy/method-binding.ts index 350530fc11900a..414c93377d5e15 100644 --- a/src/mono/wasm/runtime/method-binding.ts +++ b/src/mono/wasm/runtime/net6-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); @@ -413,23 +403,24 @@ export function mono_bind_method(method: MonoMethod, this_arg: null, args_marsha const unbox_buffer = Module._malloc(unbox_buffer_size); const token: BoundMethodToken = { - friendlyName: friendly_name, method, converter, 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 +440,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 +497,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 +553,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; } @@ -632,11 +632,57 @@ export type Converter = { } export type BoundMethodToken = { - friendlyName: string; method: MonoMethod; converter: Converter | null; scratchRootBuffer: WasmRootBuffer | null; 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/net6-legacy/method-calls.ts similarity index 52% rename from src/mono/wasm/runtime/method-calls.ts rename to src/mono/wasm/runtime/net6-legacy/method-calls.ts index 3f0301f64b8e2c..af7c17b80c8653 100644 --- a/src/mono/wasm/runtime/method-calls.ts +++ b/src/mono/wasm/runtime/net6-legacy/method-calls.ts @@ -1,147 +1,29 @@ // 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, mono_assert -} 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 { conv_string_root, js_string_to_mono_string_root } from "../strings"; +import { JSHandle, MonoStringRef, MonoObjectRef, MonoArray, MonoString, MonoObject, is_nullish, mono_assert } 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 { _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); @@ -160,36 +42,30 @@ 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 { - mono_assert(runtimeHelpers.mono_wasm_bindings_is_ready, "Expected binding to be initialized later during startup sequence."); - 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 { mono_assert(runtimeHelpers.mono_wasm_bindings_is_ready, "Expected binding to be initialized later during startup sequence."); - const method = mono_method_resolve(fqn); - if (typeof signature === "undefined") - signature = mono_method_get_call_signature_ref(method, undefined); + const key = `${fqn}-${signature}`; + let js_method = boundMethodsByFqn.get(key); + if (js_method === undefined) { + const method = mono_method_resolve(fqn); - return mono_bind_method(method, null, signature!, fqn); + if (typeof signature === "undefined") + signature = mono_method_get_call_signature_ref(method, undefined); + + 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 { @@ -209,10 +85,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); }; } @@ -416,93 +294,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/run.ts b/src/mono/wasm/runtime/run.ts index c7a2f020373c6f..fcf1c78664c075 100644 --- a/src/mono/wasm/runtime/run.ts +++ b/src/mono/wasm/runtime/run.ts @@ -1,5 +1,5 @@ import { INTERNAL, Module, runtimeHelpers } from "./imports"; -import { mono_call_assembly_entry_point } from "./method-calls"; +import { mono_call_assembly_entry_point } from "./net6-legacy/method-calls"; import { mono_wasm_wait_for_debugger } from "./debug"; import { abort_startup, mono_wasm_set_main_args } from "./startup"; diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts index 137c12a87947aa..f95c432707b075 100644 --- a/src/mono/wasm/runtime/startup.ts +++ b/src/mono/wasm/runtime/startup.ts @@ -2,18 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. import MonoWasmThreads from "consts:monoWasmThreads"; -import { mono_assert, CharPtrNull, DotnetModule, MonoConfig, wasm_type_symbol, MonoObject, MonoConfigError, LoadingResource, AssetEntry, ResourceRequest } from "./types"; -import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_PTHREAD, ENVIRONMENT_IS_SHELL, INTERNAL, Module, MONO, runtimeHelpers } from "./imports"; -import cwraps from "./cwraps"; +import { mono_assert, CharPtrNull, DotnetModule, MonoConfig, wasm_type_symbol, MonoObject, MonoConfigError, LoadingResource, AssetEntry, ResourceRequest, DotnetPublicAPI } from "./types"; +import { BINDING, ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_PTHREAD, ENVIRONMENT_IS_SHELL, INTERNAL, Module, MONO, runtimeHelpers } from "./imports"; +import cwraps, { init_c_exports } from "./cwraps"; import { mono_wasm_raise_debug_event, mono_wasm_runtime_ready } from "./debug"; 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, set_exit_code } from "./run"; import { initialize_marshalers_to_cs } from "./marshal-to-cs"; import { initialize_marshalers_to_js } from "./marshal-to-js"; @@ -24,9 +21,14 @@ import * as pthreads_worker from "./pthreads/worker"; import { createPromiseController } from "./promise-controller"; import { string_decoder } from "./strings"; import { mono_wasm_init_diagnostics } from "./diagnostics/index"; +import { init_managed_exports } from "./managed-exports"; +import { init_legacy_exports } from "./net6-legacy/corebindings"; +import { mono_wasm_load_bytes_into_heap } from "./memory"; +import { cwraps_internal } from "./exports-internal"; +import { cwraps_binding_api, cwraps_mono_api } from "./net6-legacy/exports-legacy"; let all_assets_loaded_in_memory: Promise | null = null; -const loaded_files: { url?: string, file: string }[] = []; +const loaded_files: { url: string, file: string }[] = []; const loaded_assets: { [id: string]: [VoidPtr, number] } = Object.create(null); let instantiated_assets_count = 0; let downloded_assets_count = 0; @@ -248,6 +250,10 @@ function mono_wasm_pre_init_essential(): void { // init_polyfills() is already called from export.ts init_crypto(); + init_c_exports(); + cwraps_internal(INTERNAL); + cwraps_mono_api(MONO); + cwraps_binding_api(BINDING); Module.removeRunDependency("mono_wasm_pre_init_essential"); } @@ -594,50 +600,12 @@ export function bindings_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(); } catch (err) { diff --git a/src/mono/wasm/runtime/types.ts b/src/mono/wasm/runtime/types.ts index b909569af544d0..494db151c1fdfc 100644 --- a/src/mono/wasm/runtime/types.ts +++ b/src/mono/wasm/runtime/types.ts @@ -2,7 +2,8 @@ // 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 { JavaScriptExports } from "./managed-exports"; +import { BINDINGType, MONOType } from "./net6-legacy/exports-legacy"; import { CharPtr, EmscriptenModule, ManagedPointer, NativePointer, VoidPtr, Int32Ptr } from "./types/emscripten"; export type GCHandle = { @@ -128,10 +129,12 @@ export type RuntimeHelpers = { 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; @@ -161,6 +164,7 @@ export type RuntimeHelpers = { ExitStatus: ExitStatusError; quit: Function, locateFile: (path: string, prefix?: string) => string, + javaScriptExports: JavaScriptExports, } export const wasm_type_symbol = Symbol.for("wasm type"); @@ -362,3 +366,19 @@ export function notThenable(x: T | PromiseLike): x is T { /// An identifier for an EventPipe session. The id is unique during the lifetime of the runtime. /// Primarily intended for debugging purposes. export type EventPipeSessionID = bigint; + +// 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, + } +} \ No newline at end of file diff --git a/src/mono/wasm/test-main.js b/src/mono/wasm/test-main.js index 2eb29cba95e449..d327e811252d07 100644 --- a/src/mono/wasm/test-main.js +++ b/src/mono/wasm/test-main.js @@ -490,7 +490,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;