Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/runtime.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKatelyn Gadd <kg@luminance.org>2022-04-06 12:27:12 +0300
committerGitHub <noreply@github.com>2022-04-06 12:27:12 +0300
commitc537cc6a45c4c75524ec98f6a8d4c9637438958e (patch)
tree3774ff98442af18230ed4d168675491f2782f6fc /src/mono/wasm/runtime
parentbea863ba1f3066af624454ff97aef4d86a46d4a6 (diff)
Introduce write barriers in wasm bindings, migrate to ref/out params, add gc safe regions (#65994)
Introduce write barriers in key parts of the wasm bindings and migrate most code to use ref/out parameters instead of passing raw managed pointers as arguments or return values. Introduced MonoObjectRef typescript type and corresponding 'R' signature char (for 'ref/out object') Marked various old/unsafe code as deprecated Fixed some incorrect rooting in websocket APIs Introduced 'volatile' attribute on various C pointers in the bindings Added GC unsafe/safe regions in key parts of the C bindings Expanded exported APIs
Diffstat (limited to 'src/mono/wasm/runtime')
-rw-r--r--src/mono/wasm/runtime/buffers.ts72
-rw-r--r--src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js26
-rw-r--r--src/mono/wasm/runtime/corebindings.c110
-rw-r--r--src/mono/wasm/runtime/corebindings.ts63
-rw-r--r--src/mono/wasm/runtime/cs-to-js.ts103
-rw-r--r--src/mono/wasm/runtime/cwraps.ts87
-rw-r--r--src/mono/wasm/runtime/dotnet.d.ts81
-rw-r--r--src/mono/wasm/runtime/driver.c375
-rw-r--r--src/mono/wasm/runtime/es6/dotnet.es6.lib.js30
-rw-r--r--src/mono/wasm/runtime/exports.ts83
-rw-r--r--src/mono/wasm/runtime/gc-common.h49
-rw-r--r--src/mono/wasm/runtime/gc-handles.ts22
-rw-r--r--src/mono/wasm/runtime/js-to-cs.ts218
-rw-r--r--src/mono/wasm/runtime/memory.ts2
-rw-r--r--src/mono/wasm/runtime/method-binding.ts82
-rw-r--r--src/mono/wasm/runtime/method-calls.ts219
-rw-r--r--src/mono/wasm/runtime/roots.ts119
-rw-r--r--src/mono/wasm/runtime/startup.ts12
-rw-r--r--src/mono/wasm/runtime/strings.ts179
-rw-r--r--src/mono/wasm/runtime/types.ts19
-rw-r--r--src/mono/wasm/runtime/web-socket.ts231
21 files changed, 1436 insertions, 746 deletions
diff --git a/src/mono/wasm/runtime/buffers.ts b/src/mono/wasm/runtime/buffers.ts
index b0437231ccb..f89b663924f 100644
--- a/src/mono/wasm/runtime/buffers.ts
+++ b/src/mono/wasm/runtime/buffers.ts
@@ -1,12 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-import { JSHandle, MonoArray, MonoObject, MonoString } from "./types";
+import { JSHandle, MonoArray, MonoObject, MonoObjectRef } from "./types";
import { Module } from "./imports";
import { mono_wasm_get_jsobj_from_js_handle } from "./gc-handles";
-import { wrap_error } from "./method-calls";
-import { _js_to_mono_obj } from "./js-to-cs";
+import { wrap_error_root } from "./method-calls";
+import { js_to_mono_obj_root } from "./js-to-cs";
import { Int32Ptr, TypedArray, VoidPtr } from "./types/emscripten";
+import { mono_wasm_new_external_root } from "./roots";
// Creates a new typed array from pinned array address from pinned_array allocated on the heap to the typed array.
// adress of managed pinned array -> copy from heap -> typed array memory
@@ -134,33 +135,58 @@ function typedarray_copy_from(typed_array: TypedArray, pinned_array: MonoArray,
}
}
-export function mono_wasm_typed_array_copy_to(js_handle: JSHandle, pinned_array: MonoArray, begin: number, end: number, bytes_per_element: number, is_exception: Int32Ptr): MonoObject {
- const js_obj = mono_wasm_get_jsobj_from_js_handle(js_handle);
- if (!js_obj) {
- return wrap_error(is_exception, "ERR07: Invalid JS object handle '" + js_handle + "'");
+export function mono_wasm_typed_array_copy_to_ref(js_handle: JSHandle, pinned_array: MonoArray, begin: number, end: number, bytes_per_element: number, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
+ const resultRoot = mono_wasm_new_external_root<MonoObject>(result_address);
+ try {
+ const js_obj = mono_wasm_get_jsobj_from_js_handle(js_handle);
+ if (!js_obj) {
+ wrap_error_root(is_exception, "ERR07: Invalid JS object handle '" + js_handle + "'", resultRoot);
+ return;
+ }
+
+ const res = typedarray_copy_to(js_obj, pinned_array, begin, end, bytes_per_element);
+ // FIXME: We should just return an int
+ // returns num_of_bytes boxed
+ js_to_mono_obj_root(res, resultRoot, false);
+ } catch (exc) {
+ wrap_error_root(is_exception, String(exc), resultRoot);
+ } finally {
+ resultRoot.release();
}
-
- const res = typedarray_copy_to(js_obj, pinned_array, begin, end, bytes_per_element);
- // returns num_of_bytes boxed
- return _js_to_mono_obj(false, res);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
-export function mono_wasm_typed_array_from(pinned_array: MonoArray, begin: number, end: number, bytes_per_element: number, type: number, is_exception: Int32Ptr): MonoObject {
- const res = typed_array_from(pinned_array, begin, end, bytes_per_element, type);
- // returns JS typed array like Int8Array, to be wraped with JSObject proxy
- return _js_to_mono_obj(true, res);
+export function mono_wasm_typed_array_from_ref(pinned_array: MonoArray, begin: number, end: number, bytes_per_element: number, type: number, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
+ const resultRoot = mono_wasm_new_external_root<MonoObject>(result_address);
+ try {
+ const res = typed_array_from(pinned_array, begin, end, bytes_per_element, type);
+ // returns JS typed array like Int8Array, to be wraped with JSObject proxy
+ js_to_mono_obj_root(res, resultRoot, true);
+ } catch (exc) {
+ wrap_error_root(is_exception, String(exc), resultRoot);
+ } finally {
+ resultRoot.release();
+ }
}
-export function mono_wasm_typed_array_copy_from(js_handle: JSHandle, pinned_array: MonoArray, begin: number, end: number, bytes_per_element: number, is_exception: Int32Ptr): MonoObject | MonoString {
- const js_obj = mono_wasm_get_jsobj_from_js_handle(js_handle);
- if (!js_obj) {
- return wrap_error(is_exception, "ERR08: Invalid JS object handle '" + js_handle + "'");
+export function mono_wasm_typed_array_copy_from_ref(js_handle: JSHandle, pinned_array: MonoArray, begin: number, end: number, bytes_per_element: number, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
+ const resultRoot = mono_wasm_new_external_root<MonoObject>(result_address);
+ try {
+ const js_obj = mono_wasm_get_jsobj_from_js_handle(js_handle);
+ if (!js_obj) {
+ wrap_error_root(is_exception, "ERR08: Invalid JS object handle '" + js_handle + "'", resultRoot);
+ return;
+ }
+
+ const res = typedarray_copy_from(js_obj, pinned_array, begin, end, bytes_per_element);
+ // FIXME: We should just return an int
+ // returns num_of_bytes boxed
+ js_to_mono_obj_root(res, resultRoot, false);
+ } catch (exc) {
+ wrap_error_root(is_exception, String(exc), resultRoot);
+ } finally {
+ resultRoot.release();
}
-
- const res = typedarray_copy_from(js_obj, pinned_array, begin, end, bytes_per_element);
- // returns num_of_bytes boxed
- return _js_to_mono_obj(false, res);
}
export function has_backing_array_buffer(js_obj: TypedArray): boolean {
diff --git a/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js b/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js
index 664bccf25cd..fd89a822b74 100644
--- a/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js
+++ b/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js
@@ -12,7 +12,7 @@ const DotnetSupportLib = {
$DOTNET__postset: `
let __dotnet_replacements = {readAsync, fetch: globalThis.fetch, require};
let __dotnet_exportedAPI = __dotnet_runtime.__initializeImportsAndExports(
- { isESM:false, isGlobal:ENVIRONMENT_IS_GLOBAL, isNode:ENVIRONMENT_IS_NODE, isShell:ENVIRONMENT_IS_SHELL, isWeb:ENVIRONMENT_IS_WEB, locateFile, quit_, ExitStatus, requirePromise:Promise.resolve(require)},
+ { isESM:false, isGlobal:ENVIRONMENT_IS_GLOBAL, isNode:ENVIRONMENT_IS_NODE, isShell:ENVIRONMENT_IS_SHELL, isWeb:ENVIRONMENT_IS_WEB, locateFile, quit_, ExitStatus, requirePromise:Promise.resolve(require)},
{ mono:MONO, binding:BINDING, internal:INTERNAL, module:Module },
__dotnet_replacements);
readAsync = __dotnet_replacements.readAsync;
@@ -44,22 +44,22 @@ const linked_functions = [
// corebindings.c
"mono_wasm_invoke_js_with_args",
- "mono_wasm_get_object_property",
- "mono_wasm_set_object_property",
- "mono_wasm_get_by_index",
- "mono_wasm_set_by_index",
- "mono_wasm_get_global_object",
- "mono_wasm_create_cs_owned_object",
+ "mono_wasm_get_object_property_ref",
+ "mono_wasm_set_object_property_ref",
+ "mono_wasm_get_by_index_ref",
+ "mono_wasm_set_by_index_ref",
+ "mono_wasm_get_global_object_ref",
+ "mono_wasm_create_cs_owned_object_ref",
"mono_wasm_release_cs_owned_object",
- "mono_wasm_typed_array_to_array",
- "mono_wasm_typed_array_copy_to",
- "mono_wasm_typed_array_from",
- "mono_wasm_typed_array_copy_from",
+ "mono_wasm_typed_array_to_array_ref",
+ "mono_wasm_typed_array_copy_to_ref",
+ "mono_wasm_typed_array_from_ref",
+ "mono_wasm_typed_array_copy_from_ref",
"mono_wasm_cancel_promise",
- "mono_wasm_web_socket_open",
+ "mono_wasm_web_socket_open_ref",
"mono_wasm_web_socket_send",
"mono_wasm_web_socket_receive",
- "mono_wasm_web_socket_close",
+ "mono_wasm_web_socket_close_ref",
"mono_wasm_web_socket_abort",
"mono_wasm_compile_function",
diff --git a/src/mono/wasm/runtime/corebindings.c b/src/mono/wasm/runtime/corebindings.c
index c22d5e39d3e..8cc2ff15a1e 100644
--- a/src/mono/wasm/runtime/corebindings.c
+++ b/src/mono/wasm/runtime/corebindings.c
@@ -13,46 +13,48 @@
#include <mono/metadata/object.h>
#include <mono/jit/jit.h>
+#include "gc-common.h"
+
//JS funcs
extern MonoObject* mono_wasm_invoke_js_with_args (int js_handle, MonoString *method, MonoArray *args, int *is_exception);
-extern MonoObject* mono_wasm_get_object_property (int js_handle, MonoString *propertyName, int *is_exception);
-extern MonoObject* mono_wasm_get_by_index (int js_handle, int property_index, int *is_exception);
-extern MonoObject* mono_wasm_set_object_property (int js_handle, MonoString *propertyName, MonoObject *value, int createIfNotExist, int hasOwnProperty, int *is_exception);
-extern MonoObject* mono_wasm_set_by_index (int js_handle, int property_index, MonoObject *value, int *is_exception);
-extern MonoObject* mono_wasm_get_global_object (MonoString *global_name, int *is_exception);
-extern void* mono_wasm_release_cs_owned_object (int js_handle);
-extern MonoObject* mono_wasm_create_cs_owned_object (MonoString *core_name, MonoArray *args, int *is_exception);
-extern MonoObject* mono_wasm_typed_array_to_array (int js_handle, int *is_exception);
-extern MonoObject* mono_wasm_typed_array_copy_to (int js_handle, int ptr, int begin, int end, int bytes_per_element, int *is_exception);
-extern MonoObject* mono_wasm_typed_array_from (int ptr, int begin, int end, int bytes_per_element, int type, int *is_exception);
-extern MonoObject* mono_wasm_typed_array_copy_from (int js_handle, int ptr, int begin, int end, int bytes_per_element, int *is_exception);
+extern void mono_wasm_get_object_property_ref (int js_handle, MonoString **propertyName, int *is_exception, MonoObject **result);
+extern void mono_wasm_get_by_index_ref (int js_handle, int property_index, int *is_exception, MonoObject **result);
+extern void mono_wasm_set_object_property_ref (int js_handle, MonoString **propertyName, MonoObject **value, int createIfNotExist, int hasOwnProperty, int *is_exception, MonoObject **result);
+extern void mono_wasm_set_by_index_ref (int js_handle, int property_index, MonoObject **value, int *is_exception, MonoObject **result);
+extern void mono_wasm_get_global_object_ref (MonoString **global_name, int *is_exception, MonoObject **result);
+extern void mono_wasm_release_cs_owned_object (int js_handle);
+extern void mono_wasm_create_cs_owned_object_ref (MonoString **core_name, MonoArray **args, int *is_exception, MonoObject** result);
+extern void mono_wasm_typed_array_to_array_ref (int js_handle, int *is_exception, MonoObject **result);
+extern void mono_wasm_typed_array_copy_to_ref (int js_handle, int ptr, int begin, int end, int bytes_per_element, int *is_exception, MonoObject** result);
+extern void mono_wasm_typed_array_from_ref (int ptr, int begin, int end, int bytes_per_element, int type, int *is_exception, MonoObject** result);
+extern void mono_wasm_typed_array_copy_from_ref (int js_handle, int ptr, int begin, int end, int bytes_per_element, int *is_exception, MonoObject** result);
extern MonoString* mono_wasm_cancel_promise (int thenable_js_handle, int *is_exception);
-extern MonoObject* mono_wasm_web_socket_open (MonoString *uri, MonoArray *subProtocols, MonoDelegate *on_close, int *web_socket_js_handle, int *thenable_js_handle, int *is_exception);
-extern MonoObject* mono_wasm_web_socket_send (int webSocket_js_handle, void* buffer_ptr, int offset, int length, int message_type, int end_of_message, int *thenable_js_handle, int *is_exception);
-extern MonoObject* mono_wasm_web_socket_receive (int webSocket_js_handle, void* buffer_ptr, int offset, int length, void* response_ptr, int *thenable_js_handle, int *is_exception);
-extern MonoObject* mono_wasm_web_socket_close (int webSocket_js_handle, int code, MonoString * reason, int wait_for_close_received, int *thenable_js_handle, int *is_exception);
-extern MonoString* mono_wasm_web_socket_abort (int webSocket_js_handle, int *is_exception);
+extern void mono_wasm_web_socket_open_ref (MonoString **uri, MonoArray **subProtocols, MonoDelegate **on_close, int *web_socket_js_handle, int *thenable_js_handle, int *is_exception, MonoObject **result);
+extern void mono_wasm_web_socket_send (int webSocket_js_handle, void* buffer_ptr, int offset, int length, int message_type, int end_of_message, int *thenable_js_handle, int *is_exception, MonoObject **result);
+extern void mono_wasm_web_socket_receive (int webSocket_js_handle, void* buffer_ptr, int offset, int length, void* response_ptr, int *thenable_js_handle, int *is_exception, MonoObject **result);
+extern void mono_wasm_web_socket_close_ref (int webSocket_js_handle, int code, MonoString **reason, int wait_for_close_received, int *thenable_js_handle, int *is_exception, MonoObject **result);
+extern void mono_wasm_web_socket_abort (int webSocket_js_handle, int *is_exception, MonoString **result);
extern MonoObject* mono_wasm_compile_function (MonoString *str, int *is_exception);
void core_initialize_internals ()
{
mono_add_internal_call ("Interop/Runtime::InvokeJSWithArgs", mono_wasm_invoke_js_with_args);
- mono_add_internal_call ("Interop/Runtime::GetObjectProperty", mono_wasm_get_object_property);
- mono_add_internal_call ("Interop/Runtime::GetByIndex", mono_wasm_get_by_index);
- mono_add_internal_call ("Interop/Runtime::SetObjectProperty", mono_wasm_set_object_property);
- mono_add_internal_call ("Interop/Runtime::SetByIndex", mono_wasm_set_by_index);
- mono_add_internal_call ("Interop/Runtime::GetGlobalObject", mono_wasm_get_global_object);
- mono_add_internal_call ("Interop/Runtime::CreateCSOwnedObject", mono_wasm_create_cs_owned_object);
+ mono_add_internal_call ("Interop/Runtime::GetObjectPropertyRef", mono_wasm_get_object_property_ref);
+ mono_add_internal_call ("Interop/Runtime::GetByIndexRef", mono_wasm_get_by_index_ref);
+ mono_add_internal_call ("Interop/Runtime::SetObjectPropertyRef", mono_wasm_set_object_property_ref);
+ mono_add_internal_call ("Interop/Runtime::SetByIndexRef", mono_wasm_set_by_index_ref);
+ mono_add_internal_call ("Interop/Runtime::GetGlobalObjectRef", mono_wasm_get_global_object_ref);
+ mono_add_internal_call ("Interop/Runtime::CreateCSOwnedObjectRef", mono_wasm_create_cs_owned_object_ref);
mono_add_internal_call ("Interop/Runtime::ReleaseCSOwnedObject", mono_wasm_release_cs_owned_object);
- mono_add_internal_call ("Interop/Runtime::TypedArrayToArray", mono_wasm_typed_array_to_array);
- mono_add_internal_call ("Interop/Runtime::TypedArrayCopyTo", mono_wasm_typed_array_copy_to);
- mono_add_internal_call ("Interop/Runtime::TypedArrayFrom", mono_wasm_typed_array_from);
- mono_add_internal_call ("Interop/Runtime::TypedArrayCopyFrom", mono_wasm_typed_array_copy_from);
+ mono_add_internal_call ("Interop/Runtime::TypedArrayToArrayRef", mono_wasm_typed_array_to_array_ref);
+ mono_add_internal_call ("Interop/Runtime::TypedArrayCopyToRef", mono_wasm_typed_array_copy_to_ref);
+ mono_add_internal_call ("Interop/Runtime::TypedArrayFromRef", mono_wasm_typed_array_from_ref);
+ mono_add_internal_call ("Interop/Runtime::TypedArrayCopyFromRef", mono_wasm_typed_array_copy_from_ref);
mono_add_internal_call ("Interop/Runtime::CompileFunction", mono_wasm_compile_function);
- mono_add_internal_call ("Interop/Runtime::WebSocketOpen", mono_wasm_web_socket_open);
+ mono_add_internal_call ("Interop/Runtime::WebSocketOpenRef", mono_wasm_web_socket_open_ref);
mono_add_internal_call ("Interop/Runtime::WebSocketSend", mono_wasm_web_socket_send);
mono_add_internal_call ("Interop/Runtime::WebSocketReceive", mono_wasm_web_socket_receive);
- mono_add_internal_call ("Interop/Runtime::WebSocketClose", mono_wasm_web_socket_close);
+ mono_add_internal_call ("Interop/Runtime::WebSocketCloseRef", mono_wasm_web_socket_close_ref);
mono_add_internal_call ("Interop/Runtime::WebSocketAbort", mono_wasm_web_socket_abort);
mono_add_internal_call ("Interop/Runtime::CancelPromise", mono_wasm_cancel_promise);
}
@@ -66,7 +68,8 @@ void core_initialize_internals ()
// Uint32Array | uint32_t | uint (unsigned integer)
// Float32Array | float | float
// Float64Array | double | double
-// typed array marshaling
+// typed array marshalling
+// Keep in sync with driver.c
#define MARSHAL_ARRAY_BYTE 10
#define MARSHAL_ARRAY_UBYTE 11
#define MARSHAL_ARRAY_UBYTE_C 12 // alias of MARSHAL_ARRAY_UBYTE
@@ -77,10 +80,11 @@ void core_initialize_internals ()
#define MARSHAL_ARRAY_FLOAT 17
#define MARSHAL_ARRAY_DOUBLE 18
-EMSCRIPTEN_KEEPALIVE MonoArray*
-mono_wasm_typed_array_new (char *arr, int length, int size, int type)
+EMSCRIPTEN_KEEPALIVE void
+mono_wasm_typed_array_new_ref (char *arr, int length, int size, int type, PPVOLATILE(MonoArray) result)
{
- MonoClass *typeClass = mono_get_byte_class(); // default is Byte
+ MONO_ENTER_GC_UNSAFE;
+ MonoClass * typeClass = mono_get_byte_class(); // default is Byte
switch (type) {
case MARSHAL_ARRAY_BYTE:
typeClass = mono_get_sbyte_class();
@@ -103,44 +107,62 @@ mono_wasm_typed_array_new (char *arr, int length, int size, int type)
case MARSHAL_ARRAY_DOUBLE:
typeClass = mono_get_double_class();
break;
+ case MARSHAL_ARRAY_UBYTE:
+ case MARSHAL_ARRAY_UBYTE_C:
+ typeClass = mono_get_byte_class();
+ break;
+ default:
+ printf ("Invalid marshal type %d in mono_wasm_typed_array_new", type);
+ abort();
}
- MonoArray *buffer;
+ PVOLATILE(MonoArray) buffer;
buffer = mono_array_new (mono_get_root_domain(), typeClass, length);
memcpy(mono_array_addr_with_size(buffer, sizeof(char), 0), arr, length * size);
- return buffer;
+ store_volatile((PPVOLATILE(MonoObject))result, (MonoObject *)buffer);
+ MONO_EXIT_GC_UNSAFE;
}
+// TODO: Remove - no longer used? If not, convert to ref
EMSCRIPTEN_KEEPALIVE int
-mono_wasm_unbox_enum (MonoObject *obj)
+mono_wasm_unbox_enum (PVOLATILE(MonoObject) obj)
{
if (!obj)
return 0;
-
- MonoType *type = mono_class_get_type (mono_object_get_class(obj));
- void *ptr = mono_object_unbox (obj);
+ int result = 0;
+ MONO_ENTER_GC_UNSAFE;
+ PVOLATILE(MonoType) type = mono_class_get_type (mono_object_get_class(obj));
+
+ PVOLATILE(void) ptr = mono_object_unbox (obj);
switch (mono_type_get_type(mono_type_get_underlying_type (type))) {
case MONO_TYPE_I1:
case MONO_TYPE_U1:
- return *(unsigned char*)ptr;
+ result = *(unsigned char*)ptr;
+ break;
case MONO_TYPE_I2:
- return *(short*)ptr;
+ result = *(short*)ptr;
+ break;
case MONO_TYPE_U2:
- return *(unsigned short*)ptr;
+ result = *(unsigned short*)ptr;
+ break;
case MONO_TYPE_I4:
- return *(int*)ptr;
+ result = *(int*)ptr;
+ break;
case MONO_TYPE_U4:
- return *(unsigned int*)ptr;
+ result = *(unsigned int*)ptr;
+ break;
// WASM doesn't support returning longs to JS
// case MONO_TYPE_I8:
// case MONO_TYPE_U8:
default:
printf ("Invalid type %d to mono_unbox_enum\n", mono_type_get_type(mono_type_get_underlying_type (type)));
- return 0;
+ break;
}
+ MONO_EXIT_GC_UNSAFE;
+ return result;
}
diff --git a/src/mono/wasm/runtime/corebindings.ts b/src/mono/wasm/runtime/corebindings.ts
index 8f64ad00dba..82be21ce2a2 100644
--- a/src/mono/wasm/runtime/corebindings.ts
+++ b/src/mono/wasm/runtime/corebindings.ts
@@ -1,57 +1,58 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-import { JSHandle, GCHandle, MonoObject } from "./types";
+import { JSHandle, GCHandle, MonoObjectRef } from "./types";
import { PromiseControl } from "./cancelable-promise";
import { runtimeHelpers } from "./imports";
const fn_signatures: [jsname: string, csname: string, signature: string/*ArgsMarshalString*/][] = [
- ["_get_cs_owned_object_by_js_handle", "GetCSOwnedObjectByJSHandle", "ii!"],
- ["_get_cs_owned_object_js_handle", "GetCSOwnedObjectJSHandle", "mi"],
- ["_try_get_cs_owned_object_js_handle", "TryGetCSOwnedObjectJSHandle", "mi"],
- ["_create_cs_owned_proxy", "CreateCSOwnedProxy", "iii!"],
+ ["_get_cs_owned_object_by_js_handle_ref", "GetCSOwnedObjectByJSHandleRef", "iim"],
+ ["_get_cs_owned_object_js_handle_ref", "GetCSOwnedObjectJSHandleRef", "mi"],
+ ["_try_get_cs_owned_object_js_handle_ref", "TryGetCSOwnedObjectJSHandleRef", "mi"],
+ ["_create_cs_owned_proxy_ref", "CreateCSOwnedProxyRef", "iiim"],
- ["_get_js_owned_object_by_gc_handle", "GetJSOwnedObjectByGCHandle", "i!"],
- ["_get_js_owned_object_gc_handle", "GetJSOwnedObjectGCHandle", "m"],
+ ["_get_js_owned_object_by_gc_handle_ref", "GetJSOwnedObjectByGCHandleRef", "im"],
+ ["_get_js_owned_object_gc_handle_ref", "GetJSOwnedObjectGCHandleRef", "m"],
["_release_js_owned_object_by_gc_handle", "ReleaseJSOwnedObjectByGCHandle", "i"],
["_create_tcs", "CreateTaskSource", ""],
- ["_set_tcs_result", "SetTaskSourceResult", "io"],
+ ["_set_tcs_result_ref", "SetTaskSourceResultRef", "iR"],
["_set_tcs_failure", "SetTaskSourceFailure", "is"],
- ["_get_tcs_task", "GetTaskSourceTask", "i!"],
- ["_task_from_result", "TaskFromResult", "o!"],
- ["_setup_js_cont", "SetupJSContinuation", "mo"],
+ ["_get_tcs_task_ref", "GetTaskSourceTaskRef", "im"],
+ ["_task_from_result_ref", "TaskFromResultRef", "Rm"],
+ ["_setup_js_cont_ref", "SetupJSContinuationRef", "mo"],
- ["_object_to_string", "ObjectToString", "m"],
- ["_get_date_value", "GetDateValue", "m"],
- ["_create_date_time", "CreateDateTime", "d!"],
- ["_create_uri", "CreateUri", "s!"],
- ["_is_simple_array", "IsSimpleArray", "m"],
+ ["_object_to_string_ref", "ObjectToStringRef", "m"],
+ ["_get_date_value_ref", "GetDateValueRef", "m"],
+ ["_create_date_time_ref", "CreateDateTimeRef", "dm"],
+ ["_create_uri_ref", "CreateUriRef", "sm"],
+ ["_is_simple_array_ref", "IsSimpleArrayRef", "m"],
];
export interface t_CSwraps {
// BINDING
- _get_cs_owned_object_by_js_handle(jsHandle: JSHandle, shouldAddInflight: 0 | 1): MonoObject;
- _get_cs_owned_object_js_handle(jsHandle: JSHandle, shouldAddInflight: 0 | 1): JSHandle;
- _try_get_cs_owned_object_js_handle(obj: MonoObject, shouldAddInflight: 0 | 1): JSHandle;
- _create_cs_owned_proxy(jsHandle: JSHandle, mappedType: number, shouldAddInflight: 0 | 1): MonoObject;
+ _get_cs_owned_object_by_js_handle_ref(jsHandle: JSHandle, shouldAddInflight: 0 | 1, result: MonoObjectRef): void;
+ _get_cs_owned_object_js_handle_ref(obj: MonoObjectRef, shouldAddInflight: 0 | 1): JSHandle;
+ _try_get_cs_owned_object_js_handle_ref(obj: MonoObjectRef, shouldAddInflight: 0 | 1): JSHandle;
+ _create_cs_owned_proxy_ref(jsHandle: JSHandle, mappedType: number, shouldAddInflight: 0 | 1, result: MonoObjectRef): void;
- _get_js_owned_object_by_gc_handle(gcHandle: GCHandle): MonoObject;
- _get_js_owned_object_gc_handle(obj: MonoObject): GCHandle
+ _get_js_owned_object_by_gc_handle_ref(gcHandle: GCHandle, result: MonoObjectRef): void;
+ _get_js_owned_object_gc_handle_ref(obj: MonoObjectRef): GCHandle
_release_js_owned_object_by_gc_handle(gcHandle: GCHandle): void;
_create_tcs(): GCHandle;
- _set_tcs_result(gcHandle: GCHandle, result: MonoObject): void
+ _set_tcs_result_ref(gcHandle: GCHandle, result: any): void
_set_tcs_failure(gcHandle: GCHandle, result: string): void
- _get_tcs_task(gcHandle: GCHandle): MonoObject;
- _task_from_result(result: MonoObject): MonoObject
- _setup_js_cont(task: MonoObject, continuation: PromiseControl): MonoObject
+ _get_tcs_task_ref(gcHandle: GCHandle, result: MonoObjectRef): void;
+ _task_from_result_ref(value: any, result: MonoObjectRef): void;
+ // FIXME: PromiseControl is a JS object so we can't pass an address directly
+ _setup_js_cont_ref(task: MonoObjectRef, continuation: PromiseControl): void;
- _object_to_string(obj: MonoObject): string;
- _get_date_value(obj: MonoObject): number;
- _create_date_time(ticks: number): MonoObject;
- _create_uri(uri: string): MonoObject;
- _is_simple_array(obj: MonoObject): boolean;
+ _object_to_string_ref(obj: MonoObjectRef): string;
+ _get_date_value_ref(obj: MonoObjectRef): number;
+ _create_date_time_ref(ticks: number, result: MonoObjectRef): void;
+ _create_uri_ref(uri: string, result: MonoObjectRef): void;
+ _is_simple_array_ref(obj: MonoObjectRef): boolean;
}
const wrapped_cs_functions: t_CSwraps = <any>{};
diff --git a/src/mono/wasm/runtime/cs-to-js.ts b/src/mono/wasm/runtime/cs-to-js.ts
index 591f654b0b6..dcc67dcd6e5 100644
--- a/src/mono/wasm/runtime/cs-to-js.ts
+++ b/src/mono/wasm/runtime/cs-to-js.ts
@@ -1,19 +1,19 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-import { mono_wasm_new_root, WasmRoot } from "./roots";
+import { mono_wasm_new_root, WasmRoot, mono_wasm_new_external_root } from "./roots";
import {
GCHandle, JSHandleDisposed, MarshalError, MarshalType, MonoArray,
MonoArrayNull, MonoObject, MonoObjectNull, MonoString,
- MonoType, MonoTypeNull
+ MonoType, MonoTypeNull, MonoObjectRef, MonoStringRef
} from "./types";
import { runtimeHelpers } from "./imports";
-import { conv_string } from "./strings";
+import { conv_string_root } from "./strings";
import corebindings from "./corebindings";
import cwraps from "./cwraps";
-import { get_js_owned_object_by_gc_handle, js_owned_gc_handle_symbol, mono_wasm_get_jsobj_from_js_handle, mono_wasm_get_js_handle, _js_owned_object_finalized, _js_owned_object_registry, _lookup_js_owned_object, _register_js_owned_object, _use_finalization_registry } from "./gc-handles";
-import { mono_method_get_call_signature, call_method, wrap_error } from "./method-calls";
-import { _js_to_mono_obj } from "./js-to-cs";
+import { get_js_owned_object_by_gc_handle_ref, js_owned_gc_handle_symbol, mono_wasm_get_jsobj_from_js_handle, mono_wasm_get_js_handle, _js_owned_object_finalized, _js_owned_object_registry, _lookup_js_owned_object, _register_js_owned_object, _use_finalization_registry } from "./gc-handles";
+import { mono_method_get_call_signature_ref, call_method_ref, wrap_error_root } from "./method-calls";
+import { js_to_mono_obj_root } from "./js-to-cs";
import { _are_promises_supported, _create_cancelable_promise } from "./cancelable-promise";
import { getU32, getI32, getF32, getF64 } from "./memory";
import { Int32Ptr, VoidPtr } from "./types/emscripten";
@@ -28,7 +28,7 @@ export function unbox_mono_obj(mono_obj: MonoObject): any {
const root = mono_wasm_new_root(mono_obj);
try {
- return _unbox_mono_obj_root(root);
+ return unbox_mono_obj_root(root);
} finally {
root.release();
}
@@ -36,7 +36,7 @@ export function unbox_mono_obj(mono_obj: MonoObject): any {
function _unbox_cs_owned_root_as_js_object(root: WasmRoot<any>) {
// we don't need in-flight reference as we already have it rooted here
- const js_handle = corebindings._get_cs_owned_object_js_handle(root.value, 0);
+ const js_handle = corebindings._get_cs_owned_object_js_handle_ref(root.address, 0);
const js_obj = mono_wasm_get_jsobj_from_js_handle(js_handle);
return js_obj;
}
@@ -45,13 +45,15 @@ function _unbox_cs_owned_root_as_js_object(root: WasmRoot<any>) {
function _unbox_mono_obj_root_with_known_nonprimitive_type_impl(root: WasmRoot<any>, type: MarshalType, typePtr: MonoType, unbox_buffer: VoidPtr): any {
//See MARSHAL_TYPE_ defines in driver.c
switch (type) {
+ case MarshalType.NULL:
+ return null;
case MarshalType.INT64:
case MarshalType.UINT64:
// TODO: Fix this once emscripten offers HEAPI64/HEAPU64 or can return them
throw new Error("int64 not available");
case MarshalType.STRING:
case MarshalType.STRING_INTERNED:
- return conv_string(root.value);
+ return conv_string_root(root);
case MarshalType.VT:
throw new Error("no idea on how to unbox value types");
case MarshalType.DELEGATE:
@@ -71,40 +73,41 @@ function _unbox_mono_obj_root_with_known_nonprimitive_type_impl(root: WasmRoot<a
case MarshalType.ARRAY_DOUBLE:
throw new Error("Marshaling of primitive arrays are not supported.");
case <MarshalType>20: // clr .NET DateTime
- return new Date(corebindings._get_date_value(root.value));
+ return new Date(corebindings._get_date_value_ref(root.address));
case <MarshalType>21: // clr .NET DateTimeOffset
- return corebindings._object_to_string(root.value);
+ return corebindings._object_to_string_ref(root.address);
case MarshalType.URI:
- return corebindings._object_to_string(root.value);
+ return corebindings._object_to_string_ref(root.address);
case MarshalType.SAFEHANDLE:
return _unbox_cs_owned_root_as_js_object(root);
case MarshalType.VOID:
return undefined;
default:
- throw new Error(`no idea on how to unbox object of MarshalType ${type} at offset ${root.value} (root address is ${root.get_address()})`);
+ throw new Error(`no idea on how to unbox object of MarshalType ${type} at offset ${root.value} (root address is ${root.address})`);
}
}
export function _unbox_mono_obj_root_with_known_nonprimitive_type(root: WasmRoot<any>, type: MarshalType, unbox_buffer: VoidPtr): any {
if (type >= MarshalError.FIRST)
- throw new Error(`Got marshaling error ${type} when attempting to unbox object at address ${root.value} (root located at ${root.get_address()})`);
+ throw new Error(`Got marshaling error ${type} when attempting to unbox object at address ${root.value} (root located at ${root.address})`);
let typePtr = MonoTypeNull;
if ((type === MarshalType.VT) || (type == MarshalType.OBJECT)) {
typePtr = <MonoType><any>getU32(unbox_buffer);
if (<number><any>typePtr < 1024)
- throw new Error(`Got invalid MonoType ${typePtr} for object at address ${root.value} (root located at ${root.get_address()})`);
+ throw new Error(`Got invalid MonoType ${typePtr} for object at address ${root.value} (root located at ${root.address})`);
}
return _unbox_mono_obj_root_with_known_nonprimitive_type_impl(root, type, typePtr, unbox_buffer);
}
-export function _unbox_mono_obj_root(root: WasmRoot<any>): any {
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+export function unbox_mono_obj_root(root: WasmRoot<any>): any {
if (root.value === 0)
return undefined;
const unbox_buffer = runtimeHelpers._unbox_buffer;
- const type = cwraps.mono_wasm_try_unbox_primitive_and_get_type(root.value, unbox_buffer, runtimeHelpers._unbox_buffer_size);
+ const type = cwraps.mono_wasm_try_unbox_primitive_and_get_type_ref(root.address, unbox_buffer, runtimeHelpers._unbox_buffer_size);
switch (type) {
case MarshalType.INT:
return getI32(unbox_buffer);
@@ -128,38 +131,43 @@ export function _unbox_mono_obj_root(root: WasmRoot<any>): any {
}
}
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function mono_array_to_js_array(mono_array: MonoArray): any[] | null {
if (mono_array === MonoArrayNull)
return null;
const arrayRoot = mono_wasm_new_root(mono_array);
try {
- return _mono_array_root_to_js_array(arrayRoot);
+ return mono_array_root_to_js_array(arrayRoot);
} finally {
arrayRoot.release();
}
}
-function is_nested_array(ele: MonoObject) {
- return corebindings._is_simple_array(ele);
+function is_nested_array_ref(ele: WasmRoot<MonoObject>) {
+ return corebindings._is_simple_array_ref(ele.address);
}
-export function _mono_array_root_to_js_array(arrayRoot: WasmRoot<MonoArray>): any[] | null {
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+export function mono_array_root_to_js_array(arrayRoot: WasmRoot<MonoArray>): any[] | null {
if (arrayRoot.value === MonoArrayNull)
return null;
+ const arrayAddress = arrayRoot.address;
const elemRoot = mono_wasm_new_root<MonoObject>();
+ const elemAddress = elemRoot.address;
try {
const len = cwraps.mono_wasm_array_length(arrayRoot.value);
const res = new Array(len);
for (let i = 0; i < len; ++i) {
- elemRoot.value = cwraps.mono_wasm_array_get(arrayRoot.value, i);
+ // TODO: pass arrayRoot.address and elemRoot.address into new API that copies
+ cwraps.mono_wasm_array_get_ref(arrayAddress, i, elemAddress);
- if (is_nested_array(elemRoot.value))
- res[i] = _mono_array_root_to_js_array(<any>elemRoot);
+ if (is_nested_array_ref(elemRoot))
+ res[i] = mono_array_root_to_js_array(<any>elemRoot);
else
- res[i] = _unbox_mono_obj_root(elemRoot);
+ res[i] = unbox_mono_obj_root(elemRoot);
}
return res;
} finally {
@@ -172,7 +180,7 @@ export function _wrap_delegate_root_as_function(root: WasmRoot<MonoObject>): Fun
return null;
// get strong reference to the Delegate
- const gc_handle = corebindings._get_js_owned_object_gc_handle(root.value);
+ const gc_handle = corebindings._get_js_owned_object_gc_handle_ref(root.address);
return _wrap_delegate_gc_handle_as_function(gc_handle);
}
@@ -184,9 +192,11 @@ export function _wrap_delegate_gc_handle_as_function(gc_handle: GCHandle, after_
if (!result) {
// note that we do not implement function/delegate roundtrip
result = function (...args: any[]) {
- const delegateRoot = mono_wasm_new_root(get_js_owned_object_by_gc_handle(gc_handle));
+ const delegateRoot = mono_wasm_new_root<MonoObject>();
+ get_js_owned_object_by_gc_handle_ref(gc_handle, delegateRoot.address);
try {
- const res = call_method(result[delegate_invoke_symbol], delegateRoot.value, result[delegate_invoke_signature_symbol], args);
+ // FIXME: Pass delegateRoot by-ref
+ const res = call_method_ref(result[delegate_invoke_symbol], delegateRoot, result[delegate_invoke_signature_symbol], args);
if (after_listener_callback) {
after_listener_callback();
}
@@ -197,17 +207,18 @@ export function _wrap_delegate_gc_handle_as_function(gc_handle: GCHandle, after_
};
// bind the method
- const delegateRoot = mono_wasm_new_root(get_js_owned_object_by_gc_handle(gc_handle));
+ const delegateRoot = mono_wasm_new_root<MonoObject>();
+ get_js_owned_object_by_gc_handle_ref(gc_handle, delegateRoot.address);
try {
if (typeof result[delegate_invoke_symbol] === "undefined") {
- result[delegate_invoke_symbol] = cwraps.mono_wasm_get_delegate_invoke(delegateRoot.value);
+ result[delegate_invoke_symbol] = cwraps.mono_wasm_get_delegate_invoke_ref(delegateRoot.address);
if (!result[delegate_invoke_symbol]) {
throw new Error("System.Delegate Invoke method can not be resolved.");
}
}
if (typeof result[delegate_invoke_signature_symbol] === "undefined") {
- result[delegate_invoke_signature_symbol] = mono_method_get_call_signature(result[delegate_invoke_symbol], delegateRoot.value);
+ result[delegate_invoke_signature_symbol] = mono_method_get_call_signature_ref(result[delegate_invoke_symbol], delegateRoot);
}
} finally {
delegateRoot.release();
@@ -227,21 +238,25 @@ export function _wrap_delegate_gc_handle_as_function(gc_handle: GCHandle, after_
return result;
}
-export function mono_wasm_create_cs_owned_object(core_name: MonoString, args: MonoArray, is_exception: Int32Ptr): MonoObject {
- const argsRoot = mono_wasm_new_root(args), nameRoot = mono_wasm_new_root(core_name);
+export function mono_wasm_create_cs_owned_object_ref(core_name: MonoStringRef, args: MonoObjectRef, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
+ const argsRoot = mono_wasm_new_external_root<MonoArray>(args),
+ nameRoot = mono_wasm_new_external_root<MonoString>(core_name),
+ resultRoot = mono_wasm_new_external_root<MonoObject>(result_address);
try {
- const js_name = conv_string(nameRoot.value);
+ const js_name = conv_string_root(nameRoot);
if (!js_name) {
- return wrap_error(is_exception, "Invalid name @" + nameRoot.value);
+ wrap_error_root(is_exception, "Invalid name @" + nameRoot.value, resultRoot);
+ return;
}
const coreObj = (<any>globalThis)[js_name];
if (coreObj === null || typeof coreObj === "undefined") {
- return wrap_error(is_exception, "JavaScript host object '" + js_name + "' not found.");
+ wrap_error_root(is_exception, "JavaScript host object '" + js_name + "' not found.", resultRoot);
+ return;
}
try {
- const js_args = _mono_array_root_to_js_array(argsRoot);
+ const js_args = mono_array_root_to_js_array(argsRoot);
// This is all experimental !!!!!!
const allocator = function (constructor: Function, js_args: any[] | null) {
@@ -260,11 +275,13 @@ export function mono_wasm_create_cs_owned_object(core_name: MonoString, args: Mo
const js_handle = mono_wasm_get_js_handle(js_obj);
// returns boxed js_handle int, because on exception we need to return String on same method signature
// here we don't have anything to in-flight reference, as the JSObject doesn't exist yet
- return _js_to_mono_obj(false, js_handle);
+ js_to_mono_obj_root(js_handle, resultRoot, false);
} catch (ex) {
- return wrap_error(is_exception, ex);
+ wrap_error_root(is_exception, ex, resultRoot);
+ return;
}
} finally {
+ resultRoot.release();
argsRoot.release();
nameRoot.release();
}
@@ -278,7 +295,7 @@ function _unbox_task_root_as_promise(root: WasmRoot<MonoObject>) {
throw new Error("Promises are not supported thus 'System.Threading.Tasks.Task' can not work in this context.");
// get strong reference to Task
- const gc_handle = corebindings._get_js_owned_object_gc_handle(root.value);
+ const gc_handle = corebindings._get_js_owned_object_gc_handle_ref(root.address);
// see if we have js owned instance for this gc_handle already
let result = _lookup_js_owned_object(gc_handle);
@@ -296,7 +313,7 @@ function _unbox_task_root_as_promise(root: WasmRoot<MonoObject>) {
result = promise;
// register C# side of the continuation
- corebindings._setup_js_cont(root.value, promise_control);
+ corebindings._setup_js_cont_ref(root.address, promise_control);
// register for GC of the Task after the JS side is done with the promise
if (_use_finalization_registry) {
@@ -317,7 +334,7 @@ export function _unbox_ref_type_root_as_js_object(root: WasmRoot<MonoObject>): a
// this could be JSObject proxy of a js native object
// we don't need in-flight reference as we already have it rooted here
- const js_handle = corebindings._try_get_cs_owned_object_js_handle(root.value, 0);
+ const js_handle = corebindings._try_get_cs_owned_object_js_handle_ref(root.address, 0);
if (js_handle) {
if (js_handle === JSHandleDisposed) {
throw new Error("Cannot access a disposed JSObject at " + root.value);
@@ -327,7 +344,7 @@ export function _unbox_ref_type_root_as_js_object(root: WasmRoot<MonoObject>): a
// otherwise this is C# only object
// get strong reference to Object
- const gc_handle = corebindings._get_js_owned_object_gc_handle(root.value);
+ const gc_handle = corebindings._get_js_owned_object_gc_handle_ref(root.address);
// see if we have js owned instance for this gc_handle already
let result = _lookup_js_owned_object(gc_handle);
diff --git a/src/mono/wasm/runtime/cwraps.ts b/src/mono/wasm/runtime/cwraps.ts
index 7567ac91411..861824f56d7 100644
--- a/src/mono/wasm/runtime/cwraps.ts
+++ b/src/mono/wasm/runtime/cwraps.ts
@@ -5,16 +5,17 @@ import {
assert,
MonoArray, MonoAssembly, MonoClass,
MonoMethod, MonoObject, MonoString,
- MonoType
+ MonoType, MonoObjectRef, MonoStringRef
} from "./types";
import { Module } from "./imports";
-import { VoidPtr, CharPtrPtr, Int32Ptr, CharPtr } from "./types/emscripten";
+import { VoidPtr, CharPtrPtr, Int32Ptr, CharPtr, ManagedPointer } from "./types/emscripten";
const fn_signatures: [ident: string, returnType: string | null, argTypes?: string[], opts?: any][] = [
// MONO
["mono_wasm_register_root", "number", ["number", "number", "string"]],
["mono_wasm_deregister_root", null, ["number"]],
["mono_wasm_string_get_data", null, ["number", "number", "number", "number"]],
+ ["mono_wasm_string_get_data_ref", null, ["number", "number", "number", "number"]],
["mono_wasm_set_is_debugger_attached", "void", ["bool"]],
["mono_wasm_send_dbg_command", "bool", ["number", "number", "number", "number", "number"]],
["mono_wasm_send_dbg_command_with_parms", "bool", ["number", "number", "number", "number", "number", "number", "string"]],
@@ -40,26 +41,29 @@ const fn_signatures: [ident: string, returnType: string | null, argTypes?: strin
["mono_wasm_assembly_find_type", "number", ["number", "string", "string"]],
["mono_wasm_assembly_find_method", "number", ["number", "string", "number"]],
["mono_wasm_invoke_method", "number", ["number", "number", "number", "number"]],
+ ["mono_wasm_invoke_method_ref", "void", ["number", "number", "number", "number", "number"]],
["mono_wasm_string_get_utf8", "number", ["number"]],
- ["mono_wasm_string_from_utf16", "number", ["number", "number"]],
+ ["mono_wasm_string_from_utf16_ref", "void", ["number", "number", "number"]],
["mono_wasm_get_obj_type", "number", ["number"]],
["mono_wasm_array_length", "number", ["number"]],
["mono_wasm_array_get", "number", ["number", "number"]],
+ ["mono_wasm_array_get_ref", "void", ["number", "number", "number"]],
["mono_wasm_obj_array_new", "number", ["number"]],
+ ["mono_wasm_obj_array_new_ref", "void", ["number", "number"]],
["mono_wasm_obj_array_set", "void", ["number", "number", "number"]],
+ ["mono_wasm_obj_array_set_ref", "void", ["number", "number", "number"]],
["mono_wasm_register_bundled_satellite_assemblies", "void", []],
- ["mono_wasm_try_unbox_primitive_and_get_type", "number", ["number", "number", "number"]],
- ["mono_wasm_box_primitive", "number", ["number", "number", "number"]],
- ["mono_wasm_intern_string", "number", ["number"]],
+ ["mono_wasm_try_unbox_primitive_and_get_type_ref", "number", ["number", "number", "number"]],
+ ["mono_wasm_box_primitive_ref", "void", ["number", "number", "number", "number"]],
+ ["mono_wasm_intern_string_ref", "void", ["number"]],
["mono_wasm_assembly_get_entry_point", "number", ["number"]],
- ["mono_wasm_get_delegate_invoke", "number", ["number"]],
- ["mono_wasm_string_array_new", "number", ["number"]],
- ["mono_wasm_typed_array_new", "number", ["number", "number", "number", "number"]],
+ ["mono_wasm_get_delegate_invoke_ref", "number", ["number"]],
+ ["mono_wasm_string_array_new_ref", "void", ["number", "number"]],
+ ["mono_wasm_typed_array_new_ref", "void", ["number", "number", "number", "number", "number"]],
["mono_wasm_class_get_type", "number", ["number"]],
["mono_wasm_type_get_class", "number", ["number"]],
["mono_wasm_get_type_name", "string", ["number"]],
["mono_wasm_get_type_aqn", "string", ["number"]],
- ["mono_wasm_unbox_rooted", "number", ["number"]],
//DOTNET
["mono_wasm_string_from_js", "number", ["string"]],
@@ -70,13 +74,15 @@ const fn_signatures: [ident: string, returnType: string | null, argTypes?: strin
["mono_wasm_enable_on_demand_gc", "void", ["number"]],
["mono_profiler_init_aot", "void", ["number"]],
["mono_wasm_exec_regression", "number", ["number", "string"]],
+ ["mono_wasm_write_managed_pointer_unsafe", "void", ["number", "number"]],
+ ["mono_wasm_copy_managed_pointer", "void", ["number", "number"]],
];
export interface t_Cwraps {
// MONO
mono_wasm_register_root(start: VoidPtr, size: number, name: string): number;
mono_wasm_deregister_root(addr: VoidPtr): void;
- mono_wasm_string_get_data(string: MonoString, outChars: CharPtrPtr, outLengthBytes: Int32Ptr, outIsInterned: Int32Ptr): void;
+ mono_wasm_string_get_data_ref(stringRef: MonoStringRef, outChars: CharPtrPtr, outLengthBytes: Int32Ptr, outIsInterned: Int32Ptr): void;
mono_wasm_set_is_debugger_attached(value: boolean): void;
mono_wasm_send_dbg_command(id: number, command_set: number, command: number, data: VoidPtr, size: number): boolean;
mono_wasm_send_dbg_command_with_parms(id: number, command_set: number, command: number, data: VoidPtr, size: number, valtype: number, newvalue: string): boolean;
@@ -92,6 +98,11 @@ export interface t_Cwraps {
mono_wasm_load_runtime(unused: string, debug_level: number): void;
mono_wasm_change_debugger_log_level(value: number): void;
+ /**
+ * @deprecated Not GC or thread safe
+ */
+ mono_wasm_string_get_data(string: MonoString, outChars: CharPtrPtr, outLengthBytes: Int32Ptr, outIsInterned: Int32Ptr): void;
+
// BINDING
mono_wasm_get_corlib(): MonoAssembly;
mono_wasm_assembly_load(name: string): MonoAssembly;
@@ -100,29 +111,55 @@ export interface t_Cwraps {
mono_wasm_find_corlib_type(namespace: string, name: string): MonoType;
mono_wasm_assembly_find_type(assembly: MonoAssembly, namespace: string, name: string): MonoType;
mono_wasm_assembly_find_method(klass: MonoClass, name: string, args: number): MonoMethod;
- mono_wasm_invoke_method(method: MonoMethod, this_arg: MonoObject, params: VoidPtr, out_exc: VoidPtr): MonoObject;
+ mono_wasm_invoke_method_ref(method: MonoMethod, this_arg: MonoObjectRef, params: VoidPtr, out_exc: MonoObjectRef, out_result: MonoObjectRef): void;
+ /**
+ * @deprecated Not GC or thread safe
+ */
mono_wasm_string_get_utf8(str: MonoString): CharPtr;
- mono_wasm_string_from_utf16(str: CharPtr, len: number): MonoString;
- mono_wasm_get_obj_type(str: MonoObject): number;
+ mono_wasm_string_from_utf16_ref(str: CharPtr, len: number, result: MonoObjectRef): void;
mono_wasm_array_length(array: MonoArray): number;
- mono_wasm_array_get(array: MonoArray, idx: number): MonoObject;
- mono_wasm_obj_array_new(size: number): MonoArray;
- mono_wasm_obj_array_set(array: MonoArray, idx: number, obj: MonoObject): void;
+
+ mono_wasm_array_get_ref(array: MonoObjectRef, idx: number, result: MonoObjectRef): void;
+ mono_wasm_obj_array_new_ref(size: number, result: MonoObjectRef): void;
+ mono_wasm_obj_array_set_ref(array: MonoObjectRef, idx: number, obj: MonoObjectRef): void;
mono_wasm_register_bundled_satellite_assemblies(): void;
- mono_wasm_try_unbox_primitive_and_get_type(obj: MonoObject, buffer: VoidPtr, buffer_size: number): number;
- mono_wasm_box_primitive(klass: MonoClass, value: VoidPtr, value_size: number): MonoObject;
- mono_wasm_intern_string(str: MonoString): MonoString;
+ mono_wasm_try_unbox_primitive_and_get_type_ref(obj: MonoObjectRef, buffer: VoidPtr, buffer_size: number): number;
+ mono_wasm_box_primitive_ref(klass: MonoClass, value: VoidPtr, value_size: number, result: MonoObjectRef): void;
+ mono_wasm_intern_string_ref(strRef: MonoStringRef): void;
mono_wasm_assembly_get_entry_point(assembly: MonoAssembly): MonoMethod;
- mono_wasm_get_delegate_invoke(delegate: MonoObject): MonoMethod;
- mono_wasm_string_array_new(size: number): MonoArray;
- mono_wasm_typed_array_new(arr: VoidPtr, length: number, size: number, type: number): MonoArray;
+ mono_wasm_string_array_new_ref(size: number, result: MonoObjectRef): void;
+ mono_wasm_typed_array_new_ref(arr: VoidPtr, length: number, size: number, type: number, result: MonoObjectRef): void;
mono_wasm_class_get_type(klass: MonoClass): MonoType;
mono_wasm_type_get_class(ty: MonoType): MonoClass;
+ mono_wasm_get_delegate_invoke_ref(delegate: MonoObjectRef): MonoMethod;
mono_wasm_get_type_name(ty: MonoType): string;
mono_wasm_get_type_aqn(ty: MonoType): string;
- mono_wasm_unbox_rooted(obj: MonoObject): VoidPtr;
+
+ /**
+ * @deprecated Not GC or thread safe
+ */
+ mono_wasm_get_obj_type(str: MonoObject): number;
+ /**
+ * @deprecated Not GC or thread safe
+ */
+ mono_wasm_invoke_method(method: MonoMethod, this_arg: MonoObject, params: VoidPtr, out_exc: MonoObjectRef): MonoObject;
+ /**
+ * @deprecated Not GC or thread safe
+ */
+ mono_wasm_obj_array_new(size: number): MonoArray;
+ /**
+ * @deprecated Not GC or thread safe
+ */
+ mono_wasm_array_get(array: MonoArray, idx: number): MonoObject;
+ /**
+ * @deprecated Not GC or thread safe
+ */
+ mono_wasm_obj_array_set(array: MonoArray, idx: number, obj: MonoObject): void;
//DOTNET
+ /**
+ * @deprecated Not GC or thread safe
+ */
mono_wasm_string_from_js(str: string): MonoString;
//INTERNAL
@@ -131,6 +168,8 @@ export interface t_Cwraps {
mono_wasm_set_main_args(argc: number, argv: VoidPtr): void;
mono_profiler_init_aot(desc: string): void;
mono_wasm_exec_regression(verbose_level: number, image: string): 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;
}
const wrapped_c_functions: t_Cwraps = <any>{};
diff --git a/src/mono/wasm/runtime/dotnet.d.ts b/src/mono/wasm/runtime/dotnet.d.ts
index 617b572e757..3e5083662d8 100644
--- a/src/mono/wasm/runtime/dotnet.d.ts
+++ b/src/mono/wasm/runtime/dotnet.d.ts
@@ -66,13 +66,18 @@ declare type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Arra
*/
declare function mono_wasm_new_root_buffer(capacity: number, name?: string): WasmRootBuffer;
/**
+ * Allocates a WasmRoot pointing to a root provided and controlled by external code. Typicaly on managed stack.
+ * Releasing this root will not de-allocate the root space. You still need to call .release().
+ */
+declare function mono_wasm_new_external_root<T extends MonoObject>(address: VoidPtr | MonoObjectRef): WasmRoot<T>;
+/**
* Allocates temporary storage for a pointer into the managed heap.
* Pointers stored here will be visible to the GC, ensuring that the object they point to aren't moved or collected.
* If you already have a managed pointer you can pass it as an argument to initialize the temporary storage.
* The result object has get() and set(value) methods, along with a .value property.
* When you are done using the root you must call its .release() method.
*/
-declare function mono_wasm_new_root<T extends ManagedPointer | NativePointer>(value?: T | undefined): WasmRoot<T>;
+declare function mono_wasm_new_root<T extends MonoObject>(value?: T | undefined): WasmRoot<T>;
/**
* Releases 1 or more root or root buffer objects.
* Multiple objects may be passed on the argument list.
@@ -91,23 +96,29 @@ declare class WasmRootBuffer {
constructor(offset: VoidPtr, capacity: number, ownsAllocation: boolean, name?: string);
_throw_index_out_of_range(): void;
_check_in_range(index: number): void;
- get_address(index: number): NativePointer;
+ get_address(index: number): MonoObjectRef;
get_address_32(index: number): number;
get(index: number): ManagedPointer;
set(index: number, value: ManagedPointer): ManagedPointer;
+ copy_value_from_address(index: number, sourceAddress: MonoObjectRef): void;
_unsafe_get(index: number): number;
_unsafe_set(index: number, value: ManagedPointer | NativePointer): void;
clear(): void;
release(): void;
toString(): string;
}
-interface WasmRoot<T extends ManagedPointer | NativePointer> {
- get_address(): NativePointer;
+interface WasmRoot<T extends MonoObject> {
+ get_address(): MonoObjectRef;
get_address_32(): number;
+ get address(): MonoObjectRef;
get(): T;
set(value: T): T;
get value(): T;
set value(value: T);
+ copy_from_address(source: MonoObjectRef): void;
+ copy_to_address(destination: MonoObjectRef): void;
+ copy_from(source: WasmRoot<T>): void;
+ copy_to(destination: WasmRoot<T>): void;
valueOf(): T;
clear(): void;
release(): void;
@@ -123,6 +134,9 @@ interface MonoString extends MonoObject {
interface MonoArray extends MonoObject {
__brand: "MonoArray";
}
+interface MonoObjectRef extends ManagedPointer {
+ __brandMonoObjectRef: "MonoObjectRef";
+}
declare type MonoConfig = {
isError: false;
assembly_root: string;
@@ -237,21 +251,39 @@ declare function mono_wasm_load_config(configFilePath: string): Promise<void>;
declare function mono_wasm_load_icu_data(offset: VoidPtr): boolean;
+/**
+ * @deprecated Not GC or thread safe
+ */
declare function conv_string(mono_obj: MonoString): string | null;
+declare function conv_string_root(root: WasmRoot<MonoString>): string | null;
+declare function js_string_to_mono_string_root(string: string, result: WasmRoot<MonoString>): void;
+/**
+ * @deprecated Not GC or thread safe
+ */
declare function js_string_to_mono_string(string: string): MonoString;
+/**
+ * @deprecated Not GC or thread safe. For blazor use only
+ */
declare function js_to_mono_obj(js_obj: any): MonoObject;
+declare function js_to_mono_obj_root(js_obj: any, result: WasmRoot<MonoObject>, should_add_in_flight: boolean): void;
+declare function js_typed_array_to_array_root(js_obj: any, result: WasmRoot<MonoArray>): void;
+/**
+ * @deprecated Not GC or thread safe
+ */
declare function js_typed_array_to_array(js_obj: any): MonoArray;
declare function unbox_mono_obj(mono_obj: MonoObject): any;
+declare function unbox_mono_obj_root(root: WasmRoot<any>): any;
declare function mono_array_to_js_array(mono_array: MonoArray): any[] | null;
+declare function mono_array_root_to_js_array(arrayRoot: WasmRoot<MonoArray>): any[] | null;
declare function mono_bind_static_method(fqn: string, signature?: string): Function;
declare function mono_call_assembly_entry_point(assembly: string, args?: any[], signature?: string): number;
declare function mono_wasm_load_bytes_into_heap(bytes: Uint8Array): VoidPtr;
-declare type _MemOffset = number | VoidPtr | NativePointer;
+declare type _MemOffset = number | VoidPtr | NativePointer | ManagedPointer;
declare type _NumberOrPointer = number | VoidPtr | NativePointer | ManagedPointer;
declare function setU8(offset: _MemOffset, value: number): void;
declare function setU16(offset: _MemOffset, value: number): void;
@@ -285,6 +317,7 @@ declare const MONO: {
mono_load_runtime_and_bcl_args: typeof mono_load_runtime_and_bcl_args;
mono_wasm_new_root_buffer: typeof mono_wasm_new_root_buffer;
mono_wasm_new_root: typeof mono_wasm_new_root;
+ mono_wasm_new_external_root: typeof mono_wasm_new_external_root;
mono_wasm_release_roots: typeof mono_wasm_release_roots;
mono_run_main: typeof mono_run_main;
mono_run_main_and_exit: typeof mono_run_main_and_exit;
@@ -313,16 +346,50 @@ declare const MONO: {
};
declare type MONOType = typeof MONO;
declare const BINDING: {
+ /**
+ * @deprecated Not GC or thread safe
+ */
mono_obj_array_new: (size: number) => MonoArray;
+ /**
+ * @deprecated Not GC or thread safe
+ */
mono_obj_array_set: (array: MonoArray, idx: number, obj: MonoObject) => void;
+ /**
+ * @deprecated Not GC or thread safe
+ */
js_string_to_mono_string: typeof js_string_to_mono_string;
+ /**
+ * @deprecated Not GC or thread safe
+ */
js_typed_array_to_array: typeof js_typed_array_to_array;
- js_to_mono_obj: typeof js_to_mono_obj;
+ /**
+ * @deprecated Not GC or thread safe
+ */
mono_array_to_js_array: typeof mono_array_to_js_array;
+ /**
+ * @deprecated Not GC or thread safe
+ */
+ js_to_mono_obj: typeof js_to_mono_obj;
+ /**
+ * @deprecated Not GC or thread safe
+ */
conv_string: typeof conv_string;
+ /**
+ * @deprecated Not GC or thread safe
+ */
+ unbox_mono_obj: typeof unbox_mono_obj;
+ /**
+ * @deprecated Renamed to conv_string_root
+ */
+ conv_string_rooted: typeof conv_string_root;
+ js_string_to_mono_string_root: typeof js_string_to_mono_string_root;
+ js_typed_array_to_array_root: typeof js_typed_array_to_array_root;
+ js_to_mono_obj_root: typeof js_to_mono_obj_root;
+ conv_string_root: typeof conv_string_root;
+ unbox_mono_obj_root: typeof unbox_mono_obj_root;
+ mono_array_root_to_js_array: typeof mono_array_root_to_js_array;
bind_static_method: typeof mono_bind_static_method;
call_assembly_entry_point: typeof mono_call_assembly_entry_point;
- unbox_mono_obj: typeof unbox_mono_obj;
};
declare type BINDINGType = typeof BINDING;
interface DotnetPublicAPI {
diff --git a/src/mono/wasm/runtime/driver.c b/src/mono/wasm/runtime/driver.c
index c5954cf3ef4..929631ec72e 100644
--- a/src/mono/wasm/runtime/driver.c
+++ b/src/mono/wasm/runtime/driver.c
@@ -29,6 +29,7 @@
#include <mono/jit/mono-private-unstable.h>
#include "pinvoke.h"
+#include "gc-common.h"
#ifdef CORE_BINDINGS
void core_initialize_internals ();
@@ -41,7 +42,7 @@ extern void* mono_wasm_invoke_js_blazor (MonoString **exceptionMessage, void *ca
void mono_wasm_enable_debugging (int);
-int mono_wasm_marshal_type_from_mono_type (int mono_type, MonoClass *klass, MonoType *type);
+static int _marshal_type_from_mono_type (int mono_type, MonoClass *klass, MonoType *type);
int mono_wasm_register_root (char *start, size_t size, const char *name);
void mono_wasm_deregister_root (char *addr);
@@ -159,13 +160,19 @@ void mono_gc_deregister_root (char* addr);
EMSCRIPTEN_KEEPALIVE int
mono_wasm_register_root (char *start, size_t size, const char *name)
{
- return mono_gc_register_root (start, size, (MonoGCDescriptor)NULL, MONO_ROOT_SOURCE_EXTERNAL, NULL, name ? name : "mono_wasm_register_root");
+ int result;
+ MONO_ENTER_GC_UNSAFE;
+ result = mono_gc_register_root (start, size, (MonoGCDescriptor)NULL, MONO_ROOT_SOURCE_EXTERNAL, NULL, name ? name : "mono_wasm_register_root");
+ MONO_EXIT_GC_UNSAFE;
+ return result;
}
-EMSCRIPTEN_KEEPALIVE void
+EMSCRIPTEN_KEEPALIVE void
mono_wasm_deregister_root (char *addr)
{
+ MONO_ENTER_GC_UNSAFE;
mono_gc_deregister_root (addr);
+ MONO_EXIT_GC_UNSAFE;
}
#ifdef DRIVER_GEN
@@ -382,6 +389,9 @@ icall_table_lookup_symbol (void *func)
void*
get_native_to_interp (MonoMethod *method, void *extra_arg)
{
+ void *addr;
+
+ MONO_ENTER_GC_UNSAFE;
MonoClass *klass = mono_method_get_class (method);
MonoImage *image = mono_class_get_image (klass);
MonoAssembly *assembly = mono_image_get_assembly (image);
@@ -400,7 +410,8 @@ get_native_to_interp (MonoMethod *method, void *extra_arg)
key [i] = '_';
}
- void *addr = wasm_dl_get_native_to_interp (key, extra_arg);
+ addr = wasm_dl_get_native_to_interp (key, extra_arg);
+ MONO_EXIT_GC_UNSAFE;
return addr;
}
@@ -582,72 +593,109 @@ mono_wasm_assembly_load (const char *name)
EMSCRIPTEN_KEEPALIVE MonoAssembly*
mono_wasm_get_corlib ()
{
- return mono_image_get_assembly (mono_get_corlib());
+ MonoAssembly* result;
+ MONO_ENTER_GC_UNSAFE;
+ result = mono_image_get_assembly (mono_get_corlib());
+ MONO_EXIT_GC_UNSAFE;
+ return result;
}
EMSCRIPTEN_KEEPALIVE MonoClass*
mono_wasm_assembly_find_class (MonoAssembly *assembly, const char *namespace, const char *name)
{
assert (assembly);
- return mono_class_from_name (mono_assembly_get_image (assembly), namespace, name);
+ MonoClass *result;
+ MONO_ENTER_GC_UNSAFE;
+ result = mono_class_from_name (mono_assembly_get_image (assembly), namespace, name);
+ MONO_EXIT_GC_UNSAFE;
+ return result;
}
EMSCRIPTEN_KEEPALIVE MonoMethod*
mono_wasm_assembly_find_method (MonoClass *klass, const char *name, int arguments)
{
assert (klass);
- return mono_class_get_method_from_name (klass, name, arguments);
+ MonoMethod* result;
+ MONO_ENTER_GC_UNSAFE;
+ result = mono_class_get_method_from_name (klass, name, arguments);
+ MONO_EXIT_GC_UNSAFE;
+ return result;
}
EMSCRIPTEN_KEEPALIVE MonoMethod*
-mono_wasm_get_delegate_invoke (MonoObject *delegate)
+mono_wasm_get_delegate_invoke_ref (MonoObject **delegate)
{
- return mono_get_delegate_invoke(mono_object_get_class (delegate));
+ MonoMethod * result;
+ MONO_ENTER_GC_UNSAFE;
+ result = mono_get_delegate_invoke(mono_object_get_class (*delegate));
+ MONO_EXIT_GC_UNSAFE;
+ return result;
}
-EMSCRIPTEN_KEEPALIVE MonoObject*
-mono_wasm_box_primitive (MonoClass *klass, void *value, int value_size)
+EMSCRIPTEN_KEEPALIVE void
+mono_wasm_box_primitive_ref (MonoClass *klass, void *value, int value_size, PPVOLATILE(MonoObject) result)
{
assert (klass);
+ MONO_ENTER_GC_UNSAFE;
MonoType *type = mono_class_get_type (klass);
int alignment;
- if (mono_type_size (type, &alignment) > value_size)
- return NULL;
- // TODO: use mono_value_box_checked and propagate error out
- return mono_value_box (root_domain, klass, value);
+ if (mono_type_size (type, &alignment) <= value_size)
+ // TODO: use mono_value_box_checked and propagate error out
+ store_volatile(result, mono_value_box (root_domain, klass, value));
+
+ MONO_EXIT_GC_UNSAFE;
}
-EMSCRIPTEN_KEEPALIVE MonoObject*
-mono_wasm_invoke_method (MonoMethod *method, MonoObject *this_arg, void *params[], MonoObject **out_exc)
+EMSCRIPTEN_KEEPALIVE void
+mono_wasm_invoke_method_ref (MonoMethod *method, MonoObject **this_arg_in, void *params[], MonoObject **_out_exc, MonoObject **out_result)
{
- MonoObject *exc = NULL;
- MonoObject *res;
-
+ PPVOLATILE(MonoObject) out_exc = _out_exc;
+ PVOLATILE(MonoObject) temp_exc = NULL;
if (out_exc)
*out_exc = NULL;
- res = mono_runtime_invoke (method, this_arg, params, &exc);
- if (exc) {
- if (out_exc)
- *out_exc = exc;
+ else
+ out_exc = &temp_exc;
- MonoObject *exc2 = NULL;
- res = (MonoObject*)mono_object_to_string (exc, &exc2);
+ 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)
- res = (MonoObject*) mono_string_new (root_domain, "Exception Double Fault");
- return res;
+ store_volatile(out_result, (MonoObject*)mono_string_new (root_domain, "Exception Double Fault"));
}
+ MONO_EXIT_GC_UNSAFE;
+}
- MonoMethodSignature *sig = mono_method_signature (method);
- MonoType *type = mono_signature_get_return_type (sig);
- // If the method return type is void return null
- // This gets around a memory access crash when the result return a value when
- // a void method is invoked.
- if (mono_type_get_type (type) == MONO_TYPE_VOID)
- return NULL;
+// deprecated
+MonoObject*
+mono_wasm_invoke_method (MonoMethod *method, MonoObject *this_arg, void *params[], MonoObject **out_exc)
+{
+ PVOLATILE(MonoObject) result = NULL;
+ mono_wasm_invoke_method_ref (method, &this_arg, params, out_exc, (MonoObject **)&result);
+
+ if (result) {
+ MONO_ENTER_GC_UNSAFE;
+ MonoMethodSignature *sig = mono_method_signature (method);
+ MonoType *type = mono_signature_get_return_type (sig);
+ // If the method return type is void return null
+ // This gets around a memory access crash when the result return a value when
+ // a void method is invoked.
+ if (mono_type_get_type (type) == MONO_TYPE_VOID)
+ result = NULL;
+ MONO_EXIT_GC_UNSAFE;
+ }
- return res;
+ return result;
}
EMSCRIPTEN_KEEPALIVE MonoMethod*
@@ -656,10 +704,11 @@ mono_wasm_assembly_get_entry_point (MonoAssembly *assembly)
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)
- return NULL;
+ goto end;
mono_domain_ensure_entry_assembly (root_domain, assembly);
method = mono_get_method (image, entry, NULL);
@@ -676,7 +725,7 @@ mono_wasm_assembly_get_entry_point (MonoAssembly *assembly)
int name_length = strlen (name);
if ((*name != '<') || (name [name_length - 1] != '>'))
- return method;
+ goto end;
MonoClass *klass = mono_method_get_class (method);
assert(klass);
@@ -688,7 +737,8 @@ mono_wasm_assembly_get_entry_point (MonoAssembly *assembly)
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);
- return async_method;
+ method = async_method;
+ goto end;
}
// look for "Name" by trimming the first and last character of "<Name>"
@@ -697,35 +747,48 @@ mono_wasm_assembly_get_entry_point (MonoAssembly *assembly)
free (async_name);
if (async_method != NULL)
- return async_method;
+ method = async_method;
}
+
+ end:
+ MONO_EXIT_GC_UNSAFE;
return method;
}
+// TODO: ref
EMSCRIPTEN_KEEPALIVE char *
mono_wasm_string_get_utf8 (MonoString *str)
{
- return mono_string_to_utf8 (str); //XXX JS is responsible for freeing this
+ char * result;
+ MONO_ENTER_GC_UNSAFE;
+ result = mono_string_to_utf8 (str); //XXX JS is responsible for freeing this
+ MONO_EXIT_GC_UNSAFE;
+ return result;
}
EMSCRIPTEN_KEEPALIVE MonoString *
mono_wasm_string_from_js (const char *str)
{
+ PVOLATILE(MonoString) result = NULL;
+ MONO_ENTER_GC_UNSAFE;
if (str)
- return mono_string_new (root_domain, str);
- else
- return NULL;
+ result = mono_string_new (root_domain, str);
+ MONO_EXIT_GC_UNSAFE;
+ return result;
}
-EMSCRIPTEN_KEEPALIVE MonoString *
-mono_wasm_string_from_utf16 (const mono_unichar2 * chars, int length)
+EMSCRIPTEN_KEEPALIVE void
+mono_wasm_string_from_utf16_ref (const mono_unichar2 * chars, int length, MonoString **result)
{
assert (length >= 0);
- if (chars)
- return mono_string_new_utf16 (root_domain, chars, length);
- else
- return NULL;
+ MONO_ENTER_GC_UNSAFE;
+ if (chars) {
+ mono_gc_wbarrier_generic_store_atomic(result, (MonoObject *)mono_string_new_utf16 (root_domain, chars, length));
+ } else {
+ mono_gc_wbarrier_generic_store_atomic(result, NULL);
+ }
+ MONO_EXIT_GC_UNSAFE;
}
static int
@@ -734,18 +797,20 @@ class_is_task (MonoClass *klass)
if (!klass)
return 0;
+ int result;
+ MONO_ENTER_GC_UNSAFE;
if (!task_class && !resolved_task_class) {
task_class = mono_class_from_name (mono_get_corlib(), "System.Threading.Tasks", "Task");
resolved_task_class = 1;
}
- if (task_class && (klass == task_class || mono_class_is_subclass_of(klass, task_class, 0)))
- return 1;
-
- return 0;
+ result = task_class && (klass == task_class || mono_class_is_subclass_of(klass, task_class, 0));
+ MONO_EXIT_GC_UNSAFE;
+ return result;
}
-MonoClass* mono_get_uri_class(MonoException** exc)
+static MonoClass*
+_get_uri_class(MonoException** exc)
{
MonoAssembly* assembly = mono_wasm_assembly_load ("System");
if (!assembly)
@@ -754,8 +819,10 @@ MonoClass* mono_get_uri_class(MonoException** exc)
return klass;
}
-void mono_wasm_ensure_classes_resolved ()
+static void
+_ensure_classes_resolved ()
{
+ MONO_ENTER_GC_UNSAFE;
if (!datetime_class && !resolved_datetime_class) {
datetime_class = mono_class_from_name (mono_get_corlib(), "System", "DateTime");
resolved_datetime_class = 1;
@@ -765,8 +832,8 @@ void mono_wasm_ensure_classes_resolved ()
resolved_datetimeoffset_class = 1;
}
if (!uri_class && !resolved_uri_class) {
- MonoException** exc = NULL;
- uri_class = mono_get_uri_class(exc);
+ PVOLATILE(MonoException) exc = NULL;
+ uri_class = _get_uri_class((MonoException **)&exc);
resolved_uri_class = 1;
}
if (!safehandle_class && !resolved_safehandle_class) {
@@ -777,10 +844,12 @@ void mono_wasm_ensure_classes_resolved ()
voidtaskresult_class = mono_class_from_name (mono_get_corlib(), "System.Threading.Tasks", "VoidTaskResult");
resolved_voidtaskresult_class = 1;
}
+ MONO_EXIT_GC_UNSAFE;
}
-int
-mono_wasm_marshal_type_from_mono_type (int mono_type, MonoClass *klass, MonoType *type)
+// This must be run inside a GC unsafe region
+static int
+_marshal_type_from_mono_type (int mono_type, MonoClass *klass, MonoType *type)
{
switch (mono_type) {
// case MONO_TYPE_CHAR: prob should be done not as a number?
@@ -843,7 +912,7 @@ mono_wasm_marshal_type_from_mono_type (int mono_type, MonoClass *klass, MonoType
}
}
default:
- mono_wasm_ensure_classes_resolved ();
+ _ensure_classes_resolved ();
if (klass) {
if (klass == datetime_class)
@@ -870,6 +939,7 @@ mono_wasm_marshal_type_from_mono_type (int mono_type, MonoClass *klass, MonoType
}
}
+// FIXME: Ref
EMSCRIPTEN_KEEPALIVE MonoClass *
mono_wasm_get_obj_class (MonoObject *obj)
{
@@ -879,53 +949,50 @@ mono_wasm_get_obj_class (MonoObject *obj)
return mono_object_get_class (obj);
}
-EMSCRIPTEN_KEEPALIVE int
-mono_wasm_get_obj_type (MonoObject *obj)
+// This code runs inside a gc unsafe region
+static int
+_wasm_get_obj_type_ref_impl (PPVOLATILE(MonoObject) obj)
{
- if (!obj)
+ if (!obj || !*obj)
return 0;
/* Process obj before calling into the runtime, class_from_name () can invoke managed code */
- MonoClass *klass = mono_object_get_class (obj);
+ MonoClass *klass = mono_object_get_class (*obj);
if (!klass)
return MARSHAL_ERROR_NULL_CLASS_POINTER;
if ((klass == mono_get_string_class ()) &&
- mono_string_instance_is_interned ((MonoString *)obj))
+ mono_string_instance_is_interned ((MonoString *)*obj))
return MARSHAL_TYPE_STRING_INTERNED;
MonoType *type = mono_class_get_type (klass);
if (!type)
return MARSHAL_ERROR_NULL_TYPE_POINTER;
- obj = NULL;
int mono_type = mono_type_get_type (type);
- return mono_wasm_marshal_type_from_mono_type (mono_type, klass, type);
+ return _marshal_type_from_mono_type (mono_type, klass, type);
}
+// FIXME: Ref
EMSCRIPTEN_KEEPALIVE int
-mono_wasm_try_unbox_primitive_and_get_type (MonoObject *obj, void *result, int result_capacity)
+mono_wasm_get_obj_type (MonoObject *obj)
{
+ int result;
+ MONO_ENTER_GC_UNSAFE;
+ result = _wasm_get_obj_type_ref_impl(&obj);
+ MONO_EXIT_GC_UNSAFE;
+ return result;
+}
+
+// This code runs inside a gc unsafe region
+static int
+_mono_wasm_try_unbox_primitive_and_get_type_ref_impl (PVOLATILE(MonoObject) obj, void *result, int result_capacity) {
void **resultP = result;
int *resultI = result;
int64_t *resultL = result;
float *resultF = result;
double *resultD = result;
- if (result_capacity >= sizeof (int64_t))
- *resultL = 0;
- else if (result_capacity >= sizeof (int))
- *resultI = 0;
-
- if (!result)
- return MARSHAL_ERROR_BUFFER_TOO_SMALL;
-
- if (result_capacity < 16)
- return MARSHAL_ERROR_BUFFER_TOO_SMALL;
-
- if (!obj)
- return MARSHAL_TYPE_NULL;
-
/* Process obj before calling into the runtime, class_from_name () can invoke managed code */
MonoClass *klass = mono_object_get_class (obj);
if (!klass)
@@ -956,7 +1023,7 @@ mono_wasm_try_unbox_primitive_and_get_type (MonoObject *obj, void *result, int r
if (mono_type_generic_inst_is_valuetype (type))
mono_type = MONO_TYPE_VALUETYPE;
}
-
+
// FIXME: We would prefer to unbox once here but it will fail if the value isn't unboxable
switch (mono_type) {
@@ -1004,7 +1071,7 @@ mono_wasm_try_unbox_primitive_and_get_type (MonoObject *obj, void *result, int r
// Check whether this struct has special-case marshaling
// FIXME: Do we need to null out obj before this?
- int marshal_type = mono_wasm_marshal_type_from_mono_type (mono_type, klass, original_type);
+ int marshal_type = _marshal_type_from_mono_type (mono_type, klass, original_type);
if (marshal_type != MARSHAL_TYPE_VT)
return marshal_type;
@@ -1028,21 +1095,47 @@ mono_wasm_try_unbox_primitive_and_get_type (MonoObject *obj, void *result, int r
// HACK: Store the class pointer into the result buffer so our caller doesn't
// have to call back into the native runtime later to get it
*resultP = type;
- obj = NULL;
- int fallbackResultType = mono_wasm_marshal_type_from_mono_type (mono_type, klass, original_type);
+ int fallbackResultType = _marshal_type_from_mono_type (mono_type, klass, original_type);
assert (fallbackResultType != MARSHAL_TYPE_VT);
return fallbackResultType;
}
// We successfully performed a fast unboxing here so use the type information
// matching what we unboxed (i.e. an enum's underlying type instead of its type)
- obj = NULL;
- int resultType = mono_wasm_marshal_type_from_mono_type (mono_type, klass, type);
+ int resultType = _marshal_type_from_mono_type (mono_type, klass, type);
assert (resultType != MARSHAL_TYPE_VT);
return resultType;
}
EMSCRIPTEN_KEEPALIVE int
+mono_wasm_try_unbox_primitive_and_get_type_ref (MonoObject **objRef, void *result, int result_capacity)
+{
+ if (!result)
+ return MARSHAL_ERROR_BUFFER_TOO_SMALL;
+
+ int retval;
+ int *resultI = result;
+ int64_t *resultL = result;
+
+ if (result_capacity >= sizeof (int64_t))
+ *resultL = 0;
+ else if (result_capacity >= sizeof (int))
+ *resultI = 0;
+
+ if (result_capacity < 16)
+ return MARSHAL_ERROR_BUFFER_TOO_SMALL;
+
+ if (!objRef || !(*objRef))
+ return MARSHAL_TYPE_NULL;
+
+ MONO_ENTER_GC_UNSAFE;
+ retval = _mono_wasm_try_unbox_primitive_and_get_type_ref_impl (*objRef, result, result_capacity);
+ MONO_EXIT_GC_UNSAFE;
+ return retval;
+}
+
+// FIXME: Ref
+EMSCRIPTEN_KEEPALIVE int
mono_wasm_array_length (MonoArray *array)
{
return mono_array_length (array);
@@ -1054,10 +1147,29 @@ mono_wasm_array_get (MonoArray *array, int idx)
return mono_array_get (array, MonoObject*, idx);
}
+EMSCRIPTEN_KEEPALIVE void
+mono_wasm_array_get_ref (MonoArray **array, int idx, MonoObject **result)
+{
+ MONO_ENTER_GC_UNSAFE;
+ mono_gc_wbarrier_generic_store_atomic(result, mono_array_get (*array, MonoObject*, idx));
+ MONO_EXIT_GC_UNSAFE;
+}
+
+EMSCRIPTEN_KEEPALIVE void
+mono_wasm_obj_array_new_ref (int size, MonoArray **result)
+{
+ MONO_ENTER_GC_UNSAFE;
+ mono_gc_wbarrier_generic_store_atomic(result, (MonoObject *)mono_array_new (root_domain, mono_get_object_class (), size));
+ MONO_EXIT_GC_UNSAFE;
+}
+
+// Deprecated
EMSCRIPTEN_KEEPALIVE MonoArray*
mono_wasm_obj_array_new (int size)
{
- return mono_array_new (root_domain, mono_get_object_class (), size);
+ PVOLATILE(MonoArray) result = NULL;
+ mono_wasm_obj_array_new_ref(size, (MonoArray **)&result);
+ return result;
}
EMSCRIPTEN_KEEPALIVE void
@@ -1066,10 +1178,20 @@ mono_wasm_obj_array_set (MonoArray *array, int idx, MonoObject *obj)
mono_array_setref (array, idx, obj);
}
-EMSCRIPTEN_KEEPALIVE MonoArray*
-mono_wasm_string_array_new (int size)
+EMSCRIPTEN_KEEPALIVE void
+mono_wasm_obj_array_set_ref (MonoArray **array, int idx, MonoObject **obj)
+{
+ MONO_ENTER_GC_UNSAFE;
+ mono_array_setref (*array, idx, *obj);
+ MONO_EXIT_GC_UNSAFE;
+}
+
+EMSCRIPTEN_KEEPALIVE void
+mono_wasm_string_array_new_ref (int size, MonoArray **result)
{
- return mono_array_new (root_domain, mono_get_string_class (), size);
+ MONO_ENTER_GC_UNSAFE;
+ mono_gc_wbarrier_generic_store_atomic(result, (MonoObject *)mono_array_new (root_domain, mono_get_string_class (), size));
+ MONO_EXIT_GC_UNSAFE;
}
EMSCRIPTEN_KEEPALIVE int
@@ -1108,33 +1230,42 @@ mono_wasm_enable_on_demand_gc (int enable)
mono_wasm_enable_gc = enable ? 1 : 0;
}
-EMSCRIPTEN_KEEPALIVE MonoString *
-mono_wasm_intern_string (MonoString *string)
+EMSCRIPTEN_KEEPALIVE void
+mono_wasm_intern_string_ref (MonoString **string)
{
- return mono_string_intern (string);
+ MONO_ENTER_GC_UNSAFE;
+ mono_gc_wbarrier_generic_store_atomic(string, (MonoObject *)mono_string_intern (*string));
+ MONO_EXIT_GC_UNSAFE;
}
EMSCRIPTEN_KEEPALIVE void
-mono_wasm_string_get_data (
- MonoString *string, mono_unichar2 **outChars, int *outLengthBytes, int *outIsInterned
+mono_wasm_string_get_data_ref (
+ MonoString **string, mono_unichar2 **outChars, int *outLengthBytes, int *outIsInterned
) {
- if (!string) {
+ MONO_ENTER_GC_UNSAFE;
+ if (!string || !(*string)) {
if (outChars)
*outChars = 0;
if (outLengthBytes)
*outLengthBytes = 0;
if (outIsInterned)
*outIsInterned = 1;
- return;
+ } else {
+ if (outChars)
+ *outChars = mono_string_chars (*string);
+ if (outLengthBytes)
+ *outLengthBytes = mono_string_length (*string) * 2;
+ if (outIsInterned)
+ *outIsInterned = mono_string_instance_is_interned (*string);
}
+ MONO_EXIT_GC_UNSAFE;
+}
- if (outChars)
- *outChars = mono_string_chars (string);
- if (outLengthBytes)
- *outLengthBytes = mono_string_length (string) * 2;
- if (outIsInterned)
- *outIsInterned = mono_string_instance_is_interned (string);
- return;
+EMSCRIPTEN_KEEPALIVE void
+mono_wasm_string_get_data (
+ MonoString *string, mono_unichar2 **outChars, int *outLengthBytes, int *outIsInterned
+) {
+ mono_wasm_string_get_data_ref(&string, outChars, outLengthBytes, outIsInterned);
}
EMSCRIPTEN_KEEPALIVE MonoType *
@@ -1142,7 +1273,11 @@ mono_wasm_class_get_type (MonoClass *klass)
{
if (!klass)
return NULL;
- return mono_class_get_type (klass);
+ MonoType *result;
+ MONO_ENTER_GC_UNSAFE;
+ result = mono_class_get_type (klass);
+ MONO_EXIT_GC_UNSAFE;
+ return result;
}
EMSCRIPTEN_KEEPALIVE MonoClass *
@@ -1150,15 +1285,11 @@ mono_wasm_type_get_class (MonoType *type)
{
if (!type)
return NULL;
- return mono_type_get_class (type);
-}
-
-EMSCRIPTEN_KEEPALIVE void *
-mono_wasm_unbox_rooted (MonoObject *obj)
-{
- if (!obj)
- return NULL;
- return mono_object_unbox (obj);
+ MonoClass *result;
+ MONO_ENTER_GC_UNSAFE;
+ result = mono_type_get_class (type);
+ MONO_EXIT_GC_UNSAFE;
+ return result;
}
EMSCRIPTEN_KEEPALIVE char *
@@ -1171,6 +1302,16 @@ mono_wasm_get_type_aqn (MonoType * typePtr) {
return mono_type_get_name_full (typePtr, MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED);
}
+EMSCRIPTEN_KEEPALIVE void
+mono_wasm_write_managed_pointer_unsafe (PPVOLATILE(MonoObject) destination, PVOLATILE(MonoObject) source) {
+ store_volatile(destination, source);
+}
+
+EMSCRIPTEN_KEEPALIVE void
+mono_wasm_copy_managed_pointer (PPVOLATILE(MonoObject) destination, PPVOLATILE(MonoObject) source) {
+ copy_volatile(destination, source);
+}
+
#ifdef ENABLE_AOT_PROFILER
void mono_profiler_init_aot (const char *desc);
diff --git a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js
index 402dd8b15d6..1b8586b0332 100644
--- a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js
+++ b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js
@@ -8,7 +8,7 @@ const DotnetSupportLib = {
$DOTNET: {},
// this line will be placed early on emscripten runtime creation, passing import and export objects into __dotnet_runtime IFFE
// Emscripten uses require function for nodeJS even in ES6 module. We need https://nodejs.org/api/module.html#modulecreaterequirefilename
- // We use dynamic import because there is no "module" module in the browser.
+ // We use dynamic import because there is no "module" module in the browser.
// This is async init of it, note it would become available only after first tick.
// Also fix of scriptDirectory would be delayed
// Emscripten's getBinaryPromise is not async for NodeJs, but we would like to have it async, so we replace it.
@@ -39,7 +39,7 @@ if (ENVIRONMENT_IS_NODE) {
readAsync(wasmBinaryFile, function (response) { resolve(new Uint8Array(/** @type{!ArrayBuffer} */(response))) }, reject)
});
}
-
+
}
catch (err) {
return getBinary(wasmBinaryFile);
@@ -49,7 +49,7 @@ if (ENVIRONMENT_IS_NODE) {
}
}
let __dotnet_exportedAPI = __dotnet_runtime.__initializeImportsAndExports(
- { isESM:true, isGlobal:false, isNode:ENVIRONMENT_IS_NODE, isShell:ENVIRONMENT_IS_SHELL, isWeb:ENVIRONMENT_IS_WEB, locateFile, quit_, ExitStatus, requirePromise:__dotnet_replacements.requirePromise },
+ { isESM:true, isGlobal:false, isNode:ENVIRONMENT_IS_NODE, isShell:ENVIRONMENT_IS_SHELL, isWeb:ENVIRONMENT_IS_WEB, locateFile, quit_, ExitStatus, requirePromise:__dotnet_replacements.requirePromise },
{ mono:MONO, binding:BINDING, internal:INTERNAL, module:Module },
__dotnet_replacements);
readAsync = __dotnet_replacements.readAsync;
@@ -81,22 +81,22 @@ const linked_functions = [
// corebindings.c
"mono_wasm_invoke_js_with_args",
- "mono_wasm_get_object_property",
- "mono_wasm_set_object_property",
- "mono_wasm_get_by_index",
- "mono_wasm_set_by_index",
- "mono_wasm_get_global_object",
- "mono_wasm_create_cs_owned_object",
+ "mono_wasm_get_object_property_ref",
+ "mono_wasm_set_object_property_ref",
+ "mono_wasm_get_by_index_ref",
+ "mono_wasm_set_by_index_ref",
+ "mono_wasm_get_global_object_ref",
+ "mono_wasm_create_cs_owned_object_ref",
"mono_wasm_release_cs_owned_object",
- "mono_wasm_typed_array_to_array",
- "mono_wasm_typed_array_copy_to",
- "mono_wasm_typed_array_from",
- "mono_wasm_typed_array_copy_from",
+ "mono_wasm_typed_array_to_array_ref",
+ "mono_wasm_typed_array_copy_to_ref",
+ "mono_wasm_typed_array_from_ref",
+ "mono_wasm_typed_array_copy_from_ref",
"mono_wasm_cancel_promise",
- "mono_wasm_web_socket_open",
+ "mono_wasm_web_socket_open_ref",
"mono_wasm_web_socket_send",
"mono_wasm_web_socket_receive",
- "mono_wasm_web_socket_close",
+ "mono_wasm_web_socket_close_ref",
"mono_wasm_web_socket_abort",
"mono_wasm_compile_function",
diff --git a/src/mono/wasm/runtime/exports.ts b/src/mono/wasm/runtime/exports.ts
index 1c1a5cbee49..37578887dde 100644
--- a/src/mono/wasm/runtime/exports.ts
+++ b/src/mono/wasm/runtime/exports.ts
@@ -5,7 +5,7 @@ import ProductVersion from "consts:productVersion";
import Configuration from "consts:configuration";
import {
- mono_wasm_new_root, mono_wasm_release_roots,
+ mono_wasm_new_root, mono_wasm_release_roots, mono_wasm_new_external_root,
mono_wasm_new_root_buffer
} from "./roots";
import {
@@ -38,24 +38,24 @@ import {
} from "./startup";
import { mono_set_timeout, schedule_background_exec } from "./scheduling";
import { mono_wasm_load_icu_data, mono_wasm_get_icudt_name } from "./icu";
-import { conv_string, js_string_to_mono_string, mono_intern_string } from "./strings";
-import { js_to_mono_obj, js_typed_array_to_array, mono_wasm_typed_array_to_array } from "./js-to-cs";
+import { conv_string, conv_string_root, js_string_to_mono_string, js_string_to_mono_string_root, mono_intern_string } from "./strings";
+import { js_to_mono_obj, js_typed_array_to_array, mono_wasm_typed_array_to_array_ref, js_to_mono_obj_root, js_typed_array_to_array_root } from "./js-to-cs";
import {
- mono_array_to_js_array, mono_wasm_create_cs_owned_object, unbox_mono_obj
+ mono_array_to_js_array, mono_wasm_create_cs_owned_object_ref, unbox_mono_obj, unbox_mono_obj_root, mono_array_root_to_js_array
} from "./cs-to-js";
import {
call_static_method, mono_bind_static_method, mono_call_assembly_entry_point,
mono_method_resolve,
mono_wasm_compile_function,
- mono_wasm_get_by_index, mono_wasm_get_global_object, mono_wasm_get_object_property,
+ mono_wasm_get_by_index_ref, mono_wasm_get_global_object_ref, mono_wasm_get_object_property_ref,
mono_wasm_invoke_js,
mono_wasm_invoke_js_blazor,
- mono_wasm_invoke_js_with_args, mono_wasm_set_by_index, mono_wasm_set_object_property
+ mono_wasm_invoke_js_with_args, mono_wasm_set_by_index_ref, mono_wasm_set_object_property_ref
} from "./method-calls";
-import { mono_wasm_typed_array_copy_to, mono_wasm_typed_array_from, mono_wasm_typed_array_copy_from, mono_wasm_load_bytes_into_heap } from "./buffers";
+import { mono_wasm_typed_array_copy_to_ref, mono_wasm_typed_array_from_ref, mono_wasm_typed_array_copy_from_ref, mono_wasm_load_bytes_into_heap } from "./buffers";
import { mono_wasm_cancel_promise } from "./cancelable-promise";
import { mono_wasm_release_cs_owned_object } from "./gc-handles";
-import { mono_wasm_web_socket_open, mono_wasm_web_socket_send, mono_wasm_web_socket_receive, mono_wasm_web_socket_close, mono_wasm_web_socket_abort } from "./web-socket";
+import { mono_wasm_web_socket_open_ref, mono_wasm_web_socket_send, mono_wasm_web_socket_receive, mono_wasm_web_socket_close_ref, mono_wasm_web_socket_abort } from "./web-socket";
import cwraps from "./cwraps";
import {
setI8, setI16, setI32, setI64,
@@ -79,6 +79,7 @@ const MONO = {
mono_load_runtime_and_bcl_args,
mono_wasm_new_root_buffer,
mono_wasm_new_root,
+ mono_wasm_new_external_root,
mono_wasm_release_roots,
mono_run_main,
mono_run_main_and_exit,
@@ -114,16 +115,54 @@ export type MONOType = typeof MONO;
const BINDING = {
//current "public" BINDING API
+ /**
+ * @deprecated Not GC or thread safe
+ */
mono_obj_array_new: cwraps.mono_wasm_obj_array_new,
+ /**
+ * @deprecated Not GC or thread safe
+ */
mono_obj_array_set: cwraps.mono_wasm_obj_array_set,
+ /**
+ * @deprecated Not GC or thread safe
+ */
js_string_to_mono_string,
+ /**
+ * @deprecated Not GC or thread safe
+ */
js_typed_array_to_array,
- js_to_mono_obj,
+ /**
+ * @deprecated Not GC or thread safe
+ */
mono_array_to_js_array,
+ /**
+ * @deprecated Not GC or thread safe
+ */
+ js_to_mono_obj,
+ /**
+ * @deprecated Not GC or thread safe
+ */
conv_string,
+ /**
+ * @deprecated Not GC or thread safe
+ */
+ unbox_mono_obj,
+ /**
+ * @deprecated Renamed to conv_string_root
+ */
+ conv_string_rooted: conv_string_root,
+
+ mono_obj_array_new_ref: cwraps.mono_wasm_obj_array_new_ref,
+ mono_obj_array_set_ref: cwraps.mono_wasm_obj_array_set_ref,
+ js_string_to_mono_string_root,
+ js_typed_array_to_array_root,
+ js_to_mono_obj_root,
+ conv_string_root,
+ unbox_mono_obj_root,
+ mono_array_root_to_js_array,
+
bind_static_method: mono_bind_static_method,
call_assembly_entry_point: mono_call_assembly_entry_point,
- unbox_mono_obj,
};
export type BINDINGType = typeof BINDING;
@@ -281,22 +320,22 @@ export const __linker_exports: any = {
// also keep in sync with corebindings.c
mono_wasm_invoke_js_with_args,
- mono_wasm_get_object_property,
- mono_wasm_set_object_property,
- mono_wasm_get_by_index,
- mono_wasm_set_by_index,
- mono_wasm_get_global_object,
- mono_wasm_create_cs_owned_object,
+ mono_wasm_get_object_property_ref,
+ mono_wasm_set_object_property_ref,
+ mono_wasm_get_by_index_ref,
+ mono_wasm_set_by_index_ref,
+ mono_wasm_get_global_object_ref,
+ mono_wasm_create_cs_owned_object_ref,
mono_wasm_release_cs_owned_object,
- mono_wasm_typed_array_to_array,
- mono_wasm_typed_array_copy_to,
- mono_wasm_typed_array_from,
- mono_wasm_typed_array_copy_from,
+ mono_wasm_typed_array_to_array_ref,
+ mono_wasm_typed_array_copy_to_ref,
+ mono_wasm_typed_array_from_ref,
+ mono_wasm_typed_array_copy_from_ref,
mono_wasm_cancel_promise,
- mono_wasm_web_socket_open,
+ mono_wasm_web_socket_open_ref,
mono_wasm_web_socket_send,
mono_wasm_web_socket_receive,
- mono_wasm_web_socket_close,
+ mono_wasm_web_socket_close_ref,
mono_wasm_web_socket_abort,
mono_wasm_compile_function,
diff --git a/src/mono/wasm/runtime/gc-common.h b/src/mono/wasm/runtime/gc-common.h
new file mode 100644
index 00000000000..ce7235fd773
--- /dev/null
+++ b/src/mono/wasm/runtime/gc-common.h
@@ -0,0 +1,49 @@
+#define PVOLATILE(T) T* volatile
+#define PPVOLATILE(T) T* volatile *
+
+#define gpointer void*
+
+MONO_API MONO_RT_EXTERNAL_ONLY gpointer
+mono_threads_enter_gc_unsafe_region (gpointer* stackdata);
+
+MONO_API MONO_RT_EXTERNAL_ONLY void
+mono_threads_exit_gc_unsafe_region (gpointer cookie, gpointer* stackdata);
+
+MONO_API MONO_RT_EXTERNAL_ONLY void
+mono_threads_assert_gc_unsafe_region (void);
+
+MONO_API MONO_RT_EXTERNAL_ONLY gpointer
+mono_threads_enter_gc_safe_region (gpointer *stackdata);
+
+MONO_API MONO_RT_EXTERNAL_ONLY void
+mono_threads_exit_gc_safe_region (gpointer cookie, gpointer *stackdata);
+
+MONO_API void
+mono_threads_assert_gc_safe_region (void);
+#define MONO_ENTER_GC_UNSAFE \
+ do { \
+ gpointer __dummy; \
+ gpointer __gc_unsafe_cookie = mono_threads_enter_gc_unsafe_region (&__dummy) \
+
+#define MONO_EXIT_GC_UNSAFE \
+ mono_threads_exit_gc_unsafe_region (__gc_unsafe_cookie, &__dummy); \
+ } while (0)
+
+#define MONO_ENTER_GC_SAFE \
+ do { \
+ gpointer __dummy; \
+ gpointer __gc_safe_cookie = mono_threads_enter_gc_safe_region (&__dummy) \
+
+#define MONO_EXIT_GC_SAFE \
+ mono_threads_exit_gc_safe_region (__gc_safe_cookie, &__dummy); \
+ } while (0)
+
+static void
+store_volatile (PPVOLATILE(MonoObject) destination, PVOLATILE(MonoObject) source) {
+ mono_gc_wbarrier_generic_store_atomic((void*)destination, (MonoObject*)source);
+}
+
+static void
+copy_volatile (PPVOLATILE(MonoObject) destination, PPVOLATILE(MonoObject) source) {
+ mono_gc_wbarrier_generic_store_atomic((void*)destination, (MonoObject*)(*source));
+}
diff --git a/src/mono/wasm/runtime/gc-handles.ts b/src/mono/wasm/runtime/gc-handles.ts
index 18e8462021a..8ee1cb3fcc2 100644
--- a/src/mono/wasm/runtime/gc-handles.ts
+++ b/src/mono/wasm/runtime/gc-handles.ts
@@ -2,7 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
import corebindings from "./corebindings";
-import { GCHandle, JSHandle, JSHandleDisposed, JSHandleNull, MonoObject, MonoObjectNull } from "./types";
+import { GCHandle, JSHandle, JSHandleDisposed, JSHandleNull, MonoObjectRef } from "./types";
+import { setU32 } from "./memory";
import { create_weak_ref } from "./weak-ref";
export const _use_finalization_registry = typeof globalThis.FinalizationRegistry === "function";
@@ -24,12 +25,13 @@ export const js_owned_gc_handle_symbol = Symbol.for("wasm js_owned_gc_handle");
export const cs_owned_js_handle_symbol = Symbol.for("wasm cs_owned_js_handle");
-export function get_js_owned_object_by_gc_handle(gc_handle: GCHandle): MonoObject {
+export function get_js_owned_object_by_gc_handle_ref(gc_handle: GCHandle, result: MonoObjectRef): void {
if (!gc_handle) {
- return MonoObjectNull;
+ setU32(result, 0);
+ return;
}
// this is always strong gc_handle
- return corebindings._get_js_owned_object_by_gc_handle(gc_handle);
+ corebindings._get_js_owned_object_by_gc_handle_ref(gc_handle, result);
}
export function mono_wasm_get_jsobj_from_js_handle(js_handle: JSHandle): any {
@@ -40,11 +42,12 @@ export function mono_wasm_get_jsobj_from_js_handle(js_handle: JSHandle): any {
// when should_add_in_flight === true, the JSObject would be temporarily hold by Normal gc_handle, so that it would not get collected during transition to the managed stack.
// its InFlight gc_handle would be freed when the instance arrives to managed side via Interop.Runtime.ReleaseInFlight
-export function get_cs_owned_object_by_js_handle(js_handle: JSHandle, should_add_in_flight: boolean): MonoObject {
+export function get_cs_owned_object_by_js_handle_ref(js_handle: JSHandle, should_add_in_flight: boolean, result: MonoObjectRef): void {
if (js_handle === JSHandleNull || js_handle === JSHandleDisposed) {
- return MonoObjectNull;
+ setU32(result, 0);
+ return;
}
- return corebindings._get_cs_owned_object_by_js_handle(js_handle, should_add_in_flight ? 1 : 0);
+ corebindings._get_cs_owned_object_by_js_handle_ref(js_handle, should_add_in_flight ? 1 : 0, result);
}
export function get_js_obj(js_handle: JSHandle): any {
@@ -94,13 +97,13 @@ export function mono_wasm_get_js_handle(js_obj: any): JSHandle {
return js_handle as JSHandle;
}
-export function mono_wasm_release_cs_owned_object(js_handle: JSHandle): any {
+export function mono_wasm_release_cs_owned_object(js_handle: JSHandle): void {
const obj = _cs_owned_objects_by_js_handle[<any>js_handle];
if (typeof obj !== "undefined" && obj !== null) {
// if this is the global object then do not
// unregister it.
if (globalThis === obj)
- return obj;
+ return;
if (typeof obj[cs_owned_js_handle_symbol] !== "undefined") {
obj[cs_owned_js_handle_symbol] = undefined;
@@ -109,5 +112,4 @@ export function mono_wasm_release_cs_owned_object(js_handle: JSHandle): any {
_cs_owned_objects_by_js_handle[<any>js_handle] = undefined;
_js_handle_free_list.push(js_handle);
}
- return obj;
}
diff --git a/src/mono/wasm/runtime/js-to-cs.ts b/src/mono/wasm/runtime/js-to-cs.ts
index 8bafaec669c..6901c5bacf7 100644
--- a/src/mono/wasm/runtime/js-to-cs.ts
+++ b/src/mono/wasm/runtime/js-to-cs.ts
@@ -3,131 +3,148 @@
import { Module, runtimeHelpers } from "./imports";
import {
- cs_owned_js_handle_symbol, get_cs_owned_object_by_js_handle, get_js_owned_object_by_gc_handle, js_owned_gc_handle_symbol,
+ cs_owned_js_handle_symbol, get_cs_owned_object_by_js_handle_ref,
+ get_js_owned_object_by_gc_handle_ref, js_owned_gc_handle_symbol,
mono_wasm_get_jsobj_from_js_handle, mono_wasm_get_js_handle,
mono_wasm_release_cs_owned_object, _js_owned_object_registry, _use_finalization_registry
} from "./gc-handles";
import corebindings from "./corebindings";
import cwraps from "./cwraps";
-import { mono_wasm_new_root, mono_wasm_release_roots } from "./roots";
-import { wrap_error } from "./method-calls";
-import { js_string_to_mono_string, js_string_to_mono_string_interned } from "./strings";
+import { mono_wasm_new_root, mono_wasm_release_roots, WasmRoot, mono_wasm_new_external_root } from "./roots";
+import { wrap_error_root } from "./method-calls";
+import { js_string_to_mono_string_root, js_string_to_mono_string_interned_root } from "./strings";
import { isThenable } from "./cancelable-promise";
import { has_backing_array_buffer } from "./buffers";
-import { JSHandle, MonoArray, MonoMethod, MonoObject, MonoObjectNull, MonoString, wasm_type_symbol } from "./types";
+import { JSHandle, MonoArray, MonoMethod, MonoObject, MonoObjectNull, wasm_type_symbol, MonoClass, MonoObjectRef } from "./types";
import { setI32, setU32, setF64 } from "./memory";
import { Int32Ptr, TypedArray } from "./types/emscripten";
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
-export function _js_to_mono_uri(should_add_in_flight: boolean, js_obj: any): MonoObject {
+export function _js_to_mono_uri_root(should_add_in_flight: boolean, js_obj: any, result: WasmRoot<MonoObject>): void {
switch (true) {
case js_obj === null:
case typeof js_obj === "undefined":
- return MonoObjectNull;
+ result.clear();
+ return;
case typeof js_obj === "symbol":
case typeof js_obj === "string":
- return corebindings._create_uri(js_obj);
+ corebindings._create_uri_ref(js_obj, result.address);
+ return;
default:
- return _extract_mono_obj(should_add_in_flight, js_obj);
+ _extract_mono_obj_root(should_add_in_flight, js_obj, result);
+ return;
}
}
// this is only used from Blazor
+/**
+ * @deprecated Not GC or thread safe. For blazor use only
+ */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function js_to_mono_obj(js_obj: any): MonoObject {
- return _js_to_mono_obj(false, js_obj);
+ const temp = mono_wasm_new_root<MonoObject>();
+ try {
+ js_to_mono_obj_root(js_obj, temp, false);
+ return temp.value;
+ } finally {
+ temp.release();
+ }
+}
+
+/**
+ * @deprecated Not GC or thread safe
+ */
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+export function _js_to_mono_obj_unsafe(should_add_in_flight: boolean, js_obj: any): MonoObject {
+ const temp = mono_wasm_new_root<MonoObject>();
+ try {
+ js_to_mono_obj_root(js_obj, temp, should_add_in_flight);
+ return temp.value;
+ } finally {
+ temp.release();
+ }
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
-export function _js_to_mono_obj(should_add_in_flight: boolean, js_obj: any): MonoObject {
+export function js_to_mono_obj_root(js_obj: any, result: WasmRoot<MonoObject>, should_add_in_flight: boolean): void {
switch (true) {
case js_obj === null:
case typeof js_obj === "undefined":
- return MonoObjectNull;
+ result.clear();
+ return;
case typeof js_obj === "number": {
- let result = null;
- if ((js_obj | 0) === js_obj)
- result = _box_js_int(js_obj);
- else if ((js_obj >>> 0) === js_obj)
- result = _box_js_uint(js_obj);
- else
- result = _box_js_double(js_obj);
-
- if (!result)
- throw new Error(`Boxing failed for ${js_obj}`);
-
- return result;
- } case typeof js_obj === "string":
- return <any>js_string_to_mono_string(js_obj);
+ let box_class : MonoClass;
+ if ((js_obj | 0) === js_obj) {
+ setI32(runtimeHelpers._box_buffer, js_obj);
+ box_class = runtimeHelpers._class_int32;
+ } else if ((js_obj >>> 0) === js_obj) {
+ setU32(runtimeHelpers._box_buffer, js_obj);
+ box_class = runtimeHelpers._class_uint32;
+ } else {
+ setF64(runtimeHelpers._box_buffer, js_obj);
+ box_class = runtimeHelpers._class_double;
+ }
+
+ cwraps.mono_wasm_box_primitive_ref(box_class, runtimeHelpers._box_buffer, 8, result.address);
+ return;
+ }
+ case typeof js_obj === "string":
+ js_string_to_mono_string_root(js_obj, <any>result);
+ return;
case typeof js_obj === "symbol":
- return <any>js_string_to_mono_string_interned(js_obj);
+ js_string_to_mono_string_interned_root(js_obj, <any>result);
+ return;
case typeof js_obj === "boolean":
- return _box_js_bool(js_obj);
+ setI32(runtimeHelpers._box_buffer, js_obj ? 1 : 0);
+ cwraps.mono_wasm_box_primitive_ref(runtimeHelpers._class_boolean, runtimeHelpers._box_buffer, 4, result.address);
+ return;
case isThenable(js_obj) === true: {
- const { task_ptr } = _wrap_js_thenable_as_task(js_obj);
- // task_ptr above is not rooted, we need to return it to mono without any intermediate mono call which could cause GC
- return task_ptr;
+ _wrap_js_thenable_as_task_root(js_obj, result);
+ return;
}
case js_obj.constructor.name === "Date":
// getTime() is always UTC
- return corebindings._create_date_time(js_obj.getTime());
+ corebindings._create_date_time_ref(js_obj.getTime(), result.address);
+ return;
default:
- return _extract_mono_obj(should_add_in_flight, js_obj);
+ _extract_mono_obj_root(should_add_in_flight, js_obj, result);
+ return;
}
}
-function _extract_mono_obj(should_add_in_flight: boolean, js_obj: any): MonoObject {
+function _extract_mono_obj_root(should_add_in_flight: boolean, js_obj: any, result: WasmRoot<MonoObject>): void {
+ result.clear();
+
if (js_obj === null || typeof js_obj === "undefined")
- return MonoObjectNull;
+ return;
- let result = null;
if (js_obj[js_owned_gc_handle_symbol]) {
// for js_owned_gc_handle we don't want to create new proxy
// since this is strong gc_handle we don't need to in-flight reference
- result = get_js_owned_object_by_gc_handle(js_obj[js_owned_gc_handle_symbol]);
- return result;
+ get_js_owned_object_by_gc_handle_ref(js_obj[js_owned_gc_handle_symbol], result.address);
+ return;
}
if (js_obj[cs_owned_js_handle_symbol]) {
- result = get_cs_owned_object_by_js_handle(js_obj[cs_owned_js_handle_symbol], should_add_in_flight);
+ get_cs_owned_object_by_js_handle_ref(js_obj[cs_owned_js_handle_symbol], should_add_in_flight, result.address);
// It's possible the managed object corresponding to this JS object was collected,
// in which case we need to make a new one.
- if (!result) {
+ // FIXME: This check is not thread safe
+ if (!result.value) {
delete js_obj[cs_owned_js_handle_symbol];
}
}
- if (!result) {
+ // FIXME: This check is not thread safe
+ if (!result.value) {
// Obtain the JS -> C# type mapping.
const wasm_type = js_obj[wasm_type_symbol];
const wasm_type_id = typeof wasm_type === "undefined" ? 0 : wasm_type;
const js_handle = mono_wasm_get_js_handle(js_obj);
- result = corebindings._create_cs_owned_proxy(js_handle, wasm_type_id, should_add_in_flight ? 1 : 0);
+ corebindings._create_cs_owned_proxy_ref(js_handle, wasm_type_id, should_add_in_flight ? 1 : 0, result.address);
}
-
- return result;
-}
-
-function _box_js_int(js_obj: number) {
- setI32(runtimeHelpers._box_buffer, js_obj);
- return cwraps.mono_wasm_box_primitive(runtimeHelpers._class_int32, runtimeHelpers._box_buffer, 4);
-}
-
-function _box_js_uint(js_obj: number) {
- setU32(runtimeHelpers._box_buffer, js_obj);
- return cwraps.mono_wasm_box_primitive(runtimeHelpers._class_uint32, runtimeHelpers._box_buffer, 4);
-}
-
-function _box_js_double(js_obj: number) {
- setF64(runtimeHelpers._box_buffer, js_obj);
- return cwraps.mono_wasm_box_primitive(runtimeHelpers._class_double, runtimeHelpers._box_buffer, 8);
-}
-
-export function _box_js_bool(js_obj: boolean): MonoObject {
- setI32(runtimeHelpers._box_buffer, js_obj ? 1 : 0);
- return cwraps.mono_wasm_box_primitive(runtimeHelpers._class_boolean, runtimeHelpers._box_buffer, 4);
}
// https://github.com/Planeshifter/emscripten-examples/blob/master/01_PassingArrays/sum_post.js
@@ -140,7 +157,7 @@ function js_typedarray_to_heap(typedArray: TypedArray) {
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
-export function js_typed_array_to_array(js_obj: any): MonoArray {
+export function js_typed_array_to_array_root(js_obj: any, result: WasmRoot<MonoArray>): void {
// JavaScript typed arrays are array-like objects and provide a mechanism for accessing
// raw binary data. (...) To achieve maximum flexibility and efficiency, JavaScript typed arrays
// split the implementation into buffers and views. A buffer (implemented by the ArrayBuffer object)
@@ -152,15 +169,28 @@ export function js_typed_array_to_array(js_obj: any): MonoArray {
if (has_backing_array_buffer(js_obj) && js_obj.BYTES_PER_ELEMENT) {
const arrayType = js_obj[wasm_type_symbol];
const heapBytes = js_typedarray_to_heap(js_obj);
- const bufferArray = cwraps.mono_wasm_typed_array_new(<any>heapBytes.byteOffset, js_obj.length, js_obj.BYTES_PER_ELEMENT, arrayType);
+ cwraps.mono_wasm_typed_array_new_ref(<any>heapBytes.byteOffset, js_obj.length, js_obj.BYTES_PER_ELEMENT, arrayType, result.address);
Module._free(<any>heapBytes.byteOffset);
- return bufferArray;
}
else {
throw new Error("Object '" + js_obj + "' is not a typed array");
}
}
+/**
+ * @deprecated Not GC or thread safe
+ */
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+export function js_typed_array_to_array(js_obj: any): MonoArray {
+ const temp = mono_wasm_new_root<MonoArray>();
+ try {
+ js_typed_array_to_array_root(js_obj, temp);
+ return temp.value;
+ } finally {
+ temp.release();
+ }
+}
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/explicit-module-boundary-types
export function js_to_mono_enum(js_obj: any, method: MonoMethod, parmIdx: number): number {
if (typeof (js_obj) !== "number")
@@ -170,9 +200,14 @@ export function js_to_mono_enum(js_obj: any, method: MonoMethod, parmIdx: number
}
export function js_array_to_mono_array(js_array: any[], asString: boolean, should_add_in_flight: boolean): MonoArray {
- const mono_array = asString ? cwraps.mono_wasm_string_array_new(js_array.length) : cwraps.mono_wasm_obj_array_new(js_array.length);
- const arrayRoot = mono_wasm_new_root(mono_array);
+ const arrayRoot = mono_wasm_new_root<MonoArray>();
+ if (asString)
+ cwraps.mono_wasm_string_array_new_ref(js_array.length, arrayRoot.address);
+ else
+ cwraps.mono_wasm_obj_array_new_ref(js_array.length, arrayRoot.address);
const elemRoot = mono_wasm_new_root(MonoObjectNull);
+ const arrayAddress = arrayRoot.address;
+ const elemAddress = elemRoot.address;
try {
for (let i = 0; i < js_array.length; ++i) {
@@ -180,35 +215,34 @@ export function js_array_to_mono_array(js_array: any[], asString: boolean, shoul
if (asString)
obj = obj.toString();
- elemRoot.value = _js_to_mono_obj(should_add_in_flight, obj);
- cwraps.mono_wasm_obj_array_set(arrayRoot.value, i, elemRoot.value);
+ js_to_mono_obj_root(obj, elemRoot, should_add_in_flight);
+ cwraps.mono_wasm_obj_array_set_ref(arrayAddress, i, elemAddress);
}
- return mono_array;
+ return arrayRoot.value;
} finally {
mono_wasm_release_roots(arrayRoot, elemRoot);
}
}
-export function _wrap_js_thenable_as_task(thenable: Promise<any>): {
- task_ptr: MonoObject,
+export function _wrap_js_thenable_as_task_root(thenable: Promise<any>, resultRoot: WasmRoot<MonoObject>): {
then_js_handle: JSHandle,
-
} {
-
- if (!thenable)
+ if (!thenable) {
+ resultRoot.clear();
return <any>null;
+ }
// hold strong JS reference to thenable while in flight
// ideally, this should be hold alive by lifespan of the resulting C# Task, but this is good cheap aproximation
const thenable_js_handle = mono_wasm_get_js_handle(thenable);
- // Note that we do not implement promise/task roundtrip.
+ // Note that we do not implement promise/task roundtrip.
// With more complexity we could recover original instance when this Task is marshaled back to JS.
// TODO optimization: return the tcs.Task on this same call instead of _get_tcs_task
const tcs_gc_handle = corebindings._create_tcs();
thenable.then((result) => {
- corebindings._set_tcs_result(tcs_gc_handle, result);
+ corebindings._set_tcs_result_ref(tcs_gc_handle, result);
// let go of the thenable reference
mono_wasm_release_cs_owned_object(thenable_js_handle);
@@ -232,19 +266,29 @@ export function _wrap_js_thenable_as_task(thenable: Promise<any>): {
_js_owned_object_registry.register(thenable, tcs_gc_handle);
}
+ corebindings._get_tcs_task_ref(tcs_gc_handle, resultRoot.address);
+
// returns raw pointer to tcs.Task
return {
- task_ptr: corebindings._get_tcs_task(tcs_gc_handle),
then_js_handle: thenable_js_handle,
};
}
-export function mono_wasm_typed_array_to_array(js_handle: JSHandle, is_exception: Int32Ptr): MonoArray | MonoString {
- const js_obj = mono_wasm_get_jsobj_from_js_handle(js_handle);
- if (!js_obj) {
- return wrap_error(is_exception, "ERR06: Invalid JS object handle '" + js_handle + "'");
- }
+export function mono_wasm_typed_array_to_array_ref(js_handle: JSHandle, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
+ const resultRoot = mono_wasm_new_external_root<MonoObject>(result_address);
+ try {
+ const js_obj = mono_wasm_get_jsobj_from_js_handle(js_handle);
+ if (!js_obj) {
+ wrap_error_root(is_exception, "ERR06: Invalid JS object handle '" + js_handle + "'", resultRoot);
+ return;
+ }
- // returns pointer to C# array
- return js_typed_array_to_array(js_obj);
+ // returns pointer to C# array
+ // FIXME: ref
+ resultRoot.value = js_typed_array_to_array(js_obj);
+ } catch (exc) {
+ wrap_error_root(is_exception, String(exc), resultRoot);
+ } finally {
+ resultRoot.release();
+ }
}
diff --git a/src/mono/wasm/runtime/memory.ts b/src/mono/wasm/runtime/memory.ts
index c8760a0c7a9..b61b379e87e 100644
--- a/src/mono/wasm/runtime/memory.ts
+++ b/src/mono/wasm/runtime/memory.ts
@@ -37,7 +37,7 @@ export function _release_temp_frame(): void {
alloca_offset = <VoidPtr>alloca_stack.pop();
}
-type _MemOffset = number | VoidPtr | NativePointer;
+type _MemOffset = number | VoidPtr | NativePointer | ManagedPointer;
type _NumberOrPointer = number | VoidPtr | NativePointer | ManagedPointer;
export function setU8(offset: _MemOffset, value: number): void {
diff --git a/src/mono/wasm/runtime/method-binding.ts b/src/mono/wasm/runtime/method-binding.ts
index c7de40542e0..6dc3daec9f7 100644
--- a/src/mono/wasm/runtime/method-binding.ts
+++ b/src/mono/wasm/runtime/method-binding.ts
@@ -2,10 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license.
import { WasmRoot, WasmRootBuffer, mono_wasm_new_root } from "./roots";
-import { MonoClass, MonoMethod, MonoObject, coerceNull, VoidPtrNull, MonoType, MarshalType } from "./types";
+import { MonoClass, MonoMethod, MonoObject, VoidPtrNull, MonoType, MarshalType } from "./types";
import { BINDING, Module, runtimeHelpers } from "./imports";
-import { js_to_mono_enum, _js_to_mono_obj, _js_to_mono_uri } from "./js-to-cs";
-import { js_string_to_mono_string, js_string_to_mono_string_interned } from "./strings";
+import { js_to_mono_enum, js_to_mono_obj_root, _js_to_mono_uri_root } from "./js-to-cs";
+import { js_string_to_mono_string_root, js_string_to_mono_string_interned_root } from "./strings";
import { _unbox_mono_obj_root_with_known_nonprimitive_type } from "./cs-to-js";
import {
_create_temp_frame,
@@ -117,13 +117,15 @@ export function _create_rebindable_named_function(name: string, argumentNames: s
export function _create_primitive_converters(): void {
const result = primitiveConverters;
result.set("m", { steps: [{}], size: 0 });
- result.set("s", { steps: [{ convert: js_string_to_mono_string.bind(BINDING) }], size: 0, needs_root: true });
- result.set("S", { steps: [{ convert: js_string_to_mono_string_interned.bind(BINDING) }], size: 0, needs_root: true });
- // note we also bind first argument to false for both _js_to_mono_obj and _js_to_mono_uri,
+ result.set("s", { steps: [{ convert_root: js_string_to_mono_string_root.bind(BINDING) }], size: 0, needs_root: true });
+ result.set("S", { steps: [{ convert_root: js_string_to_mono_string_interned_root.bind(BINDING) }], size: 0, needs_root: true });
+ // note we also bind first argument to false for both _js_to_mono_obj and _js_to_mono_uri,
// because we will root the reference, so we don't need in-flight reference
// also as those are callback arguments and we don't have platform code which would release the in-flight reference on C# end
- result.set("o", { steps: [{ convert: _js_to_mono_obj.bind(BINDING, false) }], size: 0, needs_root: true });
- result.set("u", { steps: [{ convert: _js_to_mono_uri.bind(BINDING, false) }], size: 0, needs_root: true });
+ result.set("o", { steps: [{ convert_root: js_to_mono_obj_root.bind(BINDING) }], size: 0, needs_root: true });
+ result.set("u", { steps: [{ convert_root: _js_to_mono_uri_root.bind(BINDING, false) }], size: 0, needs_root: true });
+ // ref object aka T&&
+ result.set("R", { steps: [{ convert_root: js_to_mono_obj_root.bind(BINDING), byref: true }], size: 0, needs_root: true });
// result.set ('k', { steps: [{ convert: js_to_mono_enum.bind (this), indirect: 'i64'}], size: 8});
result.set("j", { steps: [{ convert: js_to_mono_enum.bind(BINDING), indirect: "i32" }], size: 8 });
@@ -212,12 +214,12 @@ export function _compile_converter_for_marshal_string(args_marshal: string/*Args
const closure: any = {
Module,
_malloc: Module._malloc,
- mono_wasm_unbox_rooted: wrap_c_function("mono_wasm_unbox_rooted"),
setI32,
setU32,
setF32,
setF64,
- setI64
+ setI64,
+ scratchValueRoot: converter.scratchValueRoot
};
let indirectLocalOffset = 0;
@@ -236,25 +238,40 @@ export function _compile_converter_for_marshal_string(args_marshal: string/*Args
const argKey = "arg" + i;
argumentNames.push(argKey);
- if (step.convert) {
+ if (step.convert_root) {
+ body.push("if (!rootBuffer) throw new Error('no root buffer provided');");
+ // FIXME: Optimize this!!!
+ if (!converter.scratchValueRoot)
+ closure.scratchValueRoot = converter.scratchValueRoot = mono_wasm_new_root<MonoObject>();
+
+ closure[closureKey] = step.convert_root;
+ // Convert the object and store the managed reference in our scratch root
+ body.push(`${closureKey}(${argKey}, scratchValueRoot);`);
+ // Next, copy that managed reference into the arguments root buffer. This is its new permanent home
+ // FIXME: It would be ideal if we could skip this step, perhaps by having an external root point into the arguments root buffer
+ body.push(`let address${i} = rootBuffer.get_address(${i});`);
+ body.push(`scratchValueRoot.copy_to_address(address${i});`);
+ // Now that it's copied into the root buffer we can either pass the address of that root to the callee, or,
+ // if we're feeling particularly GC unsafe and thread hazardous, pass the managed pointer directly.
+ if (step.byref) {
+ body.push(`let ${valueKey} = address${i};`);
+ } else {
+ // FIXME: This is not GC safe! The object could move between now and the method invocation, even though we have
+ // prevented it from being GCed by storing the pointer into a root buffer.
+ body.push(`let ${valueKey} = scratchValueRoot.value;`);
+ }
+ } else if (step.convert) {
closure[closureKey] = step.convert;
body.push(`let ${valueKey} = ${closureKey}(${argKey}, method, ${i});`);
} else {
body.push(`let ${valueKey} = ${argKey};`);
}
- if (step.needs_root) {
+ if (step.needs_root && !step.convert_root) {
body.push("if (!rootBuffer) throw new Error('no root buffer provided');");
body.push(`rootBuffer.set (${i}, ${valueKey});`);
}
- // HACK: needs_unbox indicates that we were passed a pointer to a managed object, and either
- // it was already rooted by our caller or (needs_root = true) by us. Now we can unbox it and
- // pass the raw address of its boxed value into the callee.
- // FIXME: I don't think this is GC safe
- if (step.needs_unbox)
- body.push(`${valueKey} = mono_wasm_unbox_rooted (${valueKey});`);
-
if (step.indirect) {
const offsetText = `(indirectStart + ${indirectLocalOffset})`;
@@ -281,7 +298,7 @@ export function _compile_converter_for_marshal_string(args_marshal: string/*Args
body.push(`setU32(buffer + (${i} * 4), ${offsetText});`);
indirectLocalOffset += step.size!;
} else {
- body.push(`setI32(buffer + (${i} * 4), ${valueKey});`);
+ body.push(`setU32(buffer + (${i} * 4), ${valueKey});`);
indirectLocalOffset += 4;
}
body.push("");
@@ -367,10 +384,9 @@ export function _decide_if_result_is_marshaled(converter: Converter, argc: numbe
}
}
-export function mono_bind_method(method: MonoMethod, this_arg: MonoObject | null, args_marshal: string/*ArgsMarshalString*/, friendly_name: string): Function {
+export function mono_bind_method(method: MonoMethod, this_arg: null, args_marshal: string/*ArgsMarshalString*/, friendly_name: string): Function {
if (typeof (args_marshal) !== "string")
throw new Error("args_marshal argument invalid, expected string");
- this_arg = coerceNull(this_arg);
let converter: Converter | null = null;
if (typeof (args_marshal) === "string") {
@@ -398,11 +414,10 @@ export function mono_bind_method(method: MonoMethod, this_arg: MonoObject | null
_get_buffer_for_method_call,
_handle_exception_for_call,
_teardown_after_call,
- mono_wasm_try_unbox_primitive_and_get_type: wrap_c_function("mono_wasm_try_unbox_primitive_and_get_type"),
+ mono_wasm_try_unbox_primitive_and_get_type_ref: wrap_c_function("mono_wasm_try_unbox_primitive_and_get_type_ref"),
_unbox_mono_obj_root_with_known_nonprimitive_type,
- invoke_method: wrap_c_function("mono_wasm_invoke_method"),
+ invoke_method_ref: wrap_c_function("mono_wasm_invoke_method_ref"),
method,
- this_arg,
token,
unbox_buffer,
unbox_buffer_size,
@@ -475,7 +490,7 @@ export function mono_bind_method(method: MonoMethod, this_arg: MonoObject | null
// The end result is that bound method invocations don't always allocate, so no more nursery GCs. Yay! -kg
body.push(
"",
- "resultRoot.value = invoke_method (method, this_arg, buffer, exceptionRoot.get_address ());",
+ "invoke_method_ref (method, 0, buffer, exceptionRoot.address, resultRoot.address);",
`_handle_exception_for_call (${converterKey}, token, buffer, resultRoot, exceptionRoot, argsRootBuffer);`,
"",
"let resultPtr = resultRoot.value, result = undefined;"
@@ -490,12 +505,12 @@ export function mono_bind_method(method: MonoMethod, this_arg: MonoObject | null
if (!converter.is_result_definitely_unmarshaled)
body.push(
- "if (is_result_marshaled && (resultPtr !== 0)) {",
+ "if (is_result_marshaled) {",
// For the common scenario where the return type is a primitive, we want to try and unbox it directly
// into our existing heap allocation and then read it out of the heap. Doing this all in one operation
// means that we only need to enter a gc safe region twice (instead of 3+ times with the normal,
// slower check-type-and-then-unbox flow which has extra checks since unbox verifies the type).
- " let resultType = mono_wasm_try_unbox_primitive_and_get_type (resultPtr, unbox_buffer, unbox_buffer_size);",
+ " let resultType = mono_wasm_try_unbox_primitive_and_get_type_ref (resultRoot.address, unbox_buffer, unbox_buffer_size);",
" switch (resultType) {",
` case ${MarshalType.INT}:`,
" result = getI32(unbox_buffer); break;",
@@ -510,6 +525,8 @@ export function mono_bind_method(method: MonoMethod, this_arg: MonoObject | null
" result = getI32(unbox_buffer) !== 0; break;",
` case ${MarshalType.CHAR}:`,
" result = String.fromCharCode(getI32(unbox_buffer)); break;",
+ ` case ${MarshalType.NULL}:`,
+ " result = null; break;",
" default:",
" result = _unbox_mono_obj_root_with_known_nonprimitive_type (resultRoot, resultType, unbox_buffer); break;",
" }",
@@ -567,13 +584,16 @@ export type ArgsMarshalString = ""
| `${ArgsMarshal}${ArgsMarshal}${ArgsMarshal}${ArgsMarshal}${_ExtraArgsMarshalOperators}`;
*/
-type ConverterStepIndirects = "u32" | "i32" | "float" | "double" | "i64"
+type ConverterStepIndirects = "u32" | "i32" | "float" | "double" | "i64" | "reference"
export type Converter = {
steps: {
+ // (value: any, method: MonoMethod, arg_index: int)
convert?: boolean | Function;
+ // (value: any, result_root: WasmRoot<MonoObject>)
+ convert_root?: Function;
needs_root?: boolean;
- needs_unbox?: boolean;
+ byref?: boolean;
indirect?: ConverterStepIndirects;
size?: number;
}[];
@@ -586,11 +606,11 @@ export type Converter = {
key?: string;
name?: string;
needs_root?: boolean;
- needs_unbox?: boolean;
compiled_variadic_function?: Function | null;
compiled_function?: Function | null;
scratchRootBuffer?: WasmRootBuffer | null;
scratchBuffer?: VoidPtr;
+ scratchValueRoot?: WasmRoot<MonoObject>;
has_warned_about_signature?: boolean;
convert?: Function | null;
method?: MonoMethod | null;
diff --git a/src/mono/wasm/runtime/method-calls.ts b/src/mono/wasm/runtime/method-calls.ts
index b5aa2bb2f3c..52e9d74afee 100644
--- a/src/mono/wasm/runtime/method-calls.ts
+++ b/src/mono/wasm/runtime/method-calls.ts
@@ -1,23 +1,26 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-import { mono_wasm_new_root, mono_wasm_new_root_buffer, WasmRoot, WasmRootBuffer } from "./roots";
+import { mono_wasm_new_root, mono_wasm_new_root_buffer, WasmRoot, WasmRootBuffer, mono_wasm_new_external_root } from "./roots";
import {
JSHandle, MonoArray, MonoMethod, MonoObject,
MonoObjectNull, MonoString, coerceNull as coerceNull,
- VoidPtrNull, MonoStringNull
+ VoidPtrNull, MonoStringNull, MonoObjectRef,
+ MonoStringRef
} from "./types";
import { BINDING, INTERNAL, Module, MONO, runtimeHelpers } from "./imports";
-import { _mono_array_root_to_js_array, _unbox_mono_obj_root } from "./cs-to-js";
+import { mono_array_root_to_js_array, unbox_mono_obj_root } from "./cs-to-js";
import { get_js_obj, mono_wasm_get_jsobj_from_js_handle } from "./gc-handles";
-import { js_array_to_mono_array, _box_js_bool, _js_to_mono_obj } from "./js-to-cs";
+// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+// @ts-ignore used by unsafe export
+import { js_array_to_mono_array, _js_to_mono_obj_unsafe, js_to_mono_obj_root } from "./js-to-cs";
import {
mono_bind_method,
Converter, _compile_converter_for_marshal_string,
_decide_if_result_is_marshaled, find_method,
BoundMethodToken
} from "./method-binding";
-import { conv_string, js_string_to_mono_string } from "./strings";
+import { conv_string, conv_string_root, js_string_to_mono_string, js_string_to_mono_string_root } from "./strings";
import cwraps from "./cwraps";
import { bindings_lazy_init } from "./startup";
import { _create_temp_frame, _release_temp_frame } from "./memory";
@@ -113,17 +116,16 @@ function _release_buffer_from_method_call(
Module._free(buffer);
}
-function _convert_exception_for_method_call(result: MonoString, exception: MonoObject) {
- if (exception === MonoObjectNull)
+function _convert_exception_for_method_call(result: WasmRoot<MonoString>, exception: WasmRoot<MonoObject>) {
+ if (exception.value === MonoObjectNull)
return null;
- const msg = conv_string(result);
+ const msg = conv_string_root(result);
const err = new Error(msg!); //the convention is that invoke_method ToString () any outgoing exception
// console.warn (`error ${msg} at location ${err.stack});
return err;
}
-
/*
args_marshal is a string with one character per parameter that tells how to marshal it, here are the valid values:
@@ -140,15 +142,23 @@ m: raw mono object. Don't use it unless you know what you're doing
to suppress marshaling of the return value, place '!' at the end of args_marshal, i.e. 'ii!' instead of 'ii'
*/
-export function call_method(method: MonoMethod, this_arg: MonoObject | undefined, args_marshal: string/*ArgsMarshalString*/, args: ArrayLike<any>): any {
+export function call_method_ref(method: MonoMethod, this_arg: WasmRoot<MonoObject> | MonoObjectRef | undefined, args_marshal: string/*ArgsMarshalString*/, args: ArrayLike<any>): any {
// HACK: Sometimes callers pass null or undefined, coerce it to 0 since that's what wasm expects
- this_arg = coerceNull(this_arg);
+ let this_arg_ref : MonoObjectRef | undefined = undefined;
+ if (typeof (this_arg) === "number")
+ this_arg_ref = this_arg;
+ else if (typeof (this_arg) === "object")
+ this_arg_ref = (<any>this_arg).address;
+ else
+ this_arg_ref = <any>coerceNull(this_arg);
// Detect someone accidentally passing the wrong type of value to method
if (typeof method !== "number")
throw new Error(`method must be an address in the native heap, but was '${method}'`);
if (!method)
throw new Error("no method specified");
+ if (typeof (this_arg_ref) !== "number")
+ throw new Error(`this_arg must be a root instance, the address of a root, or undefined, but was ${this_arg}`);
const needs_converter = _verify_args_for_method_call(args_marshal, args);
@@ -170,7 +180,7 @@ export function call_method(method: MonoMethod, this_arg: MonoObject | undefined
buffer = converter.compiled_variadic_function!(scratchBuffer, argsRootBuffer, method, args);
}
- return _call_method_with_converted_args(method, this_arg!, converter, null, buffer, is_result_marshaled, argsRootBuffer);
+ return _call_method_with_converted_args(method, <any>this_arg_ref, converter, null, buffer, is_result_marshaled, argsRootBuffer);
}
@@ -179,7 +189,7 @@ export function _handle_exception_for_call(
buffer: VoidPtr, resultRoot: WasmRoot<MonoString>,
exceptionRoot: WasmRoot<MonoObject>, argsRootBuffer?: WasmRootBuffer
): void {
- const exc = _convert_exception_for_method_call(resultRoot.value, exceptionRoot.value);
+ const exc = _convert_exception_for_method_call(resultRoot, exceptionRoot);
if (!exc)
return;
@@ -195,10 +205,12 @@ function _handle_exception_and_produce_result_for_call(
): any {
_handle_exception_for_call(converter, token, buffer, resultRoot, exceptionRoot, argsRootBuffer);
- let result: any = resultRoot.value;
+ let result: any;
if (is_result_marshaled)
- result = _unbox_mono_obj_root(resultRoot);
+ result = unbox_mono_obj_root(resultRoot);
+ else
+ result = resultRoot.value;
_teardown_after_call(converter, token, buffer, resultRoot, exceptionRoot, argsRootBuffer);
return result;
@@ -214,14 +226,14 @@ export function _teardown_after_call(
_release_buffer_from_method_call(converter, token, buffer);
if (resultRoot) {
- resultRoot.value = 0;
+ resultRoot.clear();
if ((token !== null) && (token.scratchResultRoot === null))
token.scratchResultRoot = resultRoot;
else
resultRoot.release();
}
if (exceptionRoot) {
- exceptionRoot.value = 0;
+ exceptionRoot.clear();
if ((token !== null) && (token.scratchExceptionRoot === null))
token.scratchExceptionRoot = exceptionRoot;
else
@@ -230,12 +242,12 @@ export function _teardown_after_call(
}
function _call_method_with_converted_args(
- method: MonoMethod, this_arg: MonoObject, converter: Converter | undefined,
+ method: MonoMethod, this_arg_ref: MonoObjectRef, converter: Converter | undefined,
token: BoundMethodToken | null, buffer: VoidPtr,
is_result_marshaled: boolean, argsRootBuffer?: WasmRootBuffer
): any {
const resultRoot = mono_wasm_new_root<MonoString>(), exceptionRoot = mono_wasm_new_root<MonoObject>();
- resultRoot.value = <any>cwraps.mono_wasm_invoke_method(method, this_arg, buffer, <any>exceptionRoot.get_address());
+ cwraps.mono_wasm_invoke_method_ref(method, this_arg_ref, buffer, exceptionRoot.address, resultRoot.address);
return _handle_exception_and_produce_result_for_call(converter, token, buffer, resultRoot, exceptionRoot, argsRootBuffer, is_result_marshaled);
}
@@ -245,9 +257,9 @@ export function call_static_method(fqn: string, args: any[], signature: string/*
const method = mono_method_resolve(fqn);
if (typeof signature === "undefined")
- signature = mono_method_get_call_signature(method);
+ signature = mono_method_get_call_signature_ref(method, undefined);
- return call_method(method, undefined, signature, args);
+ return call_method_ref(method, undefined, signature, args);
}
export function mono_bind_static_method(fqn: string, signature?: string/*ArgsMarshalString*/): Function {
@@ -256,7 +268,7 @@ export function mono_bind_static_method(fqn: string, signature?: string/*ArgsMar
const method = mono_method_resolve(fqn);
if (typeof signature === "undefined")
- signature = mono_method_get_call_signature(method);
+ signature = mono_method_get_call_signature_ref(method, undefined);
return mono_bind_method(method, null, signature!, fqn);
}
@@ -273,12 +285,12 @@ export function mono_bind_assembly_entry_point(assembly: string, signature?: str
throw new Error("Could not find entry point for assembly: " + assembly);
if (!signature)
- signature = mono_method_get_call_signature(method);
+ signature = mono_method_get_call_signature_ref(method, undefined);
return async function (...args: any[]) {
if (args.length > 0 && Array.isArray(args[0]))
args[0] = js_array_to_mono_array(args[0], true, false);
- return call_method(method, undefined, signature!, args);
+ return call_method_ref(method, undefined, signature!, args);
};
}
@@ -292,9 +304,9 @@ export function mono_call_assembly_entry_point(assembly: string, args?: any[], s
export function mono_wasm_invoke_js_with_args(js_handle: JSHandle, method_name: MonoString, args: MonoArray, is_exception: Int32Ptr): any {
const argsRoot = mono_wasm_new_root(args), nameRoot = mono_wasm_new_root(method_name);
try {
- const js_name = conv_string(nameRoot.value);
+ const js_name = conv_string_root(nameRoot);
if (!js_name || (typeof (js_name) !== "string")) {
- return wrap_error(is_exception, "ERR12: Invalid method name object '" + nameRoot.value + "'");
+ return wrap_error(is_exception, "ERR12: Invalid method name object @" + nameRoot.value);
}
const obj = get_js_obj(js_handle);
@@ -302,14 +314,17 @@ export function mono_wasm_invoke_js_with_args(js_handle: JSHandle, method_name:
return wrap_error(is_exception, "ERR13: Invalid JS object handle '" + js_handle + "' while invoking '" + js_name + "'");
}
- const js_args = _mono_array_root_to_js_array(argsRoot);
+ const js_args = mono_array_root_to_js_array(argsRoot);
try {
const m = obj[js_name];
if (typeof m === "undefined")
throw new Error("Method: '" + js_name + "' not found for: '" + Object.prototype.toString.call(obj) + "'");
const res = m.apply(obj, js_args);
- return _js_to_mono_obj(true, res);
+
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore caller is unsafe also
+ return _js_to_mono_obj_unsafe(true, res);
} catch (ex) {
return wrap_error(is_exception, ex);
}
@@ -319,48 +334,53 @@ export function mono_wasm_invoke_js_with_args(js_handle: JSHandle, method_name:
}
}
-export function mono_wasm_get_object_property(js_handle: JSHandle, property_name: MonoString, is_exception: Int32Ptr): any {
- const nameRoot = mono_wasm_new_root(property_name);
+export function mono_wasm_get_object_property_ref(js_handle: JSHandle, property_name: MonoStringRef, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
+ const nameRoot = mono_wasm_new_external_root<MonoString>(property_name),
+ resultRoot = mono_wasm_new_external_root<MonoObject>(result_address);
try {
- const js_name = conv_string(nameRoot.value);
+ const js_name = conv_string_root(nameRoot);
if (!js_name) {
- return wrap_error(is_exception, "Invalid property name object '" + nameRoot.value + "'");
+ wrap_error_root(is_exception, "Invalid property name object '" + nameRoot.value + "'", resultRoot);
+ return;
}
const obj = mono_wasm_get_jsobj_from_js_handle(js_handle);
if (!obj) {
- return wrap_error(is_exception, "ERR01: Invalid JS object handle '" + js_handle + "' while geting '" + js_name + "'");
+ wrap_error_root(is_exception, "ERR01: Invalid JS object handle '" + js_handle + "' while geting '" + js_name + "'", resultRoot);
+ return;
}
- try {
- const m = obj[js_name];
-
- return _js_to_mono_obj(true, m);
- } catch (ex) {
- return wrap_error(is_exception, ex);
- }
+ const m = obj[js_name];
+ js_to_mono_obj_root(m, resultRoot, true);
+ } catch (ex) {
+ wrap_error_root(is_exception, ex, resultRoot);
} finally {
+ resultRoot.release();
nameRoot.release();
}
}
-export function mono_wasm_set_object_property(js_handle: JSHandle, property_name: MonoString, value: MonoObject, createIfNotExist: boolean, hasOwnProperty: boolean, is_exception: Int32Ptr): MonoObject {
- const valueRoot = mono_wasm_new_root(value), nameRoot = mono_wasm_new_root(property_name);
+export function mono_wasm_set_object_property_ref(js_handle: JSHandle, property_name: MonoStringRef, value: MonoObjectRef, createIfNotExist: boolean, hasOwnProperty: boolean, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
+ const valueRoot = mono_wasm_new_external_root<MonoObject>(value),
+ nameRoot = mono_wasm_new_external_root<MonoString>(property_name),
+ resultRoot = mono_wasm_new_external_root<MonoObject>(result_address);
try {
- const property = conv_string(nameRoot.value);
+ const property = conv_string_root(nameRoot);
if (!property) {
- return wrap_error(is_exception, "Invalid property name object '" + property_name + "'");
+ wrap_error_root(is_exception, "Invalid property name object '" + property_name + "'", resultRoot);
+ return;
}
const js_obj = mono_wasm_get_jsobj_from_js_handle(js_handle);
if (!js_obj) {
- return wrap_error(is_exception, "ERR02: Invalid JS object handle '" + js_handle + "' while setting '" + property + "'");
+ wrap_error_root(is_exception, "ERR02: Invalid JS object handle '" + js_handle + "' while setting '" + property + "'", resultRoot);
+ return;
}
let result = false;
- const js_value = _unbox_mono_obj_root(valueRoot);
+ const js_value = unbox_mono_obj_root(valueRoot);
if (createIfNotExist) {
js_obj[property] = js_value;
@@ -369,8 +389,10 @@ export function mono_wasm_set_object_property(js_handle: JSHandle, property_name
else {
result = false;
if (!createIfNotExist) {
- if (!Object.prototype.hasOwnProperty.call(js_obj, property))
- return _box_js_bool(false);
+ if (!Object.prototype.hasOwnProperty.call(js_obj, property)) {
+ js_to_mono_obj_root(false, resultRoot, false);
+ return;
+ }
}
if (hasOwnProperty === true) {
if (Object.prototype.hasOwnProperty.call(js_obj, property)) {
@@ -383,52 +405,60 @@ export function mono_wasm_set_object_property(js_handle: JSHandle, property_name
result = true;
}
}
- return _box_js_bool(result);
+ js_to_mono_obj_root(result, resultRoot, false);
+ } catch (ex) {
+ wrap_error_root(is_exception, ex, resultRoot);
} finally {
+ resultRoot.release();
nameRoot.release();
valueRoot.release();
}
}
-export function mono_wasm_get_by_index(js_handle: JSHandle, property_index: number, is_exception: Int32Ptr): MonoObject {
- const obj = mono_wasm_get_jsobj_from_js_handle(js_handle);
- if (!obj) {
- return wrap_error(is_exception, "ERR03: Invalid JS object handle '" + js_handle + "' while getting [" + property_index + "]");
- }
-
+export function mono_wasm_get_by_index_ref(js_handle: JSHandle, property_index: number, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
+ const resultRoot = mono_wasm_new_external_root<MonoObject>(result_address);
try {
+ const obj = mono_wasm_get_jsobj_from_js_handle(js_handle);
+ if (!obj) {
+ wrap_error_root(is_exception, "ERR03: Invalid JS object handle '" + js_handle + "' while getting [" + property_index + "]", resultRoot);
+ return;
+ }
+
const m = obj[property_index];
- return _js_to_mono_obj(true, m);
+ js_to_mono_obj_root(m, resultRoot, true);
} catch (ex) {
- return wrap_error(is_exception, ex);
+ wrap_error_root(is_exception, ex, resultRoot);
+ } finally {
+ resultRoot.release();
}
}
-export function mono_wasm_set_by_index(js_handle: JSHandle, property_index: number, value: MonoObject, is_exception: Int32Ptr): MonoString | true {
- const valueRoot = mono_wasm_new_root(value);
+export function mono_wasm_set_by_index_ref(js_handle: JSHandle, property_index: number, value: MonoObjectRef, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
+ const valueRoot = mono_wasm_new_external_root<MonoObject>(value),
+ resultRoot = mono_wasm_new_external_root<MonoObject>(result_address);
try {
const obj = mono_wasm_get_jsobj_from_js_handle(js_handle);
if (!obj) {
- return wrap_error(is_exception, "ERR04: Invalid JS object handle '" + js_handle + "' while setting [" + property_index + "]");
+ wrap_error_root(is_exception, "ERR04: Invalid JS object handle '" + js_handle + "' while setting [" + property_index + "]", resultRoot);
+ return;
}
- const js_value = _unbox_mono_obj_root(valueRoot);
-
- try {
- obj[property_index] = js_value;
- return true;// TODO check
- } catch (ex) {
- return wrap_error(is_exception, ex);
- }
+ const js_value = unbox_mono_obj_root(valueRoot);
+ obj[property_index] = js_value;
+ resultRoot.clear();
+ } catch (ex) {
+ wrap_error_root(is_exception, ex, resultRoot);
} finally {
+ resultRoot.release();
valueRoot.release();
}
}
-export function mono_wasm_get_global_object(global_name: MonoString, is_exception: Int32Ptr): MonoObject {
- const nameRoot = mono_wasm_new_root(global_name);
+export function mono_wasm_get_global_object_ref(global_name: MonoStringRef, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
+ const nameRoot = mono_wasm_new_external_root<MonoString>(global_name),
+ resultRoot = mono_wasm_new_external_root(result_address);
try {
- const js_name = conv_string(nameRoot.value);
+ const js_name = conv_string_root(nameRoot);
let globalObj;
@@ -441,17 +471,21 @@ export function mono_wasm_get_global_object(global_name: MonoString, is_exceptio
// TODO returning null may be useful when probing for browser features
if (globalObj === null || typeof globalObj === undefined) {
- return wrap_error(is_exception, "Global object '" + js_name + "' not found.");
+ wrap_error_root(is_exception, "Global object '" + js_name + "' not found.", resultRoot);
+ return;
}
- return _js_to_mono_obj(true, globalObj);
+ js_to_mono_obj_root(globalObj, resultRoot, true);
+ } catch (ex) {
+ wrap_error_root(is_exception, ex, resultRoot);
} finally {
+ resultRoot.release();
nameRoot.release();
}
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
-export function wrap_error(is_exception: Int32Ptr | null, ex: any): MonoString {
+function _wrap_error_flag(is_exception: Int32Ptr | null, ex: any): string {
let res = "unknown exception";
if (ex) {
res = ex.toString();
@@ -470,16 +504,29 @@ export function wrap_error(is_exception: Int32Ptr | null, ex: any): MonoString {
if (is_exception) {
Module.setValue(is_exception, 1, "i32");
}
+ return res;
+}
+
+/**
+ * @deprecated Not GC or thread safe
+ */
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+export function wrap_error(is_exception: Int32Ptr | null, ex: any): MonoString {
+ const res = _wrap_error_flag(is_exception, ex);
return js_string_to_mono_string(res)!;
}
-export function mono_method_get_call_signature(method: MonoMethod, mono_obj?: MonoObject): string/*ArgsMarshalString*/ {
- const instanceRoot = mono_wasm_new_root(mono_obj);
- try {
- return call_method(runtimeHelpers.get_call_sig, undefined, "im", [method, instanceRoot.value]);
- } finally {
- instanceRoot.release();
- }
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+export function wrap_error_root(is_exception: Int32Ptr | null, ex: any, result: WasmRoot<MonoObject>): void {
+ const res = _wrap_error_flag(is_exception, ex);
+ js_string_to_mono_string_root(res, <any>result);
+}
+
+export function mono_method_get_call_signature_ref(method: MonoMethod, mono_obj?: WasmRoot<MonoObject>): string/*ArgsMarshalString*/ {
+ return call_method_ref(
+ runtimeHelpers.get_call_sig_ref, undefined, "im",
+ [method, mono_obj ? mono_obj.address : runtimeHelpers._null_root.address]
+ );
}
export function parseFQN(fqn: string)
@@ -536,8 +583,10 @@ export function mono_wasm_invoke_js_blazor(exceptionMessage: Int32Ptr, callInfo:
return blazorExports._internal.invokeJSFromDotNet(callInfo, arg0, arg1, arg2);
} catch (ex: any) {
const exceptionJsString = ex.message + "\n" + ex.stack;
- const exceptionSystemString = cwraps.mono_wasm_string_from_js(exceptionJsString);
- Module.setValue(exceptionMessage, <any>exceptionSystemString, "i32"); // *exceptionMessage = exceptionSystemString;
+ const exceptionRoot = mono_wasm_new_root<MonoString>();
+ js_string_to_mono_string_root(exceptionJsString, exceptionRoot);
+ exceptionRoot.copy_to_address(<any>exceptionMessage);
+ exceptionRoot.release();
return 0;
}
}
@@ -584,7 +633,9 @@ export function mono_wasm_compile_function(code: MonoString, is_exception: Int32
if (!res || typeof res !== "function")
return wrap_error(is_exception, "Code must return an instance of a JavaScript function. Please use `return` statement to return a function.");
Module.setValue(is_exception, 0, "i32");
- return _js_to_mono_obj(true, res);
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore caller is unsafe also
+ return _js_to_mono_obj_unsafe(true, res);
} catch (ex) {
return wrap_error(is_exception, ex);
}
diff --git a/src/mono/wasm/runtime/roots.ts b/src/mono/wasm/runtime/roots.ts
index 2d473fb22ff..9d6ea463acc 100644
--- a/src/mono/wasm/runtime/roots.ts
+++ b/src/mono/wasm/runtime/roots.ts
@@ -4,6 +4,7 @@
import cwraps from "./cwraps";
import { Module } from "./imports";
import { VoidPtr, ManagedPointer, NativePointer } from "./types/emscripten";
+import { MonoObjectRef, MonoObjectRefNull, MonoObject } from "./types";
const maxScratchRoots = 8192;
let _scratch_root_buffer: WasmRootBuffer | null = null;
@@ -57,7 +58,7 @@ export function mono_wasm_new_root_buffer_from_pointer(offset: VoidPtr, capacity
* 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().
*/
-export function mono_wasm_new_external_root<T extends ManagedPointer | NativePointer>(address: VoidPtr): WasmRoot<T> {
+export function mono_wasm_new_external_root<T extends MonoObject>(address: VoidPtr | MonoObjectRef): WasmRoot<T> {
let result: WasmExternalRoot<T>;
if (!address)
@@ -80,7 +81,7 @@ export function mono_wasm_new_external_root<T extends ManagedPointer | NativePoi
* The result object has get() and set(value) methods, along with a .value property.
* When you are done using the root you must call its .release() method.
*/
-export function mono_wasm_new_root<T extends ManagedPointer | NativePointer>(value: T | undefined = undefined): WasmRoot<T> {
+export function mono_wasm_new_root<T extends MonoObject>(value: T | undefined = undefined): WasmRoot<T> {
let result: WasmRoot<T>;
if (_scratch_root_free_instances.length > 0) {
@@ -110,7 +111,7 @@ export function mono_wasm_new_root<T extends ManagedPointer | NativePointer>(val
* mono_wasm_new_roots([a, b, ...]) returns an array of new roots initialized with each element.
* Each root must be released with its release method, or using the mono_wasm_release_roots API.
*/
-export function mono_wasm_new_roots<T extends ManagedPointer | NativePointer>(count_or_values: number | T[]): WasmRoot<T>[] {
+export function mono_wasm_new_roots<T extends MonoObject>(count_or_values: number | T[]): WasmRoot<T>[] {
let result;
if (Array.isArray(count_or_values)) {
@@ -207,7 +208,7 @@ export class WasmRootBuffer {
this._throw_index_out_of_range();
}
- get_address(index: number): NativePointer {
+ get_address(index: number): MonoObjectRef {
this._check_in_range(index);
return <any>this.__offset + (index * 4);
}
@@ -223,21 +224,27 @@ export class WasmRootBuffer {
get(index: number): ManagedPointer {
this._check_in_range(index);
const offset = this.get_address_32(index);
- return <any>Module.HEAP32[offset];
+ return <any>Module.HEAPU32[offset];
}
set(index: number, value: ManagedPointer): ManagedPointer {
- const offset = this.get_address_32(index);
- Module.HEAP32[offset] = <any>value;
+ const address = this.get_address(index);
+ cwraps.mono_wasm_write_managed_pointer_unsafe(address, value);
return value;
}
+ copy_value_from_address(index: number, sourceAddress: MonoObjectRef): void {
+ const destinationAddress = this.get_address(index);
+ cwraps.mono_wasm_copy_managed_pointer(destinationAddress, sourceAddress);
+ }
+
_unsafe_get(index: number): number {
- return Module.HEAP32[this.__offset32 + index];
+ return Module.HEAPU32[this.__offset32 + index];
}
_unsafe_set(index: number, value: ManagedPointer | NativePointer): void {
- Module.HEAP32[this.__offset32 + index] = <any>value;
+ const address = <any>this.__offset + index;
+ cwraps.mono_wasm_write_managed_pointer_unsafe(<VoidPtr><any>address, <ManagedPointer>value);
}
clear(): void {
@@ -260,20 +267,25 @@ export class WasmRootBuffer {
}
}
-export interface WasmRoot<T extends ManagedPointer | NativePointer> {
- get_address(): NativePointer;
+export interface WasmRoot<T extends MonoObject> {
+ get_address(): MonoObjectRef;
get_address_32(): number;
+ get address(): MonoObjectRef;
get(): T;
set(value: T): T;
get value(): T;
set value(value: T);
+ copy_from_address(source: MonoObjectRef): void;
+ copy_to_address(destination: MonoObjectRef): void;
+ copy_from(source: WasmRoot<T>): void;
+ copy_to(destination: WasmRoot<T>): void;
valueOf(): T;
clear(): void;
release(): void;
toString(): string;
}
-class WasmJsOwnedRoot<T extends ManagedPointer | NativePointer> implements WasmRoot<T> {
+class WasmJsOwnedRoot<T extends MonoObject> implements WasmRoot<T> {
private __buffer: WasmRootBuffer;
private __index: number;
@@ -282,7 +294,7 @@ class WasmJsOwnedRoot<T extends ManagedPointer | NativePointer> implements WasmR
this.__index = index;
}
- get_address(): NativePointer {
+ get_address(): MonoObjectRef {
return this.__buffer.get_address(this.__index);
}
@@ -290,16 +302,43 @@ class WasmJsOwnedRoot<T extends ManagedPointer | NativePointer> implements WasmR
return this.__buffer.get_address_32(this.__index);
}
+ get address(): MonoObjectRef {
+ return this.__buffer.get_address(this.__index);
+ }
+
get(): T {
const result = this.__buffer._unsafe_get(this.__index);
return <any>result;
}
set(value: T): T {
- this.__buffer._unsafe_set(this.__index, value);
+ const destinationAddress = this.__buffer.get_address(this.__index);
+ cwraps.mono_wasm_write_managed_pointer_unsafe(destinationAddress, <ManagedPointer>value);
return value;
}
+ copy_from(source: WasmRoot<T>): void {
+ const sourceAddress = source.address;
+ const destinationAddress = this.address;
+ cwraps.mono_wasm_copy_managed_pointer(destinationAddress, sourceAddress);
+ }
+
+ copy_to(destination: WasmRoot<T>): void {
+ const sourceAddress = this.address;
+ const destinationAddress = destination.address;
+ cwraps.mono_wasm_copy_managed_pointer(destinationAddress, sourceAddress);
+ }
+
+ copy_from_address(source: MonoObjectRef): void {
+ const destinationAddress = this.address;
+ cwraps.mono_wasm_copy_managed_pointer(destinationAddress, source);
+ }
+
+ copy_to_address(destination: MonoObjectRef): void {
+ const sourceAddress = this.address;
+ cwraps.mono_wasm_copy_managed_pointer(destination, sourceAddress);
+ }
+
get value(): T {
return this.get();
}
@@ -309,7 +348,7 @@ class WasmJsOwnedRoot<T extends ManagedPointer | NativePointer> implements WasmR
}
valueOf(): T {
- return this.get();
+ throw new Error("Implicit conversion of roots to pointers is no longer supported. Use .value or .address as appropriate");
}
clear(): void {
@@ -332,25 +371,29 @@ class WasmJsOwnedRoot<T extends ManagedPointer | NativePointer> implements WasmR
}
toString(): string {
- return `[root @${this.get_address()}]`;
+ return `[root @${this.address}]`;
}
}
-class WasmExternalRoot<T extends ManagedPointer | NativePointer> implements WasmRoot<T> {
- private __external_address: NativePointer = <any>undefined;
- private __external_address_32: number = <any>undefined;
+class WasmExternalRoot<T extends MonoObject> implements WasmRoot<T> {
+ private __external_address: MonoObjectRef = MonoObjectRefNull;
+ private __external_address_32: number = <any>0;
- constructor(address: NativePointer) {
+ constructor(address: NativePointer | ManagedPointer) {
this._set_address(address);
}
- _set_address(address: NativePointer): void {
- this.__external_address = address;
+ _set_address(address: NativePointer | ManagedPointer): void {
+ this.__external_address = <MonoObjectRef><any>address;
this.__external_address_32 = <number><any>address >>> 2;
}
- get_address(): NativePointer {
- return this.__external_address;
+ get address(): MonoObjectRef {
+ return <MonoObjectRef><any>this.__external_address;
+ }
+
+ get_address(): MonoObjectRef {
+ return <MonoObjectRef><any>this.__external_address;
}
get_address_32(): number {
@@ -363,10 +406,32 @@ class WasmExternalRoot<T extends ManagedPointer | NativePointer> implements Wasm
}
set(value: T): T {
- Module.HEAPU32[this.__external_address_32] = <number><any>value;
+ cwraps.mono_wasm_write_managed_pointer_unsafe(this.__external_address, <ManagedPointer>value);
return value;
}
+ copy_from(source: WasmRoot<T>): void {
+ const sourceAddress = source.address;
+ const destinationAddress = this.__external_address;
+ cwraps.mono_wasm_copy_managed_pointer(destinationAddress, sourceAddress);
+ }
+
+ copy_to(destination: WasmRoot<T>): void {
+ const sourceAddress = this.__external_address;
+ const destinationAddress = destination.address;
+ cwraps.mono_wasm_copy_managed_pointer(destinationAddress, sourceAddress);
+ }
+
+ copy_from_address(source: MonoObjectRef): void {
+ const destinationAddress = this.__external_address;
+ cwraps.mono_wasm_copy_managed_pointer(destinationAddress, source);
+ }
+
+ copy_to_address(destination: MonoObjectRef): void {
+ const sourceAddress = this.__external_address;
+ cwraps.mono_wasm_copy_managed_pointer(destination, sourceAddress);
+ }
+
get value(): T {
return this.get();
}
@@ -376,7 +441,7 @@ class WasmExternalRoot<T extends ManagedPointer | NativePointer> implements Wasm
}
valueOf(): T {
- return this.get();
+ throw new Error("Implicit conversion of roots to pointers is no longer supported. Use .value or .address as appropriate");
}
clear(): void {
@@ -390,6 +455,6 @@ class WasmExternalRoot<T extends ManagedPointer | NativePointer> implements Wasm
}
toString(): string {
- return `[external root @${this.get_address()}]`;
+ return `[external root @${this.address}]`;
}
} \ No newline at end of file
diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts
index 4f60611e5d5..49427b3539c 100644
--- a/src/mono/wasm/runtime/startup.ts
+++ b/src/mono/wasm/runtime/startup.ts
@@ -1,7 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-import { AllAssetEntryTypes, assert, AssetEntry, CharPtrNull, DotnetModule, GlobalizationMode, MonoConfig, MonoConfigError, wasm_type_symbol } from "./types";
+import { AllAssetEntryTypes, assert, AssetEntry, CharPtrNull, DotnetModule, GlobalizationMode, MonoConfig, MonoConfigError, wasm_type_symbol, MonoObject } from "./types";
import { ENVIRONMENT_IS_ESM, ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, INTERNAL, locateFile, Module, MONO, requirePromise, runtimeHelpers } from "./imports";
import cwraps from "./cwraps";
import { mono_wasm_raise_debug_event, mono_wasm_runtime_ready } from "./debug";
@@ -14,6 +14,7 @@ import { find_corlib_class } from "./class-loader";
import { VoidPtr, CharPtr } from "./types/emscripten";
import { DotnetPublicAPI } from "./exports";
import { mono_on_abort } from "./run";
+import { mono_wasm_new_root } from "./roots";
export let runtime_is_initialized_resolve: Function;
export let runtime_is_initialized_reject: Function;
@@ -403,11 +404,14 @@ export function bindings_lazy_init(): void {
if (!runtimeHelpers.wasm_runtime_class)
throw "Can't find " + binding_fqn_class + " class";
- runtimeHelpers.get_call_sig = get_method("GetCallSignature");
- if (!runtimeHelpers.get_call_sig)
- throw "Can't find GetCallSignature method";
+ runtimeHelpers.get_call_sig_ref = get_method("GetCallSignatureRef");
+ if (!runtimeHelpers.get_call_sig_ref)
+ throw "Can't find GetCallSignatureRef method";
_create_primitive_converters();
+
+ runtimeHelpers._box_root = mono_wasm_new_root<MonoObject>();
+ runtimeHelpers._null_root = mono_wasm_new_root<MonoObject>();
}
// Initializes the runtime and loads assemblies, debug information, and other files.
diff --git a/src/mono/wasm/runtime/strings.ts b/src/mono/wasm/runtime/strings.ts
index da38f2c9100..cc322d0d7f6 100644
--- a/src/mono/wasm/runtime/strings.ts
+++ b/src/mono/wasm/runtime/strings.ts
@@ -2,10 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license.
import { mono_wasm_new_root_buffer, WasmRootBuffer } from "./roots";
-import { MonoString, MonoStringNull, } from "./types";
+import { MonoString, MonoStringNull } from "./types";
import { Module } from "./imports";
import cwraps from "./cwraps";
-import { mono_wasm_new_root } from "./roots";
+import { mono_wasm_new_root, WasmRoot } from "./roots";
import { getI32 } from "./memory";
import { NativePointer, CharPtr } from "./types/emscripten";
@@ -15,22 +15,38 @@ export class StringDecoder {
private mono_text_decoder: TextDecoder | undefined | null;
private mono_wasm_string_decoder_buffer: NativePointer | undefined;
- copy(mono_string: MonoString): string | null {
+ init_fields(): void {
if (!this.mono_wasm_string_decoder_buffer) {
this.mono_text_decoder = typeof TextDecoder !== "undefined" ? new TextDecoder("utf-16le") : null;
this.mono_wasm_string_root = mono_wasm_new_root();
this.mono_wasm_string_decoder_buffer = Module._malloc(12);
}
+ }
+
+ /**
+ * @deprecated Not GC or thread safe
+ */
+ copy(mono_string: MonoString): string | null {
+ this.init_fields();
if (mono_string === MonoStringNull)
return null;
this.mono_wasm_string_root.value = mono_string;
+ const result = this.copy_root(this.mono_wasm_string_root);
+ this.mono_wasm_string_root.value = MonoStringNull;
+ return result;
+ }
+
+ copy_root(root: WasmRoot<MonoString>): string | null {
+ this.init_fields();
+ if (root.value === MonoStringNull)
+ return null;
const ppChars = <any>this.mono_wasm_string_decoder_buffer + 0,
pLengthBytes = <any>this.mono_wasm_string_decoder_buffer + 4,
pIsInterned = <any>this.mono_wasm_string_decoder_buffer + 8;
- cwraps.mono_wasm_string_get_data(this.mono_wasm_string_root.value, <any>ppChars, <any>pLengthBytes, <any>pIsInterned);
+ cwraps.mono_wasm_string_get_data_ref(root.address, <any>ppChars, <any>pLengthBytes, <any>pIsInterned);
let result = mono_wasm_empty_string;
const lengthBytes = getI32(pLengthBytes),
@@ -39,20 +55,19 @@ export class StringDecoder {
if (pLengthBytes && pChars) {
if (isInterned) {
- result = interned_string_table.get(<any>this.mono_wasm_string_root.value)!;
+ result = interned_string_table.get(root.value)!;
+ // console.log(`intern table cache hit ${mono_string} ${result.length}`);
}
- if (!result) {
+ if (!isInterned || !result) {
result = this.decode(<any>pChars, <any>pChars + lengthBytes);
if (isInterned) {
// console.log("interned", mono_string, result.length);
- interned_string_table.set(<any>this.mono_wasm_string_root.value, result);
- interned_js_string_table.set(result, <any>this.mono_wasm_string_root.value);
+ interned_string_table.set(root.value, result);
}
}
}
- this.mono_wasm_string_root.value = 0;
return result;
}
@@ -87,26 +102,35 @@ let _interned_string_current_root_buffer_count = 0;
export const string_decoder = new StringDecoder();
export const mono_wasm_empty_string = "";
+/**
+ * @deprecated Not GC or thread safe
+ */
export function conv_string(mono_obj: MonoString): string | null {
return string_decoder.copy(mono_obj);
}
+export function conv_string_root(root: WasmRoot<MonoString>): string | null {
+ return string_decoder.copy_root(root);
+}
+
// Ensures the string is already interned on both the managed and JavaScript sides,
// then returns the interned string value (to provide fast reference comparisons like C#)
export function mono_intern_string(string: string): string {
if (string.length === 0)
return mono_wasm_empty_string;
+ // HACK: This would normally be unsafe, but the return value of js_string_to_mono_string_interned is always an
+ // interned string, so the address will never change and it is safe for us to use the raw pointer. Don't do this though
const ptr = js_string_to_mono_string_interned(string);
const result = interned_string_table.get(ptr);
+ if (!result)
+ throw new Error("internal error: interned_string_table did not contain string after js_string_to_mono_string_interned");
return result!;
}
-function _store_string_in_intern_table(string: string, ptr: MonoString, internIt: boolean) {
- if (!ptr)
+function _store_string_in_intern_table(string: string, root: WasmRoot<MonoString>, internIt: boolean): void {
+ if (!root.value)
throw new Error("null pointer passed to _store_string_in_intern_table");
- else if (typeof (ptr) !== "number")
- throw new Error(`non-pointer passed to _store_string_in_intern_table: ${typeof (ptr)}`);
const internBufferSize = 8192;
@@ -121,78 +145,127 @@ function _store_string_in_intern_table(string: string, ptr: MonoString, internIt
const rootBuffer = _interned_string_current_root_buffer;
const index = _interned_string_current_root_buffer_count++;
- rootBuffer.set(index, ptr);
// Store the managed string into the managed intern table. This can theoretically
// provide a different managed object than the one we passed in, so update our
// pointer (stored in the root) with the result.
if (internIt) {
- ptr = cwraps.mono_wasm_intern_string(ptr);
- rootBuffer.set(index, ptr);
+ cwraps.mono_wasm_intern_string_ref(root.address);
+ if (!root.value)
+ throw new Error("mono_wasm_intern_string_ref produced a null pointer");
}
- if (!ptr)
- throw new Error("mono_wasm_intern_string produced a null pointer");
-
- interned_js_string_table.set(string, ptr);
- interned_string_table.set(ptr, string);
+ interned_js_string_table.set(string, root.value);
+ interned_string_table.set(root.value, string);
if ((string.length === 0) && !_empty_string_ptr)
- _empty_string_ptr = ptr;
+ _empty_string_ptr = root.value;
- return ptr;
+ // Copy the final pointer into our interned string root buffer to ensure the string
+ // remains rooted. TODO: Is this actually necessary?
+ rootBuffer.copy_value_from_address(index, root.address);
}
-export function js_string_to_mono_string_interned(string: string | symbol): MonoString {
+export function js_string_to_mono_string_interned_root(string: string | symbol, result: WasmRoot<MonoString>): void {
const text = (typeof (string) === "symbol")
? (string.description || Symbol.keyFor(string) || "<unknown Symbol>")
: string;
- if ((text.length === 0) && _empty_string_ptr)
- return _empty_string_ptr;
+ if (typeof(text) !== "string") {
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+ throw new Error(`Argument to js_string_to_mono_string_interned must be a string but was ${string}`);
+ }
- let ptr = interned_js_string_table.get(text);
- if (ptr)
- return ptr;
+ if ((text.length === 0) && _empty_string_ptr) {
+ result.set(_empty_string_ptr);
+ return;
+ }
- ptr = js_string_to_mono_string_new(text);
- ptr = _store_string_in_intern_table(text, ptr, true);
+ const ptr = interned_js_string_table.get(text);
+ if (ptr) {
+ result.set(ptr);
+ return;
+ }
- return ptr;
+ js_string_to_mono_string_new_root(text, result);
+ _store_string_in_intern_table(text, result, true);
}
-export function js_string_to_mono_string(string: string): MonoString {
+export function js_string_to_mono_string_root(string: string, result: WasmRoot<MonoString>): void {
+ result.clear();
+
if (string === null)
- return MonoStringNull;
+ return;
else if (typeof (string) === "symbol")
- return js_string_to_mono_string_interned(string);
+ js_string_to_mono_string_interned_root(string, result);
else if (typeof (string) !== "string")
throw new Error("Expected string argument, got " + typeof (string));
+ else if (string.length === 0)
+ // Always use an interned pointer for empty strings
+ js_string_to_mono_string_interned_root(string, result);
+ else {
+ // Looking up large strings in the intern table will require the JS runtime to
+ // potentially hash them and then do full byte-by-byte comparisons, which is
+ // very expensive. Because we can not guarantee it won't happen, try to minimize
+ // the cost of this and prevent performance issues for large strings
+ if (string.length <= 256) {
+ const interned = interned_js_string_table.get(string);
+ if (interned) {
+ result.set(interned);
+ return;
+ }
+ }
- // Always use an interned pointer for empty strings
- if (string.length === 0)
- return js_string_to_mono_string_interned(string);
-
- // Looking up large strings in the intern table will require the JS runtime to
- // potentially hash them and then do full byte-by-byte comparisons, which is
- // very expensive. Because we can not guarantee it won't happen, try to minimize
- // the cost of this and prevent performance issues for large strings
- if (string.length <= 256) {
- const interned = interned_js_string_table.get(string);
- if (interned)
- return interned;
+ js_string_to_mono_string_new_root(string, result);
}
-
- return js_string_to_mono_string_new(string);
}
-export function js_string_to_mono_string_new(string: string): MonoString {
+export function js_string_to_mono_string_new_root(string: string, result: WasmRoot<MonoString>): void {
const buffer = Module._malloc((string.length + 1) * 2);
const buffer16 = (<any>buffer >>> 1) | 0;
for (let i = 0; i < string.length; i++)
Module.HEAP16[buffer16 + i] = string.charCodeAt(i);
Module.HEAP16[buffer16 + string.length] = 0;
- const result = cwraps.mono_wasm_string_from_utf16(<any>buffer, string.length);
+ cwraps.mono_wasm_string_from_utf16_ref(<any>buffer, string.length, result.address);
Module._free(buffer);
- return result;
-} \ No newline at end of file
+}
+
+/**
+ * @deprecated Not GC or thread safe
+ */
+export function js_string_to_mono_string_interned(string: string | symbol): MonoString {
+ const temp = mono_wasm_new_root<MonoString>();
+ try {
+ js_string_to_mono_string_interned_root(string, temp);
+ return temp.value;
+ } finally {
+ temp.release();
+ }
+}
+
+/**
+ * @deprecated Not GC or thread safe
+ */
+export function js_string_to_mono_string(string: string): MonoString {
+ const temp = mono_wasm_new_root<MonoString>();
+ try {
+ js_string_to_mono_string_root(string, temp);
+ return temp.value;
+ } finally {
+ temp.release();
+ }
+}
+
+/**
+ * @deprecated Not GC or thread safe
+ */
+export function js_string_to_mono_string_new(string: string): MonoString {
+ const temp = mono_wasm_new_root<MonoString>();
+ try {
+ js_string_to_mono_string_new_root(string, temp);
+ return temp.value;
+ } finally {
+ temp.release();
+ }
+}
diff --git a/src/mono/wasm/runtime/types.ts b/src/mono/wasm/runtime/types.ts
index abf0c048380..2b85e899538 100644
--- a/src/mono/wasm/runtime/types.ts
+++ b/src/mono/wasm/runtime/types.ts
@@ -16,6 +16,9 @@ export interface MonoObject extends ManagedPointer {
export interface MonoString extends MonoObject {
__brand: "MonoString"
}
+export interface MonoInternedString extends MonoString {
+ __brandString: "MonoInternedString"
+}
export interface MonoClass extends MonoObject {
__brand: "MonoClass"
}
@@ -31,6 +34,15 @@ export interface MonoArray extends MonoObject {
export interface MonoAssembly extends MonoObject {
__brand: "MonoAssembly"
}
+// Pointer to a MonoObject* (i.e. the address of a root)
+export interface MonoObjectRef extends ManagedPointer {
+ __brandMonoObjectRef: "MonoObjectRef"
+}
+// This exists for signature clarity, we need it to be structurally equivalent
+// so that anything requiring MonoObjectRef will work
+// eslint-disable-next-line @typescript-eslint/no-empty-interface
+export interface MonoStringRef extends MonoObjectRef {
+}
export const MonoMethodNull: MonoMethod = <MonoMethod><any>0;
export const MonoObjectNull: MonoObject = <MonoObject><any>0;
export const MonoArrayNull: MonoArray = <MonoArray><any>0;
@@ -38,6 +50,8 @@ export const MonoAssemblyNull: MonoAssembly = <MonoAssembly><any>0;
export const MonoClassNull: MonoClass = <MonoClass><any>0;
export const MonoTypeNull: MonoType = <MonoType><any>0;
export const MonoStringNull: MonoString = <MonoString><any>0;
+export const MonoObjectRefNull: MonoObjectRef = <MonoObjectRef><any>0;
+export const MonoStringRefNull: MonoStringRef = <MonoStringRef><any>0;
export const JSHandleDisposed: JSHandle = <JSHandle><any>-1;
export const JSHandleNull: JSHandle = <JSHandle><any>0;
export const GCHandleNull: GCHandle = <GCHandle><any>0;
@@ -115,7 +129,7 @@ export const enum AssetBehaviours {
}
export type RuntimeHelpers = {
- get_call_sig: MonoMethod;
+ get_call_sig_ref: MonoMethod;
runtime_namespace: string;
runtime_classname: string;
wasm_runtime_class: MonoClass;
@@ -126,6 +140,9 @@ export type RuntimeHelpers = {
_box_buffer: VoidPtr;
_unbox_buffer: VoidPtr;
+ _box_root: any;
+ // A WasmRoot that is guaranteed to contain 0
+ _null_root: any;
_class_int32: MonoClass;
_class_uint32: MonoClass;
_class_double: MonoClass;
diff --git a/src/mono/wasm/runtime/web-socket.ts b/src/mono/wasm/runtime/web-socket.ts
index e013e867aef..b0be40a2d0c 100644
--- a/src/mono/wasm/runtime/web-socket.ts
+++ b/src/mono/wasm/runtime/web-socket.ts
@@ -1,18 +1,19 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-import { mono_wasm_new_root, WasmRoot } from "./roots";
+import { mono_wasm_new_root, mono_wasm_new_external_root } from "./roots";
+import { setI32 } from "./memory";
import { prevent_timer_throttling } from "./scheduling";
import { Queue } from "./queue";
import { PromiseControl, _create_cancelable_promise } from "./cancelable-promise";
-import { _mono_array_root_to_js_array, _wrap_delegate_root_as_function } from "./cs-to-js";
+import { mono_array_root_to_js_array, _wrap_delegate_root_as_function } from "./cs-to-js";
import { mono_wasm_get_jsobj_from_js_handle, mono_wasm_get_js_handle } from "./gc-handles";
-import { _wrap_js_thenable_as_task } from "./js-to-cs";
-import { wrap_error } from "./method-calls";
-import { conv_string } from "./strings";
-import { JSHandle, MonoArray, MonoObject, MonoObjectNull, MonoString } from "./types";
+import { _wrap_js_thenable_as_task_root } from "./js-to-cs";
+import { wrap_error_root } from "./method-calls";
+import { conv_string_root } from "./strings";
+import { JSHandle, MonoArray, MonoObject, MonoString, MonoObjectRef } from "./types";
import { Module } from "./imports";
-import { Int32Ptr } from "./types/emscripten";
+import { Int32Ptr, VoidPtr } from "./types/emscripten";
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");
@@ -29,17 +30,20 @@ let _text_encoder_utf8: TextEncoder | undefined = undefined;
const ws_send_buffer_blocking_threshold = 65536;
const emptyBuffer = new Uint8Array();
-export function mono_wasm_web_socket_open(uri: MonoString, subProtocols: MonoArray, on_close: MonoObject, web_socket_js_handle: Int32Ptr, thenable_js_handle: Int32Ptr, is_exception: Int32Ptr): MonoObject {
- const uri_root = mono_wasm_new_root(uri);
- const sub_root = mono_wasm_new_root(subProtocols);
- const on_close_root = mono_wasm_new_root(on_close);
+export function mono_wasm_web_socket_open_ref(uri_address: MonoObjectRef, subProtocols: MonoObjectRef, on_close: MonoObjectRef, web_socket_js_handle: Int32Ptr, thenable_js_handle: Int32Ptr, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
+ const result_root = mono_wasm_new_external_root<MonoObject>(result_address);
+ const uri_root = mono_wasm_new_external_root<MonoString>(uri_address);
+ const sub_root = mono_wasm_new_external_root<MonoArray>(subProtocols);
+ const on_close_root = mono_wasm_new_root();
+ on_close_root.copy_from_address(on_close);
try {
- const js_uri = conv_string(uri_root.value);
+ const js_uri = conv_string_root(uri_root);
if (!js_uri) {
- return wrap_error(is_exception, "ERR12: Invalid uri '" + uri_root.value + "'");
+ wrap_error_root(is_exception, "ERR12: Invalid uri '" + uri_root.value + "'", result_root);
+ return;
}
- const js_subs = _mono_array_root_to_js_array(sub_root);
+ const js_subs = mono_array_root_to_js_array(sub_root);
const js_on_close = _wrap_delegate_root_as_function(on_close_root)!;
@@ -77,10 +81,10 @@ export function mono_wasm_web_socket_open(uri: MonoString, subProtocols: MonoArr
// send close to any pending receivers, to wake them
const receive_promise_queue = ws[wasm_ws_pending_receive_promise_queue];
receive_promise_queue.drain((receive_promise_control) => {
- const response_root = receive_promise_control.response_root;
- Module.setValue(<any>response_root.value + 0, 0, "i32");// count
- Module.setValue(<any>response_root.value + 4, 2, "i32");// type:close
- Module.setValue(<any>response_root.value + 8, 1, "i32");// end_of_message: true
+ const response_ptr = receive_promise_control.response_ptr;
+ setI32(<any>response_ptr + 0, 0);// count
+ setI32(<any>response_ptr + 4, 2);// type:close
+ setI32(<any>response_ptr + 8, 1);// end_of_message: true
receive_promise_control.resolve(null);
});
};
@@ -91,24 +95,23 @@ export function mono_wasm_web_socket_open(uri: MonoString, subProtocols: MonoArr
const ws_js_handle = mono_wasm_get_js_handle(ws);
Module.setValue(web_socket_js_handle, <any>ws_js_handle, "i32");
- const { task_ptr, then_js_handle } = _wrap_js_thenable_as_task(promise);
+ const { then_js_handle } = _wrap_js_thenable_as_task_root(promise, result_root);
// task_ptr above is not rooted, we need to return it to mono without any intermediate mono call which could cause GC
Module.setValue(thenable_js_handle, <any>then_js_handle, "i32");
-
- return task_ptr;
}
catch (ex) {
- return wrap_error(is_exception, ex);
+ wrap_error_root(is_exception, ex, result_root);
}
finally {
+ result_root.release();
uri_root.release();
sub_root.release();
on_close_root.release();
}
}
-export function mono_wasm_web_socket_send(webSocket_js_handle: JSHandle, buffer_ptr: MonoObject, offset: number, length: number, message_type: number, end_of_message: boolean, thenable_js_handle: Int32Ptr, is_exception: Int32Ptr): MonoObject {
- const buffer_root = mono_wasm_new_root(buffer_ptr);
+export function mono_wasm_web_socket_send(webSocket_js_handle: JSHandle, buffer_ptr: VoidPtr, offset: number, length: number, message_type: number, end_of_message: boolean, thenable_js_handle: Int32Ptr, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
+ const result_root = mono_wasm_new_external_root<MonoObject>(result_address);
try {
const ws = mono_wasm_get_jsobj_from_js_handle(webSocket_js_handle);
if (!ws)
@@ -118,28 +121,24 @@ export function mono_wasm_web_socket_send(webSocket_js_handle: JSHandle, buffer_
throw new Error("InvalidState: The WebSocket is not connected.");
}
- const whole_buffer = _mono_wasm_web_socket_send_buffering(ws, buffer_root, offset, length, message_type, end_of_message);
+ const whole_buffer = _mono_wasm_web_socket_send_buffering(ws, buffer_ptr, offset, length, message_type, end_of_message);
if (!end_of_message || !whole_buffer) {
- return MonoObjectNull; // we are done buffering synchronously, no promise
+ result_root.clear(); // we are done buffering synchronously, no promise
+ return;
}
- return _mono_wasm_web_socket_send_and_wait(ws, whole_buffer, thenable_js_handle);
+ _mono_wasm_web_socket_send_and_wait(ws, whole_buffer, thenable_js_handle, result_address);
}
catch (ex) {
- return wrap_error(is_exception, ex);
+ wrap_error_root(is_exception, ex, result_root);
}
finally {
- buffer_root.release();
+ result_root.release();
}
}
-export function mono_wasm_web_socket_receive(webSocket_js_handle: JSHandle, buffer_ptr: MonoObject, offset: number, length: number, response_ptr: MonoObject, thenable_js_handle: Int32Ptr, is_exception: Int32Ptr): MonoObject {
- const buffer_root = mono_wasm_new_root(buffer_ptr);
- const response_root = mono_wasm_new_root(response_ptr);
- const release_buffer = () => {
- buffer_root.release();
- response_root.release();
- };
+export function mono_wasm_web_socket_receive(webSocket_js_handle: JSHandle, buffer_ptr: VoidPtr, offset: number, length: number, response_ptr: VoidPtr, thenable_js_handle: Int32Ptr, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
+ const result_root = mono_wasm_new_external_root<MonoObject>(result_address);
try {
const ws = mono_wasm_get_jsobj_from_js_handle(webSocket_js_handle);
@@ -158,42 +157,46 @@ export function mono_wasm_web_socket_receive(webSocket_js_handle: JSHandle, buff
throw new Error("ERR20: Invalid WS state");// assert
}
// finish synchronously
- _mono_wasm_web_socket_receive_buffering(receive_event_queue, buffer_root, offset, length, response_root);
- release_buffer();
+ _mono_wasm_web_socket_receive_buffering(receive_event_queue, buffer_ptr, offset, length, response_ptr);
Module.setValue(thenable_js_handle, 0, "i32");
- return MonoObjectNull;
+ result_root.clear();
+ return;
}
- const { promise, promise_control } = _create_cancelable_promise(release_buffer, release_buffer);
+ const { promise, promise_control } = _create_cancelable_promise(undefined, undefined);
const receive_promise_control = promise_control as ReceivePromiseControl;
- receive_promise_control.buffer_root = buffer_root;
+ receive_promise_control.buffer_ptr = buffer_ptr;
receive_promise_control.buffer_offset = offset;
receive_promise_control.buffer_length = length;
- receive_promise_control.response_root = response_root;
+ receive_promise_control.response_ptr = response_ptr;
receive_promise_queue.enqueue(receive_promise_control);
- const { task_ptr, then_js_handle } = _wrap_js_thenable_as_task(promise);
+ const { then_js_handle } = _wrap_js_thenable_as_task_root(promise, result_root);
// task_ptr above is not rooted, we need to return it to mono without any intermediate mono call which could cause GC
Module.setValue(thenable_js_handle, <any>then_js_handle, "i32");
- return task_ptr;
}
catch (ex) {
- return wrap_error(is_exception, ex);
+ wrap_error_root(is_exception, ex, result_root);
+ }
+ finally {
+ result_root.release();
}
}
-export function mono_wasm_web_socket_close(webSocket_js_handle: JSHandle, code: number, reason: MonoString, wait_for_close_received: boolean, thenable_js_handle: Int32Ptr, is_exception: Int32Ptr): MonoObject {
- const reason_root = mono_wasm_new_root(reason);
+export function mono_wasm_web_socket_close_ref(webSocket_js_handle: JSHandle, code: number, reason: MonoObjectRef, wait_for_close_received: boolean, thenable_js_handle: Int32Ptr, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
+ const result_root = mono_wasm_new_external_root<MonoObject>(result_address);
+ const reason_root = mono_wasm_new_external_root<MonoString>(reason);
try {
const ws = mono_wasm_get_jsobj_from_js_handle(webSocket_js_handle);
if (!ws)
throw new Error("ERR19: Invalid JS object handle " + webSocket_js_handle);
if (ws.readyState == WebSocket.CLOSED) {
- return MonoObjectNull;// no promise
+ result_root.clear();
+ return;
}
- const js_reason = conv_string(reason_root.value);
+ const js_reason = conv_string_root(reason_root);
if (wait_for_close_received) {
const { promise, promise_control } = _create_cancelable_promise();
@@ -205,11 +208,11 @@ export function mono_wasm_web_socket_close(webSocket_js_handle: JSHandle, code:
ws.close(code);
}
- const { task_ptr, then_js_handle } = _wrap_js_thenable_as_task(promise);
+ const { then_js_handle } = _wrap_js_thenable_as_task_root(promise, result_root);
// task_ptr above is not rooted, we need to return it to mono without any intermediate mono call which could cause GC
Module.setValue(thenable_js_handle, <any>then_js_handle, "i32");
- return task_ptr;
+ return;
}
else {
if (!mono_wasm_web_socket_close_warning) {
@@ -222,18 +225,21 @@ export function mono_wasm_web_socket_close(webSocket_js_handle: JSHandle, code:
ws.close(code);
}
Module.setValue(thenable_js_handle, 0, "i32");
- return MonoObjectNull;// no promise
+ result_root.clear();
+ return;
}
}
catch (ex) {
- return wrap_error(is_exception, ex);
+ wrap_error_root(is_exception, ex, result_root);
}
finally {
+ result_root.release();
reason_root.release();
}
}
-export function mono_wasm_web_socket_abort(webSocket_js_handle: JSHandle, is_exception: Int32Ptr): MonoObject {
+export function mono_wasm_web_socket_abort(webSocket_js_handle: JSHandle, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
+ const result_root = mono_wasm_new_external_root<MonoObject>(result_address);
try {
const ws = mono_wasm_get_jsobj_from_js_handle(webSocket_js_handle) as WebSocketExtension;
if (!ws)
@@ -258,61 +264,68 @@ export function mono_wasm_web_socket_abort(webSocket_js_handle: JSHandle, is_exc
// this is different from Managed implementation
ws.close(1000, "Connection was aborted.");
- return MonoObjectNull;
+ result_root.clear();
}
catch (ex) {
- return wrap_error(is_exception, ex);
+ wrap_error_root(is_exception, ex, result_root);
}
-}
-
-function _mono_wasm_web_socket_send_and_wait(ws: WebSocketExtension, buffer: Uint8Array | string, thenable_js_handle: Int32Ptr): MonoObject {
- // send and return promise
- ws.send(buffer);
- ws[wasm_ws_pending_send_buffer] = null;
-
- // if the remaining send buffer is small, we don't block so that the throughput doesn't suffer.
- // Otherwise we block so that we apply some backpresure to the application sending large data.
- // this is different from Managed implementation
- if (ws.bufferedAmount < ws_send_buffer_blocking_threshold) {
- return MonoObjectNull; // no promise
+ finally {
+ result_root.release();
}
+}
- // block the promise/task until the browser passed the buffer to OS
- const { promise, promise_control } = _create_cancelable_promise();
- const pending = ws[wasm_ws_pending_send_promises];
- pending.push(promise_control);
+function _mono_wasm_web_socket_send_and_wait(ws: WebSocketExtension, buffer: Uint8Array | string, thenable_js_handle: Int32Ptr, result_address: MonoObjectRef): void {
+ const result_root = mono_wasm_new_external_root<MonoObject>(result_address);
+ try {
+ // send and return promise
+ ws.send(buffer);
+ ws[wasm_ws_pending_send_buffer] = null;
- let nextDelay = 1;
- const polling_check = () => {
- // was it all sent yet ?
- if (ws.bufferedAmount === 0) {
- promise_control.resolve(null);
- }
- else if (ws.readyState != WebSocket.OPEN) {
- // only reject if the data were not sent
- // bufferedAmount does not reset to zero once the connection closes
- promise_control.reject("InvalidState: The WebSocket is not connected.");
- }
- else if (!promise_control.isDone) {
- globalThis.setTimeout(polling_check, nextDelay);
- // exponentially longer delays, up to 1000ms
- nextDelay = Math.min(nextDelay * 1.5, 1000);
+ // if the remaining send buffer is small, we don't block so that the throughput doesn't suffer.
+ // Otherwise we block so that we apply some backpresure to the application sending large data.
+ // this is different from Managed implementation
+ if (ws.bufferedAmount < ws_send_buffer_blocking_threshold) {
+ result_root.clear();
return;
}
- // remove from pending
- const index = pending.indexOf(promise_control);
- if (index > -1) {
- pending.splice(index, 1);
- }
- };
- globalThis.setTimeout(polling_check, 0);
+ // block the promise/task until the browser passed the buffer to OS
+ const { promise, promise_control } = _create_cancelable_promise();
+ const pending = ws[wasm_ws_pending_send_promises];
+ pending.push(promise_control);
+
+ let nextDelay = 1;
+ const polling_check = () => {
+ // was it all sent yet ?
+ if (ws.bufferedAmount === 0) {
+ promise_control.resolve(null);
+ }
+ else if (ws.readyState != WebSocket.OPEN) {
+ // only reject if the data were not sent
+ // bufferedAmount does not reset to zero once the connection closes
+ promise_control.reject("InvalidState: The WebSocket is not connected.");
+ }
+ else if (!promise_control.isDone) {
+ globalThis.setTimeout(polling_check, nextDelay);
+ // exponentially longer delays, up to 1000ms
+ nextDelay = Math.min(nextDelay * 1.5, 1000);
+ return;
+ }
+ // remove from pending
+ const index = pending.indexOf(promise_control);
+ if (index > -1) {
+ pending.splice(index, 1);
+ }
+ };
- const { task_ptr, then_js_handle } = _wrap_js_thenable_as_task(promise);
- // task_ptr above is not rooted, we need to return it to mono without any intermediate mono call which could cause GC
- Module.setValue(thenable_js_handle, <any>then_js_handle, "i32");
+ globalThis.setTimeout(polling_check, 0);
- return task_ptr;
+ const { then_js_handle } = _wrap_js_thenable_as_task_root(promise, result_root);
+ // task_ptr above is not rooted, we need to return it to mono without any intermediate mono call which could cause GC
+ Module.setValue(thenable_js_handle, <any>then_js_handle, "i32");
+ } finally {
+ result_root.release();
+ }
}
function _mono_wasm_web_socket_on_message(ws: WebSocketExtension, event: MessageEvent) {
@@ -348,19 +361,19 @@ function _mono_wasm_web_socket_on_message(ws: WebSocketExtension, event: Message
while (promise_queue.getLength() && event_queue.getLength()) {
const promise_control = promise_queue.dequeue()!;
_mono_wasm_web_socket_receive_buffering(event_queue,
- promise_control.buffer_root, promise_control.buffer_offset, promise_control.buffer_length,
- promise_control.response_root);
+ promise_control.buffer_ptr, promise_control.buffer_offset, promise_control.buffer_length,
+ promise_control.response_ptr);
promise_control.resolve(null);
}
prevent_timer_throttling();
}
-function _mono_wasm_web_socket_receive_buffering(event_queue: Queue<any>, buffer_root: WasmRoot<MonoObject>, buffer_offset: number, buffer_length: number, response_root: WasmRoot<MonoObject>) {
+function _mono_wasm_web_socket_receive_buffering(event_queue: Queue<any>, buffer_ptr: VoidPtr, buffer_offset: number, buffer_length: number, response_ptr: VoidPtr) {
const event = event_queue.peek();
const count = Math.min(buffer_length, event.data.length - event.offset);
if (count > 0) {
- const targetView = Module.HEAPU8.subarray(<any>buffer_root.value + buffer_offset, <any>buffer_root.value + buffer_offset + buffer_length);
+ const targetView = Module.HEAPU8.subarray(<any>buffer_ptr + buffer_offset, <any>buffer_ptr + buffer_offset + buffer_length);
const sourceView = event.data.subarray(event.offset, event.offset + count);
targetView.set(sourceView, 0);
event.offset += count;
@@ -369,15 +382,15 @@ function _mono_wasm_web_socket_receive_buffering(event_queue: Queue<any>, buffer
if (end_of_message) {
event_queue.dequeue();
}
- Module.setValue(<any>response_root.value + 0, count, "i32");
- Module.setValue(<any>response_root.value + 4, event.type, "i32");
- Module.setValue(<any>response_root.value + 8, end_of_message, "i32");
+ setI32(<any>response_ptr + 0, count);
+ setI32(<any>response_ptr + 4, event.type);
+ setI32(<any>response_ptr + 8, end_of_message);
}
-function _mono_wasm_web_socket_send_buffering(ws: WebSocketExtension, buffer_root: WasmRoot<MonoObject>, buffer_offset: number, length: number, message_type: number, end_of_message: boolean): Uint8Array | string | null {
+function _mono_wasm_web_socket_send_buffering(ws: WebSocketExtension, buffer_ptr: VoidPtr, buffer_offset: number, length: number, message_type: number, end_of_message: boolean): Uint8Array | string | null {
let buffer = ws[wasm_ws_pending_send_buffer];
let offset = 0;
- const message_ptr = <any>buffer_root.value + buffer_offset;
+ const message_ptr = <any>buffer_ptr + buffer_offset;
if (buffer) {
offset = ws[wasm_ws_pending_send_buffer_offset];
@@ -456,8 +469,8 @@ type WebSocketExtension = WebSocket & {
}
type ReceivePromiseControl = PromiseControl & {
- response_root: WasmRoot<MonoObject>
- buffer_root: WasmRoot<MonoObject>
+ response_ptr: VoidPtr
+ buffer_ptr: VoidPtr
buffer_offset: number
buffer_length: number
}