diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs
index 01888e365ee0cb..000dd103d21890 100644
--- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs
+++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
+using System.Reflection;
using System.Runtime.CompilerServices;
internal static partial class Interop
@@ -25,8 +26,6 @@ internal static unsafe partial class Runtime
public static extern void InvokeJSFunctionSend(nint targetNativeTID, nint functionHandle, nint data);
#endif
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern unsafe void BindCSFunction(in string fully_qualified_name, int signature_hash, void* signature, out int is_exception, out object result);
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void ResolveOrRejectPromise(nint data);
#if FEATURE_WASM_MANAGED_THREADS
@@ -65,7 +64,7 @@ internal static unsafe partial class Runtime
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void CancelPromise(nint gcHandle);
#endif
-
-
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern void SetEntryAssembly(Assembly assembly, int entryPointMetadataToken);
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/Resources/Strings.resx b/src/libraries/System.Runtime.InteropServices.JavaScript/src/Resources/Strings.resx
index 9cb71cc66223ad..e77e419997996d 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/Resources/Strings.resx
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/Resources/Strings.resx
@@ -138,8 +138,8 @@
Managed entrypoint handle is not set.
-
- Cannot resolve managed entrypoint handle.
+
+ Cannot resolve managed entrypoint {0} in assembly {1}.
Return type '{0}' from main method in not supported.
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 f4a6efb3c333f3..9ec4a2400cd211 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs
@@ -1,14 +1,11 @@
// 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;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
-using static System.Runtime.InteropServices.JavaScript.JSHostImplementation;
namespace System.Runtime.InteropServices.JavaScript
{
@@ -18,13 +15,14 @@ namespace System.Runtime.InteropServices.JavaScript
internal static unsafe partial class JavaScriptExports
{
// the marshaled signature is:
- // Task? CallEntrypoint(MonoMethod* entrypointPtr, string[] args)
+ // Task? CallEntrypoint(string mainAssemblyName, string[] args, bool waitForDebugger)
public static void CallEntrypoint(JSMarshalerArgument* arguments_buffer)
{
ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame()
ref JSMarshalerArgument arg_result = ref arguments_buffer[1]; // initialized by caller in alloc_stack_frame()
ref JSMarshalerArgument arg_1 = ref arguments_buffer[2]; // initialized and set by caller
ref JSMarshalerArgument arg_2 = ref arguments_buffer[3]; // initialized and set by caller
+ ref JSMarshalerArgument arg_3 = ref arguments_buffer[4]; // initialized and set by caller
try
{
#if FEATURE_WASM_MANAGED_THREADS
@@ -32,62 +30,12 @@ public static void CallEntrypoint(JSMarshalerArgument* arguments_buffer)
arg_exc.AssertCurrentThreadContext();
#endif
- arg_1.ToManaged(out IntPtr entrypointPtr);
- if (entrypointPtr == IntPtr.Zero)
- {
- throw new MissingMethodException(SR.MissingManagedEntrypointHandle);
- }
+ arg_1.ToManaged(out string? mainAssemblyName);
+ arg_2.ToManaged(out string?[]? args);
+ arg_3.ToManaged(out bool waitForDebugger);
- RuntimeMethodHandle methodHandle = GetMethodHandleFromIntPtr(entrypointPtr);
- // this would not work for generic types. But Main() could not be generic, so we are fine.
- MethodInfo? method = MethodBase.GetMethodFromHandle(methodHandle) as MethodInfo;
- if (method == null)
- {
- throw new InvalidOperationException(SR.CannotResolveManagedEntrypointHandle);
- }
+ Task? result = JSHostImplementation.CallEntrypoint(mainAssemblyName, args, waitForDebugger);
- arg_2.ToManaged(out string?[]? args);
- object[] argsToPass = System.Array.Empty();
- Task? result = null;
- var parameterInfos = method.GetParameters();
- if (parameterInfos.Length > 0 && parameterInfos[0].ParameterType == typeof(string[]))
- {
- argsToPass = new object[] { args ?? System.Array.Empty() };
- }
- if (method.ReturnType == typeof(void))
- {
- method.Invoke(null, argsToPass);
- }
- else if (method.ReturnType == typeof(int))
- {
- int intResult = (int)method.Invoke(null, argsToPass)!;
- result = Task.FromResult(intResult);
- }
- else if (method.ReturnType == typeof(Task))
- {
- Task methodResult = (Task)method.Invoke(null, argsToPass)!;
- TaskCompletionSource tcs = new TaskCompletionSource();
- result = tcs.Task;
- methodResult.ContinueWith((t) =>
- {
- if (t.IsFaulted)
- {
- tcs.SetException(t.Exception!);
- }
- else
- {
- tcs.SetResult(0);
- }
- }, TaskScheduler.Default);
- }
- else if (method.ReturnType == typeof(Task))
- {
- result = (Task)method.Invoke(null, argsToPass)!;
- }
- else
- {
- throw new InvalidOperationException(SR.Format(SR.ReturnTypeNotSupportedForMain, method.ReturnType.FullName));
- }
arg_result.ToJS(result, (ref JSMarshalerArgument arg, int value) =>
{
arg.ToJS(value);
@@ -95,10 +43,7 @@ public static void CallEntrypoint(JSMarshalerArgument* arguments_buffer)
}
catch (Exception ex)
{
- if (ex is TargetInvocationException refEx && refEx.InnerException != null)
- ex = refEx.InnerException;
-
- arg_exc.ToJS(ex);
+ Environment.FailFast("CallEntrypoint: Unexpected synchronous failure. " + ex);
}
}
@@ -163,7 +108,7 @@ public static void ReleaseJSOwnedObjectByGCHandle(JSMarshalerArgument* arguments
}
catch (Exception ex)
{
- arg_exc.ToJS(ex);
+ Environment.FailFast("ReleaseJSOwnedObjectByGCHandle: Unexpected synchronous failure. " + ex);
}
}
@@ -188,7 +133,7 @@ public static void CallDelegate(JSMarshalerArgument* arguments_buffer)
#endif
GCHandle callback_gc_handle = (GCHandle)arg_1.slot.GCHandle;
- if (callback_gc_handle.Target is ToManagedCallback callback)
+ if (callback_gc_handle.Target is JSHostImplementation.ToManagedCallback callback)
{
// arg_2, arg_3, arg_4, arg_res are processed by the callback
callback(arguments_buffer);
@@ -219,7 +164,7 @@ public static void CompleteTask(JSMarshalerArgument* arguments_buffer)
// 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);
- ToManagedCallback callback;
+ JSHostImplementation.ToManagedCallback callback;
#if FEATURE_WASM_MANAGED_THREADS
lock (ctx)
@@ -260,7 +205,7 @@ public static void CompleteTask(JSMarshalerArgument* arguments_buffer)
}
catch (Exception ex)
{
- arg_exc.ToJS(ex);
+ Environment.FailFast("CompleteTask: Unexpected synchronous failure. " + ex);
}
}
@@ -318,6 +263,30 @@ public static void InstallMainSynchronizationContext(JSMarshalerArgument* argume
#endif
+ // the marshaled signature is:
+ // Task BindAssemblyExports(string assemblyName)
+ public static void BindAssemblyExports(JSMarshalerArgument* arguments_buffer)
+ {
+ ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame()
+ ref JSMarshalerArgument arg_result = ref arguments_buffer[1]; // used as return value
+ ref JSMarshalerArgument arg_1 = ref arguments_buffer[2];// initialized and set by caller
+ try
+ {
+ string? assemblyName;
+ // when we arrive here, we are on the thread which owns the proxies
+ arg_exc.AssertCurrentThreadContext();
+ arg_1.ToManaged(out assemblyName);
+
+ var result = JSHostImplementation.BindAssemblyExports(assemblyName);
+
+ arg_result.ToJS(result);
+ }
+ catch (Exception ex)
+ {
+ Environment.FailFast("BindAssemblyExports: Unexpected synchronous failure. " + ex);
+ }
+ }
+
[MethodImpl(MethodImplOptions.NoInlining)] // profiler needs to find it executed under this name
public static void StopProfile()
{
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.Generated.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.Generated.cs
index 8c5c3782a2f379..2e41abd76c5c1e 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.Generated.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.Generated.cs
@@ -45,6 +45,10 @@ internal static unsafe partial class JavaScriptImports
[JSImport("INTERNAL.dynamic_import")]
// TODO: the continuation should be running on deputy or TP in MT
public static partial Task DynamicImport(string moduleName, string moduleUrl);
+
+ [JSImport("INTERNAL.mono_wasm_bind_cs_function")]
+ public static partial void BindCSFunction(IntPtr monoMethod, string assemblyName, string namespaceName, string shortClassName, string methodName, int signatureHash, IntPtr signature);
+
#if FEATURE_WASM_MANAGED_THREADS
[JSImport("INTERNAL.thread_available")]
// TODO: the continuation should be running on deputy or TP in MT
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 acbd4a92ef9042..ae432cafc323cc 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
@@ -191,10 +191,11 @@ public static JSFunctionBinding BindManagedFunction(string fullyQualifiedName, i
{
if (RuntimeInformation.OSArchitecture != Architecture.Wasm)
throw new PlatformNotSupportedException();
-#if FEATURE_WASM_MANAGED_THREADS
- JSProxyContext.AssertIsInteropThread();
-#endif
- return BindManagedFunctionImpl(fullyQualifiedName, signatureHash, signatures);
+
+ // this could be called by assembly module initializer from Net7 code-gen
+ // on wrong thread, in which case we will bind it to UI thread
+
+ return JSHostImplementation.BindManagedFunction(fullyQualifiedName, signatureHash, signatures);
}
#if !DEBUG
@@ -407,21 +408,6 @@ internal static unsafe JSFunctionBinding BindJSImportImpl(string functionName, s
return signature;
}
- internal static unsafe JSFunctionBinding BindManagedFunctionImpl(string fullyQualifiedName, int signatureHash, ReadOnlySpan signatures)
- {
- var signature = JSHostImplementation.GetMethodSignature(signatures, null, null);
-
- Interop.Runtime.BindCSFunction(fullyQualifiedName, signatureHash, signature.Header, out int isException, out object exceptionMessage);
- if (isException != 0)
- {
- throw new JSException((string)exceptionMessage);
- }
-
- JSHostImplementation.FreeMethodSignatureBuffer(signature);
-
- return signature;
- }
-
#if !DEBUG
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
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 3035781eb730f1..7268951b68828b 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
@@ -33,14 +33,6 @@ public static bool GetTaskResultDynamic(Task task, out object? value)
throw new InvalidOperationException();
}
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static RuntimeMethodHandle GetMethodHandleFromIntPtr(IntPtr ptr)
- {
- var temp = new IntPtrAndHandle { ptr = ptr };
- return temp.methodHandle;
- }
-
///
/// Gets the MethodInfo for the Task{T}.Result property getter.
///
@@ -208,6 +200,176 @@ public static void LoadSatelliteAssembly(byte[] dllBytes)
AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(dllBytes));
}
+ [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Dynamic access from JavaScript")]
+ [UnconditionalSuppressMessage("Trimming", "IL2075", Justification = "Dynamic access from JavaScript")]
+ public static Task? CallEntrypoint(string? assemblyName, string?[]? args, bool waitForDebugger)
+ {
+ try
+ {
+ if (string.IsNullOrEmpty(assemblyName))
+ {
+ throw new MissingMethodException(SR.MissingManagedEntrypointHandle);
+ }
+ if (!assemblyName.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase))
+ {
+ assemblyName += ".dll";
+ }
+ Assembly mainAssembly = Assembly.LoadFrom(assemblyName);
+
+ MethodInfo? method = mainAssembly.EntryPoint;
+ if (method == null)
+ {
+ throw new InvalidOperationException(string.Format(SR.CannotResolveManagedEntrypoint, "Main", assemblyName));
+ }
+ if (method.IsSpecialName)
+ {
+ // we are looking for the original async method, rather than for the compiler generated wrapper like
+ // because we need to yield to browser event loop
+ var type = method.DeclaringType!;
+ var name = method.Name;
+ var asyncName = name + "$";
+ method = type.GetMethod(asyncName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
+ if (method == null)
+ {
+ asyncName = name.Substring(1, name.Length - 2);
+ method = type.GetMethod(asyncName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
+ }
+ if (method == null)
+ {
+ throw new InvalidOperationException(string.Format(SR.CannotResolveManagedEntrypoint, asyncName, assemblyName));
+ }
+ }
+
+ Interop.Runtime.SetEntryAssembly(mainAssembly, waitForDebugger ? method.MetadataToken : 0);
+
+ object[] argsToPass = System.Array.Empty();
+ Task? result = null;
+ var parameterInfos = method.GetParameters();
+ if (parameterInfos.Length > 0 && parameterInfos[0].ParameterType == typeof(string[]))
+ {
+ argsToPass = new object[] { args ?? System.Array.Empty() };
+ }
+ if (method.ReturnType == typeof(void))
+ {
+ method.Invoke(null, argsToPass);
+ }
+ else if (method.ReturnType == typeof(int))
+ {
+ int intResult = (int)method.Invoke(null, argsToPass)!;
+ result = Task.FromResult(intResult);
+ }
+ else if (method.ReturnType == typeof(Task))
+ {
+ Task methodResult = (Task)method.Invoke(null, argsToPass)!;
+ TaskCompletionSource tcs = new TaskCompletionSource();
+ result = tcs.Task;
+ methodResult.ContinueWith((t) =>
+ {
+ if (t.IsFaulted)
+ {
+ tcs.SetException(t.Exception!);
+ }
+ else
+ {
+ tcs.SetResult(0);
+ }
+ }, TaskScheduler.Default);
+ }
+ else if (method.ReturnType == typeof(Task))
+ {
+ result = (Task)method.Invoke(null, argsToPass)!;
+ }
+ else
+ {
+ throw new InvalidOperationException(SR.Format(SR.ReturnTypeNotSupportedForMain, method.ReturnType.FullName));
+ }
+ return result;
+ }
+ catch (Exception ex)
+ {
+ if (ex is TargetInvocationException refEx && refEx.InnerException != null)
+ ex = refEx.InnerException;
+ return Task.FromException(ex);
+ }
+ }
+
+ private static string GeneratedInitializerClassName = "System.Runtime.InteropServices.JavaScript.__GeneratedInitializer";
+ private static string GeneratedInitializerMethodName = "__Register_";
+
+ [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Dynamic access from JavaScript")]
+ [UnconditionalSuppressMessage("Trimming", "IL2075", Justification = "Dynamic access from JavaScript")]
+ public static Task BindAssemblyExports(string? assemblyName)
+ {
+ try
+ {
+ if (string.IsNullOrEmpty(assemblyName))
+ {
+ throw new MissingMethodException("Missing assembly name");
+ }
+ if (!assemblyName.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase))
+ {
+ assemblyName += ".dll";
+ }
+
+ Assembly assembly = Assembly.LoadFrom(assemblyName);
+ Type? type = assembly.GetType(GeneratedInitializerClassName);
+ if (type == null)
+ {
+ foreach (var module in assembly.Modules)
+ {
+ RuntimeHelpers.RunModuleConstructor(module.ModuleHandle);
+ }
+ }
+ else
+ {
+ MethodInfo? methodInfo = type.GetMethod(GeneratedInitializerMethodName, BindingFlags.NonPublic | BindingFlags.Static);
+ methodInfo?.Invoke(null, []);
+ }
+
+ return Task.CompletedTask;
+ }
+ catch (Exception ex)
+ {
+ if (ex is TargetInvocationException refEx && refEx.InnerException != null)
+ ex = refEx.InnerException;
+ return Task.FromException(ex);
+ }
+ }
+
+ [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "TODO https://github.com/dotnet/runtime/issues/98366")]
+ [UnconditionalSuppressMessage("Trimming", "IL2075", Justification = "TODO https://github.com/dotnet/runtime/issues/98366")]
+ public static unsafe JSFunctionBinding BindManagedFunction(string fullyQualifiedName, int signatureHash, ReadOnlySpan signatures)
+ {
+ if (string.IsNullOrEmpty(fullyQualifiedName))
+ {
+ throw new ArgumentNullException(nameof(fullyQualifiedName));
+ }
+
+ var signature = GetMethodSignature(signatures, null, null);
+ var (assemblyName, className, nameSpace, shortClassName, methodName) = ParseFQN(fullyQualifiedName);
+
+ Assembly assembly = Assembly.LoadFrom(assemblyName + ".dll");
+ Type? type = assembly.GetType(className);
+ if (type == null)
+ {
+ throw new InvalidOperationException("Class not found " + className);
+ }
+ var wrapper_name = $"__Wrapper_{methodName}_{signatureHash}";
+ var methodInfo = type.GetMethod(wrapper_name, BindingFlags.NonPublic | BindingFlags.Static);
+ if (methodInfo == null)
+ {
+ throw new InvalidOperationException("Method not found " + wrapper_name);
+ }
+
+ var monoMethod = GetIntPtrFromMethodHandle(methodInfo.MethodHandle);
+
+ JavaScriptImports.BindCSFunction(monoMethod, assemblyName, nameSpace, shortClassName, methodName, signatureHash, (IntPtr)signature.Header);
+
+ FreeMethodSignatureBuffer(signature);
+
+ return signature;
+ }
+
#if FEATURE_WASM_MANAGED_THREADS
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "external_eventloop")]
private static extern ref bool GetThreadExternalEventloop(Thread @this);
@@ -218,5 +380,37 @@ public static void SetHasExternalEventLoop(Thread thread)
}
#endif
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static IntPtr GetIntPtrFromMethodHandle(RuntimeMethodHandle methodHandle)
+ {
+ var temp = new IntPtrAndHandle { methodHandle = methodHandle };
+ return temp.ptr;
+ }
+
+
+ public static (string assemblyName, string className, string nameSpace, string shortClassName, string methodName) ParseFQN(string fqn)
+ {
+ var assembly = fqn.Substring(fqn.IndexOf('[') + 1, fqn.IndexOf(']') - 1).Trim();
+ fqn = fqn.Substring(fqn.IndexOf(']') + 1).Trim();
+ var methodName = fqn.Substring(fqn.IndexOf(':') + 1);
+ var className = fqn.Substring(0, fqn.IndexOf(':')).Trim();
+
+ var nameSpace = "";
+ var shortClassName = className;
+ var idx = fqn.LastIndexOf(".");
+ if (idx != -1)
+ {
+ nameSpace = fqn.Substring(0, idx);
+ shortClassName = className.Substring(idx + 1);
+ }
+
+ if (string.IsNullOrEmpty(assembly))
+ throw new InvalidOperationException("No assembly name specified " + fqn);
+ if (string.IsNullOrEmpty(className))
+ throw new InvalidOperationException("No class name specified " + fqn);
+ if (string.IsNullOrEmpty(methodName))
+ throw new InvalidOperationException("No method name specified " + fqn);
+ return (assembly, className, nameSpace, shortClassName, methodName);
+ }
}
}
diff --git a/src/mono/browser/runtime/corebindings.c b/src/mono/browser/runtime/corebindings.c
index 2814b0da915d06..68e8c6c7097660 100644
--- a/src/mono/browser/runtime/corebindings.c
+++ b/src/mono/browser/runtime/corebindings.c
@@ -12,6 +12,7 @@
#include
#include
#include
+#include
#include
#include "wasm-config.h"
@@ -22,9 +23,12 @@ extern void mono_wasm_release_cs_owned_object (int js_handle);
extern void mono_wasm_resolve_or_reject_promise (void *data);
extern void mono_wasm_cancel_promise (int task_holder_gc_handle);
extern void mono_wasm_console_clear ();
+extern void mono_wasm_set_entrypoint_breakpoint (int entry_point_metadata_token);
typedef void (*background_job_cb)(void);
+void mono_wasm_set_entry_assembly (MonoReflectionAssembly* ref_assembly, int entry_point_metadata_token);
+
#ifndef DISABLE_THREADS
void mono_wasm_release_cs_owned_object_post (pthread_t target_tid, int js_handle);
void mono_wasm_resolve_or_reject_promise_post (pthread_t target_tid, void *data);
@@ -32,7 +36,6 @@ void mono_wasm_cancel_promise_post (pthread_t target_tid, int task_holder_gc_han
extern void mono_wasm_install_js_worker_interop (int context_gc_handle);
extern void mono_wasm_uninstall_js_worker_interop ();
-extern void mono_wasm_bind_cs_function (MonoString **fully_qualified_name, int signature_hash, void* signatures, int *is_exception, MonoObject **result);
extern void mono_wasm_invoke_import_async (void* args, void* signature);
void mono_wasm_invoke_import_async_post (pthread_t target_tid, void* args, void* signature);
extern void mono_wasm_invoke_import_sync (void* args, void* signature);
@@ -43,7 +46,6 @@ extern void mono_threads_wasm_async_run_in_target_thread_vi (pthread_t target_th
extern void mono_threads_wasm_async_run_in_target_thread_vii (pthread_t target_thread, void (*func) (gpointer, gpointer), gpointer user_data1, gpointer user_data2);
extern void mono_threads_wasm_sync_run_in_target_thread_vii (pthread_t target_thread, void (*func) (gpointer, gpointer), gpointer user_data1, gpointer user_data2);
#else
-extern void mono_wasm_bind_cs_function (MonoString **fully_qualified_name, int signature_hash, void* signatures, int *is_exception, MonoObject **result);
extern void mono_wasm_bind_js_import (void *signature, int *is_exception, MonoObject **result);
extern void mono_wasm_invoke_js_import (int function_handle, void *args);
extern void mono_wasm_invoke_js_function (int function_js_handle, void *args);
@@ -75,7 +77,6 @@ void bindings_initialize_internals (void)
mono_add_internal_call ("Interop/Runtime::ResolveOrRejectPromisePost", mono_wasm_resolve_or_reject_promise_post);
mono_add_internal_call ("Interop/Runtime::InstallWebWorkerInterop", mono_wasm_install_js_worker_interop);
mono_add_internal_call ("Interop/Runtime::UninstallWebWorkerInterop", mono_wasm_uninstall_js_worker_interop);
- mono_add_internal_call ("Interop/Runtime::BindCSFunction", mono_wasm_bind_cs_function);
mono_add_internal_call ("Interop/Runtime::InvokeJSImportSync", mono_wasm_invoke_import_sync);
mono_add_internal_call ("Interop/Runtime::InvokeJSImportSyncSend", mono_wasm_invoke_import_sync_send);
mono_add_internal_call ("Interop/Runtime::InvokeJSImportAsyncPost", mono_wasm_invoke_import_async_post);
@@ -83,14 +84,15 @@ void bindings_initialize_internals (void)
mono_add_internal_call ("Interop/Runtime::InvokeJSFunctionSend", mono_wasm_invoke_js_function_send);
mono_add_internal_call ("Interop/Runtime::CancelPromise", mono_wasm_cancel_promise);
mono_add_internal_call ("Interop/Runtime::CancelPromisePost", mono_wasm_cancel_promise_post);
+ mono_add_internal_call ("Interop/Runtime::SetEntryAssembly", mono_wasm_set_entry_assembly);
#else
mono_add_internal_call ("Interop/Runtime::ReleaseCSOwnedObject", mono_wasm_release_cs_owned_object);
mono_add_internal_call ("Interop/Runtime::ResolveOrRejectPromise", mono_wasm_resolve_or_reject_promise);
- mono_add_internal_call ("Interop/Runtime::BindCSFunction", mono_wasm_bind_cs_function);
mono_add_internal_call ("Interop/Runtime::BindJSImport", mono_wasm_bind_js_import);
mono_add_internal_call ("Interop/Runtime::InvokeJSImport", mono_wasm_invoke_js_import);
mono_add_internal_call ("Interop/Runtime::InvokeJSFunction", mono_wasm_invoke_js_function);
mono_add_internal_call ("Interop/Runtime::CancelPromise", mono_wasm_cancel_promise);
+ mono_add_internal_call ("Interop/Runtime::SetEntryAssembly", mono_wasm_set_entry_assembly);
#endif /* DISABLE_THREADS */
mono_add_internal_call ("Interop/JsGlobalization::ChangeCaseInvariant", mono_wasm_change_case_invariant);
@@ -106,6 +108,16 @@ void bindings_initialize_internals (void)
mono_add_internal_call ("System.ConsolePal::Clear", mono_wasm_console_clear);
}
+void mono_wasm_set_entry_assembly (MonoReflectionAssembly* ref_assembly, int entry_point_metadata_token)
+{
+ MonoAssembly *assembly = mono_reflection_assembly_get_assembly (ref_assembly);
+ mono_domain_ensure_entry_assembly (mono_get_root_domain (), assembly);
+ if (entry_point_metadata_token != 0)
+ {
+ mono_wasm_set_entrypoint_breakpoint (entry_point_metadata_token);
+ }
+}
+
#ifndef DISABLE_THREADS
void mono_wasm_release_cs_owned_object_post (pthread_t target_tid, int js_handle)
diff --git a/src/mono/browser/runtime/cwraps.ts b/src/mono/browser/runtime/cwraps.ts
index 7c38a34ee4e852..65e245585a671b 100644
--- a/src/mono/browser/runtime/cwraps.ts
+++ b/src/mono/browser/runtime/cwraps.ts
@@ -48,12 +48,9 @@ const fn_signatures: SigLine[] = [
[true, "mono_wasm_assembly_load", "number", ["string"]],
[true, "mono_wasm_assembly_find_class", "number", ["number", "string", "string"]],
- [true, "mono_wasm_runtime_run_module_cctor", "void", ["number"]],
[true, "mono_wasm_assembly_find_method", "number", ["number", "string", "number"]],
- [false, "mono_wasm_invoke_method_ref", "void", ["number", "number", "number", "number", "number"]],
[true, "mono_wasm_string_from_utf16_ref", "void", ["number", "number", "number"]],
[true, "mono_wasm_intern_string_ref", "void", ["number"]],
- [true, "mono_wasm_assembly_get_entry_point", "number", ["number", "number"]],
[false, "mono_wasm_exit", "void", ["number"]],
[false, "mono_wasm_abort", "void", []],
@@ -64,7 +61,7 @@ const fn_signatures: SigLine[] = [
[() => !runtimeHelpers.emscriptenBuildOptions.enableBrowserProfiler, "mono_wasm_profiler_init_aot", "void", ["string"]],
[true, "mono_wasm_profiler_init_browser", "void", ["number"]],
[false, "mono_wasm_exec_regression", "number", ["number", "string"]],
- [false, "mono_wasm_invoke_method", "number", ["number", "number", "number"]],
+ [false, "mono_wasm_invoke_method", "void", ["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"]],
@@ -171,9 +168,7 @@ export interface t_Cwraps {
mono_wasm_assembly_load(name: string): MonoAssembly;
mono_wasm_assembly_find_class(assembly: MonoAssembly, namespace: string, name: string): MonoClass;
mono_wasm_assembly_find_method(klass: MonoClass, name: string, args: number): MonoMethod;
- mono_wasm_invoke_method_ref(method: MonoMethod, this_arg: MonoObjectRef, params: VoidPtr, out_exc: MonoObjectRef, out_result: MonoObjectRef): void;
mono_wasm_string_from_utf16_ref(str: CharPtr, len: number, result: MonoObjectRef): void;
- mono_wasm_assembly_get_entry_point(assembly: MonoAssembly, idx: number): MonoMethod;
mono_wasm_intern_string_ref(strRef: MonoStringRef): void;
mono_wasm_exit(exit_code: number): void;
@@ -181,14 +176,13 @@ export interface t_Cwraps {
mono_wasm_getenv(name: string): CharPtr;
mono_wasm_set_main_args(argc: number, argv: VoidPtr): void;
mono_wasm_exec_regression(verbose_level: number, image: string): number;
- mono_wasm_invoke_method(method: MonoMethod, args: JSMarshalerArguments, fail: MonoStringRef): number;
+ mono_wasm_invoke_method(method: MonoMethod, args: JSMarshalerArguments): void;
mono_wasm_write_managed_pointer_unsafe(destination: VoidPtr | MonoObjectRef, pointer: ManagedPointer): void;
mono_wasm_copy_managed_pointer(destination: VoidPtr | MonoObjectRef, source: VoidPtr | MonoObjectRef): void;
mono_wasm_i52_to_f64(source: VoidPtr, error: Int32Ptr): number;
mono_wasm_u52_to_f64(source: VoidPtr, error: Int32Ptr): number;
mono_wasm_f64_to_i52(destination: VoidPtr, value: number): I52Error;
mono_wasm_f64_to_u52(destination: VoidPtr, value: number): I52Error;
- mono_wasm_runtime_run_module_cctor(assembly: MonoAssembly): void;
mono_wasm_method_get_name(method: MonoMethod): CharPtr;
mono_wasm_method_get_full_name(method: MonoMethod): CharPtr;
mono_wasm_gc_lock(): void;
diff --git a/src/mono/browser/runtime/debug.ts b/src/mono/browser/runtime/debug.ts
index 76562bc8f24930..acc467e4b6d3e1 100644
--- a/src/mono/browser/runtime/debug.ts
+++ b/src/mono/browser/runtime/debug.ts
@@ -2,7 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
import BuildConfiguration from "consts:configuration";
-import { INTERNAL, Module, runtimeHelpers } from "./globals";
+import { INTERNAL, Module, loaderHelpers, runtimeHelpers } from "./globals";
import { toBase64StringImpl } from "./base64";
import cwraps from "./cwraps";
import { VoidPtr, CharPtr } from "./types/emscripten";
@@ -158,9 +158,9 @@ export function mono_wasm_debugger_attached(): void {
cwraps.mono_wasm_set_is_debugger_attached(true);
}
-export function mono_wasm_set_entrypoint_breakpoint(assembly_name: CharPtr, entrypoint_method_token: number): void {
+export function mono_wasm_set_entrypoint_breakpoint(entrypoint_method_token: number): void {
//keep these assignments, these values are used by BrowserDebugProxy
- _assembly_name_str = utf8ToString(assembly_name).concat(".dll");
+ _assembly_name_str = loaderHelpers.config.mainAssemblyName + ".dll";
_entrypoint_method_token = entrypoint_method_token;
//keep this console.assert, otherwise optimization will remove the assignments
// eslint-disable-next-line no-console
diff --git a/src/mono/browser/runtime/driver.c b/src/mono/browser/runtime/driver.c
index 215eaca4dfe85e..3f47245a71efa6 100644
--- a/src/mono/browser/runtime/driver.c
+++ b/src/mono/browser/runtime/driver.c
@@ -227,92 +227,30 @@ mono_wasm_load_runtime (int debug_level)
bindings_initialize_internals();
}
-EMSCRIPTEN_KEEPALIVE int
-mono_wasm_invoke_method (MonoMethod *method, void* args, MonoString **out_exc)
+EMSCRIPTEN_KEEPALIVE void
+mono_wasm_invoke_method (MonoMethod *method, void* args)
{
PVOLATILE(MonoObject) temp_exc = NULL;
void *invoke_args[1] = { args };
- int is_err = 0;
MONO_ENTER_GC_UNSAFE;
mono_runtime_invoke (method, NULL, args ? invoke_args : NULL, (MonoObject **)&temp_exc);
// this failure is unlikely because it would be runtime error, not application exception.
// the application exception is passed inside JSMarshalerArguments `args`
- if (temp_exc && out_exc) {
+ // so, if that happens, we should abort the runtime
+ if (temp_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)
-{
- MonoImage *image;
- MonoMethod *method;
-
- MONO_ENTER_GC_UNSAFE;
- image = mono_assembly_get_image (assembly);
- uint32_t entry = mono_image_get_entry_point (image);
- if (!entry)
- goto end;
-
- mono_domain_ensure_entry_assembly (root_domain, assembly);
- method = mono_get_method (image, entry, NULL);
-
- /*
- * If the entry point looks like a compiler generated wrapper around
- * an async method in the form "" then try to look up the async methods
- * "$" and "Name" it could be wrapping. We do this because the generated
- * sync wrapper will call task.GetAwaiter().GetResult() when we actually want
- * to yield to the host runtime.
- */
- if (mono_method_get_flags (method, NULL) & 0x0800 /* METHOD_ATTRIBUTE_SPECIAL_NAME */) {
- const char *name = mono_method_get_name (method);
- int name_length = strlen (name);
-
- if ((*name != '<') || (name [name_length - 1] != '>'))
- goto end;
-
- MonoClass *klass = mono_method_get_class (method);
- assert(klass);
- char *async_name = malloc (name_length + 2);
- snprintf (async_name, name_length + 2, "%s$", name);
-
- // look for "$"
- MonoMethodSignature *sig = mono_method_get_signature (method, image, mono_method_get_token (method));
- MonoMethod *async_method = mono_class_get_method_from_name (klass, async_name, mono_signature_get_param_count (sig));
- if (async_method != NULL) {
- free (async_name);
- method = async_method;
- goto end;
+ store_volatile((MonoObject**)temp_exc, (MonoObject*)mono_object_to_string ((MonoObject*)temp_exc, (MonoObject **)&exc2));
+ if (exc2) {
+ mono_wasm_trace_logger ("jsinterop", "critical", "mono_wasm_invoke_method unexpected double fault", 1, NULL);
+ } else {
+ mono_wasm_trace_logger ("jsinterop", "critical", mono_string_to_utf8((MonoString*)temp_exc), 1, NULL);
}
-
- // look for "Name" by trimming the first and last character of ""
- async_name [name_length - 1] = '\0';
- async_method = mono_class_get_method_from_name (klass, async_name + 1, mono_signature_get_param_count (sig));
-
- free (async_name);
- if (async_method != NULL)
- method = async_method;
+ abort ();
}
-
- end:
MONO_EXIT_GC_UNSAFE;
- if (auto_insert_breakpoint)
- {
- MonoAssemblyName *aname = mono_assembly_get_name (assembly);
- const char *name = mono_assembly_name_get_name (aname);
- if (name != NULL)
- mono_wasm_set_entrypoint_breakpoint(name, mono_method_get_token (method));
- }
- return method;
}
EMSCRIPTEN_KEEPALIVE void
diff --git a/src/mono/browser/runtime/exports-binding.ts b/src/mono/browser/runtime/exports-binding.ts
index cb286789879b67..17aadf3780e431 100644
--- a/src/mono/browser/runtime/exports-binding.ts
+++ b/src/mono/browser/runtime/exports-binding.ts
@@ -5,7 +5,6 @@ import WasmEnableThreads from "consts:wasmEnableThreads";
import { mono_wasm_debugger_log, mono_wasm_add_dbg_command_received, mono_wasm_set_entrypoint_breakpoint, mono_wasm_fire_debugger_agent_message_with_data, mono_wasm_fire_debugger_agent_message_with_data_to_pause } from "./debug";
import { mono_wasm_release_cs_owned_object } from "./gc-handles";
-import { mono_wasm_bind_cs_function } from "./invoke-cs";
import { mono_wasm_bind_js_import, mono_wasm_invoke_js_function, mono_wasm_invoke_import_async, mono_wasm_invoke_import_sync, mono_wasm_invoke_js_import } from "./invoke-js";
import { mono_interp_tier_prepare_jiterpreter, mono_jiterp_free_method_data_js } from "./jiterpreter";
import { mono_interp_jit_wasm_entry_trampoline, mono_interp_record_interp_entry } from "./jiterpreter-interp-entry";
@@ -92,7 +91,6 @@ export const mono_wasm_imports = [
mono_wasm_bind_js_import,
mono_wasm_invoke_js_function,
mono_wasm_invoke_js_import,
- mono_wasm_bind_cs_function,
mono_wasm_resolve_or_reject_promise,
mono_wasm_cancel_promise,
mono_wasm_change_case_invariant,
diff --git a/src/mono/browser/runtime/exports-internal.ts b/src/mono/browser/runtime/exports-internal.ts
index f431298cb1f5af..5fe5773f975777 100644
--- a/src/mono/browser/runtime/exports-internal.ts
+++ b/src/mono/browser/runtime/exports-internal.ts
@@ -20,6 +20,7 @@ import { mono_wasm_get_func_id_to_name_mappings } from "./logging";
import { MonoObject, MonoObjectNull } from "./types/internal";
import { monoStringToStringUnsafe } from "./strings";
import { thread_available } from "./pthreads/browser";
+import { mono_wasm_bind_cs_function } from "./invoke-cs";
export function export_internal(): any {
return {
@@ -57,6 +58,7 @@ export function export_internal(): any {
get_dotnet_instance: () => exportedRuntimeAPI,
dynamic_import,
thread_available,
+ mono_wasm_bind_cs_function,
// BrowserWebSocket
ws_wasm_create,
diff --git a/src/mono/browser/runtime/gc-lock.ts b/src/mono/browser/runtime/gc-lock.ts
index 876e6a2eb58693..ea373eecc0447d 100644
--- a/src/mono/browser/runtime/gc-lock.ts
+++ b/src/mono/browser/runtime/gc-lock.ts
@@ -1,11 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
import WasmEnableThreads from "consts:wasmEnableThreads";
import { ENVIRONMENT_IS_PTHREAD } from "./globals";
import cwraps from "./cwraps";
-let locked = false;
+export let gc_locked = false;
export function mono_wasm_gc_lock(): void {
- if (locked) {
+ if (gc_locked) {
throw new Error("GC is already locked");
}
if (WasmEnableThreads) {
@@ -14,11 +17,11 @@ export function mono_wasm_gc_lock(): void {
}
cwraps.mono_wasm_gc_lock();
}
- locked = true;
+ gc_locked = true;
}
export function mono_wasm_gc_unlock(): void {
- if (!locked) {
+ if (!gc_locked) {
throw new Error("GC is not locked");
}
if (WasmEnableThreads) {
@@ -27,5 +30,5 @@ export function mono_wasm_gc_unlock(): void {
}
cwraps.mono_wasm_gc_unlock();
}
- locked = false;
+ gc_locked = false;
}
diff --git a/src/mono/browser/runtime/invoke-cs.ts b/src/mono/browser/runtime/invoke-cs.ts
index d45063ab30bbb6..1b0338180ebffe 100644
--- a/src/mono/browser/runtime/invoke-cs.ts
+++ b/src/mono/browser/runtime/invoke-cs.ts
@@ -6,131 +6,101 @@ import WasmEnableThreads from "consts:wasmEnableThreads";
import { Module, loaderHelpers, mono_assert, runtimeHelpers } from "./globals";
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 { 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, set_args_context,
+ get_sig, get_signature_argument_count,
+ bound_cs_function_symbol, get_signature_version, alloc_stack_frame, get_signature_type,
} from "./marshal";
-import { mono_wasm_new_external_root, mono_wasm_new_root } from "./roots";
-import { monoStringToString } from "./strings";
-import { MonoObjectRef, MonoStringRef, MonoString, MonoObject, MonoMethod, JSMarshalerArguments, JSFunctionSignature, BoundMarshalerToCs, BoundMarshalerToJs, VoidPtrNull, MonoObjectRefNull, MonoObjectNull, MarshalerType, MonoAssembly } from "./types/internal";
-import { Int32Ptr } from "./types/emscripten";
+import { MonoMethod, JSFunctionSignature, BoundMarshalerToCs, BoundMarshalerToJs, MarshalerType, MonoAssembly } from "./types/internal";
import cwraps from "./cwraps";
-import { assert_js_interop, wrap_error_root, wrap_no_error_root } from "./invoke-js";
+import { assert_js_interop } from "./invoke-js";
import { startMeasure, MeasuredBlock, endMeasure } from "./profiler";
+import { bind_assembly_exports, invoke_sync_method } from "./managed-exports";
import { mono_log_debug } from "./logging";
const _assembly_cache_by_name = new Map();
-export function mono_wasm_bind_cs_function(fully_qualified_name: MonoStringRef, signature_hash: number, signature: JSFunctionSignature, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
- assert_js_interop();
- const fqn_root = mono_wasm_new_external_root(fully_qualified_name), resultRoot = mono_wasm_new_external_root(result_address);
+export function mono_wasm_bind_cs_function(method: MonoMethod, assemblyName: string, namespaceName: string, shortClassName: string, methodName: string, signatureHash: number, signature: JSFunctionSignature): void {
+ const fullyQualifiedName = `[${assemblyName}] ${namespaceName}.${shortClassName}:${methodName}`;
const mark = startMeasure();
- try {
- const version = get_signature_version(signature);
- mono_assert(version === 2, () => `Signature version ${version} mismatch.`);
-
- const args_count = get_signature_argument_count(signature);
- const js_fqn = monoStringToString(fqn_root)!;
- mono_assert(js_fqn, "fully_qualified_name must be string");
-
- mono_log_debug(`Binding [JSExport] ${js_fqn}`);
-
- const { assembly, namespace, classname, methodname } = parseFQN(js_fqn);
-
- const asm = assembly_load(assembly);
- if (!asm)
- throw new Error("Could not find assembly: " + assembly);
-
- const klass = cwraps.mono_wasm_assembly_find_class(asm, namespace, classname);
- if (!klass)
- throw new Error("Could not find class: " + namespace + ":" + classname + " in assembly " + assembly);
-
- const wrapper_name = `__Wrapper_${methodname}_${signature_hash}`;
- const method = cwraps.mono_wasm_assembly_find_method(klass, wrapper_name, -1);
- if (!method)
- throw new Error(`Could not find method: ${wrapper_name} in ${klass} [${assembly}]`);
-
- const arg_marshalers: (BoundMarshalerToCs)[] = new Array(args_count);
- for (let index = 0; index < args_count; index++) {
- const sig = get_sig(signature, index + 2);
- const marshaler_type = get_signature_type(sig);
- const arg_marshaler = bind_arg_marshal_to_cs(sig, marshaler_type, index + 2);
- mono_assert(arg_marshaler, "ERR43: argument marshaler must be resolved");
- arg_marshalers[index] = arg_marshaler;
- }
+ mono_log_debug(`Binding [JSExport] ${assemblyName}.${shortClassName} from ${assemblyName} assembly`);
+ const version = get_signature_version(signature);
+ mono_assert(version === 2, () => `Signature version ${version} mismatch.`);
- const res_sig = get_sig(signature, 1);
- let res_marshaler_type = get_signature_type(res_sig);
- const is_async = res_marshaler_type == MarshalerType.Task;
- if (is_async) {
- res_marshaler_type = MarshalerType.TaskPreCreated;
- }
- const res_converter = bind_arg_marshal_to_js(res_sig, res_marshaler_type, 1);
-
- const closure: BindingClosure = {
- method,
- fqn: js_fqn,
- args_count,
- arg_marshalers,
- res_converter,
- is_async,
- isDisposed: false,
- };
- let bound_fn: Function;
- // void
- if (args_count == 0 && !res_converter) {
- bound_fn = bind_fn_0V(closure);
- }
- else if (args_count == 1 && !res_converter) {
- bound_fn = bind_fn_1V(closure);
- }
- else if (is_async && args_count == 1 && res_converter) {
- bound_fn = bind_fn_1RA(closure);
- }
- else if (is_async && args_count == 2 && res_converter) {
- bound_fn = bind_fn_2RA(closure);
- }
- else if (args_count == 1 && res_converter) {
- bound_fn = bind_fn_1R(closure);
- }
- else if (args_count == 2 && res_converter) {
- bound_fn = bind_fn_2R(closure);
- }
- else {
- bound_fn = bind_fn(closure);
- }
- // this is just to make debugging easier.
- // It's not CSP compliant and possibly not performant, that's why it's only enabled in debug builds
- // in Release configuration, it would be a trimmed by rollup
- if (BuildConfiguration === "Debug" && !runtimeHelpers.cspPolicy) {
- try {
- bound_fn = new Function("fn", "return (function JSExport_" + methodname + "(){ return fn.apply(this, arguments)});")(bound_fn);
- }
- catch (ex) {
- runtimeHelpers.cspPolicy = true;
- }
- }
+ const args_count = get_signature_argument_count(signature);
- (bound_fn)[bound_cs_function_symbol] = closure;
+ const arg_marshalers: (BoundMarshalerToCs)[] = new Array(args_count);
+ for (let index = 0; index < args_count; index++) {
+ const sig = get_sig(signature, index + 2);
+ const marshaler_type = get_signature_type(sig);
+ const arg_marshaler = bind_arg_marshal_to_cs(sig, marshaler_type, index + 2);
+ mono_assert(arg_marshaler, "ERR43: argument marshaler must be resolved");
+ arg_marshalers[index] = arg_marshaler;
+ }
- _walk_exports_to_set_function(assembly, namespace, classname, methodname, signature_hash, bound_fn);
- endMeasure(mark, MeasuredBlock.bindCsFunction, js_fqn);
- wrap_no_error_root(is_exception, resultRoot);
+ const res_sig = get_sig(signature, 1);
+ let res_marshaler_type = get_signature_type(res_sig);
+ const is_async = res_marshaler_type == MarshalerType.Task;
+ if (is_async) {
+ res_marshaler_type = MarshalerType.TaskPreCreated;
+ }
+ const res_converter = bind_arg_marshal_to_js(res_sig, res_marshaler_type, 1);
+
+ const closure: BindingClosure = {
+ method,
+ fullyQualifiedName,
+ args_count,
+ arg_marshalers,
+ res_converter,
+ is_async,
+ isDisposed: false,
+ };
+ let bound_fn: Function;
+ // void
+ if (args_count == 0 && !res_converter) {
+ bound_fn = bind_fn_0V(closure);
+ }
+ else if (args_count == 1 && !res_converter) {
+ bound_fn = bind_fn_1V(closure);
+ }
+ else if (is_async && args_count == 1 && res_converter) {
+ bound_fn = bind_fn_1RA(closure);
}
- catch (ex: any) {
- Module.err(ex.toString());
- wrap_error_root(is_exception, ex, resultRoot);
- } finally {
- resultRoot.release();
- fqn_root.release();
+ else if (is_async && args_count == 2 && res_converter) {
+ bound_fn = bind_fn_2RA(closure);
}
+ else if (args_count == 1 && res_converter) {
+ bound_fn = bind_fn_1R(closure);
+ }
+ else if (args_count == 2 && res_converter) {
+ bound_fn = bind_fn_2R(closure);
+ }
+ else {
+ bound_fn = bind_fn(closure);
+ }
+
+ // this is just to make debugging easier.
+ // It's not CSP compliant and possibly not performant, that's why it's only enabled in debug builds
+ // in Release configuration, it would be a trimmed by rollup
+ if (BuildConfiguration === "Debug" && !runtimeHelpers.cspPolicy) {
+ try {
+ bound_fn = new Function("fn", "return (function JSExport_" + methodName + "(){ return fn.apply(this, arguments)});")(bound_fn);
+ }
+ catch (ex) {
+ runtimeHelpers.cspPolicy = true;
+ }
+ }
+
+ (bound_fn)[bound_cs_function_symbol] = closure;
+
+ _walk_exports_to_set_function(assemblyName, namespaceName, shortClassName, methodName, signatureHash, bound_fn);
+ endMeasure(mark, MeasuredBlock.bindCsFunction, fullyQualifiedName);
}
function bind_fn_0V(closure: BindingClosure) {
const method = closure.method;
- const fqn = closure.fqn;
+ const fqn = closure.fullyQualifiedName;
if (!WasmEnableThreads) (closure) = null;
return function bound_fn_0V() {
const mark = startMeasure();
@@ -151,7 +121,7 @@ function bind_fn_0V(closure: BindingClosure) {
function bind_fn_1V(closure: BindingClosure) {
const method = closure.method;
const marshaler1 = closure.arg_marshalers[0]!;
- const fqn = closure.fqn;
+ const fqn = closure.fullyQualifiedName;
if (!WasmEnableThreads) (closure) = null;
return function bound_fn_1V(arg1: any) {
const mark = startMeasure();
@@ -175,7 +145,7 @@ function bind_fn_1R(closure: BindingClosure) {
const method = closure.method;
const marshaler1 = closure.arg_marshalers[0]!;
const res_converter = closure.res_converter!;
- const fqn = closure.fqn;
+ const fqn = closure.fullyQualifiedName;
if (!WasmEnableThreads) (closure) = null;
return function bound_fn_1R(arg1: any) {
const mark = startMeasure();
@@ -202,7 +172,7 @@ function bind_fn_1RA(closure: BindingClosure) {
const method = closure.method;
const marshaler1 = closure.arg_marshalers[0]!;
const res_converter = closure.res_converter!;
- const fqn = closure.fqn;
+ const fqn = closure.fullyQualifiedName;
if (!WasmEnableThreads) (closure) = null;
return function bound_fn_1R(arg1: any) {
const mark = startMeasure();
@@ -235,7 +205,7 @@ function bind_fn_2R(closure: BindingClosure) {
const marshaler1 = closure.arg_marshalers[0]!;
const marshaler2 = closure.arg_marshalers[1]!;
const res_converter = closure.res_converter!;
- const fqn = closure.fqn;
+ const fqn = closure.fullyQualifiedName;
if (!WasmEnableThreads) (closure) = null;
return function bound_fn_2R(arg1: any, arg2: any) {
const mark = startMeasure();
@@ -264,7 +234,7 @@ function bind_fn_2RA(closure: BindingClosure) {
const marshaler1 = closure.arg_marshalers[0]!;
const marshaler2 = closure.arg_marshalers[1]!;
const res_converter = closure.res_converter!;
- const fqn = closure.fqn;
+ const fqn = closure.fullyQualifiedName;
if (!WasmEnableThreads) (closure) = null;
return function bound_fn_2R(arg1: any, arg2: any) {
const mark = startMeasure();
@@ -298,7 +268,7 @@ function bind_fn(closure: BindingClosure) {
const arg_marshalers = closure.arg_marshalers;
const res_converter = closure.res_converter;
const method = closure.method;
- const fqn = closure.fqn;
+ const fqn = closure.fullyQualifiedName;
const is_async = closure.is_async;
if (!WasmEnableThreads) (closure) = null;
return function bound_fn(...js_args: any[]) {
@@ -339,7 +309,7 @@ function bind_fn(closure: BindingClosure) {
}
type BindingClosure = {
- fqn: string,
+ fullyQualifiedName: string,
args_count: number,
method: MonoMethod,
arg_marshalers: (BoundMarshalerToCs)[],
@@ -348,23 +318,6 @@ type BindingClosure = {
isDisposed: boolean,
}
-export function invoke_sync_method(method: MonoMethod, args: JSMarshalerArguments): void {
- assert_js_interop();
- const fail_root = mono_wasm_new_root();
- try {
- set_args_context(args);
- const fail = cwraps.mono_wasm_invoke_method(method, args, fail_root.address);
- if (fail) runtimeHelpers.nativeAbort("ERR24: Unexpected error: " + monoStringToString(fail_root));
- if (is_args_exception(args)) {
- const exc = get_arg(args, 0);
- throw marshal_exception_to_js(exc);
- }
- }
- 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(".");
@@ -399,66 +352,12 @@ export async function mono_wasm_get_assembly_exports(assembly: string): Promise<
assert_js_interop();
const result = exportsByAssembly.get(assembly);
if (!result) {
- const mark = startMeasure();
- const asm = assembly_load(assembly);
- if (!asm)
- throw new Error("Could not find assembly: " + assembly);
-
- const klass = cwraps.mono_wasm_assembly_find_class(asm, runtimeHelpers.runtime_interop_namespace, "__GeneratedInitializer");
- if (klass) {
- const method = cwraps.mono_wasm_assembly_find_method(klass, "__Register_", -1);
- if (method) {
- const outException = mono_wasm_new_root();
- const outResult = mono_wasm_new_root();
- try {
- cwraps.mono_wasm_invoke_method_ref(method, MonoObjectRefNull, VoidPtrNull, outException.address, outResult.address);
- if (outException.value !== MonoObjectNull) {
- const msg = monoStringToString(outResult)!;
- throw new Error(msg);
- }
- }
- finally {
- outException.release();
- outResult.release();
- }
- }
- } else {
- mono_assert(!WasmEnableThreads, () => `JSExport with multi-threading enabled is not supported with assembly ${assembly} as it was generated with the .NET 7 SDK`);
- // this needs to stay here for compatibility with assemblies generated in Net7
- // it doesn't have the __GeneratedInitializer class
- cwraps.mono_wasm_runtime_run_module_cctor(asm);
- }
- endMeasure(mark, MeasuredBlock.getAssemblyExports, assembly);
+ await bind_assembly_exports(assembly);
}
return exportsByAssembly.get(assembly) || {};
}
-export function parseFQN(fqn: string)
- : { assembly: string, namespace: string, classname: string, methodname: string } {
- const assembly = fqn.substring(fqn.indexOf("[") + 1, fqn.indexOf("]")).trim();
- fqn = fqn.substring(fqn.indexOf("]") + 1).trim();
-
- const methodname = fqn.substring(fqn.indexOf(":") + 1);
- fqn = fqn.substring(0, fqn.indexOf(":")).trim();
-
- let namespace = "";
- let classname = fqn;
- if (fqn.indexOf(".") != -1) {
- const idx = fqn.lastIndexOf(".");
- namespace = fqn.substring(0, idx);
- classname = fqn.substring(idx + 1);
- }
-
- if (!assembly.trim())
- throw new Error("No assembly name specified " + fqn);
- if (!classname.trim())
- throw new Error("No class name specified " + fqn);
- if (!methodname.trim())
- throw new Error("No method name specified " + fqn);
- return { assembly, namespace, classname, methodname };
-}
-
export function assembly_load(name: string): MonoAssembly {
if (_assembly_cache_by_name.has(name))
return _assembly_cache_by_name.get(name);
diff --git a/src/mono/browser/runtime/invoke-js.ts b/src/mono/browser/runtime/invoke-js.ts
index ccd7e294a3f883..5323407eefa0fe 100644
--- a/src/mono/browser/runtime/invoke-js.ts
+++ b/src/mono/browser/runtime/invoke-js.ts
@@ -449,6 +449,8 @@ export function wrap_error_root(is_exception: Int32Ptr | null, ex: any, result:
}
// to set out parameters of icalls
+// TODO replace it with replace it with UTF8 char*, no GC root needed
+// https://github.com/dotnet/runtime/issues/98365
export function wrap_no_error_root(is_exception: Int32Ptr | null, result?: WasmRoot): void {
if (is_exception) {
receiveWorkerHeapViews();
diff --git a/src/mono/browser/runtime/jiterpreter-tables.ts b/src/mono/browser/runtime/jiterpreter-tables.ts
index 71a3eb86c9f014..0ab6ce675267be 100644
--- a/src/mono/browser/runtime/jiterpreter-tables.ts
+++ b/src/mono/browser/runtime/jiterpreter-tables.ts
@@ -1,3 +1,6 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
import {
WasmOpcode, WasmSimdOpcode, JiterpSpecialOpcode
} from "./jiterpreter-opcodes";
diff --git a/src/mono/browser/runtime/managed-exports.ts b/src/mono/browser/runtime/managed-exports.ts
index 4a95d0ca362a55..9a140ac0f6c70a 100644
--- a/src/mono/browser/runtime/managed-exports.ts
+++ b/src/mono/browser/runtime/managed-exports.ts
@@ -3,15 +3,14 @@
import WasmEnableThreads from "consts:wasmEnableThreads";
-import { GCHandle, GCHandleNull, MarshalerToCs, MarshalerToJs, MarshalerType, MonoMethod } from "./types/internal";
+import { GCHandle, GCHandleNull, JSMarshalerArguments, MarshalerToCs, MarshalerToJs, MarshalerType, MonoMethod } from "./types/internal";
import cwraps from "./cwraps";
import { runtimeHelpers, Module, loaderHelpers, mono_assert } from "./globals";
import { JavaScriptMarshalerArgSize, alloc_stack_frame, get_arg, get_arg_gc_handle, is_args_exception, set_arg_intptr, set_arg_type, set_gc_handle } from "./marshal";
-import { invoke_sync_method } 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_array_to_cs, marshal_array_to_cs_impl, marshal_bool_to_cs, marshal_exception_to_cs, marshal_string_to_cs } from "./marshal-to-cs";
import { marshal_int32_to_js, end_marshal_task_to_js, marshal_string_to_js, begin_marshal_task_to_js, marshal_exception_to_js } from "./marshal-to-js";
import { do_not_force_dispose } from "./gc-handles";
-import { assert_c_interop } from "./invoke-js";
+import { assert_c_interop, assert_js_interop } from "./invoke-js";
import { mono_wasm_main_thread_ptr } from "./pthreads/shared";
import { _zero_region } from "./memory";
@@ -19,18 +18,21 @@ const managedExports: ManagedExports = {} as any;
export function init_managed_exports(): void {
const exports_fqn_asm = "System.Runtime.InteropServices.JavaScript";
+ // TODO https://github.com/dotnet/runtime/issues/98366
runtimeHelpers.runtime_interop_module = cwraps.mono_wasm_assembly_load(exports_fqn_asm);
if (!runtimeHelpers.runtime_interop_module)
throw "Can't find bindings module assembly: " + exports_fqn_asm;
- runtimeHelpers.runtime_interop_namespace = "System.Runtime.InteropServices.JavaScript";
+ runtimeHelpers.runtime_interop_namespace = exports_fqn_asm;
runtimeHelpers.runtime_interop_exports_classname = "JavaScriptExports";
+ // TODO https://github.com/dotnet/runtime/issues/98366
runtimeHelpers.runtime_interop_exports_class = cwraps.mono_wasm_assembly_find_class(runtimeHelpers.runtime_interop_module, runtimeHelpers.runtime_interop_namespace, runtimeHelpers.runtime_interop_exports_classname);
if (!runtimeHelpers.runtime_interop_exports_class)
throw "Can't find " + runtimeHelpers.runtime_interop_namespace + "." + runtimeHelpers.runtime_interop_exports_classname + " class";
managedExports.InstallMainSynchronizationContext = WasmEnableThreads ? get_method("InstallMainSynchronizationContext") : undefined;
managedExports.CallEntrypoint = get_method("CallEntrypoint");
+ managedExports.BindAssemblyExports = get_method("BindAssemblyExports");
managedExports.ReleaseJSOwnedObjectByGCHandle = get_method("ReleaseJSOwnedObjectByGCHandle");
managedExports.CompleteTask = get_method("CompleteTask");
managedExports.CallDelegate = get_method("CallDelegate");
@@ -39,26 +41,23 @@ export function init_managed_exports(): void {
managedExports.LoadLazyAssembly = get_method("LoadLazyAssembly");
}
-// the marshaled signature is: Task? CallEntrypoint(MonoMethod* entrypointPtr, string[] args)
-export async function call_entry_point(entry_point: MonoMethod, program_args?: string[]): Promise {
+// the marshaled signature is: Task? CallEntrypoint(string mainAssemblyName, string[] args)
+export function call_entry_point(main_assembly_name: string, program_args: string[] | undefined, waitForDebugger: boolean): Promise {
loaderHelpers.assert_runtime_running();
const sp = Module.stackSave();
try {
- Module.runtimeKeepalivePush();
- const args = alloc_stack_frame(4);
+ const args = alloc_stack_frame(5);
const res = get_arg(args, 1);
const arg1 = get_arg(args, 2);
const arg2 = get_arg(args, 3);
- marshal_intptr_to_cs(arg1, entry_point);
- if (program_args && program_args.length == 0) {
- program_args = undefined;
- }
- marshal_array_to_cs_impl(arg2, program_args, MarshalerType.String);
+ const arg3 = get_arg(args, 4);
+ marshal_string_to_cs(arg1, main_assembly_name);
+ marshal_array_to_cs_impl(arg2, program_args && !program_args.length ? undefined : program_args, MarshalerType.String);
+ marshal_bool_to_cs(arg3, waitForDebugger);
// because this is async, we could pre-allocate the promise
let promise = begin_marshal_task_to_js(res, MarshalerType.TaskPreCreated, marshal_int32_to_js);
- // NOTE: at the moment this is synchronous call on the same thread and therefore we could marshal (null) result synchronously
invoke_sync_method(managedExports.CallEntrypoint, args);
// in case the C# side returned synchronously
@@ -68,10 +67,10 @@ export async function call_entry_point(entry_point: MonoMethod, program_args?: s
promise = Promise.resolve(0);
}
(promise as any)[do_not_force_dispose] = true; // prevent disposing the task in forceDisposeProxies()
- return await promise;
+
+ return promise;
} finally {
- Module.runtimeKeepalivePop();// after await promise !
- Module.stackRestore(sp);
+ Module.stackRestore(sp); // synchronously
}
}
@@ -219,8 +218,7 @@ export function install_main_synchronization_context(): GCHandle {
const arg1 = get_arg(args, 2);
const arg2 = get_arg(args, 3);
set_arg_intptr(arg1, mono_wasm_main_thread_ptr() as any);
- const fail = cwraps.mono_wasm_invoke_method(managedExports.InstallMainSynchronizationContext!, args, 0 as any);
- if (fail) runtimeHelpers.nativeAbort("ERR24: Unexpected error");
+ cwraps.mono_wasm_invoke_method(managedExports.InstallMainSynchronizationContext!, args);
if (is_args_exception(args)) {
const exc = get_arg(args, 0);
throw marshal_exception_to_js(exc);
@@ -231,7 +229,45 @@ export function install_main_synchronization_context(): GCHandle {
}
}
+export function invoke_sync_method(method: MonoMethod, args: JSMarshalerArguments): void {
+ assert_js_interop();
+ cwraps.mono_wasm_invoke_method(method, args as any);
+ if (is_args_exception(args)) {
+ const exc = get_arg(args, 0);
+ throw marshal_exception_to_js(exc);
+ }
+}
+
+// the marshaled signature is: Task BindAssemblyExports(string assemblyName)
+export function bind_assembly_exports(assemblyName: string): Promise {
+ loaderHelpers.assert_runtime_running();
+ const sp = Module.stackSave();
+ try {
+ const args = alloc_stack_frame(3);
+ const res = get_arg(args, 1);
+ const arg1 = get_arg(args, 2);
+ marshal_string_to_cs(arg1, assemblyName);
+
+ // because this is async, we could pre-allocate the promise
+ let promise = begin_marshal_task_to_js(res, MarshalerType.TaskPreCreated);
+
+ invoke_sync_method(managedExports.BindAssemblyExports, args);
+
+ // in case the C# side returned synchronously
+ promise = end_marshal_task_to_js(args, marshal_int32_to_js, promise);
+
+ if (promise === null || promise === undefined) {
+ promise = Promise.resolve();
+ }
+ return promise;
+ } finally {
+ Module.stackRestore(sp); // synchronously
+ }
+}
+
+
function get_method(method_name: string): MonoMethod {
+ // TODO https://github.com/dotnet/runtime/issues/98366
const res = cwraps.mono_wasm_assembly_find_method(runtimeHelpers.runtime_interop_exports_class, method_name, -1);
if (!res)
throw "Can't find method " + runtimeHelpers.runtime_interop_namespace + "." + runtimeHelpers.runtime_interop_exports_classname + "." + method_name;
@@ -242,6 +278,7 @@ type ManagedExports = {
InstallMainSynchronizationContext: MonoMethod | undefined,
entry_point: MonoMethod,
CallEntrypoint: MonoMethod,
+ BindAssemblyExports: MonoMethod,
ReleaseJSOwnedObjectByGCHandle: MonoMethod,
CompleteTask: MonoMethod,
CallDelegate: MonoMethod,
diff --git a/src/mono/browser/runtime/marshal-to-cs.ts b/src/mono/browser/runtime/marshal-to-cs.ts
index 63657a122195e7..c1b663044c8b84 100644
--- a/src/mono/browser/runtime/marshal-to-cs.ts
+++ b/src/mono/browser/runtime/marshal-to-cs.ts
@@ -25,6 +25,7 @@ import { TypedArray } from "./types/emscripten";
import { addUnsettledPromise, settleUnsettledPromise } from "./pthreads/shared/eventloop";
import { mono_log_debug } from "./logging";
import { complete_task } from "./managed-exports";
+import { gc_locked } from "./gc-lock";
export const jsinteropDoc = "For more information see https://aka.ms/dotnet-wasm-jsinterop";
@@ -33,7 +34,7 @@ export function initialize_marshalers_to_cs(): void {
js_to_cs_marshalers.set(MarshalerType.Array, marshal_array_to_cs);
js_to_cs_marshalers.set(MarshalerType.Span, _marshal_span_to_cs);
js_to_cs_marshalers.set(MarshalerType.ArraySegment, _marshal_array_segment_to_cs);
- js_to_cs_marshalers.set(MarshalerType.Boolean, _marshal_bool_to_cs);
+ js_to_cs_marshalers.set(MarshalerType.Boolean, marshal_bool_to_cs);
js_to_cs_marshalers.set(MarshalerType.Byte, _marshal_byte_to_cs);
js_to_cs_marshalers.set(MarshalerType.Char, _marshal_char_to_cs);
js_to_cs_marshalers.set(MarshalerType.Int16, _marshal_int16_to_cs);
@@ -45,7 +46,7 @@ export function initialize_marshalers_to_cs(): void {
js_to_cs_marshalers.set(MarshalerType.IntPtr, marshal_intptr_to_cs);
js_to_cs_marshalers.set(MarshalerType.DateTime, _marshal_date_time_to_cs);
js_to_cs_marshalers.set(MarshalerType.DateTimeOffset, _marshal_date_time_offset_to_cs);
- js_to_cs_marshalers.set(MarshalerType.String, _marshal_string_to_cs);
+ js_to_cs_marshalers.set(MarshalerType.String, marshal_string_to_cs);
js_to_cs_marshalers.set(MarshalerType.Exception, marshal_exception_to_cs);
js_to_cs_marshalers.set(MarshalerType.JSException, marshal_exception_to_cs);
js_to_cs_marshalers.set(MarshalerType.JSObject, marshal_js_object_to_cs);
@@ -97,7 +98,7 @@ export function get_marshaler_to_cs_by_type(marshaler_type: MarshalerType): Mars
return converter;
}
-function _marshal_bool_to_cs(arg: JSMarshalerArgument, value: any): void {
+export function marshal_bool_to_cs(arg: JSMarshalerArgument, value: any): void {
if (value === null || value === undefined) {
set_arg_type(arg, MarshalerType.None);
}
@@ -219,7 +220,7 @@ function _marshal_date_time_offset_to_cs(arg: JSMarshalerArgument, value: Date):
}
}
-function _marshal_string_to_cs(arg: JSMarshalerArgument, value: string) {
+export function marshal_string_to_cs(arg: JSMarshalerArgument, value: string) {
if (value === null || value === undefined) {
set_arg_type(arg, MarshalerType.None);
}
@@ -556,17 +557,19 @@ export function marshal_array_to_cs_impl(arg: JSMarshalerArgument, value: Array<
mono_check(Array.isArray(value), "Value is not an Array");
_zero_region(buffer_ptr, buffer_length);
if (!WasmEnableJsInteropByValue) {
+ mono_assert(!WasmEnableThreads || !gc_locked, "GC must not be locked when creating a GC root");
cwraps.mono_wasm_register_root(buffer_ptr, buffer_length, "marshal_array_to_cs");
}
for (let index = 0; index < length; index++) {
const element_arg = get_arg(buffer_ptr, index);
- _marshal_string_to_cs(element_arg, value[index]);
+ marshal_string_to_cs(element_arg, value[index]);
}
}
else if (element_type == MarshalerType.Object) {
mono_check(Array.isArray(value), "Value is not an Array");
_zero_region(buffer_ptr, buffer_length);
if (!WasmEnableJsInteropByValue) {
+ mono_assert(!WasmEnableThreads || !gc_locked, "GC must not be locked when creating a GC root");
cwraps.mono_wasm_register_root(buffer_ptr, buffer_length, "marshal_array_to_cs");
}
for (let index = 0; index < length; index++) {
diff --git a/src/mono/browser/runtime/marshal-to-js.ts b/src/mono/browser/runtime/marshal-to-js.ts
index e40c83899bc07f..d3987586b01d4d 100644
--- a/src/mono/browser/runtime/marshal-to-js.ts
+++ b/src/mono/browser/runtime/marshal-to-js.ts
@@ -22,6 +22,7 @@ import { TypedArray } from "./types/emscripten";
import { get_marshaler_to_cs_by_type, jsinteropDoc, marshal_exception_to_cs } from "./marshal-to-cs";
import { localHeapViewF64, localHeapViewI32, localHeapViewU8 } from "./memory";
import { call_delegate } from "./managed-exports";
+import { gc_locked } from "./gc-lock";
export function initialize_marshalers_to_js(): void {
if (cs_to_js_marshalers.size == 0) {
@@ -494,6 +495,7 @@ function _marshal_array_to_js_impl(arg: JSMarshalerArgument, element_type: Marsh
result[index] = marshal_string_to_js(element_arg);
}
if (!WasmEnableJsInteropByValue) {
+ mono_assert(!WasmEnableThreads || !gc_locked, "GC must not be locked when disposing a GC root");
cwraps.mono_wasm_deregister_root(buffer_ptr);
}
}
@@ -504,6 +506,7 @@ function _marshal_array_to_js_impl(arg: JSMarshalerArgument, element_type: Marsh
result[index] = _marshal_cs_object_to_js(element_arg);
}
if (!WasmEnableJsInteropByValue) {
+ mono_assert(!WasmEnableThreads || !gc_locked, "GC must not be locked when disposing a GC root");
cwraps.mono_wasm_deregister_root(buffer_ptr);
}
}
diff --git a/src/mono/browser/runtime/roots.ts b/src/mono/browser/runtime/roots.ts
index 21f9f18077b7c7..24958a22e08458 100644
--- a/src/mono/browser/runtime/roots.ts
+++ b/src/mono/browser/runtime/roots.ts
@@ -1,11 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+import WasmEnableThreads from "consts:wasmEnableThreads";
+
import cwraps from "./cwraps";
-import { Module } from "./globals";
+import { Module, mono_assert } from "./globals";
import { VoidPtr, ManagedPointer, NativePointer } from "./types/emscripten";
import { MonoObjectRef, MonoObjectRefNull, MonoObject, is_nullish, WasmRoot, WasmRootBuffer } from "./types/internal";
import { _zero_region, localHeapViewU32 } from "./memory";
+import { gc_locked } from "./gc-lock";
const maxScratchRoots = 8192;
let _scratch_root_buffer: WasmRootBuffer | null = null;
@@ -36,25 +39,6 @@ export function mono_wasm_new_root_buffer(capacity: number, name?: string): Wasm
return new WasmRootBufferImpl(offset, capacity, true, name);
}
-/**
- * Creates a root buffer object representing an existing allocation in the native heap and registers
- * the allocation with the GC. The caller is responsible for managing the lifetime of the allocation.
- */
-export function mono_wasm_new_root_buffer_from_pointer(offset: VoidPtr, capacity: number, name?: string): WasmRootBuffer {
- if (capacity <= 0)
- throw new Error("capacity >= 1");
-
- capacity = capacity | 0;
-
- const capacityBytes = capacity * 4;
- if ((offset % 4) !== 0)
- throw new Error("Unaligned offset");
-
- _zero_region(offset, capacityBytes);
-
- return new WasmRootBufferImpl(offset, capacity, false, name);
-}
-
/**
* Allocates a WasmRoot pointing to a root provided and controlled by external code. Typicaly on managed stack.
* Releasing this root will not de-allocate the root space. You still need to call .release().
@@ -188,6 +172,7 @@ export class WasmRootBufferImpl implements WasmRootBuffer {
this.__offset32 = offset >>> 2;
this.__count = capacity;
this.length = capacity;
+ mono_assert(!WasmEnableThreads || !gc_locked, "GC must not be locked when creating a GC root");
this.__handle = cwraps.mono_wasm_register_root(offset, capacityBytes, name || "noname");
this.__ownsAllocation = ownsAllocation;
}
@@ -247,6 +232,7 @@ export class WasmRootBufferImpl implements WasmRootBuffer {
release(): void {
if (this.__offset && this.__ownsAllocation) {
+ mono_assert(!WasmEnableThreads || !gc_locked, "GC must not be locked when disposing a GC root");
cwraps.mono_wasm_deregister_root(this.__offset);
_zero_region(this.__offset, this.__count * 4);
Module._free(this.__offset);
diff --git a/src/mono/browser/runtime/run.ts b/src/mono/browser/runtime/run.ts
index 932d851973b752..8760aab6514cad 100644
--- a/src/mono/browser/runtime/run.ts
+++ b/src/mono/browser/runtime/run.ts
@@ -3,13 +3,11 @@
import WasmEnableThreads from "consts:wasmEnableThreads";
-import { ENVIRONMENT_IS_NODE, loaderHelpers, mono_assert, runtimeHelpers } from "./globals";
+import { ENVIRONMENT_IS_NODE, Module, loaderHelpers, mono_assert, runtimeHelpers } from "./globals";
import { mono_wasm_wait_for_debugger } from "./debug";
import { mono_wasm_set_main_args } from "./startup";
import cwraps from "./cwraps";
import { mono_log_info } from "./logging";
-import { assert_js_interop } from "./invoke-js";
-import { assembly_load } from "./invoke-cs";
import { cancelThreads } from "./pthreads/browser";
import { call_entry_point } from "./managed-exports";
@@ -58,36 +56,26 @@ export async function mono_run_main(main_assembly_name?: string, args?: string[]
}
mono_wasm_set_main_args(main_assembly_name, args);
+ loaderHelpers.config.mainAssemblyName = main_assembly_name;
+
if (runtimeHelpers.waitForDebugger == -1) {
mono_log_info("waiting for debugger...");
await mono_wasm_wait_for_debugger();
}
- const method = find_entry_point(main_assembly_name);
- const res = await call_entry_point(method, args);
+ try {
+ Module.runtimeKeepalivePush();
- // one more timer loop before we return, so that any remaining queued calls could run
- await new Promise(resolve => globalThis.setTimeout(resolve, 0));
+ // one more timer loop before we return, so that any remaining queued calls could run
+ await new Promise(resolve => globalThis.setTimeout(resolve, 0));
- return res;
+ return await call_entry_point(main_assembly_name, args, runtimeHelpers.waitForDebugger == 1);
+ } finally {
+ Module.runtimeKeepalivePop();// after await promise !
+ }
}
-export function find_entry_point(assembly: string) {
- loaderHelpers.assert_runtime_running();
- assert_js_interop();
- const asm = assembly_load(assembly);
- if (!asm)
- throw new Error("Could not find assembly: " + assembly);
- let auto_set_breakpoint = 0;
- if (runtimeHelpers.waitForDebugger == 1)
- auto_set_breakpoint = 1;
-
- const method = cwraps.mono_wasm_assembly_get_entry_point(asm, auto_set_breakpoint);
- if (!method)
- throw new Error("Could not find entry point for assembly: " + assembly);
- return method;
-}
export function nativeExit(code: number) {
if (WasmEnableThreads) {
diff --git a/src/mono/browser/runtime/runtime.c b/src/mono/browser/runtime/runtime.c
index 73e64f0a8a3fea..9030c4a52ee556 100644
--- a/src/mono/browser/runtime/runtime.c
+++ b/src/mono/browser/runtime/runtime.c
@@ -318,6 +318,7 @@ mono_wasm_load_runtime_common (int debug_level, MonoLogCallback log_callback, co
return domain;
}
+// TODO https://github.com/dotnet/runtime/issues/98366
EMSCRIPTEN_KEEPALIVE MonoAssembly*
mono_wasm_assembly_load (const char *name)
{
@@ -331,6 +332,7 @@ mono_wasm_assembly_load (const char *name)
return res;
}
+// TODO https://github.com/dotnet/runtime/issues/98366
EMSCRIPTEN_KEEPALIVE MonoClass*
mono_wasm_assembly_find_class (MonoAssembly *assembly, const char *namespace, const char *name)
{
@@ -342,21 +344,7 @@ mono_wasm_assembly_find_class (MonoAssembly *assembly, const char *namespace, co
return result;
}
-extern int mono_runtime_run_module_cctor (MonoImage *image, MonoError *error);
-
-EMSCRIPTEN_KEEPALIVE void
-mono_wasm_runtime_run_module_cctor (MonoAssembly *assembly)
-{
- assert (assembly);
- MonoError error;
- MONO_ENTER_GC_UNSAFE;
- MonoImage *image = mono_assembly_get_image (assembly);
- if (!mono_runtime_run_module_cctor(image, &error)) {
- //g_print ("Failed to run module constructor due to %s\n", mono_error_get_message (error));
- }
- MONO_EXIT_GC_UNSAFE;
-}
-
+// TODO https://github.com/dotnet/runtime/issues/98366
EMSCRIPTEN_KEEPALIVE MonoMethod*
mono_wasm_assembly_find_method (MonoClass *klass, const char *name, int arguments)
{
@@ -367,31 +355,3 @@ mono_wasm_assembly_find_method (MonoClass *klass, const char *name, int argument
MONO_EXIT_GC_UNSAFE;
return result;
}
-
-EMSCRIPTEN_KEEPALIVE void
-mono_wasm_invoke_method_ref (MonoMethod *method, MonoObject **this_arg_in, void *params[], MonoObject **_out_exc, MonoObject **out_result)
-{
- PPVOLATILE(MonoObject) out_exc = _out_exc;
- PVOLATILE(MonoObject) temp_exc = NULL;
- if (out_exc)
- *out_exc = NULL;
- else
- out_exc = &temp_exc;
-
- MONO_ENTER_GC_UNSAFE;
- if (out_result) {
- *out_result = NULL;
- PVOLATILE(MonoObject) invoke_result = mono_runtime_invoke (method, this_arg_in ? *this_arg_in : NULL, params, (MonoObject **)out_exc);
- store_volatile(out_result, invoke_result);
- } else {
- mono_runtime_invoke (method, this_arg_in ? *this_arg_in : NULL, params, (MonoObject **)out_exc);
- }
-
- if (*out_exc && out_result) {
- PVOLATILE(MonoObject) exc2 = NULL;
- store_volatile(out_result, (MonoObject*)mono_object_to_string (*out_exc, (MonoObject **)&exc2));
- if (exc2)
- store_volatile(out_result, (MonoObject*)mono_string_new (mono_get_root_domain (), "Exception Double Fault"));
- }
- MONO_EXIT_GC_UNSAFE;
-}
diff --git a/src/mono/browser/runtime/strings.ts b/src/mono/browser/runtime/strings.ts
index 9058f29d0bf2a9..ae73fe57ff50d0 100644
--- a/src/mono/browser/runtime/strings.ts
+++ b/src/mono/browser/runtime/strings.ts
@@ -31,6 +31,8 @@ export function strings_init(): void {
}
mono_wasm_string_decoder_buffer = Module._malloc(12);
}
+ if (!mono_wasm_string_root)
+ mono_wasm_string_root = mono_wasm_new_root();
}
export function stringToUTF8(str: string): Uint8Array {
@@ -257,8 +259,6 @@ let mono_wasm_string_root: any;
export function monoStringToStringUnsafe(mono_string: MonoString): string | null {
if (mono_string === MonoStringNull)
return null;
- if (!mono_wasm_string_root)
- mono_wasm_string_root = mono_wasm_new_root();
mono_wasm_string_root.value = mono_string;
const result = monoStringToString(mono_wasm_string_root);