diff --git a/.gitignore b/.gitignore
index 71943be3a350db..1bbf3bb104201f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -187,10 +187,6 @@ node_modules/
*.metaproj
*.metaproj.tmp
bin.localpkg/
-src/mono/wasm/runtime/dotnet.d.ts.sha256
-src/mono/wasm/runtime/dotnet-legacy.d.ts.sha256
-
-src/mono/sample/wasm/browser-nextjs/public/
# RIA/Silverlight projects
Generated_Code/
diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs
index e22b5edf99bf12..fc627a32e1fa0f 100644
--- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs
+++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs
@@ -34,7 +34,7 @@ internal static unsafe partial class Runtime
#if FEATURE_WASM_THREADS
[MethodImpl(MethodImplOptions.InternalCall)]
- public static extern void InstallWebWorkerInterop();
+ public static extern void InstallWebWorkerInterop(IntPtr proxyContextGCHandle);
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void UninstallWebWorkerInterop();
#endif
diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj b/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj
index 2451c867a9489d..773d73a39a0943 100644
--- a/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj
+++ b/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj
@@ -16,6 +16,8 @@
$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)'))
$(DefineConstants);TargetsWindows
$(DefineConstants);TARGETS_BROWSER
+
+ <_XUnitBackgroundExec>false
diff --git a/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj b/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj
index b797fcf1894599..54be463b694e12 100644
--- a/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj
+++ b/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj
@@ -17,6 +17,9 @@
01:15:00
+
+
+ <_XUnitBackgroundExec>false
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/PrimitiveJSGenerator.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/PrimitiveJSGenerator.cs
index d3de4818346979..7690aa4fe43c88 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/PrimitiveJSGenerator.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/PrimitiveJSGenerator.cs
@@ -21,6 +21,7 @@ public PrimitiveJSGenerator(MarshalerType marshalerType)
{
}
+ // TODO order parameters in such way that affinity capturing parameters are emitted first
public override IEnumerable Generate(TypePositionInfo info, StubCodeContext context)
{
string argName = context.GetAdditionalIdentifier(info, "js_arg");
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 0f4f0fd6d41844..1b8664dc435917 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
@@ -46,6 +46,7 @@
+
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/CancelablePromise.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/CancelablePromise.cs
index bc34b5f9fc27a3..240199d470238f 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/CancelablePromise.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/CancelablePromise.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Threading;
using System.Threading.Tasks;
namespace System.Runtime.InteropServices.JavaScript
@@ -21,13 +22,24 @@ public static void CancelPromise(Task promise)
JSHostImplementation.PromiseHolder? holder = promise.AsyncState as JSHostImplementation.PromiseHolder;
if (holder == null) throw new InvalidOperationException("Expected Task converted from JS Promise");
-
-#if FEATURE_WASM_THREADS
- holder.SynchronizationContext!.Send(static (JSHostImplementation.PromiseHolder holder) =>
+#if !FEATURE_WASM_THREADS
+ if (holder.IsDisposed)
{
-#endif
+ return;
+ }
_CancelPromise(holder.GCHandle);
-#if FEATURE_WASM_THREADS
+#else
+ holder.ProxyContext.SynchronizationContext.Post(static (object? h) =>
+ {
+ var holder = (JSHostImplementation.PromiseHolder)h!;
+ lock (holder.ProxyContext)
+ {
+ if (holder.IsDisposed)
+ {
+ return;
+ }
+ }
+ _CancelPromise(holder.GCHandle);
}, holder);
#endif
}
@@ -42,15 +54,27 @@ public static void CancelPromise(Task promise, Action callback, T state)
JSHostImplementation.PromiseHolder? holder = promise.AsyncState as JSHostImplementation.PromiseHolder;
if (holder == null) throw new InvalidOperationException("Expected Task converted from JS Promise");
-
-#if FEATURE_WASM_THREADS
- holder.SynchronizationContext!.Send((JSHostImplementation.PromiseHolder holder) =>
+#if !FEATURE_WASM_THREADS
+ if (holder.IsDisposed)
{
-#endif
+ return;
+ }
+ _CancelPromise(holder.GCHandle);
+ callback.Invoke(state);
+#else
+ holder.ProxyContext.SynchronizationContext.Post(_ =>
+ {
+ lock (holder.ProxyContext)
+ {
+ if (holder.IsDisposed)
+ {
+ return;
+ }
+ }
+
_CancelPromise(holder.GCHandle);
callback.Invoke(state);
-#if FEATURE_WASM_THREADS
- }, holder);
+ }, null);
#endif
}
}
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 1233115fab0728..87676e9699cd97 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
@@ -26,6 +26,11 @@ public static void CallEntrypoint(JSMarshalerArgument* arguments_buffer)
ref JSMarshalerArgument arg_2 = ref arguments_buffer[3]; // initialized and set by caller
try
{
+#if FEATURE_WASM_THREADS
+ // when we arrive here, we are on the thread which owns the proxies
+ arg_exc.AssertCurrentThreadContext();
+#endif
+
arg_1.ToManaged(out IntPtr entrypointPtr);
if (entrypointPtr == IntPtr.Zero)
{
@@ -103,6 +108,10 @@ public static void LoadLazyAssembly(JSMarshalerArgument* arguments_buffer)
ref JSMarshalerArgument arg_2 = ref arguments_buffer[3];
try
{
+#if FEATURE_WASM_THREADS
+ // when we arrive here, we are on the thread which owns the proxies
+ arg_exc.AssertCurrentThreadContext();
+#endif
arg_1.ToManaged(out byte[]? dllBytes);
arg_2.ToManaged(out byte[]? pdbBytes);
@@ -121,6 +130,10 @@ public static void LoadSatelliteAssembly(JSMarshalerArgument* arguments_buffer)
ref JSMarshalerArgument arg_1 = ref arguments_buffer[2];
try
{
+#if FEATURE_WASM_THREADS
+ // when we arrive here, we are on the thread which owns the proxies
+ arg_exc.AssertCurrentThreadContext();
+#endif
arg_1.ToManaged(out byte[]? dllBytes);
if (dllBytes != null)
@@ -140,32 +153,12 @@ public static void ReleaseJSOwnedObjectByGCHandle(JSMarshalerArgument* arguments
{
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
{
- var gcHandle = arg_1.slot.GCHandle;
- if (IsGCVHandle(gcHandle))
- {
- if (ThreadJsOwnedHolders.Remove(gcHandle, out PromiseHolder? holder))
- {
- holder.GCHandle = IntPtr.Zero;
- holder.Callback!(null);
- }
- }
- else
- {
- GCHandle handle = (GCHandle)gcHandle;
- var target = handle.Target!;
- if (target is PromiseHolder holder)
- {
- holder.GCHandle = IntPtr.Zero;
- holder.Callback!(null);
- }
- else
- {
- ThreadJsOwnedObjects.Remove(target);
- }
- handle.Free();
- }
+ // when we arrive here, we are on the thread which owns the proxies
+ var ctx = arg_exc.AssertCurrentThreadContext();
+ ctx.ReleaseJSOwnedObjectByGCHandle(arg_1.slot.GCHandle);
}
catch (Exception ex)
{
@@ -185,6 +178,11 @@ public static void CallDelegate(JSMarshalerArgument* arguments_buffer)
// arg_4 set by JS caller when there are arguments
try
{
+#if FEATURE_WASM_THREADS
+ // when we arrive here, we are on the thread which owns the proxies
+ arg_exc.AssertCurrentThreadContext();
+#endif
+
GCHandle callback_gc_handle = (GCHandle)arg_1.slot.GCHandle;
if (callback_gc_handle.Target is ToManagedCallback callback)
{
@@ -210,34 +208,34 @@ public static void CompleteTask(JSMarshalerArgument* arguments_buffer)
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
{
- var holderGCHandle = arg_1.slot.GCHandle;
- if (IsGCVHandle(holderGCHandle))
+ // when we arrive here, we are on the thread which owns the proxies
+ var ctx = arg_exc.AssertCurrentThreadContext();
+ var holder = ctx.GetPromiseHolder(arg_1.slot.GCHandle);
+
+#if FEATURE_WASM_THREADS
+ lock (ctx)
{
- if (ThreadJsOwnedHolders.Remove(holderGCHandle, out PromiseHolder? holder))
+ if (holder.Callback == null)
{
- holder.GCHandle = IntPtr.Zero;
- // arg_2, arg_3 are processed by the callback
- holder.Callback!(arguments_buffer);
+ holder.CallbackReady = new ManualResetEventSlim(false);
}
}
- else
+ if (holder.CallbackReady != null)
{
- GCHandle handle = (GCHandle)holderGCHandle;
- var target = handle.Target!;
- if (target is PromiseHolder holder)
- {
- holder.GCHandle = IntPtr.Zero;
- // arg_2, arg_3 are processed by the callback
- holder.Callback!(arguments_buffer);
- }
- else
- {
- ThreadJsOwnedObjects.Remove(target);
- }
- handle.Free();
+#pragma warning disable CA1416 // Validate platform compatibility
+ holder.CallbackReady?.Wait();
+#pragma warning restore CA1416 // Validate platform compatibility
}
+#endif
+ var callback = holder.Callback!;
+ ctx.ReleasePromiseHolder(arg_1.slot.GCHandle);
+
+ // arg_2, arg_3 are processed by the callback
+ // JSProxyContext.PopOperation() is called by the callback
+ callback!(arguments_buffer);
}
catch (Exception ex)
{
@@ -254,6 +252,9 @@ public static void GetManagedStackTrace(JSMarshalerArgument* arguments_buffer)
ref JSMarshalerArgument arg_1 = ref arguments_buffer[2];// initialized and set by caller
try
{
+ // when we arrive here, we are on the thread which owns the proxies
+ arg_exc.AssertCurrentThreadContext();
+
GCHandle exception_gc_handle = (GCHandle)arg_1.slot.GCHandle;
if (exception_gc_handle.Target is Exception exception)
{
@@ -275,17 +276,10 @@ public static void GetManagedStackTrace(JSMarshalerArgument* arguments_buffer)
// this is here temporarily, until JSWebWorker becomes public API
[DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicMethods, "System.Runtime.InteropServices.JavaScript.JSWebWorker", "System.Runtime.InteropServices.JavaScript")]
// the marshaled signature is:
- // void InstallSynchronizationContext()
- public static void InstallSynchronizationContext (JSMarshalerArgument* arguments_buffer) {
- ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame()
- try
- {
- InstallWebWorkerInterop(true);
- }
- catch (Exception ex)
- {
- arg_exc.ToJS(ex);
- }
+ // void InstallMainSynchronizationContext()
+ public static void InstallMainSynchronizationContext()
+ {
+ InstallWebWorkerInterop(true);
}
#endif
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.cs
index 4ebb8a772e236b..667fed536adaf4 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.cs
@@ -7,19 +7,6 @@ namespace System.Runtime.InteropServices.JavaScript
{
internal static unsafe partial class JavaScriptImports
{
- public static void ResolveOrRejectPromise(Span arguments)
- {
- fixed (JSMarshalerArgument* ptr = arguments)
- {
- Interop.Runtime.ResolveOrRejectPromise(ptr);
- ref JSMarshalerArgument exceptionArg = ref arguments[0];
- if (exceptionArg.slot.Type != MarshalerType.None)
- {
- JSHostImplementation.ThrowException(ref exceptionArg);
- }
- }
- }
-
#if !DISABLE_LEGACY_JS_INTEROP
#region legacy
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
index bb4b017ef743fb..928d30f74f513c 100644
--- 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
@@ -32,17 +32,10 @@ internal static void PreventTrimming()
public static void GetCSOwnedObjectByJSHandleRef(nint jsHandle, int shouldAddInflight, out JSObject? result)
{
- if (JSHostImplementation.ThreadCsOwnedObjects.TryGetValue(jsHandle, out WeakReference? reference))
- {
- reference.TryGetTarget(out JSObject? jsObject);
- if (shouldAddInflight != 0)
- {
- jsObject?.AddInFlight();
- }
- result = jsObject;
- return;
- }
- result = null;
+#if FEATURE_WASM_THREADS
+ LegacyHostImplementation.ThrowIfLegacyWorkerThread();
+#endif
+ result = JSProxyContext.MainThreadContext.GetCSOwnedObjectByJSHandle(jsHandle, shouldAddInflight);
}
public static IntPtr GetCSOwnedObjectJSHandleRef(in JSObject jsObject, int shouldAddInflight)
@@ -71,32 +64,7 @@ public static void CreateCSOwnedProxyRef(nint jsHandle, LegacyHostImplementation
#if FEATURE_WASM_THREADS
LegacyHostImplementation.ThrowIfLegacyWorkerThread();
#endif
-
- JSObject? res = null;
-
- if (!JSHostImplementation.ThreadCsOwnedObjects.TryGetValue(jsHandle, out WeakReference? reference) ||
- !reference.TryGetTarget(out res) ||
- res.IsDisposed)
- {
-#pragma warning disable CS0612 // Type or member is obsolete
- res = mappedType switch
- {
- LegacyHostImplementation.MappedType.JSObject => new JSObject(jsHandle),
- LegacyHostImplementation.MappedType.Array => new Array(jsHandle),
- LegacyHostImplementation.MappedType.ArrayBuffer => new ArrayBuffer(jsHandle),
- LegacyHostImplementation.MappedType.DataView => new DataView(jsHandle),
- LegacyHostImplementation.MappedType.Function => new Function(jsHandle),
- LegacyHostImplementation.MappedType.Uint8Array => new Uint8Array(jsHandle),
- _ => throw new ArgumentOutOfRangeException(nameof(mappedType))
- };
-#pragma warning restore CS0612 // Type or member is obsolete
- JSHostImplementation.ThreadCsOwnedObjects[jsHandle] = new WeakReference(res, trackResurrection: true);
- }
- if (shouldAddInflight != 0)
- {
- res.AddInFlight();
- }
- jsObject = res;
+ jsObject = JSProxyContext.MainThreadContext.CreateCSOwnedProxy(jsHandle, mappedType, shouldAddInflight);
}
public static void GetJSOwnedObjectByGCHandleRef(int gcHandle, out object result)
@@ -107,7 +75,7 @@ public static void GetJSOwnedObjectByGCHandleRef(int gcHandle, out object result
public static IntPtr GetJSOwnedObjectGCHandleRef(in object obj)
{
- return JSHostImplementation.GetJSOwnedObjectGCHandle(obj, GCHandleType.Normal);
+ return JSProxyContext.MainThreadContext.GetJSOwnedObjectGCHandle(obj, GCHandleType.Normal);
}
public static IntPtr CreateTaskSource()
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSException.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSException.cs
index e623ef19238224..1a9e2278d57caa 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSException.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSException.cs
@@ -47,10 +47,10 @@ public override string? StackTrace
}
#if FEATURE_WASM_THREADS
- var currentTID = JSSynchronizationContext.CurrentJSSynchronizationContext?.TargetTID;
- if (jsException.OwnerTID != currentTID)
+ if (!jsException.ProxyContext.IsCurrentThread())
{
- return bs;
+ // if we are on another thread, it would be too expensive and risky to obtain lazy stack trace.
+ return bs + Environment.NewLine + "... omitted JavaScript stack trace from another thread.";
}
#endif
string? jsStackTrace = jsException.GetPropertyAsString("stack");
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 17a81b059f817b..25c0b74d4a80a0 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
@@ -30,9 +30,6 @@ internal JSFunctionBinding() { }
internal static volatile uint nextImportHandle = 1;
internal int ImportHandle;
internal bool IsAsync;
-#if FEATURE_WASM_THREADS
- internal bool IsThreadCaptured;
-#endif
[StructLayout(LayoutKind.Sequential, Pack = 4)]
internal struct JSBindingHeader
@@ -177,7 +174,7 @@ public static JSFunctionBinding BindJSFunction(string functionName, string modul
if (RuntimeInformation.OSArchitecture != Architecture.Wasm)
throw new PlatformNotSupportedException();
- return BindJSFunctionImpl(functionName, moduleName, signatures);
+ return BindJSImportImpl(functionName, moduleName, signatures);
}
///
@@ -220,15 +217,19 @@ internal static unsafe void InvokeJSFunction(JSObject jsFunction, Span arguments)
{
- if (signature.IsAsync)
- {
- // pre-allocate the result handle and Task
#if FEATURE_WASM_THREADS
- JSSynchronizationContext.AssertWebWorkerContext();
- var holder = new JSHostImplementation.PromiseHolder(JSSynchronizationContext.CurrentJSSynchronizationContext!);
+ var targetContext = JSProxyContext.SealJSImportCapturing();
+ JSProxyContext.AssertIsInteropThread();
+ arguments[0].slot.ContextHandle = targetContext.ContextHandle;
+ arguments[1].slot.ContextHandle = targetContext.ContextHandle;
#else
- var holder = new JSHostImplementation.PromiseHolder();
+ var targetContext = JSProxyContext.MainThreadContext;
#endif
+
+ if (signature.IsAsync)
+ {
+ // pre-allocate the result handle and Task
+ var holder = new JSHostImplementation.PromiseHolder(targetContext);
arguments[1].slot.Type = MarshalerType.TaskPreCreated;
arguments[1].slot.GCHandle = holder.GCHandle;
}
@@ -253,10 +254,10 @@ internal static unsafe void InvokeJSImportImpl(JSFunctionBinding signature, Span
}
}
- internal static unsafe JSFunctionBinding BindJSFunctionImpl(string functionName, string moduleName, ReadOnlySpan signatures)
+ internal static unsafe JSFunctionBinding BindJSImportImpl(string functionName, string moduleName, ReadOnlySpan signatures)
{
#if FEATURE_WASM_THREADS
- JSSynchronizationContext.AssertWebWorkerContext();
+ JSProxyContext.AssertIsInteropThread();
#endif
var signature = JSHostImplementation.GetMethodSignature(signatures, functionName, moduleName);
@@ -284,5 +285,19 @@ internal static unsafe JSFunctionBinding BindManagedFunctionImpl(string fullyQua
return signature;
}
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static unsafe void ResolveOrRejectPromise(Span arguments)
+ {
+ fixed (JSMarshalerArgument* ptr = arguments)
+ {
+ Interop.Runtime.ResolveOrRejectPromise(ptr);
+ ref JSMarshalerArgument exceptionArg = ref arguments[0];
+ if (exceptionArg.slot.Type != MarshalerType.None)
+ {
+ JSHostImplementation.ThrowException(ref exceptionArg);
+ }
+ }
+ }
}
}
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 89a7eb8a0246e2..c1ec22aecdd7b4 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
@@ -22,7 +22,7 @@ public static JSObject GlobalThis
get
{
#if FEATURE_WASM_THREADS
- JSSynchronizationContext.AssertWebWorkerContext();
+ JSProxyContext.AssertIsInteropThread();
#endif
return JavaScriptImports.GetGlobalThis();
}
@@ -36,7 +36,7 @@ public static JSObject DotnetInstance
get
{
#if FEATURE_WASM_THREADS
- JSSynchronizationContext.AssertWebWorkerContext();
+ JSProxyContext.AssertIsInteropThread();
#endif
return JavaScriptImports.GetDotnetInstance();
}
@@ -54,7 +54,7 @@ public static JSObject DotnetInstance
public static Task ImportAsync(string moduleName, string moduleUrl, CancellationToken cancellationToken = default)
{
#if FEATURE_WASM_THREADS
- JSSynchronizationContext.AssertWebWorkerContext();
+ JSProxyContext.AssertIsInteropThread();
#endif
return JSHostImplementation.ImportAsync(moduleName, moduleUrl, cancellationToken);
}
@@ -65,7 +65,7 @@ public static SynchronizationContext CurrentOrMainJSSynchronizationContext
get
{
#if FEATURE_WASM_THREADS
- return JSSynchronizationContext.CurrentJSSynchronizationContext ?? JSSynchronizationContext.MainJSSynchronizationContext!;
+ return (JSProxyContext.ExecutionContext ?? JSProxyContext.MainThreadContext).SynchronizationContext;
#else
return null!;
#endif
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.Types.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.Types.cs
index a8b496237fd8ed..8d4dd3e6a3e877 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.Types.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.Types.cs
@@ -12,34 +12,24 @@ internal static partial class JSHostImplementation
public sealed class PromiseHolder
{
- public nint GCHandle; // could be also virtual GCVHandle
+ public readonly nint GCHandle; // could be also virtual GCVHandle
public ToManagedCallback? Callback;
+ public JSProxyContext ProxyContext;
+ public bool IsDisposed;
#if FEATURE_WASM_THREADS
- // the JavaScript object could only exist on the single web worker and can't migrate to other workers
- internal JSSynchronizationContext SynchronizationContext;
+ public ManualResetEventSlim? CallbackReady;
#endif
-#if FEATURE_WASM_THREADS
- // TODO possibly unify signature with non-MT and pass null
- public PromiseHolder(JSSynchronizationContext targetContext)
- {
- GCHandle = (IntPtr)InteropServices.GCHandle.Alloc(this, GCHandleType.Normal);
- SynchronizationContext = targetContext;
- }
-#else
- public PromiseHolder()
+ public PromiseHolder(JSProxyContext targetContext)
{
GCHandle = (IntPtr)InteropServices.GCHandle.Alloc(this, GCHandleType.Normal);
+ ProxyContext = targetContext;
}
-#endif
- public PromiseHolder(nint gcvHandle)
+ public PromiseHolder(JSProxyContext targetContext, nint gcvHandle)
{
GCHandle = gcvHandle;
-#if FEATURE_WASM_THREADS
- JSSynchronizationContext.AssertWebWorkerContext();
- SynchronizationContext = JSSynchronizationContext.CurrentJSSynchronizationContext!;
-#endif
+ ProxyContext = targetContext;
}
}
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 b2c0504e4d4f39..877940b6bdacb1 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
@@ -1,7 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
@@ -16,115 +15,6 @@ internal static partial class JSHostImplementation
{
private const string TaskGetResultName = "get_Result";
private static MethodInfo? s_taskGetResultMethodInfo;
- // we use this to maintain identity of JSHandle for a JSObject proxy
-#if FEATURE_WASM_THREADS
- [ThreadStatic]
-#endif
- private static Dictionary>? s_csOwnedObjects;
-
- public static Dictionary> ThreadCsOwnedObjects
- {
- get
- {
- s_csOwnedObjects ??= new();
- return s_csOwnedObjects;
- }
- }
-
- // we use this to maintain identity of GCHandle for a managed object
-#if FEATURE_WASM_THREADS
- [ThreadStatic]
-#endif
- private static Dictionary
/// Parameters.
public Array(params object[] _params)
- : base(JavaScriptImports.CreateCSOwnedObject(nameof(Array), _params))
+ : base(JavaScriptImports.CreateCSOwnedObject(nameof(Array), _params), JSProxyContext.MainThreadContext)
{
#if FEATURE_WASM_THREADS
LegacyHostImplementation.ThrowIfLegacyWorkerThread();
#endif
- LegacyHostImplementation.RegisterCSOwnedObject(this);
+ JSProxyContext.MainThreadContext.RegisterCSOwnedObject(this);
}
///
/// Initializes a new instance of the Array/> class.
///
/// Js handle.
- internal Array(IntPtr jsHandle) : base(jsHandle)
+ internal Array(IntPtr jsHandle) : base(jsHandle, JSProxyContext.MainThreadContext)
{ }
///
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/ArrayBuffer.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/ArrayBuffer.cs
index 39db0563089d0d..92e78a14fe1b27 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/ArrayBuffer.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/ArrayBuffer.cs
@@ -11,19 +11,19 @@ public class ArrayBuffer : JSObject
///
/// Length.
public ArrayBuffer(int length)
- : base(JavaScriptImports.CreateCSOwnedObject(nameof(ArrayBuffer), new object[] { length }))
+ : base(JavaScriptImports.CreateCSOwnedObject(nameof(ArrayBuffer), new object[] { length }), JSProxyContext.MainThreadContext)
{
#if FEATURE_WASM_THREADS
LegacyHostImplementation.ThrowIfLegacyWorkerThread();
#endif
- LegacyHostImplementation.RegisterCSOwnedObject(this);
+ JSProxyContext.MainThreadContext.RegisterCSOwnedObject(this);
}
///
/// Initializes a new instance of the JavaScript Core ArrayBuffer class.
///
/// Js handle.
- internal ArrayBuffer(IntPtr jsHandle) : base(jsHandle)
+ internal ArrayBuffer(IntPtr jsHandle) : base(jsHandle, JSProxyContext.MainThreadContext)
{ }
///
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/DataView.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/DataView.cs
index 3d3300ec5c5039..1046fa8f99fa45 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/DataView.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/DataView.cs
@@ -15,12 +15,12 @@ public class DataView : JSObject
///
/// ArrayBuffer to use as the storage backing the new DataView object.
public DataView(ArrayBuffer buffer)
- : base(JavaScriptImports.CreateCSOwnedObject(nameof(DataView), new object[] { buffer }))
+ : base(JavaScriptImports.CreateCSOwnedObject(nameof(DataView), new object[] { buffer }), JSProxyContext.MainThreadContext)
{
#if FEATURE_WASM_THREADS
LegacyHostImplementation.ThrowIfLegacyWorkerThread();
#endif
- LegacyHostImplementation.RegisterCSOwnedObject(this);
+ JSProxyContext.MainThreadContext.RegisterCSOwnedObject(this);
}
///
@@ -29,12 +29,12 @@ public DataView(ArrayBuffer buffer)
/// ArrayBuffer to use as the storage backing the new DataView object.
/// The offset, in bytes, to the first byte in the above buffer for the new view to reference. If unspecified, the buffer view starts with the first byte.
public DataView(ArrayBuffer buffer, int byteOffset)
- : base(JavaScriptImports.CreateCSOwnedObject(nameof(DataView), new object[] { buffer, byteOffset }))
+ : base(JavaScriptImports.CreateCSOwnedObject(nameof(DataView), new object[] { buffer, byteOffset }), JSProxyContext.MainThreadContext)
{
#if FEATURE_WASM_THREADS
LegacyHostImplementation.ThrowIfLegacyWorkerThread();
#endif
- LegacyHostImplementation.RegisterCSOwnedObject(this);
+ JSProxyContext.MainThreadContext.RegisterCSOwnedObject(this);
}
///
@@ -44,19 +44,19 @@ public DataView(ArrayBuffer buffer, int byteOffset)
/// The offset, in bytes, to the first byte in the above buffer for the new view to reference. If unspecified, the buffer view starts with the first byte.
/// The number of elements in the byte array. If unspecified, the view's length will match the buffer's length.
public DataView(ArrayBuffer buffer, int byteOffset, int byteLength)
- : base(JavaScriptImports.CreateCSOwnedObject(nameof(DataView), new object[] { buffer, byteOffset, byteLength }))
+ : base(JavaScriptImports.CreateCSOwnedObject(nameof(DataView), new object[] { buffer, byteOffset, byteLength }), JSProxyContext.MainThreadContext)
{
#if FEATURE_WASM_THREADS
LegacyHostImplementation.ThrowIfLegacyWorkerThread();
#endif
- LegacyHostImplementation.RegisterCSOwnedObject(this);
+ JSProxyContext.MainThreadContext.RegisterCSOwnedObject(this);
}
///
/// Initializes a new instance of the DataView class.
///
/// Js handle.
- internal DataView(IntPtr jsHandle) : base(jsHandle)
+ internal DataView(IntPtr jsHandle) : base(jsHandle, JSProxyContext.MainThreadContext)
{ }
///
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Function.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Function.cs
index f87fb94c01610a..ebce83d01417ad 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Function.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Function.cs
@@ -16,15 +16,15 @@ namespace System.Runtime.InteropServices.JavaScript
public class Function : JSObject
{
public Function(params object[] args)
- : base(JavaScriptImports.CreateCSOwnedObject(nameof(Function), args))
+ : base(JavaScriptImports.CreateCSOwnedObject(nameof(Function), args), JSProxyContext.MainThreadContext)
{
#if FEATURE_WASM_THREADS
LegacyHostImplementation.ThrowIfLegacyWorkerThread();
#endif
- LegacyHostImplementation.RegisterCSOwnedObject(this);
+ JSProxyContext.MainThreadContext.RegisterCSOwnedObject(this);
}
- internal Function(IntPtr jsHandle) : base(jsHandle)
+ internal Function(IntPtr jsHandle) : base(jsHandle, JSProxyContext.MainThreadContext)
{ }
///
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/LegacyHostImplementation.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/LegacyHostImplementation.cs
index f726c26262b283..8e6242017979f2 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/LegacyHostImplementation.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/LegacyHostImplementation.cs
@@ -18,12 +18,6 @@ public static void ReleaseInFlight(object obj)
jsObj?.ReleaseInFlight();
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void RegisterCSOwnedObject(JSObject proxy)
- {
- JSHostImplementation.ThreadCsOwnedObjects[(int)proxy.JSHandle] = new WeakReference(proxy, trackResurrection: true);
- }
-
public static MarshalType GetMarshalTypeFromType(Type type)
{
if (type is null)
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Uint8Array.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Uint8Array.cs
index b80f7c31ad64b4..1cea722c1cc623 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Uint8Array.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Uint8Array.cs
@@ -9,24 +9,24 @@ namespace System.Runtime.InteropServices.JavaScript
public sealed class Uint8Array : JSObject
{
public Uint8Array(int length)
- : base(JavaScriptImports.CreateCSOwnedObject(nameof(Uint8Array), new object[] { length }))
+ : base(JavaScriptImports.CreateCSOwnedObject(nameof(Uint8Array), new object[] { length }), JSProxyContext.MainThreadContext)
{
#if FEATURE_WASM_THREADS
LegacyHostImplementation.ThrowIfLegacyWorkerThread();
#endif
- LegacyHostImplementation.RegisterCSOwnedObject(this);
+ JSProxyContext.MainThreadContext.RegisterCSOwnedObject(this);
}
public Uint8Array(ArrayBuffer buffer)
- : base(JavaScriptImports.CreateCSOwnedObject(nameof(Uint8Array), new object[] { buffer }))
+ : base(JavaScriptImports.CreateCSOwnedObject(nameof(Uint8Array), new object[] { buffer }), JSProxyContext.MainThreadContext)
{
#if FEATURE_WASM_THREADS
LegacyHostImplementation.ThrowIfLegacyWorkerThread();
#endif
- LegacyHostImplementation.RegisterCSOwnedObject(this);
+ JSProxyContext.MainThreadContext.RegisterCSOwnedObject(this);
}
- internal Uint8Array(IntPtr jsHandle) : base(jsHandle)
+ internal Uint8Array(IntPtr jsHandle) : base(jsHandle, JSProxyContext.MainThreadContext)
{ }
public int Length
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 1ce1dc2da6a7e2..5392fca48fae8b 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
@@ -133,7 +133,8 @@ public unsafe void ToJS(ArraySegment value)
return;
}
slot.Type = MarshalerType.ArraySegment;
- slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(value.Array, GCHandleType.Pinned);
+ var ctx = ToJSContext;
+ slot.GCHandle = ctx.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 9589ea0f42f7ab..9b7f48ed4b3acd 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
@@ -135,7 +135,8 @@ public unsafe void ToJS(ArraySegment value)
return;
}
slot.Type = MarshalerType.ArraySegment;
- slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(value.Array, GCHandleType.Pinned);
+ var ctx = ToJSContext;
+ slot.GCHandle = ctx.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 e19a3f428c8a23..f89c4a669818c7 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
@@ -33,7 +33,8 @@ public unsafe void ToManaged(out Exception? value)
if (slot.JSHandle != IntPtr.Zero)
{
// this is JSException round-trip
- jsException = JSHostImplementation.CreateCSOwnedProxy(slot.JSHandle);
+ var ctx = ToManagedContext;
+ jsException = ctx.CreateCSOwnedProxy(slot.JSHandle);
}
string? message;
@@ -65,11 +66,21 @@ public unsafe void ToJS(Exception? value)
var jse = cpy as JSException;
if (jse != null && jse.jsException != null)
{
+ ObjectDisposedException.ThrowIf(jse.jsException.IsDisposed, value);
#if FEATURE_WASM_THREADS
JSObject.AssertThreadAffinity(value);
+ var ctx = jse.jsException.ProxyContext;
+ if (JSProxyContext.CapturingState == JSProxyContext.JSImportOperationState.JSImportParams)
+ {
+ JSProxyContext.CaptureContextFromParameter(ctx);
+ slot.ContextHandle = ctx.ContextHandle;
+ }
+ else if (slot.ContextHandle != ctx.ContextHandle)
+ {
+ Environment.FailFast($"ContextHandle mismatch, ManagedThreadId: {Environment.CurrentManagedThreadId}. {Environment.NewLine} {Environment.StackTrace}");
+ }
#endif
// this is JSException roundtrip
- ObjectDisposedException.ThrowIf(jse.jsException.IsDisposed, value);
slot.Type = MarshalerType.JSException;
slot.JSHandle = jse.jsException.JSHandle;
}
@@ -77,7 +88,9 @@ public unsafe void ToJS(Exception? value)
{
ToJS(cpy.Message);
slot.Type = MarshalerType.Exception;
- slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(cpy);
+
+ var ctx = ToJSContext;
+ slot.GCHandle = ctx.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 ca2c0bab7bdff8..ff3183c78ed397 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
@@ -9,9 +9,9 @@ private sealed class ActionJS
{
private JSObject JSObject;
- public ActionJS(IntPtr jsHandle)
+ public ActionJS(JSObject holder)
{
- JSObject = JSHostImplementation.CreateCSOwnedProxy(jsHandle);
+ JSObject = holder;
}
public void InvokeJS()
@@ -26,8 +26,14 @@ public void InvokeJS()
Span arguments = stackalloc JSMarshalerArgument[4];
ref JSMarshalerArgument args_exception = ref arguments[0];
ref JSMarshalerArgument args_return = ref arguments[1];
+#if FEATURE_WASM_THREADS
+ args_exception.InitializeWithContext(JSObject.ProxyContext);
+ args_return.InitializeWithContext(JSObject.ProxyContext);
+ JSProxyContext.JSImportNoCapture();
+#else
args_exception.Initialize();
args_return.Initialize();
+#endif
JSFunctionBinding.InvokeJSFunction(JSObject, arguments);
}
@@ -39,9 +45,9 @@ private sealed class ActionJS
private ArgumentToJSCallback Arg1Marshaler;
private JSObject JSObject;
- public ActionJS(IntPtr jsHandle, ArgumentToJSCallback arg1Marshaler)
+ public ActionJS(JSObject holder, ArgumentToJSCallback arg1Marshaler)
{
- JSObject = JSHostImplementation.CreateCSOwnedProxy(jsHandle);
+ JSObject = holder;
Arg1Marshaler = arg1Marshaler;
}
@@ -56,10 +62,18 @@ public void InvokeJS(T arg1)
ref JSMarshalerArgument args_return = ref arguments[1];
ref JSMarshalerArgument args_arg1 = ref arguments[2];
+#if FEATURE_WASM_THREADS
+ args_exception.InitializeWithContext(JSObject.ProxyContext);
+ args_return.InitializeWithContext(JSObject.ProxyContext);
+ args_arg1.InitializeWithContext(JSObject.ProxyContext);
+ JSProxyContext.JSImportNoCapture();
+#else
args_exception.Initialize();
args_return.Initialize();
+#endif
Arg1Marshaler(ref args_arg1, arg1);
+
JSFunctionBinding.InvokeJSFunction(JSObject, arguments);
}
}
@@ -70,9 +84,9 @@ private sealed class ActionJS
private ArgumentToJSCallback Arg2Marshaler;
private JSObject JSObject;
- public ActionJS(IntPtr jsHandle, ArgumentToJSCallback arg1Marshaler, ArgumentToJSCallback arg2Marshaler)
+ public ActionJS(JSObject holder, ArgumentToJSCallback arg1Marshaler, ArgumentToJSCallback arg2Marshaler)
{
- JSObject = JSHostImplementation.CreateCSOwnedProxy(jsHandle);
+ JSObject = holder;
Arg1Marshaler = arg1Marshaler;
Arg2Marshaler = arg2Marshaler;
}
@@ -89,11 +103,20 @@ public void InvokeJS(T1 arg1, T2 arg2)
ref JSMarshalerArgument args_arg1 = ref arguments[2];
ref JSMarshalerArgument args_arg2 = ref arguments[3];
+#if FEATURE_WASM_THREADS
+ args_exception.InitializeWithContext(JSObject.ProxyContext);
+ args_return.InitializeWithContext(JSObject.ProxyContext);
+ args_arg1.InitializeWithContext(JSObject.ProxyContext);
+ args_arg2.InitializeWithContext(JSObject.ProxyContext);
+ JSProxyContext.JSImportNoCapture();
+#else
args_exception.Initialize();
args_return.Initialize();
+#endif
Arg1Marshaler(ref args_arg1, arg1);
Arg2Marshaler(ref args_arg2, arg2);
+
JSFunctionBinding.InvokeJSFunction(JSObject, arguments);
}
}
@@ -105,9 +128,9 @@ private sealed class ActionJS
private ArgumentToJSCallback Arg3Marshaler;
private JSObject JSObject;
- public ActionJS(IntPtr jsHandle, ArgumentToJSCallback arg1Marshaler, ArgumentToJSCallback arg2Marshaler, ArgumentToJSCallback arg3Marshaler)
+ public ActionJS(JSObject holder, ArgumentToJSCallback arg1Marshaler, ArgumentToJSCallback arg2Marshaler, ArgumentToJSCallback arg3Marshaler)
{
- JSObject = JSHostImplementation.CreateCSOwnedProxy(jsHandle);
+ JSObject = holder;
Arg1Marshaler = arg1Marshaler;
Arg2Marshaler = arg2Marshaler;
Arg3Marshaler = arg3Marshaler;
@@ -126,12 +149,22 @@ public void InvokeJS(T1 arg1, T2 arg2, T3 arg3)
ref JSMarshalerArgument args_arg2 = ref arguments[3];
ref JSMarshalerArgument args_arg3 = ref arguments[4];
+#if FEATURE_WASM_THREADS
+ args_exception.InitializeWithContext(JSObject.ProxyContext);
+ args_return.InitializeWithContext(JSObject.ProxyContext);
+ args_arg1.InitializeWithContext(JSObject.ProxyContext);
+ args_arg2.InitializeWithContext(JSObject.ProxyContext);
+ args_arg3.InitializeWithContext(JSObject.ProxyContext);
+ JSProxyContext.JSImportNoCapture();
+#else
args_exception.Initialize();
args_return.Initialize();
+#endif
Arg1Marshaler(ref args_arg1, arg1);
Arg2Marshaler(ref args_arg2, arg2);
Arg3Marshaler(ref args_arg3, arg3);
+
JSFunctionBinding.InvokeJSFunction(JSObject, arguments);
}
}
@@ -149,7 +182,9 @@ public unsafe void ToManaged(out Action? value)
return;
}
- value = new ActionJS(slot.JSHandle).InvokeJS;
+ var ctx = ToManagedContext;
+ var holder = ctx.CreateCSOwnedProxy(slot.JSHandle);
+ value = new ActionJS(holder).InvokeJS;
}
///
@@ -167,7 +202,9 @@ public unsafe void ToManaged(out Action? value, ArgumentToJSCallback ar
return;
}
- value = new ActionJS(slot.JSHandle, arg1Marshaler).InvokeJS;
+ var ctx = ToManagedContext;
+ var holder = ctx.CreateCSOwnedProxy(slot.JSHandle);
+ value = new ActionJS(holder, arg1Marshaler).InvokeJS;
}
///
@@ -187,7 +224,9 @@ public unsafe void ToManaged(out Action? value, ArgumentToJSCall
return;
}
- value = new ActionJS(slot.JSHandle, arg1Marshaler, arg2Marshaler).InvokeJS;
+ var ctx = ToManagedContext;
+ var holder = ctx.CreateCSOwnedProxy(slot.JSHandle);
+ value = new ActionJS(holder, arg1Marshaler, arg2Marshaler).InvokeJS;
}
///
@@ -209,7 +248,9 @@ public unsafe void ToManaged(out Action? value, Argument
return;
}
- value = new ActionJS(slot.JSHandle, arg1Marshaler, arg2Marshaler, arg3Marshaler).InvokeJS;
+ var ctx = ToManagedContext;
+ var holder = ctx.CreateCSOwnedProxy(slot.JSHandle);
+ value = new ActionJS(holder, arg1Marshaler, arg2Marshaler, arg3Marshaler).InvokeJS;
}
private sealed class FuncJS
@@ -217,9 +258,9 @@ private sealed class FuncJS
private JSObject JSObject;
private ArgumentToManagedCallback ResMarshaler;
- public FuncJS(IntPtr jsHandle, ArgumentToManagedCallback resMarshaler)
+ public FuncJS(JSObject holder, ArgumentToManagedCallback resMarshaler)
{
- JSObject = JSHostImplementation.CreateCSOwnedProxy(jsHandle);
+ JSObject = holder;
ResMarshaler = resMarshaler;
}
@@ -235,12 +276,19 @@ public TResult InvokeJS()
Span arguments = stackalloc JSMarshalerArgument[4];
ref JSMarshalerArgument args_exception = ref arguments[0];
ref JSMarshalerArgument args_return = ref arguments[1];
+#if FEATURE_WASM_THREADS
+ args_exception.InitializeWithContext(JSObject.ProxyContext);
+ args_return.InitializeWithContext(JSObject.ProxyContext);
+ JSProxyContext.JSImportNoCapture();
+#else
args_exception.Initialize();
args_return.Initialize();
+#endif
JSFunctionBinding.InvokeJSFunction(JSObject, arguments);
ResMarshaler(ref args_return, out TResult res);
+
return res;
}
@@ -252,9 +300,9 @@ private sealed class FuncJS
private ArgumentToManagedCallback ResMarshaler;
private JSObject JSObject;
- public FuncJS(IntPtr jsHandle, ArgumentToJSCallback arg1Marshaler, ArgumentToManagedCallback resMarshaler)
+ public FuncJS(JSObject holder, ArgumentToJSCallback arg1Marshaler, ArgumentToManagedCallback resMarshaler)
{
- JSObject = JSHostImplementation.CreateCSOwnedProxy(jsHandle);
+ JSObject = holder;
Arg1Marshaler = arg1Marshaler;
ResMarshaler = resMarshaler;
}
@@ -270,8 +318,15 @@ public TResult InvokeJS(T arg1)
ref JSMarshalerArgument args_return = ref arguments[1];
ref JSMarshalerArgument args_arg1 = ref arguments[2];
+#if FEATURE_WASM_THREADS
+ args_exception.InitializeWithContext(JSObject.ProxyContext);
+ args_return.InitializeWithContext(JSObject.ProxyContext);
+ args_arg1.InitializeWithContext(JSObject.ProxyContext);
+ JSProxyContext.JSImportNoCapture();
+#else
args_exception.Initialize();
args_return.Initialize();
+#endif
Arg1Marshaler(ref args_arg1, arg1);
JSFunctionBinding.InvokeJSFunction(JSObject, arguments);
@@ -288,9 +343,9 @@ private sealed class FuncJS
private ArgumentToManagedCallback ResMarshaler;
private JSObject JSObject;
- public FuncJS(IntPtr jsHandle, ArgumentToJSCallback arg1Marshaler, ArgumentToJSCallback arg2Marshaler, ArgumentToManagedCallback resMarshaler)
+ public FuncJS(JSObject holder, ArgumentToJSCallback arg1Marshaler, ArgumentToJSCallback arg2Marshaler, ArgumentToManagedCallback resMarshaler)
{
- JSObject = JSHostImplementation.CreateCSOwnedProxy(jsHandle);
+ JSObject = holder;
Arg1Marshaler = arg1Marshaler;
Arg2Marshaler = arg2Marshaler;
ResMarshaler = resMarshaler;
@@ -308,8 +363,16 @@ public TResult InvokeJS(T1 arg1, T2 arg2)
ref JSMarshalerArgument args_arg1 = ref arguments[2];
ref JSMarshalerArgument args_arg2 = ref arguments[3];
+#if FEATURE_WASM_THREADS
+ args_exception.InitializeWithContext(JSObject.ProxyContext);
+ args_return.InitializeWithContext(JSObject.ProxyContext);
+ args_arg1.InitializeWithContext(JSObject.ProxyContext);
+ args_arg2.InitializeWithContext(JSObject.ProxyContext);
+ JSProxyContext.JSImportNoCapture();
+#else
args_exception.Initialize();
args_return.Initialize();
+#endif
Arg1Marshaler(ref args_arg1, arg1);
Arg2Marshaler(ref args_arg2, arg2);
@@ -328,9 +391,9 @@ private sealed class FuncJS
private ArgumentToManagedCallback ResMarshaler;
private JSObject JSObject;
- public FuncJS(IntPtr jsHandle, ArgumentToJSCallback arg1Marshaler, ArgumentToJSCallback arg2Marshaler, ArgumentToJSCallback arg3Marshaler, ArgumentToManagedCallback resMarshaler)
+ public FuncJS(JSObject holder, ArgumentToJSCallback arg1Marshaler, ArgumentToJSCallback arg2Marshaler, ArgumentToJSCallback arg3Marshaler, ArgumentToManagedCallback resMarshaler)
{
- JSObject = JSHostImplementation.CreateCSOwnedProxy(jsHandle);
+ JSObject = holder;
Arg1Marshaler = arg1Marshaler;
Arg2Marshaler = arg2Marshaler;
Arg3Marshaler = arg3Marshaler;
@@ -350,15 +413,24 @@ public TResult InvokeJS(T1 arg1, T2 arg2, T3 arg3)
ref JSMarshalerArgument args_arg2 = ref arguments[3];
ref JSMarshalerArgument args_arg3 = ref arguments[4];
+#if FEATURE_WASM_THREADS
+ args_exception.InitializeWithContext(JSObject.ProxyContext);
+ args_return.InitializeWithContext(JSObject.ProxyContext);
+ args_arg1.InitializeWithContext(JSObject.ProxyContext);
+ args_arg2.InitializeWithContext(JSObject.ProxyContext);
+ args_arg3.InitializeWithContext(JSObject.ProxyContext);
+ JSProxyContext.JSImportNoCapture();
+#else
args_exception.Initialize();
args_return.Initialize();
+#endif
Arg1Marshaler(ref args_arg1, arg1);
Arg2Marshaler(ref args_arg2, arg2);
Arg3Marshaler(ref args_arg3, arg3);
JSFunctionBinding.InvokeJSFunction(JSObject, arguments);
-
ResMarshaler(ref args_return, out TResult res);
+
return res;
}
}
@@ -378,7 +450,9 @@ public unsafe void ToManaged(out Func? value, ArgumentToManage
return;
}
- value = new FuncJS(slot.JSHandle, resMarshaler).InvokeJS;
+ var ctx = ToManagedContext;
+ var holder = ctx.CreateCSOwnedProxy(slot.JSHandle);
+ value = new FuncJS(holder, resMarshaler).InvokeJS;
}
///
@@ -398,7 +472,9 @@ public unsafe void ToManaged(out Func? value, ArgumentTo
return;
}
- value = new FuncJS(slot.JSHandle, arg1Marshaler, resMarshaler).InvokeJS;
+ var ctx = ToManagedContext;
+ var holder = ctx.CreateCSOwnedProxy(slot.JSHandle);
+ value = new FuncJS(holder, arg1Marshaler, resMarshaler).InvokeJS;
}
@@ -421,7 +497,9 @@ public unsafe void ToManaged(out Func? value,
return;
}
- value = new FuncJS(slot.JSHandle, arg1Marshaler, arg2Marshaler, resMarshaler).InvokeJS;
+ var ctx = ToManagedContext;
+ var holder = ctx.CreateCSOwnedProxy(slot.JSHandle);
+ value = new FuncJS(holder, arg1Marshaler, arg2Marshaler, resMarshaler).InvokeJS;
}
///
@@ -444,8 +522,9 @@ public unsafe void ToManaged(out Func?
value = null;
return;
}
-
- value = new FuncJS(slot.JSHandle, arg1Marshaler, arg2Marshaler, arg3Marshaler, resMarshaler).InvokeJS;
+ var ctx = ToManagedContext;
+ var holder = ctx.CreateCSOwnedProxy(slot.JSHandle);
+ value = new FuncJS(holder, arg1Marshaler, arg2Marshaler, arg3Marshaler, resMarshaler).InvokeJS;
}
///
@@ -463,7 +542,8 @@ public unsafe void ToJS(Action value)
// eventual exception is handled by C# caller
};
slot.Type = MarshalerType.Function;
- slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(cb);
+ var ctx = ToJSContext;
+ slot.GCHandle = ctx.GetJSOwnedObjectGCHandle(cb);
}
///
@@ -484,7 +564,8 @@ public unsafe void ToJS(Action value, ArgumentToManagedCallback arg1Mar
// eventual exception is handled by C# caller
};
slot.Type = MarshalerType.Action;
- slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(cb);
+ var ctx = ToJSContext;
+ slot.GCHandle = ctx.GetJSOwnedObjectGCHandle(cb);
}
///
@@ -509,7 +590,8 @@ public unsafe void ToJS(Action value, ArgumentToManagedCallback<
// eventual exception is handled by C# caller
};
slot.Type = MarshalerType.Action;
- slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(cb);
+ var ctx = ToJSContext;
+ slot.GCHandle = ctx.GetJSOwnedObjectGCHandle(cb);
}
///
@@ -538,7 +620,8 @@ public unsafe void ToJS(Action value, ArgumentToManagedC
// eventual exception is handled by C# caller
};
slot.Type = MarshalerType.Action;
- slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(cb);
+ var ctx = ToJSContext;
+ slot.GCHandle = ctx.GetJSOwnedObjectGCHandle(cb);
}
///
@@ -559,7 +642,8 @@ public unsafe void ToJS(Func value, ArgumentToJSCallback
@@ -584,7 +668,8 @@ public unsafe void ToJS(Func value, ArgumentToManagedCal
// eventual exception is handled by C# caller
};
slot.Type = MarshalerType.Function;
- slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(cb);
+ var ctx = ToJSContext;
+ slot.GCHandle = ctx.GetJSOwnedObjectGCHandle(cb);
}
///
@@ -613,7 +698,8 @@ public unsafe void ToJS(Func value, ArgumentTo
// eventual exception is handled by C# caller
};
slot.Type = MarshalerType.Function;
- slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(cb);
+ var ctx = ToJSContext;
+ slot.GCHandle = ctx.GetJSOwnedObjectGCHandle(cb);
}
///
@@ -646,7 +732,8 @@ public unsafe void ToJS(Func value, Ar
// eventual exception is handled by C# caller
};
slot.Type = MarshalerType.Function;
- slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(cb);
+ var ctx = ToJSContext;
+ slot.GCHandle = ctx.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 0f384d1bf75a03..501484af3ab4fa 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
@@ -132,8 +132,9 @@ public unsafe void ToJS(ArraySegment value)
slot.Type = MarshalerType.None;
return;
}
+ var ctx = ToJSContext;
slot.Type = MarshalerType.ArraySegment;
- slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(value.Array, GCHandleType.Pinned);
+ slot.GCHandle = ctx.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 a7220c8934a6d9..3c41ec2fccc564 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
@@ -20,7 +20,8 @@ public unsafe void ToManaged(out JSObject? value)
value = null;
return;
}
- value = JSHostImplementation.CreateCSOwnedProxy(slot.JSHandle);
+ var ctx = ToManagedContext;
+ value = ctx.CreateCSOwnedProxy(slot.JSHandle);
}
///
@@ -34,13 +35,25 @@ public void ToJS(JSObject? value)
if (value == null)
{
slot.Type = MarshalerType.None;
+ // Note: when null JSObject is passed as argument, it can't be used to capture the target thread in JSProxyContext.CapturedInstance
+ // in case there is no other argument to capture it from, the call will be dispatched according to JSProxyContext.Default
}
else
{
+ ObjectDisposedException.ThrowIf(value.IsDisposed, value);
#if FEATURE_WASM_THREADS
JSObject.AssertThreadAffinity(value);
+ var ctx = value.ProxyContext;
+ if (JSProxyContext.CapturingState == JSProxyContext.JSImportOperationState.JSImportParams)
+ {
+ JSProxyContext.CaptureContextFromParameter(ctx);
+ slot.ContextHandle = ctx.ContextHandle;
+ }
+ else if (slot.ContextHandle != ctx.ContextHandle)
+ {
+ Environment.FailFast($"ContextHandle mismatch, ManagedThreadId: {Environment.CurrentManagedThreadId}. {Environment.NewLine} {Environment.StackTrace}");
+ }
#endif
- ObjectDisposedException.ThrowIf(value.IsDisposed, value);
slot.Type = MarshalerType.JSObject;
slot.JSHandle = value.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 cf3d7e45856660..75fa4d6aa2f0ed 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
@@ -317,7 +317,8 @@ public void ToJS(object? value)
else
{
slot.Type = MarshalerType.Object;
- slot.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(value);
+ var ctx = ToJSContext;
+ slot.GCHandle = ctx.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 bf721e40b4ab0f..51bfa81989133c 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
@@ -4,6 +4,8 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
+using System.ComponentModel;
+using System.Threading;
using static System.Runtime.InteropServices.JavaScript.JSHostImplementation;
namespace System.Runtime.InteropServices.JavaScript
@@ -17,7 +19,7 @@ public partial struct JSMarshalerArgument
/// Type of the marshaled value.
/// The low-level argument representation.
/// The value to be marshaled.
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
+ [EditorBrowsableAttribute(EditorBrowsableState.Never)]
public delegate void ArgumentToManagedCallback(ref JSMarshalerArgument arg, out T value);
///
@@ -27,7 +29,7 @@ public partial struct JSMarshalerArgument
/// Type of the marshaled value.
/// The low-level argument representation.
/// The value to be marshaled.
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
+ [EditorBrowsableAttribute(EditorBrowsableState.Never)]
public delegate void ArgumentToJSCallback(ref JSMarshalerArgument arg, T value);
///
@@ -43,30 +45,38 @@ public unsafe void ToManaged(out Task? value)
value = null;
return;
}
- PromiseHolder holder = GetPromiseHolder(slot.GCHandle);
- TaskCompletionSource tcs = new TaskCompletionSource(holder);
- ToManagedCallback callback = (JSMarshalerArgument* arguments_buffer) =>
+ var ctx = ToManagedContext;
+ lock (ctx)
{
- if (arguments_buffer == null)
+ PromiseHolder holder = ctx.GetPromiseHolder(slot.GCHandle);
+ TaskCompletionSource tcs = new TaskCompletionSource(holder);
+ ToManagedCallback callback = (JSMarshalerArgument* arguments_buffer) =>
{
- tcs.TrySetException(new TaskCanceledException("WebWorker which is origin of the Promise is being terminated."));
- return;
- }
- 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)
- {
- arg_2.ToManaged(out Exception? fail);
- tcs.SetException(fail!);
- }
- else
- {
- tcs.SetResult();
- }
- // eventual exception is handled by caller
- };
- holder.Callback = callback;
- value = tcs.Task;
+ if (arguments_buffer == null)
+ {
+ tcs.TrySetException(new TaskCanceledException("WebWorker which is origin of the Promise is being terminated."));
+ return;
+ }
+ 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)
+ {
+ arg_2.ToManaged(out Exception? fail);
+ tcs.SetException(fail!);
+ }
+ else
+ {
+ tcs.SetResult();
+ }
+ // eventual exception is handled by caller
+ };
+ holder.Callback = callback;
+ value = tcs.Task;
+#if FEATURE_WASM_THREADS
+ // if the other thread created it, signal that it's ready
+ holder.CallbackReady?.Set();
+#endif
+ }
}
///
@@ -74,8 +84,8 @@ public unsafe void ToManaged(out Task? value)
/// It's used by JSImport code generator and should not be used by developers in source code.
///
/// The value to be marshaled.
- /// The generated callback which marshals the result value of the .
- /// Type of marshaled result of the .
+ /// The generated callback which marshals the result value of the .
+ /// Type of marshaled result of the .
public unsafe void ToManaged(out Task? value, ArgumentToManagedCallback marshaler)
{
// there is no nice way in JS how to check that JS promise is already resolved, to send MarshalerType.TaskRejected, MarshalerType.TaskResolved
@@ -84,53 +94,44 @@ public unsafe void ToManaged(out Task? value, ArgumentToManagedCallback
value = null;
return;
}
- PromiseHolder holder = GetPromiseHolder(slot.GCHandle);
- TaskCompletionSource tcs = new TaskCompletionSource(holder);
- ToManagedCallback callback = (JSMarshalerArgument* arguments_buffer) =>
+ var ctx = ToManagedContext;
+ lock (ctx)
{
- if (arguments_buffer == null)
- {
- tcs.TrySetException(new TaskCanceledException("WebWorker which is origin of the Promise is being terminated."));
- return;
- }
-
- 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)
- {
- arg_2.ToManaged(out Exception? fail);
- if (fail == null) throw new InvalidOperationException(SR.FailedToMarshalException);
- tcs.SetException(fail);
- }
- else
+ var holder = ctx.GetPromiseHolder(slot.GCHandle);
+ TaskCompletionSource tcs = new TaskCompletionSource(holder);
+ ToManagedCallback callback = (JSMarshalerArgument* arguments_buffer) =>
{
- marshaler(ref arg_3, out T result);
- tcs.SetResult(result);
- }
- // eventual exception is handled by caller
- };
- holder.Callback = callback;
- value = tcs.Task;
- }
+ if (arguments_buffer == null)
+ {
+ tcs.TrySetException(new TaskCanceledException("WebWorker which is origin of the Promise is being terminated."));
+ return;
+ }
- // TODO unregister and collect pending PromiseHolder also when no C# is awaiting ?
- private static PromiseHolder GetPromiseHolder(nint gcHandle)
- {
- PromiseHolder holder;
- if (IsGCVHandle(gcHandle))
- {
- // this path should only happen when the Promise is passed as argument of JSExport
- holder = new PromiseHolder(gcHandle);
- // TODO for MT this must hit the ThreadJsOwnedHolders in the correct thread
- ThreadJsOwnedHolders.Add(gcHandle, holder);
- }
- else
- {
- holder = (PromiseHolder)((GCHandle)gcHandle).Target!;
+ 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)
+ {
+ arg_2.ToManaged(out Exception? fail);
+ if (fail == null) throw new InvalidOperationException(SR.FailedToMarshalException);
+ tcs.SetException(fail);
+ }
+ else
+ {
+ marshaler(ref arg_3, out T result);
+ tcs.SetResult(result);
+ }
+ // eventual exception is handled by caller
+ };
+ holder.Callback = callback;
+ value = tcs.Task;
+#if FEATURE_WASM_THREADS
+ // if the other thread created it, signal that it's ready
+ holder.CallbackReady?.Set();
+#endif
}
- return holder;
}
+
internal void ToJSDynamic(Task? value)
{
Task? task = value;
@@ -167,10 +168,12 @@ internal void ToJSDynamic(Task? value)
}
}
+ var ctx = ToJSContext;
+
if (slot.Type != MarshalerType.TaskPreCreated)
{
// this path should only happen when the Task is passed as argument of JSImport
- slot.JSHandle = AllocJSVHandle();
+ slot.JSHandle = ctx.AllocJSVHandle();
slot.Type = MarshalerType.Task;
}
else
@@ -179,7 +182,7 @@ internal void ToJSDynamic(Task? value)
// promise and handle is pre-allocated in slot.JSHandle
}
- var taskHolder = new JSObject(slot.JSHandle);
+ var taskHolder = ctx.CreateCSOwnedProxy(slot.JSHandle);
#if FEATURE_WASM_THREADS
task.ContinueWith(Complete, taskHolder, TaskScheduler.FromCurrentSynchronizationContext());
@@ -245,10 +248,12 @@ public void ToJS(Task? value)
}
}
+ var ctx = ToJSContext;
+
if (slot.Type != MarshalerType.TaskPreCreated)
{
// this path should only happen when the Task is passed as argument of JSImport
- slot.JSHandle = AllocJSVHandle();
+ slot.JSHandle = ctx.AllocJSVHandle();
slot.Type = MarshalerType.Task;
}
else
@@ -257,7 +262,7 @@ public void ToJS(Task? value)
// promise and handle is pre-allocated in slot.JSHandle
}
- var taskHolder = new JSObject(slot.JSHandle);
+ var taskHolder = ctx.CreateCSOwnedProxy(slot.JSHandle);
#if FEATURE_WASM_THREADS
task.ContinueWith(Complete, taskHolder, TaskScheduler.FromCurrentSynchronizationContext());
@@ -316,10 +321,11 @@ public void ToJS(Task? value, ArgumentToJSCallback marshaler)
}
}
+ var ctx = ToJSContext;
if (slot.Type != MarshalerType.TaskPreCreated)
{
// this path should only happen when the Task is passed as argument of JSImport
- slot.JSHandle = AllocJSVHandle();
+ slot.JSHandle = ctx.AllocJSVHandle();
slot.Type = MarshalerType.Task;
}
else
@@ -328,7 +334,7 @@ public void ToJS(Task? value, ArgumentToJSCallback marshaler)
// promise and handle is pre-allocated in slot.JSHandle
}
- var taskHolder = new JSObject(slot.JSHandle);
+ var taskHolder = ctx.CreateCSOwnedProxy(slot.JSHandle);
#if FEATURE_WASM_THREADS
task.ContinueWith(Complete, new HolderAndMarshaler(taskHolder, marshaler), TaskScheduler.FromCurrentSynchronizationContext());
@@ -357,14 +363,26 @@ private static void RejectPromise(JSObject holder, Exception ex)
{
holder.AssertNotDisposed();
+#if FEATURE_WASM_THREADS
+ JSObject.AssertThreadAffinity(holder);
+#endif
+
Span args = stackalloc JSMarshalerArgument[4];
ref JSMarshalerArgument exc = ref args[0];
ref JSMarshalerArgument res = ref args[1];
ref JSMarshalerArgument arg_handle = ref args[2];
ref JSMarshalerArgument arg_value = ref args[3];
+#if FEATURE_WASM_THREADS
+ exc.InitializeWithContext(holder.ProxyContext);
+ res.InitializeWithContext(holder.ProxyContext);
+ arg_value.InitializeWithContext(holder.ProxyContext);
+ arg_handle.InitializeWithContext(holder.ProxyContext);
+ JSProxyContext.JSImportNoCapture();
+#else
exc.Initialize();
res.Initialize();
+#endif
// should update existing promise
arg_handle.slot.Type = MarshalerType.TaskRejected;
@@ -373,14 +391,24 @@ private static void RejectPromise(JSObject holder, Exception ex)
// should fail it with exception
arg_value.ToJS(ex);
- JavaScriptImports.ResolveOrRejectPromise(args);
+ // we can free the JSHandle here and the holder.resolve_or_reject will do the rest
+ holder.DisposeImpl(skipJsCleanup: true);
- holder.DisposeLocal();
+#if !FEATURE_WASM_THREADS
+ // order of operations with DisposeImpl matters
+ JSFunctionBinding.ResolveOrRejectPromise(args);
+#else
+ // order of operations with DisposeImpl matters
+ JSFunctionBinding.ResolveOrRejectPromise(args);
+#endif
}
private static void ResolveVoidPromise(JSObject holder)
{
holder.AssertNotDisposed();
+#if FEATURE_WASM_THREADS
+ JSObject.AssertThreadAffinity(holder);
+#endif
Span args = stackalloc JSMarshalerArgument[4];
ref JSMarshalerArgument exc = ref args[0];
@@ -388,8 +416,16 @@ private static void ResolveVoidPromise(JSObject holder)
ref JSMarshalerArgument arg_handle = ref args[2];
ref JSMarshalerArgument arg_value = ref args[3];
+#if FEATURE_WASM_THREADS
+ exc.InitializeWithContext(holder.ProxyContext);
+ res.InitializeWithContext(holder.ProxyContext);
+ arg_value.InitializeWithContext(holder.ProxyContext);
+ arg_handle.InitializeWithContext(holder.ProxyContext);
+ JSProxyContext.JSImportNoCapture();
+#else
exc.Initialize();
res.Initialize();
+#endif
// should update existing promise
arg_handle.slot.Type = MarshalerType.TaskResolved;
@@ -397,14 +433,24 @@ private static void ResolveVoidPromise(JSObject holder)
arg_value.slot.Type = MarshalerType.Void;
- JavaScriptImports.ResolveOrRejectPromise(args);
+ // we can free the JSHandle here and the holder.resolve_or_reject will do the rest
+ holder.DisposeImpl(skipJsCleanup: true);
- holder.DisposeLocal();
+#if !FEATURE_WASM_THREADS
+ // order of operations with DisposeImpl matters
+ JSFunctionBinding.ResolveOrRejectPromise(args);
+#else
+ // order of operations with DisposeImpl matters
+ JSFunctionBinding.ResolveOrRejectPromise(args);
+#endif
}
private static void ResolvePromise(JSObject holder, T value, ArgumentToJSCallback marshaler)
{
holder.AssertNotDisposed();
+#if FEATURE_WASM_THREADS
+ JSObject.AssertThreadAffinity(holder);
+#endif
Span args = stackalloc JSMarshalerArgument[4];
ref JSMarshalerArgument exc = ref args[0];
@@ -412,8 +458,16 @@ private static void ResolvePromise(JSObject holder, T value, ArgumentToJSCall
ref JSMarshalerArgument arg_handle = ref args[2];
ref JSMarshalerArgument arg_value = ref args[3];
+#if FEATURE_WASM_THREADS
+ exc.InitializeWithContext(holder.ProxyContext);
+ res.InitializeWithContext(holder.ProxyContext);
+ arg_value.InitializeWithContext(holder.ProxyContext);
+ arg_handle.InitializeWithContext(holder.ProxyContext);
+ JSProxyContext.JSImportNoCapture();
+#else
exc.Initialize();
res.Initialize();
+#endif
// should update existing promise
arg_handle.slot.Type = MarshalerType.TaskResolved;
@@ -422,9 +476,16 @@ private static void ResolvePromise(JSObject holder, T value, ArgumentToJSCall
// and resolve it with value
marshaler(ref arg_value, value);
- JavaScriptImports.ResolveOrRejectPromise(args);
+ // we can free the JSHandle here and the holder.resolve_or_reject will do the rest
+ holder.DisposeImpl(skipJsCleanup: true);
- holder.DisposeLocal();
+#if !FEATURE_WASM_THREADS
+ // order of operations with DisposeImpl matters
+ JSFunctionBinding.ResolveOrRejectPromise(args);
+#else
+ // order of operations with DisposeImpl matters
+ JSFunctionBinding.ResolveOrRejectPromise(args);
+#endif
}
}
}
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 c4d5494f93718f..8d6cf7aaf28095 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
@@ -23,5 +23,6 @@
+
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs
index 314c3e190c335d..9fabc04697af81 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs
@@ -7,6 +7,7 @@
using System.Threading.Tasks;
using System.Threading;
using Xunit;
+using System.Diagnostics.CodeAnalysis;
#pragma warning disable xUnit1026 // Theory methods should use all of their parameters
namespace System.Runtime.InteropServices.JavaScript.Tests
@@ -16,7 +17,7 @@ public class JSImportExportTest : IAsyncLifetime
[Fact]
public unsafe void StructSize()
{
- Assert.Equal(16, sizeof(JSMarshalerArgument));
+ Assert.Equal(32, sizeof(JSMarshalerArgument));
}
[Fact]
@@ -378,7 +379,7 @@ static void dummyDelegateA()
[Theory]
[MemberData(nameof(MarshalObjectArrayCasesThrow))]
- public unsafe void JsImportObjectArrayThrows(object[]? expected)
+ public void JsImportObjectArrayThrows(object[]? expected)
{
Assert.Throws(() => JavaScriptTestHelper.echo1_ObjectArray(expected));
}
@@ -1959,15 +1960,15 @@ public void JsImportMath()
#endregion
- private void JsExportTest(T value
+ private void JsExportTest<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] T>(T value
, Func invoke, string echoName, string jsType, string? jsClass = null)
{
T res;
res = invoke(value, echoName);
- Assert.Equal(value, res);
+ Assert.Equal(value, res);
}
- private void JsImportTest(T value
+ private void JsImportTest<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] T>(T value
, Action store1
, Func retrieve1
, Func echo1
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs
index 26227d1f00549c..559b9b4ff88e87 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs
@@ -999,19 +999,20 @@ public static async Task InitializeAsync()
{
if (_module == null)
{
- // Log("JavaScriptTestHelper.mjs importing");
- _module = await JSHost.ImportAsync("JavaScriptTestHelper", "../JavaScriptTestHelper.mjs");
- await Setup();
- // Log("JavaScriptTestHelper.mjs imported");
+ _module = await JSHost.ImportAsync("JavaScriptTestHelper", "../JavaScriptTestHelper.mjs"); ;
+ await Setup(); ;
}
+ var p = echopromise_String("aaa");
+ await p;
+
// this gives browser chance to serve UI thread event loop before every test
await Task.Yield();
}
public static Task DisposeAsync()
{
- _module.Dispose();
+ _module?.Dispose();
_module = null;
return Task.CompletedTask;
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs
index 624af0df0fe9f1..baec1cb231c2b8 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs
@@ -367,7 +367,15 @@ export function backback(arg1, arg2, arg3) {
// console.log('backback A')
return (brg1, brg2) => {
// console.log('backback B')
- return arg1(brg1 + arg2, brg2 + arg3);
+ try {
+ var res = arg1(brg1 + arg2, brg2 + arg3);
+ // console.log('backback C')
+ return res
+ }
+ catch (e) {
+ // console.log('backback E ' + e)
+ throw e;
+ }
}
}
diff --git a/src/mono/browser/.gitignore b/src/mono/browser/.gitignore
index 752927a4aeefbf..6047d67b80823f 100644
--- a/src/mono/browser/.gitignore
+++ b/src/mono/browser/.gitignore
@@ -1,3 +1,6 @@
!Makefile
.stamp-wasm-install-and-select*
emsdk
+
+runtime/dotnet.d.ts.sha256
+runtime/dotnet-legacy.d.ts.sha256
diff --git a/src/mono/browser/runtime/corebindings.c b/src/mono/browser/runtime/corebindings.c
index 00fed13f2d0a54..8657cd261459f4 100644
--- a/src/mono/browser/runtime/corebindings.c
+++ b/src/mono/browser/runtime/corebindings.c
@@ -42,7 +42,7 @@ extern void* mono_wasm_invoke_js_blazor (MonoString **exceptionMessage, void *ca
#endif /* DISABLE_LEGACY_JS_INTEROP */
#ifndef DISABLE_THREADS
-extern void mono_wasm_install_js_worker_interop ();
+extern void mono_wasm_install_js_worker_interop (int context_gc_handle);
extern void mono_wasm_uninstall_js_worker_interop ();
#endif /* DISABLE_THREADS */
diff --git a/src/mono/browser/runtime/cwraps.ts b/src/mono/browser/runtime/cwraps.ts
index e76f78e4fc285f..0a3f2e0894cd1d 100644
--- a/src/mono/browser/runtime/cwraps.ts
+++ b/src/mono/browser/runtime/cwraps.ts
@@ -88,6 +88,7 @@ const fn_signatures: SigLine[] = [
[true, "mono_wasm_profiler_init_browser", "void", ["number"]],
[false, "mono_wasm_exec_regression", "number", ["number", "string"]],
[false, "mono_wasm_invoke_method_bound", "number", ["number", "number", "number"]],
+ [false, "mono_wasm_invoke_method_raw", "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"]],
@@ -229,6 +230,7 @@ export interface t_Cwraps {
mono_wasm_set_main_args(argc: number, argv: VoidPtr): void;
mono_wasm_exec_regression(verbose_level: number, image: string): number;
mono_wasm_invoke_method_bound(method: MonoMethod, args: JSMarshalerArguments, fail: MonoStringRef): number;
+ mono_wasm_invoke_method_raw(method: MonoMethod, fail: MonoStringRef): number;
mono_wasm_write_managed_pointer_unsafe(destination: VoidPtr | MonoObjectRef, pointer: ManagedPointer): void;
mono_wasm_copy_managed_pointer(destination: VoidPtr | MonoObjectRef, source: VoidPtr | MonoObjectRef): void;
mono_wasm_i52_to_f64(source: VoidPtr, error: Int32Ptr): number;
diff --git a/src/mono/browser/runtime/driver.c b/src/mono/browser/runtime/driver.c
index 860140d9cf3273..a48e77894018e6 100644
--- a/src/mono/browser/runtime/driver.c
+++ b/src/mono/browser/runtime/driver.c
@@ -322,6 +322,27 @@ mono_wasm_invoke_method_bound (MonoMethod *method, void* args /*JSMarshalerArgum
return is_err;
}
+EMSCRIPTEN_KEEPALIVE int
+mono_wasm_invoke_method_raw (MonoMethod *method, MonoString **out_exc)
+{
+ PVOLATILE(MonoObject) temp_exc = NULL;
+
+ int is_err = 0;
+
+ MONO_ENTER_GC_UNSAFE;
+ mono_runtime_invoke (method, NULL, NULL, (MonoObject **)&temp_exc);
+
+ if (temp_exc && out_exc) {
+ PVOLATILE(MonoObject) exc2 = NULL;
+ store_volatile((MonoObject**)out_exc, (MonoObject*)mono_object_to_string ((MonoObject*)temp_exc, (MonoObject **)&exc2));
+ if (exc2)
+ store_volatile((MonoObject**)out_exc, (MonoObject*)mono_string_new (root_domain, "Exception Double Fault"));
+ is_err = 1;
+ }
+ MONO_EXIT_GC_UNSAFE;
+ return is_err;
+}
+
EMSCRIPTEN_KEEPALIVE MonoMethod*
mono_wasm_assembly_get_entry_point (MonoAssembly *assembly, int auto_insert_breakpoint)
{
diff --git a/src/mono/browser/runtime/gc-handles.ts b/src/mono/browser/runtime/gc-handles.ts
index feacccce2a5f0a..6d95160ada062c 100644
--- a/src/mono/browser/runtime/gc-handles.ts
+++ b/src/mono/browser/runtime/gc-handles.ts
@@ -98,6 +98,7 @@ export function register_with_jsv_handle(js_obj: any, jsv_handle: JSHandle) {
}
}
+// note: in MT, this is called from locked JSProxyContext. Don't call anything that would need locking.
export function mono_wasm_release_cs_owned_object(js_handle: JSHandle): void {
let obj: any;
if (is_js_handle(js_handle)) {
@@ -108,6 +109,7 @@ export function mono_wasm_release_cs_owned_object(js_handle: JSHandle): void {
else if (is_jsv_handle(js_handle)) {
obj = _cs_owned_objects_by_jsv_handle[0 - js_handle];
_cs_owned_objects_by_jsv_handle[0 - js_handle] = undefined;
+ // see free list in JSProxyContext.FreeJSVHandle
}
mono_assert(obj !== undefined && obj !== null, "ObjectDisposedException");
if (typeof obj[cs_owned_js_handle_symbol] !== "undefined") {
diff --git a/src/mono/browser/runtime/http.ts b/src/mono/browser/runtime/http.ts
index cdb60bd862ab08..23f8b20e3e4be6 100644
--- a/src/mono/browser/runtime/http.ts
+++ b/src/mono/browser/runtime/http.ts
@@ -4,6 +4,7 @@
import { wrap_as_cancelable_promise } from "./cancelable-promise";
import { ENVIRONMENT_IS_NODE, Module, loaderHelpers, mono_assert } from "./globals";
import { MemoryViewType, Span } from "./marshal";
+import { assert_synchronization_context } from "./pthreads/shared";
import type { VoidPtr } from "./types/emscripten";
import { ControllablePromise } from "./types/internal";
@@ -112,6 +113,7 @@ export function http_wasm_fetch_bytes(url: string, header_names: string[], heade
export function http_wasm_fetch(url: string, header_names: string[], header_values: string[], option_names: string[], option_values: any[], abort_controller: AbortController, body: Uint8Array | ReadableStream | null): ControllablePromise {
verifyEnvironment();
+ assert_synchronization_context();
mono_assert(url && typeof url === "string", "expected url string");
mono_assert(header_names && header_values && Array.isArray(header_names) && Array.isArray(header_values) && header_names.length === header_values.length, "expected headerNames and headerValues arrays");
mono_assert(option_names && option_values && Array.isArray(option_names) && Array.isArray(option_values) && option_names.length === option_values.length, "expected headerNames and headerValues arrays");
diff --git a/src/mono/browser/runtime/invoke-cs.ts b/src/mono/browser/runtime/invoke-cs.ts
index 17e9cf181b5a16..46324a66175598 100644
--- a/src/mono/browser/runtime/invoke-cs.ts
+++ b/src/mono/browser/runtime/invoke-cs.ts
@@ -9,7 +9,7 @@ import { bind_arg_marshal_to_cs } from "./marshal-to-cs";
import { marshal_exception_to_js, bind_arg_marshal_to_js, end_marshal_task_to_js } from "./marshal-to-js";
import {
get_arg, get_sig, get_signature_argument_count, is_args_exception,
- bound_cs_function_symbol, get_signature_version, alloc_stack_frame, get_signature_type,
+ bound_cs_function_symbol, get_signature_version, alloc_stack_frame, get_signature_type, set_args_context,
} from "./marshal";
import { mono_wasm_new_external_root, mono_wasm_new_root } from "./roots";
import { monoStringToString } from "./strings";
@@ -356,8 +356,9 @@ export function invoke_method_and_handle_exception(method: MonoMethod, args: JSM
assert_bindings();
const fail_root = mono_wasm_new_root();
try {
+ set_args_context(args);
const fail = cwraps.mono_wasm_invoke_method_bound(method, args, fail_root.address);
- if (fail) throw new Error("ERR24: Unexpected error: " + monoStringToString(fail_root));
+ if (fail) runtimeHelpers.abort("ERR24: Unexpected error: " + monoStringToString(fail_root));
if (is_args_exception(args)) {
const exc = get_arg(args, 0);
throw marshal_exception_to_js(exc);
@@ -368,6 +369,18 @@ export function invoke_method_and_handle_exception(method: MonoMethod, args: JSM
}
}
+export function invoke_method_raw(method: MonoMethod): void {
+ assert_bindings();
+ const fail_root = mono_wasm_new_root();
+ try {
+ const fail = cwraps.mono_wasm_invoke_method_raw(method, fail_root.address);
+ if (fail) runtimeHelpers.abort("ERR24: Unexpected error: " + monoStringToString(fail_root));
+ }
+ finally {
+ fail_root.release();
+ }
+}
+
export const exportsByAssembly: Map = new Map();
function _walk_exports_to_set_function(assembly: string, namespace: string, classname: string, methodname: string, signature_hash: number, fn: Function): void {
const parts = `${namespace}.${classname}`.replace(/\//g, ".").split(".");
diff --git a/src/mono/browser/runtime/managed-exports.ts b/src/mono/browser/runtime/managed-exports.ts
index 7e1347617aab75..fff5deb4bb9092 100644
--- a/src/mono/browser/runtime/managed-exports.ts
+++ b/src/mono/browser/runtime/managed-exports.ts
@@ -7,7 +7,7 @@ import { GCHandle, MarshalerToCs, MarshalerToJs, MarshalerType, MonoMethod } fro
import cwraps from "./cwraps";
import { runtimeHelpers, Module, loaderHelpers, mono_assert } from "./globals";
import { alloc_stack_frame, get_arg, set_arg_type, set_gc_handle } from "./marshal";
-import { invoke_method_and_handle_exception } from "./invoke-cs";
+import { invoke_method_and_handle_exception, invoke_method_raw } from "./invoke-cs";
import { marshal_array_to_cs, marshal_array_to_cs_impl, marshal_exception_to_cs, marshal_intptr_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 } from "./marshal-to-js";
import { do_not_force_dispose } from "./gc-handles";
@@ -24,8 +24,8 @@ export function init_managed_exports(): void {
if (!runtimeHelpers.runtime_interop_exports_class)
throw "Can't find " + runtimeHelpers.runtime_interop_namespace + "." + runtimeHelpers.runtime_interop_exports_classname + " class";
- const install_sync_context = MonoWasmThreads ? get_method("InstallSynchronizationContext") : undefined;
- mono_assert(!MonoWasmThreads || install_sync_context, "Can't find InstallSynchronizationContext method");
+ const install_main_synchronization_context = MonoWasmThreads ? get_method("InstallMainSynchronizationContext") : undefined;
+ mono_assert(!MonoWasmThreads || install_main_synchronization_context, "Can't find InstallMainSynchronizationContext method");
const call_entry_point = get_method("CallEntrypoint");
mono_assert(call_entry_point, "Can't find CallEntrypoint method");
const release_js_owned_object_by_gc_handle_method = get_method("ReleaseJSOwnedObjectByGCHandle");
@@ -188,17 +188,8 @@ export function init_managed_exports(): void {
Module.stackRestore(sp);
}
};
-
- if (MonoWasmThreads && install_sync_context) {
- runtimeHelpers.javaScriptExports.install_synchronization_context = () => {
- const sp = Module.stackSave();
- try {
- const args = alloc_stack_frame(2);
- invoke_method_and_handle_exception(install_sync_context, args);
- } finally {
- Module.stackRestore(sp);
- }
- };
+ if (MonoWasmThreads && install_main_synchronization_context) {
+ runtimeHelpers.javaScriptExports.install_main_synchronization_context = () => invoke_method_raw(install_main_synchronization_context);
}
}
diff --git a/src/mono/browser/runtime/marshal-to-cs.ts b/src/mono/browser/runtime/marshal-to-cs.ts
index 6765b95b07e1d7..c0dc159eaa8fa0 100644
--- a/src/mono/browser/runtime/marshal-to-cs.ts
+++ b/src/mono/browser/runtime/marshal-to-cs.ts
@@ -337,10 +337,14 @@ function _marshal_task_to_cs(arg: JSMarshalerArgument, value: Promise, _?:
}
try {
mono_assert(!holder.isDisposed, "This promise can't be propagated to managed code, because the Task was already freed.");
- if (MonoWasmThreads)
+ if (MonoWasmThreads) {
settleUnsettledPromise();
+ }
+ // we can unregister the GC handle on JS side
+ teardown_managed_proxy(holder, gc_handle, true);
+ // order of operations with teardown_managed_proxy matters
+ // so that managed user code running in the continuation could allocate the same GCHandle number and the local registry would be already ok with that
runtimeHelpers.javaScriptExports.complete_task(gc_handle, null, data, res_converter || _marshal_cs_object_to_cs);
- teardown_managed_proxy(holder, gc_handle, true); // this holds holder alive for finalizer, until the promise is freed, (holding promise instead would not work)
}
catch (ex) {
runtimeHelpers.abort(ex);
@@ -352,10 +356,13 @@ function _marshal_task_to_cs(arg: JSMarshalerArgument, value: Promise, _?:
}
try {
mono_assert(!holder.isDisposed, "This promise can't be propagated to managed code, because the Task was already freed.");
- if (MonoWasmThreads)
+ if (MonoWasmThreads) {
settleUnsettledPromise();
+ }
+ // we can unregister the GC handle on JS side
+ teardown_managed_proxy(holder, gc_handle, true);
+ // order of operations with teardown_managed_proxy matters
runtimeHelpers.javaScriptExports.complete_task(gc_handle, reason, null, undefined);
- teardown_managed_proxy(holder, gc_handle, true); // this holds holder alive for finalizer, until the promise is freed
}
catch (ex) {
runtimeHelpers.abort(ex);
diff --git a/src/mono/browser/runtime/marshal-to-js.ts b/src/mono/browser/runtime/marshal-to-js.ts
index 0053102be61e33..0aa489b3b83c35 100644
--- a/src/mono/browser/runtime/marshal-to-js.ts
+++ b/src/mono/browser/runtime/marshal-to-js.ts
@@ -265,7 +265,7 @@ export function end_marshal_task_to_js(args: JSMarshalerArguments, res_converter
}
// otherwise drop the eagerPromise's handle
- const js_handle = get_arg_js_handle(res);
+ const js_handle = mono_wasm_get_js_handle(eagerPromise);
mono_wasm_release_cs_owned_object(js_handle);
// get the synchronous result
diff --git a/src/mono/browser/runtime/marshal.ts b/src/mono/browser/runtime/marshal.ts
index a788512e9d567d..bf4c5145badd47 100644
--- a/src/mono/browser/runtime/marshal.ts
+++ b/src/mono/browser/runtime/marshal.ts
@@ -18,7 +18,7 @@ export const bound_js_function_symbol = Symbol.for("wasm bound_js_function");
export const imported_js_function_symbol = Symbol.for("wasm imported_js_function");
export const proxy_debug_symbol = Symbol.for("wasm proxy_debug");
-export const JavaScriptMarshalerArgSize = 16;
+export const JavaScriptMarshalerArgSize = 32;
export const JSMarshalerTypeSize = 32;
export const JSMarshalerSignatureHeaderSize = 4 * 8; // without Exception and Result
@@ -26,6 +26,7 @@ export function alloc_stack_frame(size: number): JSMarshalerArguments {
const bytes = JavaScriptMarshalerArgSize * size;
const args = Module.stackAlloc(bytes) as any;
_zero_region(args, bytes);
+ set_args_context(args);
return args;
}
@@ -40,6 +41,15 @@ export function is_args_exception(args: JSMarshalerArguments): boolean {
return exceptionType !== MarshalerType.None;
}
+export function set_args_context(args: JSMarshalerArguments): void {
+ if (!MonoWasmThreads) return;
+ mono_assert(args, "Null args");
+ const exc = get_arg(args, 0);
+ const res = get_arg(args, 1);
+ set_arg_proxy_context(exc);
+ set_arg_proxy_context(res);
+}
+
export function get_sig(signature: JSFunctionSignature, index: number): JSMarshalerType {
mono_assert(signature, "Null signatures");
return signature + (index * JSMarshalerTypeSize) + JSMarshalerSignatureHeaderSize;
@@ -252,9 +262,16 @@ export function get_arg_js_handle(arg: JSMarshalerArgument): JSHandle {
return getI32(arg + 4);
}
+export function set_arg_proxy_context(arg: JSMarshalerArgument): void {
+ if (!MonoWasmThreads) return;
+ mono_assert(arg, "Null arg");
+ setI32(arg + 16, runtimeHelpers.proxy_context_gc_handle);
+}
+
export function set_js_handle(arg: JSMarshalerArgument, jsHandle: JSHandle): void {
mono_assert(arg, "Null arg");
setI32(arg + 4, jsHandle);
+ set_arg_proxy_context(arg);
}
export function get_arg_gc_handle(arg: JSMarshalerArgument): GCHandle {
@@ -265,6 +282,7 @@ export function get_arg_gc_handle(arg: JSMarshalerArgument): GCHandle {
export function set_gc_handle(arg: JSMarshalerArgument, gcHandle: GCHandle): void {
mono_assert(arg, "Null arg");
setI32(arg + 4, gcHandle);
+ set_arg_proxy_context(arg);
}
export function get_string_root(arg: JSMarshalerArgument): WasmRoot {
@@ -331,7 +349,7 @@ export class ManagedError extends Error implements IDisposable {
if (this.managed_stack) {
return this.managed_stack;
}
- if (loaderHelpers.is_runtime_running() && (!MonoWasmThreads || runtimeHelpers.jsSynchronizationContextInstalled)) {
+ if (loaderHelpers.is_runtime_running() && (!MonoWasmThreads || runtimeHelpers.proxy_context_gc_handle)) {
const gc_handle = (this)[js_owned_gc_handle_symbol];
if (gc_handle !== GCHandleNull) {
const managed_stack = runtimeHelpers.javaScriptExports.get_managed_stack_trace(gc_handle);
diff --git a/src/mono/browser/runtime/pthreads/shared/index.ts b/src/mono/browser/runtime/pthreads/shared/index.ts
index af991aa7b3fe3b..499ff3f129c0da 100644
--- a/src/mono/browser/runtime/pthreads/shared/index.ts
+++ b/src/mono/browser/runtime/pthreads/shared/index.ts
@@ -11,6 +11,7 @@ import { mono_log_debug } from "../../logging";
import { bindings_init } from "../../startup";
import { forceDisposeProxies } from "../../gc-handles";
import { pthread_self } from "../worker";
+import { GCHandle, GCHandleNull } from "../../types/internal";
export interface PThreadInfo {
readonly pthreadId: pthreadPtr;
@@ -166,11 +167,11 @@ export function isMonoWorkerMessagePreload(message: MonoWorkerMessage): message
return false;
}
-export function mono_wasm_install_js_worker_interop(): void {
+export function mono_wasm_install_js_worker_interop(context_gc_handle: GCHandle): void {
if (!MonoWasmThreads) return;
bindings_init();
- if (!runtimeHelpers.jsSynchronizationContextInstalled) {
- runtimeHelpers.jsSynchronizationContextInstalled = true;
+ if (!runtimeHelpers.proxy_context_gc_handle) {
+ runtimeHelpers.proxy_context_gc_handle = context_gc_handle;
mono_log_debug("Installed JSSynchronizationContext");
}
Module.runtimeKeepalivePush();
@@ -184,19 +185,19 @@ export function mono_wasm_install_js_worker_interop(): void {
export function mono_wasm_uninstall_js_worker_interop(): void {
if (!MonoWasmThreads) return;
mono_assert(runtimeHelpers.mono_wasm_bindings_is_ready, "JS interop is not installed on this worker.");
- mono_assert(runtimeHelpers.jsSynchronizationContextInstalled, "JSSynchronizationContext is not installed on this worker.");
+ mono_assert(runtimeHelpers.proxy_context_gc_handle, "JSSynchronizationContext is not installed on this worker.");
forceDisposeProxies(true, runtimeHelpers.diagnosticTracing);
Module.runtimeKeepalivePop();
- runtimeHelpers.jsSynchronizationContextInstalled = false;
+ runtimeHelpers.proxy_context_gc_handle = GCHandleNull;
runtimeHelpers.mono_wasm_bindings_is_ready = false;
set_thread_info(pthread_self ? pthread_self.pthreadId : 0, true, false, false);
}
export function assert_synchronization_context(): void {
if (MonoWasmThreads) {
- mono_assert(runtimeHelpers.jsSynchronizationContextInstalled, "Please use dedicated worker for working with JavaScript interop. See https://github.com/dotnet/runtime/blob/main/src/mono/wasm/threads.md#JS-interop-on-dedicated-threads");
+ mono_assert(runtimeHelpers.proxy_context_gc_handle, "Please use dedicated worker for working with JavaScript interop. See https://github.com/dotnet/runtime/blob/main/src/mono/wasm/threads.md#JS-interop-on-dedicated-threads");
}
}
diff --git a/src/mono/browser/runtime/startup.ts b/src/mono/browser/runtime/startup.ts
index 556dd158c9700c..6ec998ccaabebf 100644
--- a/src/mono/browser/runtime/startup.ts
+++ b/src/mono/browser/runtime/startup.ts
@@ -289,8 +289,7 @@ async function onRuntimeInitializedAsync(userOnRuntimeInitialized: () => void) {
}
if (MonoWasmThreads) {
- runtimeHelpers.javaScriptExports.install_synchronization_context();
- runtimeHelpers.jsSynchronizationContextInstalled = true;
+ runtimeHelpers.javaScriptExports.install_main_synchronization_context();
}
if (!runtimeHelpers.mono_wasm_runtime_is_ready) mono_wasm_runtime_ready();
diff --git a/src/mono/browser/runtime/types/internal.ts b/src/mono/browser/runtime/types/internal.ts
index 4c086350182a69..6e7b649a56c6b6 100644
--- a/src/mono/browser/runtime/types/internal.ts
+++ b/src/mono/browser/runtime/types/internal.ts
@@ -194,7 +194,7 @@ export type RuntimeHelpers = {
getMemory(): WebAssembly.Memory,
getWasmIndirectFunctionTable(): WebAssembly.Table,
runtimeReady: boolean,
- jsSynchronizationContextInstalled: boolean,
+ proxy_context_gc_handle: GCHandle,
cspPolicy: boolean,
allAssetsInMemory: PromiseAndController,
@@ -348,8 +348,8 @@ export interface JavaScriptExports {
// the marshaled signature is: Task? CallEntrypoint(MonoMethod* entrypointPtr, string[] args)
call_entry_point(entry_point: MonoMethod, args?: string[]): Promise;
- // the marshaled signature is: void InstallSynchronizationContext()
- install_synchronization_context(): void;
+ // the marshaled signature is: void InstallMainSynchronizationContext()
+ install_main_synchronization_context(): void;
// the marshaled signature is: string GetManagedStackTrace(GCHandle exception)
get_managed_stack_trace(exception_gc_handle: GCHandle): string | null
diff --git a/src/mono/browser/runtime/web-socket.ts b/src/mono/browser/runtime/web-socket.ts
index 9c43849f39534c..41b3ca22994281 100644
--- a/src/mono/browser/runtime/web-socket.ts
+++ b/src/mono/browser/runtime/web-socket.ts
@@ -13,6 +13,7 @@ import { mono_log_warn } from "./logging";
import { viewOrCopy, utf8ToStringRelaxed, stringToUTF8 } from "./strings";
import { IDisposable } from "./marshal";
import { wrap_as_cancelable } from "./cancelable-promise";
+import { assert_synchronization_context } from "./pthreads/shared";
const wasm_ws_pending_send_buffer = Symbol.for("wasm ws_pending_send_buffer");
const wasm_ws_pending_send_buffer_offset = Symbol.for("wasm ws_pending_send_buffer_offset");
@@ -44,6 +45,7 @@ function verifyEnvironment() {
export function ws_wasm_create(uri: string, sub_protocols: string[] | null, receive_status_ptr: VoidPtr, onClosed: (code: number, reason: string) => void): WebSocketExtension {
verifyEnvironment();
+ assert_synchronization_context();
mono_assert(uri && typeof uri === "string", () => `ERR12: Invalid uri ${typeof uri}`);
mono_assert(typeof onClosed === "function", () => `ERR12: Invalid onClosed ${typeof onClosed}`);
diff --git a/src/mono/sample/wasm/browser-nextjs/.gitignore b/src/mono/sample/wasm/browser-nextjs/.gitignore
index 20fccdd4b84d99..36a31210aea2d3 100644
--- a/src/mono/sample/wasm/browser-nextjs/.gitignore
+++ b/src/mono/sample/wasm/browser-nextjs/.gitignore
@@ -28,3 +28,5 @@ yarn-error.log*
.env.development.local
.env.test.local
.env.production.local
+
+public
\ No newline at end of file