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 1cd0730d41f53..e083c09a41f6f 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 @@ -45,7 +45,7 @@ public static void CallEntrypoint(JSMarshalerArgument* arguments_buffer) } catch (Exception ex) { - Environment.FailFast("CallEntrypoint: Unexpected synchronous failure. " + ex); + Environment.FailFast($"CallEntrypoint: Unexpected synchronous failure (ManagedThreadId {Environment.CurrentManagedThreadId}): " + ex); } } @@ -110,7 +110,7 @@ public static void ReleaseJSOwnedObjectByGCHandle(JSMarshalerArgument* arguments } catch (Exception ex) { - Environment.FailFast("ReleaseJSOwnedObjectByGCHandle: Unexpected synchronous failure. " + ex); + Environment.FailFast($"ReleaseJSOwnedObjectByGCHandle: Unexpected synchronous failure (ManagedThreadId {Environment.CurrentManagedThreadId}): " + ex); } } @@ -205,7 +205,7 @@ public static void CompleteTask(JSMarshalerArgument* arguments_buffer) } catch (Exception ex) { - Environment.FailFast("CompleteTask: Unexpected synchronous failure. " + ex); + Environment.FailFast($"CompleteTask: Unexpected synchronous failure (ManagedThreadId {Environment.CurrentManagedThreadId}): " + ex); } } @@ -280,7 +280,7 @@ public static void BindAssemblyExports(JSMarshalerArgument* arguments_buffer) } catch (Exception ex) { - Environment.FailFast("BindAssemblyExports: Unexpected synchronous failure. " + ex); + Environment.FailFast($"BindAssemblyExports: Unexpected synchronous failure (ManagedThreadId {Environment.CurrentManagedThreadId}): " + ex); } } 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 666f2caeb5a4a..974b0ce7e3eb7 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 @@ -151,7 +151,9 @@ internal unsafe JSBindingType this[int position] /// /// Generated metadata about the method signature used for marshaling. /// The intermediate buffer with marshalled arguments. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public static void InvokeJS(JSFunctionBinding signature, Span arguments) { InvokeJSImportImpl(signature, arguments); diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHost.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHost.cs index 0a685e996882d..20570bbab0fff 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHost.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHost.cs @@ -44,7 +44,9 @@ public static JSObject DotnetInstance /// The location of the module file. /// The token to monitor for cancellation requests. /// A proxy for the JavaScript object that contains the module's exports. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public static Task ImportAsync(string moduleName, string moduleUrl, CancellationToken cancellationToken = default) { return JSHostImplementation.ImportAsync(moduleName, moduleUrl, cancellationToken); 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 736f2de5a134d..232613b132841 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 @@ -64,7 +64,9 @@ public static MethodInfo GetTaskResultMethodInfo(Type taskType) throw new InvalidOperationException(); } +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public static void ThrowException(ref JSMarshalerArgument arg) { arg.ToManaged(out Exception? ex); @@ -85,7 +87,9 @@ public static async Task ImportAsync(string moduleName, string moduleU ConfigureAwaitOptions.ForceYielding); // this helps to finish the import before we bind the module in [JSImport] } +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public static async Task CancellationHelper(Task jsTask, CancellationToken cancellationToken) { if (jsTask.IsCompletedSuccessfully) @@ -293,6 +297,7 @@ public static unsafe JSFunctionBinding BindManagedFunction(string fullyQualified var signature = GetMethodSignature(signatures, null, null); + // this will hit JS side possibly on another thread, depending on JSProxyContext.CurrentThreadContext JavaScriptImports.BindCSFunction(monoMethod, assemblyName, nameSpace, shortClassName, methodName, signatureHash, (IntPtr)signature.Header); FreeMethodSignatureBuffer(signature); @@ -310,7 +315,9 @@ public static void SetHasExternalEventLoop(Thread thread) } #endif +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public static RuntimeMethodHandle GetMethodHandleFromIntPtr(IntPtr ptr) { var temp = new IntPtrAndHandle { ptr = ptr }; diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSMarshalerArgument.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSMarshalerArgument.cs index cdb4b0bc62775..9402aa5e8b80e 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSMarshalerArgument.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSMarshalerArgument.cs @@ -71,7 +71,9 @@ internal struct JSMarshalerArgumentImpl /// /// This API supports JSImport infrastructure and is not intended to be used directly from your code. /// +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void Initialize() { slot.Type = MarshalerType.None; @@ -85,7 +87,9 @@ public unsafe void Initialize() } #if FEATURE_WASM_MANAGED_THREADS +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif internal unsafe void InitializeWithContext(JSProxyContext knownProxyContext) { slot.Type = MarshalerType.None; @@ -111,11 +115,11 @@ internal JSProxyContext ToManagedContext // during JSExport, this is marshaling parameters and it would be set by: // - alloc_stack_frame // - set_js_handle/set_gc_handle - var proxyContextGCHandle = (GCHandle)slot.ContextHandle; - if (proxyContextGCHandle == default) + if (slot.ContextHandle == IntPtr.Zero) { - Environment.FailFast($"ContextHandle not set, ManagedThreadId: {Environment.CurrentManagedThreadId}. {Environment.NewLine} {Environment.StackTrace}"); + Environment.FailFast($"ContextHandle not set (ManagedThreadId {Environment.CurrentManagedThreadId}): {Environment.NewLine} {Environment.StackTrace}"); } + var proxyContextGCHandle = (GCHandle)slot.ContextHandle; var argumentContext = (JSProxyContext)proxyContextGCHandle.Target!; return argumentContext; #endif diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.References.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.References.cs index 35424deea557b..48c6cbc32862c 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.References.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.References.cs @@ -42,7 +42,9 @@ internal JSObject(IntPtr jsHandle, JSProxyContext ctx) /// public override string ToString() => $"(js-obj js '{JSHandle}')"; +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif internal void AssertNotDisposed() { lock (ProxyContext) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSProxyContext.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSProxyContext.cs index e03f924de110c..7a86f173d11e1 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSProxyContext.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSProxyContext.cs @@ -41,7 +41,9 @@ private JSProxyContext() public bool IsMainThread; public JSSynchronizationContext SynchronizationContext; +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public bool IsCurrentThread() { return ManagedTID == Environment.CurrentManagedThreadId; @@ -232,7 +234,9 @@ public static JSProxyContext CurrentOperationContext #endif +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public static JSProxyContext AssertIsInteropThread() { #if FEATURE_WASM_MANAGED_THREADS diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.BigInt64.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.BigInt64.cs index a2bce80d13b89..87eaa76966039 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.BigInt64.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.BigInt64.cs @@ -12,7 +12,9 @@ public partial struct JSMarshalerArgument /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManagedBig(out long value) { if (slot.Type == MarshalerType.None) @@ -28,7 +30,9 @@ public unsafe void ToManagedBig(out long value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJSBig(long value) { slot.Type = MarshalerType.BigInt64; @@ -40,7 +44,9 @@ public void ToJSBig(long value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManagedBig(out long? value) { if (slot.Type == MarshalerType.None) @@ -56,7 +62,9 @@ public unsafe void ToManagedBig(out long? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJSBig(long? value) { if (value.HasValue) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Bool.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Bool.cs index 375e4b97f4a5a..51ae3b6aed043 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Bool.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Bool.cs @@ -12,7 +12,9 @@ public partial struct JSMarshalerArgument /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out bool value) { if (slot.Type == MarshalerType.None) @@ -28,7 +30,9 @@ public unsafe void ToManaged(out bool value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJS(bool value) { slot.Type = MarshalerType.Boolean; @@ -40,7 +44,9 @@ public void ToJS(bool value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out bool? value) { if (slot.Type == MarshalerType.None) @@ -56,7 +62,9 @@ public unsafe void ToManaged(out bool? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJS(bool? value) { if (value.HasValue) 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 5392fca48fae8..113d4f1a06908 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 @@ -12,7 +12,9 @@ public partial struct JSMarshalerArgument /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out byte value) { if (slot.Type == MarshalerType.None) @@ -28,7 +30,9 @@ public unsafe void ToManaged(out byte value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJS(byte value) { slot.Type = MarshalerType.Byte; @@ -40,7 +44,9 @@ public void ToJS(byte value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out byte? value) { if (slot.Type == MarshalerType.None) @@ -56,7 +62,9 @@ public unsafe void ToManaged(out byte? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJS(byte? value) { if (value.HasValue) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Char.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Char.cs index 7daddfb0fd444..d31a8d3cd35c8 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Char.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Char.cs @@ -12,7 +12,9 @@ public partial struct JSMarshalerArgument /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out char value) { if (slot.Type == MarshalerType.None) @@ -28,7 +30,9 @@ public unsafe void ToManaged(out char value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJS(char value) { slot.Type = MarshalerType.Char; @@ -40,7 +44,9 @@ public void ToJS(char value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out char? value) { if (slot.Type == MarshalerType.None) @@ -56,7 +62,9 @@ public unsafe void ToManaged(out char? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJS(char? value) { if (value.HasValue) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.DateTime.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.DateTime.cs index 6521ac0c54b9a..7f63e5034c3b8 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.DateTime.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.DateTime.cs @@ -12,7 +12,9 @@ public partial struct JSMarshalerArgument /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out DateTimeOffset value) { if (slot.Type == MarshalerType.None) @@ -28,7 +30,9 @@ public unsafe void ToManaged(out DateTimeOffset value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJS(DateTimeOffset value) { slot.Type = MarshalerType.DateTimeOffset; @@ -40,7 +44,9 @@ public void ToJS(DateTimeOffset value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out DateTimeOffset? value) { if (slot.Type == MarshalerType.None) @@ -56,7 +62,9 @@ public unsafe void ToManaged(out DateTimeOffset? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJS(DateTimeOffset? value) { if (value.HasValue) @@ -75,7 +83,9 @@ public void ToJS(DateTimeOffset? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out DateTime value) { if (slot.Type == MarshalerType.None) @@ -91,7 +101,9 @@ public unsafe void ToManaged(out DateTime value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJS(DateTime value) { slot.Type = MarshalerType.DateTime; @@ -103,7 +115,9 @@ public void ToJS(DateTime value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out DateTime? value) { if (slot.Type == MarshalerType.None) @@ -119,7 +133,9 @@ public unsafe void ToManaged(out DateTime? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJS(DateTime? value) { if (value.HasValue) 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 9b7f48ed4b3ac..c83930bda8254 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 @@ -12,7 +12,9 @@ public partial struct JSMarshalerArgument /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out double value) { if (slot.Type == MarshalerType.None) @@ -28,7 +30,9 @@ public unsafe void ToManaged(out double value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJS(double value) { slot.Type = MarshalerType.Double; @@ -40,7 +44,9 @@ public void ToJS(double value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out double? value) { if (slot.Type == MarshalerType.None) @@ -56,7 +62,9 @@ public unsafe void ToManaged(out double? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJS(double? value) { if (value.HasValue) @@ -75,7 +83,9 @@ public void ToJS(double? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out double[]? value) { if (slot.Type == MarshalerType.None) @@ -93,7 +103,9 @@ public unsafe void ToManaged(out double[]? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJS(double[] value) { if (value == null) 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 e526fe4b52c89..86a57b48b345a 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 @@ -12,7 +12,9 @@ public partial struct JSMarshalerArgument /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out Exception? value) { if (slot.Type == MarshalerType.None) @@ -48,7 +50,9 @@ public unsafe void ToManaged(out Exception? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToJS(Exception? value) { if (value == null) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Int16.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Int16.cs index 6a2fec5e0f2fa..54ef3ee53c242 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Int16.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Int16.cs @@ -12,7 +12,9 @@ public partial struct JSMarshalerArgument /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out short value) { if (slot.Type == MarshalerType.None) @@ -28,7 +30,9 @@ public unsafe void ToManaged(out short value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJS(short value) { slot.Type = MarshalerType.Int16; @@ -40,7 +44,9 @@ public void ToJS(short value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out short? value) { if (slot.Type == MarshalerType.None) @@ -56,7 +62,9 @@ public unsafe void ToManaged(out short? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJS(short? value) { if (value.HasValue) 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 501484af3ab4f..a6990113c8ff4 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 @@ -12,7 +12,9 @@ public partial struct JSMarshalerArgument /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out int value) { if (slot.Type == MarshalerType.None) @@ -28,7 +30,9 @@ public unsafe void ToManaged(out int value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJS(int value) { slot.Type = MarshalerType.Int32; @@ -40,7 +44,9 @@ public void ToJS(int value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out int? value) { if (slot.Type == MarshalerType.None) @@ -56,7 +62,9 @@ public unsafe void ToManaged(out int? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJS(int? value) { if (value.HasValue) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Int52.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Int52.cs index 4893f32f5f20b..6b639ae77ef87 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Int52.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Int52.cs @@ -15,7 +15,9 @@ public partial struct JSMarshalerArgument /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out long value) { if (slot.Type == MarshalerType.None) @@ -31,7 +33,9 @@ public unsafe void ToManaged(out long value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJS(long value) { if (value < I52_MIN_VALUE || value > I52_MAX_VALUE) @@ -48,7 +52,9 @@ public void ToJS(long value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out long? value) { if (slot.Type == MarshalerType.None) @@ -64,7 +70,9 @@ public unsafe void ToManaged(out long? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJS(long? value) { if (value.HasValue) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.IntPtr.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.IntPtr.cs index 251db16215122..2737005542df4 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.IntPtr.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.IntPtr.cs @@ -12,7 +12,9 @@ public partial struct JSMarshalerArgument /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out IntPtr value) { if (slot.Type == MarshalerType.None) @@ -28,7 +30,9 @@ public unsafe void ToManaged(out IntPtr value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJS(IntPtr value) { slot.Type = MarshalerType.IntPtr; @@ -40,7 +44,9 @@ public void ToJS(IntPtr value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out IntPtr? value) { if (slot.Type == MarshalerType.None) @@ -56,7 +62,9 @@ public unsafe void ToManaged(out IntPtr? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJS(IntPtr? value) { if (value.HasValue) @@ -75,7 +83,9 @@ public void ToJS(IntPtr? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out void* value) { if (slot.Type == MarshalerType.None) @@ -91,7 +101,9 @@ public unsafe void ToManaged(out void* value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToJS(void* value) { slot.Type = MarshalerType.IntPtr; 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 7eb4440c565d6..bdaf228f6a720 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 @@ -12,7 +12,9 @@ public partial struct JSMarshalerArgument /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out JSObject? value) { if (slot.Type == MarshalerType.None) @@ -29,7 +31,9 @@ public unsafe void ToManaged(out JSObject? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJS(JSObject? value) { if (value == null) @@ -64,7 +68,9 @@ public void ToJS(JSObject? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out JSObject?[]? value) { if (slot.Type == MarshalerType.None) @@ -90,7 +96,9 @@ public unsafe void ToManaged(out JSObject?[]? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToJS(JSObject?[] value) { if (value == null) 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 3ce627aeff21c..8a5105cbce828 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 @@ -18,7 +18,9 @@ public partial struct JSMarshalerArgument /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out object? value) { if (slot.Type == MarshalerType.None) @@ -110,7 +112,9 @@ public unsafe void ToManaged(out object? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJS(object? value) { if (value == null) @@ -327,7 +331,9 @@ public void ToJS(object? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out object?[]? value) { if (slot.Type == MarshalerType.None) @@ -356,7 +362,9 @@ public unsafe void ToManaged(out object?[]? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToJS(object?[] value) { if (value == null) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Single.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Single.cs index c22d26c86520a..696cc9a60089b 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Single.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Single.cs @@ -12,7 +12,9 @@ public partial struct JSMarshalerArgument /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out float value) { if (slot.Type == MarshalerType.None) @@ -28,7 +30,9 @@ public unsafe void ToManaged(out float value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJS(float value) { slot.Type = MarshalerType.Single; @@ -40,7 +44,9 @@ public void ToJS(float value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out float? value) { if (slot.Type == MarshalerType.None) @@ -56,7 +62,9 @@ public unsafe void ToManaged(out float? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public void ToJS(float? value) { if (value.HasValue) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.String.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.String.cs index efe764cd837fb..247aad1ec613a 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.String.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.String.cs @@ -12,7 +12,9 @@ public partial struct JSMarshalerArgument /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out string? value) { if (slot.Type == MarshalerType.None) @@ -36,7 +38,9 @@ public unsafe void ToManaged(out string? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToJS(string? value) { if (value == null) @@ -69,7 +73,9 @@ public unsafe void ToJS(string? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToManaged(out string?[]? value) { if (slot.Type == MarshalerType.None) @@ -98,7 +104,9 @@ public unsafe void ToManaged(out string?[]? value) /// It's used by JSImport code generator and should not be used by developers in source code. /// /// The value to be marshaled. +#if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif public unsafe void ToJS(string?[] value) { if (value == null) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj index c3881268f2a6c..ef19eaa15fc20 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj @@ -20,17 +20,17 @@ false - - - - - - + + + + + + - - - + + + @@ -41,19 +41,19 @@ - - - + + + - - - - - - - - - + + + + + + + + + diff --git a/src/mono/browser/runtime/corebindings.c b/src/mono/browser/runtime/corebindings.c index 7329fb4bcd192..abaa7cab7b6f6 100644 --- a/src/mono/browser/runtime/corebindings.c +++ b/src/mono/browser/runtime/corebindings.c @@ -253,31 +253,37 @@ void mono_wasm_get_assembly_export (char *assembly_name, char *namespace, char * #ifndef DISABLE_THREADS +// async void mono_wasm_release_cs_owned_object_post (pthread_t target_tid, int js_handle) { mono_threads_wasm_async_run_in_target_thread_vi (target_tid, (void (*) (gpointer))mono_wasm_release_cs_owned_object, (gpointer)js_handle); } +// async void mono_wasm_resolve_or_reject_promise_post (pthread_t target_tid, void* args) { mono_threads_wasm_async_run_in_target_thread_vi (target_tid, (void (*) (gpointer))mono_wasm_resolve_or_reject_promise, (gpointer)args); } +// async void mono_wasm_cancel_promise_post (pthread_t target_tid, int task_holder_gc_handle) { mono_threads_wasm_async_run_in_target_thread_vi (target_tid, (void (*) (gpointer))mono_wasm_cancel_promise, (gpointer)task_holder_gc_handle); } +// async void mono_wasm_invoke_jsimport_async_post (pthread_t target_tid, void* signature, void* args) { mono_threads_wasm_async_run_in_target_thread_vii (target_tid, (void (*) (gpointer, gpointer))mono_wasm_invoke_jsimport, (gpointer)signature, (gpointer)args); } +// sync void mono_wasm_invoke_jsimport_sync_send (pthread_t target_tid, void* signature, void* args) { mono_threads_wasm_sync_run_in_target_thread_vii (target_tid, (void (*) (gpointer, gpointer))mono_wasm_invoke_jsimport, (gpointer)signature, (gpointer)args); } +// sync void mono_wasm_invoke_js_function_send (pthread_t target_tid, int function_js_handle, void *args) { mono_threads_wasm_sync_run_in_target_thread_vii (target_tid, (void (*) (gpointer, gpointer))mono_wasm_invoke_js_function, (gpointer)function_js_handle, (gpointer)args); diff --git a/src/mono/browser/runtime/invoke-cs.ts b/src/mono/browser/runtime/invoke-cs.ts index 287ee5ca6f681..f8aa906ce2df5 100644 --- a/src/mono/browser/runtime/invoke-cs.ts +++ b/src/mono/browser/runtime/invoke-cs.ts @@ -100,7 +100,9 @@ export function mono_wasm_bind_cs_function(method: MonoMethod, assemblyName: str // in Release configuration, it would be a trimmed by rollup if (BuildConfiguration === "Debug" && !runtimeHelpers.cspPolicy) { try { - bound_fn = new Function("fn", "return (function JSExport_" + methodName + "(){ return fn.apply(this, arguments)});")(bound_fn); + const url = `//# sourceURL=https://dotnet/JSExport/${methodName}`; + const body = `return (function JSExport_${methodName}(){ return fn.apply(this, arguments)});`; + bound_fn = new Function("fn", url + "\r\n" + body)(bound_fn); } catch (ex) { runtimeHelpers.cspPolicy = true; @@ -123,7 +125,8 @@ function bind_fn_0V(closure: BindingClosure) { mono_assert(!WasmEnableThreads || !closure.isDisposed, "The function was already disposed"); const sp = Module.stackSave(); try { - const args = alloc_stack_frame(2); + const size = 2; + const args = alloc_stack_frame(size); // call C# side invoke_sync_jsexport(method, args); } finally { @@ -144,7 +147,8 @@ function bind_fn_1V(closure: BindingClosure) { mono_assert(!WasmEnableThreads || !closure.isDisposed, "The function was already disposed"); const sp = Module.stackSave(); try { - const args = alloc_stack_frame(3); + const size = 3; + const args = alloc_stack_frame(size); marshaler1(args, arg1); // call C# side @@ -168,7 +172,8 @@ function bind_fn_1R(closure: BindingClosure) { mono_assert(!WasmEnableThreads || !closure.isDisposed, "The function was already disposed"); const sp = Module.stackSave(); try { - const args = alloc_stack_frame(3); + const size = 3; + const args = alloc_stack_frame(size); marshaler1(args, arg1); // call C# side @@ -195,14 +200,15 @@ function bind_fn_1RA(closure: BindingClosure) { mono_assert(!WasmEnableThreads || !closure.isDisposed, "The function was already disposed"); const sp = Module.stackSave(); try { - const args = alloc_stack_frame(3); + const size = 3; + const args = alloc_stack_frame(size); marshaler1(args, arg1); // pre-allocate the promise let promise = res_converter(args); // call C# side - invoke_async_jsexport(method, args, 3); + invoke_async_jsexport(method, args, size); // in case the C# side returned synchronously promise = end_marshal_task_to_js(args, undefined, promise); @@ -228,7 +234,8 @@ function bind_fn_2R(closure: BindingClosure) { mono_assert(!WasmEnableThreads || !closure.isDisposed, "The function was already disposed"); const sp = Module.stackSave(); try { - const args = alloc_stack_frame(4); + const size = 4; + const args = alloc_stack_frame(size); marshaler1(args, arg1); marshaler2(args, arg2); @@ -257,7 +264,8 @@ function bind_fn_2RA(closure: BindingClosure) { mono_assert(!WasmEnableThreads || !closure.isDisposed, "The function was already disposed"); const sp = Module.stackSave(); try { - const args = alloc_stack_frame(4); + const size = 4; + const args = alloc_stack_frame(size); marshaler1(args, arg1); marshaler2(args, arg2); @@ -265,7 +273,7 @@ function bind_fn_2RA(closure: BindingClosure) { let promise = res_converter(args); // call C# side - invoke_async_jsexport(method, args, 4); + invoke_async_jsexport(method, args, size); // in case the C# side returned synchronously promise = end_marshal_task_to_js(args, undefined, promise); @@ -293,7 +301,8 @@ function bind_fn(closure: BindingClosure) { mono_assert(!WasmEnableThreads || !closure.isDisposed, "The function was already disposed"); const sp = Module.stackSave(); try { - const args = alloc_stack_frame(2 + args_count); + const size = 2 + args_count; + const args = alloc_stack_frame(size); for (let index = 0; index < args_count; index++) { const marshaler = arg_marshalers[index]; if (marshaler) { @@ -309,13 +318,13 @@ function bind_fn(closure: BindingClosure) { // call C# side if (is_async) { - invoke_async_jsexport(method, args, 2 + args_count); + invoke_async_jsexport(method, args, size); // in case the C# side returned synchronously js_result = end_marshal_task_to_js(args, undefined, js_result); } else if (is_discard_no_wait) { // call C# side, fire and forget - invoke_async_jsexport(method, args, 2 + args_count); + invoke_async_jsexport(method, args, size); } else { invoke_sync_jsexport(method, args); diff --git a/src/mono/browser/runtime/invoke-js.ts b/src/mono/browser/runtime/invoke-js.ts index 75d7c96b9c737..d4c0459a0f1a3 100644 --- a/src/mono/browser/runtime/invoke-js.ts +++ b/src/mono/browser/runtime/invoke-js.ts @@ -132,31 +132,16 @@ function bind_js_import(signature: JSFunctionSignature): Function { } } - // this is just to make debugging easier by naming the function in the stack trace. - // It's not CSP compliant and possibly not performant, that's why it's only enabled in debug builds - // in Release configuration, it would be a trimmed by rollup - if (BuildConfiguration === "Debug" && !runtimeHelpers.cspPolicy) { - try { - bound_fn = new Function("fn", "return (function JSImport_" + js_function_name.replaceAll(".", "_") + "(){ return fn.apply(this, arguments)});")(bound_fn); - } - catch (ex) { - runtimeHelpers.cspPolicy = true; - } - } - function async_bound_fn(args: JSMarshalerArguments): void { - if (WasmEnableThreads) { - forceThreadMemoryViewRefresh(); - } + forceThreadMemoryViewRefresh(); bound_fn(args); } + function sync_bound_fn(args: JSMarshalerArguments): void { const previous = runtimeHelpers.isPendingSynchronousCall; try { + forceThreadMemoryViewRefresh(); runtimeHelpers.isPendingSynchronousCall = true; - if (WasmEnableThreads) { - forceThreadMemoryViewRefresh(); - } bound_fn(args); } finally { @@ -164,12 +149,29 @@ function bind_js_import(signature: JSFunctionSignature): Function { } } - let wrapped_fn: WrappedJSFunction; - if (is_async || is_discard_no_wait) { - wrapped_fn = async_bound_fn; + let wrapped_fn: WrappedJSFunction = bound_fn; + if (WasmEnableThreads) { + if (is_async || is_discard_no_wait) { + wrapped_fn = async_bound_fn; + } + else { + wrapped_fn = sync_bound_fn; + } } - else { - wrapped_fn = sync_bound_fn; + + // this is just to make debugging easier by naming the function in the stack trace. + // It's not CSP compliant and possibly not performant, that's why it's only enabled in debug builds + // in Release configuration, it would be a trimmed by rollup + if (BuildConfiguration === "Debug" && !runtimeHelpers.cspPolicy) { + try { + const fname = js_function_name.replaceAll(".", "_"); + const url = `//# sourceURL=https://dotnet/JSImport/${fname}`; + const body = `return (function JSImport_${fname}(){ return fn.apply(this, arguments)});`; + wrapped_fn = new Function("fn", url + "\r\n" + body)(wrapped_fn); + } + catch (ex) { + runtimeHelpers.cspPolicy = true; + } } (wrapped_fn)[imported_js_function_symbol] = closure; diff --git a/src/mono/browser/runtime/loader/assets.ts b/src/mono/browser/runtime/loader/assets.ts index 49d2f0f8ac6d8..2416f931b52f1 100644 --- a/src/mono/browser/runtime/loader/assets.ts +++ b/src/mono/browser/runtime/loader/assets.ts @@ -737,7 +737,6 @@ export async function streamingCompileWasm() { loaderHelpers.wasmCompilePromise.promise_control.reject(err); } } - export function preloadWorkers() { if (!WasmEnableThreads) return; const jsModuleWorker = resolve_single_asset_path("js-module-threads"); diff --git a/src/mono/browser/runtime/loader/exit.ts b/src/mono/browser/runtime/loader/exit.ts index 7f62ef7b56066..057197f87d452 100644 --- a/src/mono/browser/runtime/loader/exit.ts +++ b/src/mono/browser/runtime/loader/exit.ts @@ -22,7 +22,12 @@ export function assert_runtime_running() { mono_assert(runtimeHelpers.runtimeReady, ".NET runtime didn't start yet. Please call dotnet.create() first."); } } else { - mono_assert(!loaderHelpers.assertAfterExit, () => `.NET runtime already exited with ${loaderHelpers.exitCode} ${loaderHelpers.exitReason}. You can use runtime.runMain() which doesn't exit the runtime.`); + const message = `.NET runtime already exited with ${loaderHelpers.exitCode} ${loaderHelpers.exitReason}. You can use runtime.runMain() which doesn't exit the runtime.`; + if (loaderHelpers.assertAfterExit) { + mono_assert(false, message); + } else { + mono_log_warn(message); + } } } diff --git a/src/mono/browser/runtime/managed-exports.ts b/src/mono/browser/runtime/managed-exports.ts index c8aabae88e37b..62fee7656e282 100644 --- a/src/mono/browser/runtime/managed-exports.ts +++ b/src/mono/browser/runtime/managed-exports.ts @@ -9,7 +9,7 @@ import { runtimeHelpers, Module, loaderHelpers, mono_assert } from "./globals"; import { JavaScriptMarshalerArgSize, alloc_stack_frame, get_arg, get_arg_gc_handle, is_args_exception, set_arg_intptr, set_arg_type, set_gc_handle } from "./marshal"; import { marshal_array_to_cs, marshal_array_to_cs_impl, marshal_bool_to_cs, marshal_exception_to_cs, marshal_intptr_to_cs, marshal_string_to_cs } from "./marshal-to-cs"; import { marshal_int32_to_js, end_marshal_task_to_js, marshal_string_to_js, begin_marshal_task_to_js, marshal_exception_to_js } from "./marshal-to-js"; -import { do_not_force_dispose } from "./gc-handles"; +import { do_not_force_dispose, is_gcv_handle } from "./gc-handles"; import { assert_c_interop, assert_js_interop } from "./invoke-js"; import { mono_wasm_main_thread_ptr } from "./pthreads"; import { _zero_region } from "./memory"; @@ -47,7 +47,8 @@ export function call_entry_point(main_assembly_name: string, program_args: strin loaderHelpers.assert_runtime_running(); const sp = Module.stackSave(); try { - const args = alloc_stack_frame(5); + const size = 5; + const args = alloc_stack_frame(size); const res = get_arg(args, 1); const arg1 = get_arg(args, 2); const arg2 = get_arg(args, 3); @@ -60,7 +61,7 @@ export function call_entry_point(main_assembly_name: string, program_args: strin // because this is async, we could pre-allocate the promise let promise = begin_marshal_task_to_js(res, MarshalerType.TaskPreCreated, marshal_int32_to_js); - invoke_async_jsexport(managedExports.CallEntrypoint, args, 5); + invoke_async_jsexport(managedExports.CallEntrypoint, args, size); // in case the C# side returned synchronously promise = end_marshal_task_to_js(args, marshal_int32_to_js, promise); @@ -80,7 +81,8 @@ export function call_entry_point(main_assembly_name: string, program_args: strin export function load_satellite_assembly(dll: Uint8Array): void { const sp = Module.stackSave(); try { - const args = alloc_stack_frame(3); + const size = 3; + const args = alloc_stack_frame(size); const arg1 = get_arg(args, 2); set_arg_type(arg1, MarshalerType.Array); marshal_array_to_cs(arg1, dll, MarshalerType.Byte); @@ -94,7 +96,8 @@ export function load_satellite_assembly(dll: Uint8Array): void { export function load_lazy_assembly(dll: Uint8Array, pdb: Uint8Array | null): void { const sp = Module.stackSave(); try { - const args = alloc_stack_frame(4); + const size = 4; + const args = alloc_stack_frame(size); const arg1 = get_arg(args, 2); const arg2 = get_arg(args, 3); set_arg_type(arg1, MarshalerType.Array); @@ -113,12 +116,17 @@ export function release_js_owned_object_by_gc_handle(gc_handle: GCHandle) { loaderHelpers.assert_runtime_running(); const sp = Module.stackSave(); try { - const args = alloc_stack_frame(3); + const size = 3; + const args = alloc_stack_frame(size); const arg1 = get_arg(args, 2); set_arg_type(arg1, MarshalerType.Object); set_gc_handle(arg1, gc_handle); - // this must stay synchronous for free_gcv_handle sake - invoke_sync_jsexport(managedExports.ReleaseJSOwnedObjectByGCHandle, args); + if (is_gcv_handle(gc_handle)) { + // this must stay synchronous for free_gcv_handle sake + invoke_sync_jsexport(managedExports.ReleaseJSOwnedObjectByGCHandle, args); + } else { + invoke_async_jsexport(managedExports.ReleaseJSOwnedObjectByGCHandle, args, size); + } } finally { Module.stackRestore(sp); } @@ -129,7 +137,8 @@ export function complete_task(holder_gc_handle: GCHandle, isCanceling: boolean, loaderHelpers.assert_runtime_running(); const sp = Module.stackSave(); try { - const args = alloc_stack_frame(5); + const size = 5; + const args = alloc_stack_frame(size); const res = get_arg(args, 1); const arg1 = get_arg(args, 2); set_arg_type(arg1, MarshalerType.Object); @@ -146,7 +155,7 @@ export function complete_task(holder_gc_handle: GCHandle, isCanceling: boolean, mono_assert(res_converter, "res_converter missing"); res_converter(arg3, data); } - invoke_async_jsexport(managedExports.CompleteTask, args, 4); + invoke_async_jsexport(managedExports.CompleteTask, args, size); } finally { Module.stackRestore(sp); } @@ -157,7 +166,8 @@ export function call_delegate(callback_gc_handle: GCHandle, arg1_js: any, arg2_j loaderHelpers.assert_runtime_running(); const sp = Module.stackSave(); try { - const args = alloc_stack_frame(6); + const size = 6; + const args = alloc_stack_frame(size); const arg1 = get_arg(args, 2); set_arg_type(arg1, MarshalerType.Object); @@ -193,7 +203,8 @@ export function get_managed_stack_trace(exception_gc_handle: GCHandle) { loaderHelpers.assert_runtime_running(); const sp = Module.stackSave(); try { - const args = alloc_stack_frame(3); + const size = 3; + const args = alloc_stack_frame(size); const arg1 = get_arg(args, 2); set_arg_type(arg1, MarshalerType.Exception); @@ -237,7 +248,7 @@ export function install_main_synchronization_context(): GCHandle { export function invoke_async_jsexport(method: MonoMethod, args: JSMarshalerArguments, size: number): void { assert_js_interop(); - if (!WasmEnableThreads || runtimeHelpers.isCurrentThread) { + if (!WasmEnableThreads || runtimeHelpers.isManagedRunningOnCurrentThread) { cwraps.mono_wasm_invoke_jsexport(method, args as any); if (is_args_exception(args)) { const exc = get_arg(args, 0); @@ -257,12 +268,12 @@ export function invoke_async_jsexport(method: MonoMethod, args: JSMarshalerArgum export function invoke_sync_jsexport(method: MonoMethod, args: JSMarshalerArguments): void { assert_js_interop(); - if (!WasmEnableThreads || runtimeHelpers.isCurrentThread) { + if (!WasmEnableThreads || runtimeHelpers.isManagedRunningOnCurrentThread) { cwraps.mono_wasm_invoke_jsexport(method, args as any); } else { throw new Error("Should be unreachable until we implement deputy."); /* - if (!runtimeHelpers.isCurrentThread && runtimeHelpers.isPendingSynchronousCall) { + if (!runtimeHelpers.isManagedRunningOnCurrentThread && runtimeHelpers.isPendingSynchronousCall) { throw new Error("Cannot call synchronous C# method from inside a synchronous call to a JS method."); } // this is blocking too @@ -280,7 +291,8 @@ export function bind_assembly_exports(assemblyName: string): Promise { loaderHelpers.assert_runtime_running(); const sp = Module.stackSave(); try { - const args = alloc_stack_frame(3); + const size = 3; + const args = alloc_stack_frame(size); const res = get_arg(args, 1); const arg1 = get_arg(args, 2); marshal_string_to_cs(arg1, assemblyName); @@ -288,7 +300,7 @@ export function bind_assembly_exports(assemblyName: string): Promise { // because this is async, we could pre-allocate the promise let promise = begin_marshal_task_to_js(res, MarshalerType.TaskPreCreated); - invoke_async_jsexport(managedExports.BindAssemblyExports, args, 3); + invoke_async_jsexport(managedExports.BindAssemblyExports, args, size); // in case the C# side returned synchronously promise = end_marshal_task_to_js(args, marshal_int32_to_js, promise); diff --git a/src/mono/browser/runtime/marshal-to-cs.ts b/src/mono/browser/runtime/marshal-to-cs.ts index a42eb724a9215..f6ddf436d2165 100644 --- a/src/mono/browser/runtime/marshal-to-cs.ts +++ b/src/mono/browser/runtime/marshal-to-cs.ts @@ -11,11 +11,11 @@ import { alloc_gcv_handle, assert_not_disposed, cs_owned_js_handle_symbol, js_ow import { Module, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; import { 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_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_bool, set_arg_date, set_arg_length, get_arg, get_signature_arg1_type, get_signature_arg2_type, js_to_cs_marshalers, get_signature_res_type, bound_js_function_symbol, set_arg_u16, array_element_size, get_string_root, Span, ArraySegment, MemoryViewType, get_signature_arg3_type, set_arg_i64_big, set_arg_intptr, - set_arg_element_type, ManagedObject, JavaScriptMarshalerArgSize, proxy_debug_symbol, get_arg_gc_handle, get_arg_type + set_arg_element_type, ManagedObject, JavaScriptMarshalerArgSize, proxy_debug_symbol, get_arg_gc_handle, get_arg_type, set_arg_proxy_context } from "./marshal"; import { get_marshaler_to_js_by_type } from "./marshal-to-js"; import { _zero_region, forceThreadMemoryViewRefresh, localHeapViewF64, localHeapViewI32, localHeapViewU8 } from "./memory"; @@ -105,7 +105,7 @@ export function marshal_bool_to_cs(arg: JSMarshalerArgument, value: any): void { } else { set_arg_type(arg, MarshalerType.Boolean); - set_arg_b8(arg, value); + set_arg_bool(arg, value); } } @@ -285,7 +285,7 @@ function _marshal_function_to_cs(arg: JSMarshalerArgument, value: Function, _?: if (arg3_converter) { arg3_js = arg3_converter(arg3); } - runtimeHelpers.isPendingSynchronousCall = true; // this is alway synchronous call for now + runtimeHelpers.isPendingSynchronousCall = true; // this is always synchronous call for now const res_js = value(arg1_js, arg2_js, arg3_js); if (res_converter) { res_converter(res, res_js); @@ -441,6 +441,7 @@ export function marshal_exception_to_cs(arg: JSMarshalerArgument, value: any): v export function marshal_js_object_to_cs(arg: JSMarshalerArgument, value: any): void { if (value === undefined || value === null) { set_arg_type(arg, MarshalerType.None); + set_arg_proxy_context(arg); } else { // if value was ManagedObject, it would be double proxied, but the C# signature requires that @@ -459,6 +460,7 @@ export function marshal_js_object_to_cs(arg: JSMarshalerArgument, value: any): v function _marshal_cs_object_to_cs(arg: JSMarshalerArgument, value: any): void { if (value === undefined || value === null) { set_arg_type(arg, MarshalerType.None); + set_arg_proxy_context(arg); } else { const gc_handle = value[js_owned_gc_handle_symbol]; @@ -478,7 +480,7 @@ function _marshal_cs_object_to_cs(arg: JSMarshalerArgument, value: any): void { } else if (js_type === "boolean") { set_arg_type(arg, MarshalerType.Boolean); - set_arg_b8(arg, value); + set_arg_bool(arg, value); } else if (value instanceof Date) { set_arg_type(arg, MarshalerType.DateTime); diff --git a/src/mono/browser/runtime/marshal-to-js.ts b/src/mono/browser/runtime/marshal-to-js.ts index f02c81765e6a4..bee7d560c8890 100644 --- a/src/mono/browser/runtime/marshal-to-js.ts +++ b/src/mono/browser/runtime/marshal-to-js.ts @@ -11,7 +11,7 @@ import { Module, loaderHelpers, mono_assert } from "./globals"; import { ManagedObject, ManagedError, 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, get_arg, set_arg_type, + get_arg_bool, get_arg_date, get_arg_length, get_arg, set_arg_type, get_signature_arg2_type, get_signature_arg1_type, cs_to_js_marshalers, get_signature_res_type, get_arg_u16, array_element_size, get_string_root, ArraySegment, Span, MemoryViewType, get_signature_arg3_type, get_arg_i64_big, get_arg_intptr, get_arg_element_type, JavaScriptMarshalerArgSize, proxy_debug_symbol, set_js_handle, is_receiver_should_free @@ -101,7 +101,7 @@ function _marshal_bool_to_js(arg: JSMarshalerArgument): boolean | null { if (type == MarshalerType.None) { return null; } - return get_arg_b8(arg); + return get_arg_bool(arg); } function _marshal_byte_to_js(arg: JSMarshalerArgument): number | null { diff --git a/src/mono/browser/runtime/marshal.ts b/src/mono/browser/runtime/marshal.ts index c3c07d1c18895..b6f825c4f5738 100644 --- a/src/mono/browser/runtime/marshal.ts +++ b/src/mono/browser/runtime/marshal.ts @@ -85,7 +85,7 @@ export function is_args_exception(args: JSMarshalerArguments): boolean { } export function is_receiver_should_free(args: JSMarshalerArguments): boolean { - if (WasmEnableThreads) return false; + if (!WasmEnableThreads) return false; mono_assert(args, "Null args"); return getB32(args + JSMarshalerArgumentOffsets.ReceiverShouldFree); } @@ -193,9 +193,9 @@ export function set_arg_element_type(arg: JSMarshalerArgument, type: MarshalerTy setU8(arg + JSMarshalerArgumentOffsets.ElementType, type); } -export function get_arg_b8(arg: JSMarshalerArgument): boolean { +export function get_arg_bool(arg: JSMarshalerArgument): boolean { mono_assert(arg, "Null arg"); - return !!getU8(arg); + return getB32(arg); } export function get_arg_u8(arg: JSMarshalerArgument): number { @@ -251,10 +251,10 @@ export function get_arg_f64(arg: JSMarshalerArgument): number { return getF64(arg); } -export function set_arg_b8(arg: JSMarshalerArgument, value: boolean): void { +export function set_arg_bool(arg: JSMarshalerArgument, value: boolean): void { mono_assert(arg, "Null arg"); mono_check(typeof value === "boolean", () => `Value is not a Boolean: ${value} (${typeof (value)})`); - setU8(arg, value ? 1 : 0); + setB32(arg, value ? 1 : 0); } export function set_arg_u8(arg: JSMarshalerArgument, value: number): void { diff --git a/src/mono/browser/runtime/polyfills.ts b/src/mono/browser/runtime/polyfills.ts index 0f8700f84754e..26150c797c81e 100644 --- a/src/mono/browser/runtime/polyfills.ts +++ b/src/mono/browser/runtime/polyfills.ts @@ -5,7 +5,7 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import type { EmscriptenReplacements } from "./types/internal"; import type { TypedArray } from "./types/emscripten"; import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_WORKER, INTERNAL, Module, loaderHelpers, runtimeHelpers } from "./globals"; -import { replaceEmscriptenPThreadWorker } from "./pthreads"; +import { replaceEmscriptenTLSInit } from "./pthreads"; import { replaceEmscriptenPThreadUI } from "./pthreads"; const dummyPerformance = { @@ -36,7 +36,7 @@ export function initializeReplacements(replacements: EmscriptenReplacements): vo // threads if (WasmEnableThreads && replacements.modulePThread) { if (ENVIRONMENT_IS_WORKER) { - replaceEmscriptenPThreadWorker(replacements.modulePThread); + replaceEmscriptenTLSInit(replacements.modulePThread); } else { replaceEmscriptenPThreadUI(replacements.modulePThread); } diff --git a/src/mono/browser/runtime/pthreads/index.ts b/src/mono/browser/runtime/pthreads/index.ts index a7b5da11e03fb..74d9a4be7561b 100644 --- a/src/mono/browser/runtime/pthreads/index.ts +++ b/src/mono/browser/runtime/pthreads/index.ts @@ -14,5 +14,5 @@ export { addUnsettledPromise, settleUnsettledPromise, mono_wasm_eventloop_has_un export { mono_wasm_pthread_on_pthread_attached, mono_wasm_pthread_on_pthread_unregistered, mono_wasm_pthread_on_pthread_registered, mono_wasm_pthread_set_name, currentWorkerThreadEvents, - dotnetPthreadCreated, initWorkerThreadEvents, replaceEmscriptenPThreadWorker, pthread_self + dotnetPthreadCreated, initWorkerThreadEvents, replaceEmscriptenTLSInit, pthread_self } from "./worker-thread"; diff --git a/src/mono/browser/runtime/pthreads/shared.ts b/src/mono/browser/runtime/pthreads/shared.ts index 97c41197d35e0..eda95db4c6226 100644 --- a/src/mono/browser/runtime/pthreads/shared.ts +++ b/src/mono/browser/runtime/pthreads/shared.ts @@ -39,7 +39,7 @@ export function mono_wasm_install_js_worker_interop(context_gc_handle: GCHandle) runtimeHelpers.proxyGCHandle = context_gc_handle; if (ENVIRONMENT_IS_PTHREAD) { runtimeHelpers.managedThreadTID = mono_wasm_pthread_ptr(); - runtimeHelpers.isCurrentThread = true; + runtimeHelpers.isManagedRunningOnCurrentThread = true; } Module.runtimeKeepalivePush(); monoThreadInfo.isDirtyBecauseOfInterop = true; @@ -87,11 +87,16 @@ export function update_thread_info(): void { set_thread_prefix(monoThreadInfo.threadPrefix!); } - (globalThis as any).monoThreadInfo = monoThreadInfo; + // this is just to make debugging easier by naming the thread debugger window. + // It's not CSP compliant and possibly not performant, that's why it's only enabled in debug builds + // in Release configuration, it would be a trimmed by rollup if (WasmEnableThreads && BuildConfiguration === "Debug" && !runtimeHelpers.cspPolicy) { monoThreadInfo.updateCount++; try { - (globalThis as any).monoThreadInfoFn = new Function(`//# sourceURL=https://${monoThreadInfo.updateCount}WorkerInfo${monoThreadInfo.isAttached ? monoThreadInfo.threadPrefix : ""}/\r\nconsole.log("${JSON.stringify(monoThreadInfo)}");`); + const url = `//# sourceURL=https://dotnet/thread/${monoThreadInfo.updateCount}-${monoThreadInfo.threadPrefix}`; + const infoJson = JSON.stringify(monoThreadInfo, null, 2); + const body = `const monoThreadInfo=${infoJson};\r\nconsole.log(monoThreadInfo);`; + (globalThis as any).monoThreadInfoFn = new Function(body + "\r\n" + url); } catch (ex) { runtimeHelpers.cspPolicy = true; diff --git a/src/mono/browser/runtime/pthreads/ui-thread.ts b/src/mono/browser/runtime/pthreads/ui-thread.ts index 878ee38eadbb9..d145af3c50a6c 100644 --- a/src/mono/browser/runtime/pthreads/ui-thread.ts +++ b/src/mono/browser/runtime/pthreads/ui-thread.ts @@ -5,7 +5,7 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import BuildConfiguration from "consts:configuration"; import { } from "../globals"; -import { mono_log_warn } from "../logging"; +import { mono_log_debug, mono_log_warn } from "../logging"; import { MonoWorkerToMainMessage, monoThreadInfo, mono_wasm_pthread_ptr, update_thread_info, worker_empty_prefix } from "./shared"; import { Module, ENVIRONMENT_IS_WORKER, createPromiseController, loaderHelpers, mono_assert, runtimeHelpers } from "../globals"; import { PThreadLibrary, MainToWorkerMessageType, MonoThreadMessage, PThreadInfo, PThreadPtr, PThreadPtrNull, PThreadWorker, PromiseAndController, PromiseController, Thread, WorkerToMainMessageType, monoMessageSymbol } from "../types/internal"; @@ -148,10 +148,11 @@ export async function mono_wasm_init_threads() { if (!WasmEnableThreads) return; // setup the UI thread - monoThreadInfo.pthreadId = mono_wasm_pthread_ptr(); + runtimeHelpers.currentThreadTID = monoThreadInfo.pthreadId = mono_wasm_pthread_ptr(); monoThreadInfo.threadName = "UI Thread"; monoThreadInfo.isUI = true; monoThreadInfo.isRunning = true; + monoThreadInfo.workerNumber = 0; update_thread_info(); // wait until all workers in the pool are loaded - ready to be used as pthread synchronously @@ -211,7 +212,11 @@ export function init_finalizer_thread() { // we don't need it immediately, so we can wait a bit, to keep CPU working on normal startup setTimeout(() => { try { - cwraps.mono_wasm_init_finalizer_thread(); + if (loaderHelpers.is_runtime_running()) { + cwraps.mono_wasm_init_finalizer_thread(); + } else { + mono_log_debug("init_finalizer_thread skipped"); + } } catch (err) { mono_log_error("init_finalizer_thread() failed", err); diff --git a/src/mono/browser/runtime/pthreads/worker-thread.ts b/src/mono/browser/runtime/pthreads/worker-thread.ts index 083f87d7de025..5631c2475e652 100644 --- a/src/mono/browser/runtime/pthreads/worker-thread.ts +++ b/src/mono/browser/runtime/pthreads/worker-thread.ts @@ -5,11 +5,9 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; -import { Module } from "../globals"; - -import { ENVIRONMENT_IS_PTHREAD, loaderHelpers, mono_assert, runtimeHelpers } from "../globals"; +import { ENVIRONMENT_IS_PTHREAD, Module, loaderHelpers, mono_assert, runtimeHelpers } from "../globals"; import { PThreadSelf, monoThreadInfo, mono_wasm_pthread_ptr, postMessageToMain, update_thread_info } from "./shared"; -import { PThreadLibrary, MonoThreadMessage, PThreadInfo, PThreadPtr, WorkerToMainMessageType, is_nullish } from "../types/internal"; +import { PThreadLibrary, MonoThreadMessage, PThreadInfo, PThreadPtr, WorkerToMainMessageType } from "../types/internal"; import { makeWorkerThreadEvent, dotnetPthreadCreated, @@ -71,7 +69,7 @@ function monoDedicatedChannelMessageFromMainToWorker(event: MessageEvent } export function on_emscripten_thread_init(pthread_ptr: PThreadPtr) { - monoThreadInfo.pthreadId = pthread_ptr; + runtimeHelpers.currentThreadTID = monoThreadInfo.pthreadId = pthread_ptr; forceThreadMemoryViewRefresh(); } @@ -82,10 +80,9 @@ export function mono_wasm_pthread_on_pthread_created(): void { if (!WasmEnableThreads) return; try { forceThreadMemoryViewRefresh(); - const pthread_id = mono_wasm_pthread_ptr(); - mono_assert(!is_nullish(pthread_id), "pthread_self() returned null"); - monoThreadInfo.pthreadId = pthread_id; + mono_assert(pthread_id == monoThreadInfo.pthreadId, `needs to match (mono_wasm_pthread_ptr ${pthread_id}, threadId from thread info ${monoThreadInfo.pthreadId})`); + monoThreadInfo.reuseCount++; monoThreadInfo.updateCount++; monoThreadInfo.threadName = "pthread-assigned"; @@ -206,18 +203,24 @@ export function mono_wasm_pthread_on_pthread_unregistered(pthread_id: PThreadPtr } } -export function replaceEmscriptenPThreadWorker(modulePThread: PThreadLibrary): void { +export function replaceEmscriptenTLSInit(modulePThread: PThreadLibrary): void { if (!WasmEnableThreads) return; const originalThreadInitTLS = modulePThread.threadInitTLS; - const original_emscripten_thread_init = (Module as any)["__emscripten_thread_init"]; - (Module as any)["__emscripten_thread_init"] = (pthread_ptr: PThreadPtr, isMainBrowserThread: number, isMainRuntimeThread: number, canBlock: number) => { - on_emscripten_thread_init(pthread_ptr); - original_emscripten_thread_init(pthread_ptr, isMainBrowserThread, isMainRuntimeThread, canBlock); - }; modulePThread.threadInitTLS = (): void => { originalThreadInitTLS(); mono_wasm_pthread_on_pthread_created(); }; -} \ No newline at end of file +} + +export function replaceEmscriptenPThreadInit(): void { + const original_emscripten_thread_init = Module["__emscripten_thread_init"]; + function emscripten_thread_init_wrapper(pthread_ptr: PThreadPtr, isMainBrowserThread: number, isMainRuntimeThread: number, canBlock: number) { + on_emscripten_thread_init(pthread_ptr); + original_emscripten_thread_init(pthread_ptr, isMainBrowserThread, isMainRuntimeThread, canBlock); + // re-install self + Module["__emscripten_thread_init"] = emscripten_thread_init_wrapper; + } + Module["__emscripten_thread_init"] = emscripten_thread_init_wrapper; +} diff --git a/src/mono/browser/runtime/startup.ts b/src/mono/browser/runtime/startup.ts index 9759e879254b0..8d98635b55637 100644 --- a/src/mono/browser/runtime/startup.ts +++ b/src/mono/browser/runtime/startup.ts @@ -32,6 +32,7 @@ import { assertNoProxies } from "./gc-handles"; import { runtimeList } from "./exports"; import { nativeAbort, nativeExit } from "./run"; import { mono_wasm_init_diagnostics } from "./diagnostics"; +import { replaceEmscriptenPThreadInit } from "./pthreads/worker-thread"; export async function configureRuntimeStartup(): Promise { await init_polyfills_async(); @@ -125,6 +126,7 @@ async function instantiateWasmWorker( await loaderHelpers.afterConfigLoaded.promise; replace_linker_placeholders(imports); + replaceEmscriptenPThreadInit(); // Instantiate from the module posted from the main thread. // We can just use sync instantiation in the worker. @@ -268,13 +270,9 @@ async function onRuntimeInitializedAsync(userOnRuntimeInitialized: () => void) { Module.runtimeKeepalivePush(); - // load runtime and apply environment settings (if necessary) + // load mono runtime and apply environment settings (if necessary) await start_runtime(); - if (runtimeHelpers.config.interpreterPgo) { - await interp_pgo_load_data(); - } - if (!ENVIRONMENT_IS_WORKER) { Module.runtimeKeepalivePush(); } @@ -515,12 +513,12 @@ export async function start_runtime() { if (WasmEnableThreads) { monoThreadInfo.isAttached = true; + monoThreadInfo.isRunning = true; monoThreadInfo.isRegistered = true; - monoThreadInfo.pthreadId = runtimeHelpers.managedThreadTID = mono_wasm_pthread_ptr(); - monoThreadInfo.workerNumber = 0; + runtimeHelpers.currentThreadTID = monoThreadInfo.pthreadId = runtimeHelpers.managedThreadTID = mono_wasm_pthread_ptr(); update_thread_info(); runtimeHelpers.proxyGCHandle = install_main_synchronization_context(); - runtimeHelpers.isCurrentThread = true; + runtimeHelpers.isManagedRunningOnCurrentThread = true; // start finalizer thread, lazy init_finalizer_thread(); @@ -529,6 +527,10 @@ export async function start_runtime() { // get GCHandle of the ctx runtimeHelpers.afterMonoStarted.promise_control.resolve(runtimeHelpers.proxyGCHandle); + if (runtimeHelpers.config.interpreterPgo) { + await interp_pgo_load_data(); + } + endMeasure(mark, MeasuredBlock.startRuntime); } catch (err) { mono_log_error("start_runtime() failed", err); diff --git a/src/mono/browser/runtime/types/internal.ts b/src/mono/browser/runtime/types/internal.ts index ae4ce1ff4813a..e92c24faa03b2 100644 --- a/src/mono/browser/runtime/types/internal.ts +++ b/src/mono/browser/runtime/types/internal.ts @@ -207,8 +207,9 @@ export type RuntimeHelpers = { monoThreadInfo: PThreadInfo, proxyGCHandle: GCHandle | undefined, managedThreadTID: PThreadPtr, - isCurrentThread: boolean, - isPendingSynchronousCall: boolean, // true when we are in the middle of a synchronous call from managed code with the same JSProxyContext + currentThreadTID: PThreadPtr, + isManagedRunningOnCurrentThread: boolean, + isPendingSynchronousCall: boolean, // true when we are in the middle of a synchronous call from managed code from same thread cspPolicy: boolean, allAssetsInMemory: PromiseAndController, @@ -426,6 +427,7 @@ export declare interface EmscriptenModuleInternal { runtimeKeepalivePush(): void; runtimeKeepalivePop(): void; maybeExit(): void; + __emscripten_thread_init(pthread_ptr: PThreadPtr, isMainBrowserThread: number, isMainRuntimeThread: number, canBlock: number): void; } /// A PromiseController encapsulates a Promise together with easy access to its resolve and reject functions. diff --git a/src/mono/sample/wasm/browser-threads/Program.cs b/src/mono/sample/wasm/browser-threads/Program.cs index 148a002dfe1ed..907b3d3356b3c 100644 --- a/src/mono/sample/wasm/browser-threads/Program.cs +++ b/src/mono/sample/wasm/browser-threads/Program.cs @@ -8,153 +8,72 @@ using System.Threading.Tasks; using System.Collections.Generic; -namespace Sample -{ - public partial class Test - { - public static int Main(string[] args) - { - Console.WriteLine("Hello, World!"); - return 0; - } - - [JSImport("globalThis.console.log")] - public static partial void ConsoleLog(string status); - - [JSImport("Sample.Test.updateProgress", "main.js")] - static partial void updateProgress(string status); +namespace Sample; - internal static void UpdateProgress(string status) => updateProgress(status); +public partial class Test +{ + private static int _animationCounter = 0; + private static int _callCounter = 0; + private static bool _isRunning = false; + private static readonly IReadOnlyList _animations = new string[] { "\u2680", "\u2681", "\u2682", "\u2683", "\u2684", "\u2685" }; - static Demo _demo = null; + public static int Main(string[] args) + { + Console.WriteLine("Hello, World!"); + return 0; + } - [JSExport] - public static void Start(int n) - { - var comp = new ExpensiveComputation(n); - comp.Start(); - #pragma warning disable CS4014 - WaitForCompletion(comp); - _demo = new Demo(UpdateProgress, comp); - } + [JSImport("globalThis.console.log")] + public static partial void ConsoleLog(string status); - public static async Task WaitForCompletion (ExpensiveComputation comp) { - Console.WriteLine($"WaitForCompletion started on thread {Thread.CurrentThread.ManagedThreadId}"); - await comp.Completion; - Console.WriteLine($"WaitForCompletion completed on thread {Thread.CurrentThread.ManagedThreadId}"); - UpdateProgress("\u270C\uFE0E"); - } + [JSImport("Sample.Test.updateProgress", "main.js")] + private static partial Task updateProgress(string status); - [JSExport] - public static int Progress() + [JSExport] + public static bool Progress() + { + updateProgress(""+_animations[_animationCounter++]); + if (_animationCounter >= _animations.Count) { - if (_demo.Progress()) - return 0; /* done */ - else - return 1; /* continue */ + _animationCounter = 0; } - - [JSExport] - public static int GetAnswer() { return _demo.Result; } + return _isRunning; } -} - -public class ExpensiveComputation -{ - private readonly TaskCompletionSource _tcs = new(); - private readonly int UpTo; - public ExpensiveComputation(int n) { UpTo = n; } - public long CallCounter { get; private set; } - public Task Completion => _tcs.Task; - - public void Start() + [JSExport] + [return: JSMarshalAs>] + public static Task Fib(int n) { - new Thread((o) => ((ExpensiveComputation)o).Run()).Start(this); + return Task.Run(()=>{ + _isRunning = true; + var res = FibImpl(n); + _isRunning = false; + return Task.FromResult(res); + }); } - public void Run() + private static long FibImpl(int n) { - Console.WriteLine("Hello from ManagedThreadId " + Thread.CurrentThread.ManagedThreadId); - long result = Fib(UpTo); - if (result < (long)int.MaxValue) - _tcs.SetResult((int)result); - else - _tcs.SetException(new Exception("Fibonacci computation exceeded Int32.MaxValue")); - } - public long Fib(int n) - { - CallCounter++; + _callCounter++; // make some garbage every 1000 calls - if (CallCounter % 1000 == 0) + if (_callCounter % 1000 == 0) { AllocateGarbage(); } // and collect it every once in a while - if (CallCounter % 500000 == 0) + if (_callCounter % 500000 == 0) GC.Collect(); if (n < 2) return n; - return Fib(n - 1) + Fib(n - 2); + return FibImpl(n - 1) + FibImpl(n - 2); } [MethodImpl(MethodImplOptions.NoInlining)] - private void AllocateGarbage() + private static void AllocateGarbage() { object[] garbage = new object[200]; garbage[12] = new object(); garbage[197] = garbage; } - -} - -public class Demo -{ - public class Animation - { - private readonly Action _updateProgress; - private int _counter = 0; - - private readonly IReadOnlyList _animations = new string[] { "\u2680", "\u2681", "\u2682", "\u2683", "\u2684", "\u2685" }; - - public void Step(string suffix = "") - { - _updateProgress(_animations[_counter++] + suffix); - if (_counter >= _animations.Count) - { - _counter = 0; - } - } - - public Animation(Action updateProgress) - { - _updateProgress = updateProgress; - } - - - } - - private readonly Action _updateProgress; - private readonly Animation _animation; - private readonly ExpensiveComputation _expensiveComputation; - - public Demo(Action updateProgress, ExpensiveComputation comp) - { - _updateProgress = updateProgress; - _animation = new Animation(updateProgress); - _expensiveComputation = comp; - } - - public bool Progress() - { - if (!_expensiveComputation.Completion.IsCompleted) - { - _animation.Step($"{_expensiveComputation.CallCounter} calls"); - } - - return _expensiveComputation.Completion.IsCompleted; - } - - public int Result => _expensiveComputation.Completion.Result; } diff --git a/src/mono/sample/wasm/browser-threads/main.js b/src/mono/sample/wasm/browser-threads/main.js index d53e59304e10d..127e2402f7854 100644 --- a/src/mono/sample/wasm/browser-threads/main.js +++ b/src/mono/sample/wasm/browser-threads/main.js @@ -4,54 +4,14 @@ import { dotnet, exit } from './_framework/dotnet.js' let progressElement = null; - -function updateProgress(status) { - if (progressElement) { - progressElement.innerText = status; - } else { - console.log("Progress: " + status); - } -} - +let inputElement = null; +let exports = null; const assemblyName = "Wasm.Browser.Threads.Sample.dll"; -function delay(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); -} - -async function Run(exports, N) { - while (true) { - await delay(50); - const p = exports.Sample.Test.Progress(); - if (p === 0) - break; - } - const answer = exports.Sample.Test.GetAnswer(); - document.getElementById("out").innerText = `Fib(${N}) = ${answer}`; -} - -async function doMathSlowly(exports) { +try { progressElement = document.getElementById("progressElement"); - const N = parseInt(document.getElementById("inputN").value); - exports.Sample.Test.Start(N); - await Run(exports, N); -} - -function setEditable(inputElement, isEditable) { - inputElement.disabled = !isEditable; -} - -function onInputValueChanged(exports, inputElement) { - async function handler() { - setEditable(inputElement, false); - await doMathSlowly(exports); - setEditable(inputElement, true); - } - return handler; -} + inputElement = document.getElementById("inputN"); -try { - const inputElement = document.getElementById("inputN"); const { setModuleImports, getAssemblyExports, runMain } = await dotnet .withEnvironmentVariable("MONO_LOG_LEVEL", "debug") .withElementOnExit() @@ -67,14 +27,52 @@ try { } }); - const exports = await getAssemblyExports(assemblyName); + exports = await getAssemblyExports(assemblyName); - await doMathSlowly(exports); - setEditable(inputElement, true); - inputElement.addEventListener("change", onInputValueChanged(exports, inputElement)); + await doSlowMath(); + setEditable(true); + inputElement.addEventListener("change", onInputValueChanged); let exit_code = await runMain(assemblyName, []); + // comment out the following line for interactive testing, otherwise further call would be rejected by runtime exit(exit_code); } catch (err) { exit(2, err); } + +async function doSlowMath() { + const N = parseInt(document.getElementById("inputN").value); + const resultPromise = exports.Sample.Test.Fib(N); + + while (true) { + await delay(50); + const isRunning = exports.Sample.Test.Progress(); + if (!isRunning) + break; + } + const answer = await resultPromise; + document.getElementById("out").innerText = `Fib(${N}) = ${answer}`; + +} + +export async function updateProgress(status) { + if (progressElement) { + progressElement.innerText = status; + } else { + console.log("Progress: " + status); + } +} + +function delay(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +function setEditable(isEditable) { + inputElement.disabled = !isEditable; +} + +async function onInputValueChanged() { + setEditable(false); + await doSlowMath(exports); + setEditable(true); +} diff --git a/src/mono/wasm/Wasm.Build.Tests/Blazor/BlazorWasmTestBase.cs b/src/mono/wasm/Wasm.Build.Tests/Blazor/BlazorWasmTestBase.cs index 2e3491374c7d9..ccea19a5b4970 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Blazor/BlazorWasmTestBase.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Blazor/BlazorWasmTestBase.cs @@ -198,7 +198,7 @@ public async Task BlazorRunTest(string runArgs, var page = await runner.RunAsync(runCommand, runArgs, onConsoleMessage: OnConsoleMessage, onError: OnErrorMessage, modifyBrowserUrl: browserUrl => browserUrl + runOptions.QueryString); _testOutput.WriteLine("Waiting for page to load"); - await page.WaitForLoadStateAsync(LoadState.DOMContentLoaded); + await page.WaitForLoadStateAsync(LoadState.DOMContentLoaded, new () { Timeout = 1 * 60 * 1000 }); if (runOptions.CheckCounter) { diff --git a/src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleMultiThreadedTests.cs b/src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleMultiThreadedTests.cs index 5f1e1711151c0..bdee6a275a0ed 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleMultiThreadedTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleMultiThreadedTests.cs @@ -38,7 +38,7 @@ public SimpleMultiThreadedTests(ITestOutputHelper output, SharedBuildPerTestClas // } [ConditionalTheory(typeof(BuildTestBase), nameof(IsWorkloadWithMultiThreadingForDefaultFramework))] - [InlineData("Debug", false)] + // [InlineData("Debug", false)] // ActiveIssue https://github.com/dotnet/runtime/issues/98758 // [InlineData("Debug", true)] [InlineData("Release", false)] // [InlineData("Release", true)] diff --git a/src/mono/wasm/Wasm.Build.Tests/Common/EnvironmentVariables.cs b/src/mono/wasm/Wasm.Build.Tests/Common/EnvironmentVariables.cs index 841aa6a9b3456..50bf882d072d3 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Common/EnvironmentVariables.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Common/EnvironmentVariables.cs @@ -18,10 +18,10 @@ internal static class EnvironmentVariables internal static readonly string? BuiltNuGetsPath = Environment.GetEnvironmentVariable("BUILT_NUGETS_PATH"); internal static readonly string? BrowserPathForTests = Environment.GetEnvironmentVariable("BROWSER_PATH_FOR_TESTS"); internal static readonly string? V8PathForTests = Environment.GetEnvironmentVariable("V8_PATH_FOR_TESTS"); - internal static readonly bool ShowBuildOutput = Environment.GetEnvironmentVariable("SHOW_BUILD_OUTPUT") is not null; + internal static readonly bool IsRunningOnCI = Environment.GetEnvironmentVariable("IS_RUNNING_ON_CI") is "true"; + internal static readonly bool ShowBuildOutput = IsRunningOnCI || Environment.GetEnvironmentVariable("SHOW_BUILD_OUTPUT") is not null; internal static readonly bool UseWebcil = Environment.GetEnvironmentVariable("USE_WEBCIL_FOR_TESTS") is "true"; internal static readonly string? SdkDirName = Environment.GetEnvironmentVariable("SDK_DIR_NAME"); internal static readonly string? WasiSdkPath = Environment.GetEnvironmentVariable("WASI_SDK_PATH"); - internal static readonly bool IsRunningOnCI = Environment.GetEnvironmentVariable("IS_RUNNING_ON_CI") is "true"; } }