Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[browser] reduce use of wrap_error_root #100233

Merged
merged 3 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ internal static unsafe partial class Runtime
public static extern void CancelPromisePost(nint targetNativeTID, nint taskHolderGCHandle);
#else
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern unsafe void BindJSImport(void* signature, out int is_exception, out object result);
public static extern unsafe nint BindJSImportST(void* signature);
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void InvokeJSImportST(int importHandle, nint args);
[MethodImpl(MethodImplOptions.InternalCall)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -449,9 +449,13 @@ internal static unsafe JSFunctionBinding BindJSImportImpl(string functionName, s

#if !FEATURE_WASM_MANAGED_THREADS

Interop.Runtime.BindJSImport(signature.Header, out int isException, out object exceptionMessage);
if (isException != 0)
throw new JSException((string)exceptionMessage);
nint exceptionPtr = Interop.Runtime.BindJSImportST(signature.Header);
if (exceptionPtr != IntPtr.Zero)
{
var message = Marshal.PtrToStringUni(exceptionPtr)!;
Marshal.FreeHGlobal(exceptionPtr);
throw new JSException(message);
}

JSHostImplementation.FreeMethodSignatureBuffer(signature);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ public async Task MultipleImportAsync()
instance1.Dispose();
}

[Fact]
public void MissingImport()
{
var ex = Assert.Throws<JSException>(() => JavaScriptTestHelper.IntentionallyMissingImport());
Assert.Contains("intentionallyMissingImport must be a Function but was undefined", ex.Message);
}

#if !FEATURE_WASM_MANAGED_THREADS // because in MT JSHost.ImportAsync is really async, it will finish before the caller could cancel it
[Fact]
public async Task CancelableImportAsync()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ public static void ConsoleWriteLine([JSMarshalAs<JSType.String>] string message)
[JSImport("delay", "JavaScriptTestHelper")]
public static partial Task Delay(int ms);

[JSImport("intentionallyMissingImport", "JavaScriptTestHelper")]
public static partial void IntentionallyMissingImport();

[JSImport("catch1toString", "JavaScriptTestHelper")]
public static partial string catch1toString(string message, string functionName);

Expand Down
4 changes: 2 additions & 2 deletions src/mono/browser/runtime/corebindings.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ extern void mono_threads_wasm_async_run_in_target_thread_vi (pthread_t target_th
extern void mono_threads_wasm_async_run_in_target_thread_vii (pthread_t target_thread, void (*func) (gpointer, gpointer), gpointer user_data1, gpointer user_data2);
extern void mono_threads_wasm_sync_run_in_target_thread_vii (pthread_t target_thread, void (*func) (gpointer, gpointer), gpointer user_data1, gpointer args);
#else
extern void mono_wasm_bind_js_import (void *signature, int *is_exception, MonoObject **result);
extern void* mono_wasm_bind_js_import_ST (void *signature);
extern void mono_wasm_invoke_jsimport_ST (int function_handle, void *args);
#endif /* DISABLE_THREADS */

Expand Down Expand Up @@ -87,7 +87,7 @@ void bindings_initialize_internals (void)
mono_add_internal_call ("Interop/Runtime::InvokeJSFunctionSend", mono_wasm_invoke_js_function_send);
mono_add_internal_call ("Interop/Runtime::CancelPromisePost", mono_wasm_cancel_promise_post);
#else
mono_add_internal_call ("Interop/Runtime::BindJSImport", mono_wasm_bind_js_import);
mono_add_internal_call ("Interop/Runtime::BindJSImportST", mono_wasm_bind_js_import_ST);
mono_add_internal_call ("Interop/Runtime::InvokeJSImportST", mono_wasm_invoke_jsimport_ST);
#endif /* DISABLE_THREADS */

Expand Down
4 changes: 2 additions & 2 deletions src/mono/browser/runtime/exports-binding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import WasmEnableThreads from "consts:wasmEnableThreads";

import { mono_wasm_debugger_log, mono_wasm_add_dbg_command_received, mono_wasm_set_entrypoint_breakpoint, mono_wasm_fire_debugger_agent_message_with_data, mono_wasm_fire_debugger_agent_message_with_data_to_pause } from "./debug";
import { mono_wasm_release_cs_owned_object } from "./gc-handles";
import { mono_wasm_bind_js_import, mono_wasm_invoke_js_function, mono_wasm_invoke_jsimport_MT, mono_wasm_invoke_jsimport_ST } from "./invoke-js";
import { mono_wasm_bind_js_import_ST, mono_wasm_invoke_js_function, mono_wasm_invoke_jsimport_MT, mono_wasm_invoke_jsimport_ST } from "./invoke-js";
import { mono_interp_tier_prepare_jiterpreter, mono_jiterp_free_method_data_js } from "./jiterpreter";
import { mono_interp_jit_wasm_entry_trampoline, mono_interp_record_interp_entry } from "./jiterpreter-interp-entry";
import { mono_interp_jit_wasm_jit_call_trampoline, mono_interp_invoke_wasm_jit_call_trampoline, mono_interp_flush_jitcall_queue } from "./jiterpreter-jit-call";
Expand Down Expand Up @@ -94,7 +94,7 @@ export const mono_wasm_imports = [
// corebindings.c
mono_wasm_console_clear,
mono_wasm_release_cs_owned_object,
mono_wasm_bind_js_import,
mono_wasm_bind_js_import_ST,
mono_wasm_invoke_js_function,
mono_wasm_invoke_jsimport_ST,
mono_wasm_resolve_or_reject_promise,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { mono_wasm_new_external_root } from "../roots";
import { monoStringToString, stringToUTF16 } from "../strings";
import { MonoObject, MonoObjectRef, MonoString, MonoStringRef } from "../types/internal";
import { Int32Ptr } from "../types/emscripten";
import { wrap_error_root, wrap_no_error_root } from "../invoke-js";
import { wrap_error_root, wrap_no_error_root } from "./helpers";
import { INNER_SEPARATOR, OUTER_SEPARATOR, normalizeSpaces } from "./helpers";

const MONTH_CODE = "MMMM";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { mono_wasm_new_external_root } from "../roots";
import { monoStringToString, utf16ToStringLoop, stringToUTF16 } from "../strings";
import { MonoObject, MonoObjectRef, MonoString, MonoStringRef } from "../types/internal";
import { Int32Ptr } from "../types/emscripten";
import { wrap_error_root, wrap_no_error_root } from "../invoke-js";
import { wrap_error_root, wrap_no_error_root } from "./helpers";
import { localHeapViewU16, setU16_local } from "../memory";
import { isSurrogate } from "./helpers";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { mono_wasm_new_external_root } from "../roots";
import { monoStringToString, utf16ToString } from "../strings";
import { MonoObject, MonoObjectRef, MonoString, MonoStringRef } from "../types/internal";
import { Int32Ptr } from "../types/emscripten";
import { wrap_error_root, wrap_no_error_root } from "../invoke-js";
import { wrap_error_root, wrap_no_error_root } from "./helpers";
import { GraphemeSegmenter } from "./grapheme-segmenter";

const COMPARISON_ERROR = -2;
Expand Down
Original file line number Diff line number Diff line change
@@ -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 { wrap_error_root, wrap_no_error_root } from "../invoke-js";
import { wrap_error_root, wrap_no_error_root } from "./helpers";
import { mono_wasm_new_external_root } from "../roots";
import { monoStringToString, stringToUTF16 } from "../strings";
import { Int32Ptr } from "../types/emscripten";
Expand Down
32 changes: 32 additions & 0 deletions src/mono/browser/runtime/hybrid-globalization/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

import { normalize_exception } from "../invoke-js";
import { receiveWorkerHeapViews, setI32_unchecked } from "../memory";
import { stringToMonoStringRoot } from "../strings";
import { Int32Ptr } from "../types/emscripten";
import { MonoObject, WasmRoot } from "../types/internal";

const SURROGATE_HIGHER_START = "\uD800";
const SURROGATE_HIGHER_END = "\uDBFF";
const SURROGATE_LOWER_START = "\uDC00";
Expand Down Expand Up @@ -42,3 +48,29 @@ export function isSurrogate (str: string, startIdx: number): boolean {
SURROGATE_LOWER_START <= str[startIdx + 1] &&
str[startIdx + 1] <= SURROGATE_LOWER_END;
}

function _wrap_error_flag (is_exception: Int32Ptr | null, ex: any): string {
const res = normalize_exception(ex);
if (is_exception) {
receiveWorkerHeapViews();
setI32_unchecked(is_exception, 1);
}
return res;
}

export function wrap_error_root (is_exception: Int32Ptr | null, ex: any, result: WasmRoot<MonoObject>): void {
const res = _wrap_error_flag(is_exception, ex);
stringToMonoStringRoot(res, <any>result);
}

// TODO replace it with replace it with UTF16 char*, no GC root needed
// https://github.com/dotnet/runtime/issues/98365
export function wrap_no_error_root (is_exception: Int32Ptr | null, result?: WasmRoot<MonoObject>): void {
if (is_exception) {
receiveWorkerHeapViews();
setI32_unchecked(is_exception, 0);
}
if (result) {
result.clear();
}
}
2 changes: 1 addition & 1 deletion src/mono/browser/runtime/hybrid-globalization/locales.ts
Original file line number Diff line number Diff line change
@@ -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 { wrap_error_root, wrap_no_error_root } from "../invoke-js";
import { wrap_error_root, wrap_no_error_root } from "./helpers";
import { mono_wasm_new_external_root } from "../roots";
import { monoStringToString, stringToUTF16 } from "../strings";
import { Int32Ptr } from "../types/emscripten";
Expand Down
60 changes: 22 additions & 38 deletions src/mono/browser/runtime/invoke-js.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,30 @@ import BuildConfiguration from "consts:configuration";

import { marshal_exception_to_cs, bind_arg_marshal_to_cs } from "./marshal-to-cs";
import { get_signature_argument_count, bound_js_function_symbol, get_sig, get_signature_version, get_signature_type, imported_js_function_symbol, get_signature_handle, get_signature_function_name, get_signature_module_name, is_receiver_should_free, get_caller_native_tid, get_sync_done_semaphore_ptr } from "./marshal";
import { setI32_unchecked, receiveWorkerHeapViews, forceThreadMemoryViewRefresh } from "./memory";
import { stringToMonoStringRoot } from "./strings";
import { MonoObject, MonoObjectRef, JSFunctionSignature, JSMarshalerArguments, WasmRoot, BoundMarshalerToJs, JSFnHandle, BoundMarshalerToCs, JSHandle, MarshalerType } from "./types/internal";
import { Int32Ptr } from "./types/emscripten";
import { forceThreadMemoryViewRefresh } from "./memory";
import { JSFunctionSignature, JSMarshalerArguments, BoundMarshalerToJs, JSFnHandle, BoundMarshalerToCs, JSHandle, MarshalerType, VoidPtrNull } from "./types/internal";
import { VoidPtr } from "./types/emscripten";
import { INTERNAL, Module, loaderHelpers, mono_assert, runtimeHelpers } from "./globals";
import { bind_arg_marshal_to_js } from "./marshal-to-js";
import { mono_wasm_new_external_root } from "./roots";
import { mono_log_debug, mono_wasm_symbolicate_string } from "./logging";
import { mono_wasm_get_jsobj_from_js_handle } from "./gc-handles";
import { endMeasure, MeasuredBlock, startMeasure } from "./profiler";
import { wrap_as_cancelable_promise } from "./cancelable-promise";
import { threads_c_functions as tcwraps } from "./cwraps";
import { monoThreadInfo } from "./pthreads";
import { stringToUTF16Ptr } from "./strings";

export const js_import_wrapper_by_fn_handle: Function[] = <any>[null];// 0th slot is dummy, main thread we free them on shutdown. On web worker thread we free them when worker is detached.

export function mono_wasm_bind_js_import (signature: JSFunctionSignature, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
if (WasmEnableThreads) return;
export function mono_wasm_bind_js_import_ST (signature: JSFunctionSignature): VoidPtr {
if (WasmEnableThreads) return VoidPtrNull;
assert_js_interop();
const resultRoot = mono_wasm_new_external_root<MonoObject>(result_address);
try {
bind_js_import(signature);
wrap_no_error_root(is_exception, resultRoot);
return VoidPtrNull;
} catch (ex: any) {
Module.err(ex.toString());
wrap_error_root(is_exception, ex, resultRoot);
} finally {
resultRoot.release();
return stringToUTF16Ptr(normalize_exception(ex));
}
}

Expand All @@ -46,7 +42,13 @@ export function mono_wasm_invoke_jsimport_MT (signature: JSFunctionSignature, ar
let bound_fn = js_import_wrapper_by_fn_handle[function_handle];
if (bound_fn == undefined) {
// it was not bound yet, let's do it now
bound_fn = bind_js_import(signature);
try {
bound_fn = bind_js_import(signature);
} catch (ex: any) {
Module.err(ex.toString());
marshal_exception_to_cs(<any>args, ex);
return;
}
}
mono_assert(bound_fn, () => `Imported function handle expected ${function_handle}`);

Expand Down Expand Up @@ -378,14 +380,18 @@ function mono_wasm_lookup_js_import (function_name: string, js_module_name: stri
for (let i = 0; i < parts.length - 1; i++) {
const part = parts[i];
const newscope = scope[part];
mono_assert(newscope, () => `${part} not found while looking up ${function_name}`);
if (!newscope) {
throw new Error(`${part} not found while looking up ${function_name}`);
}
scope = newscope;
}

const fname = parts[parts.length - 1];
const fn = scope[fname];

mono_assert(typeof (fn) === "function", () => `${function_name} must be a Function but was ${typeof fn}`);
if (typeof (fn) !== "function") {
throw new Error(`${function_name} must be a Function but was ${typeof fn}`);
}

// if the function was already bound to some object it would stay bound to original object. That's good.
return fn.bind(scope);
Expand Down Expand Up @@ -440,7 +446,7 @@ export function dynamic_import (module_name: string, module_url: string): Promis
});
}

function _wrap_error_flag (is_exception: Int32Ptr | null, ex: any): string {
export function normalize_exception (ex: any) {
let res = "unknown exception";
if (ex) {
res = ex.toString();
Expand All @@ -456,31 +462,9 @@ function _wrap_error_flag (is_exception: Int32Ptr | null, ex: any): string {

res = mono_wasm_symbolicate_string(res);
}
if (is_exception) {
receiveWorkerHeapViews();
setI32_unchecked(is_exception, 1);
}
return res;
}

export function wrap_error_root (is_exception: Int32Ptr | null, ex: any, result: WasmRoot<MonoObject>): void {
const res = _wrap_error_flag(is_exception, ex);
stringToMonoStringRoot(res, <any>result);
}

// to set out parameters of icalls
// TODO replace it with replace it with UTF8 char*, no GC root needed
// https://github.com/dotnet/runtime/issues/98365
export function wrap_no_error_root (is_exception: Int32Ptr | null, result?: WasmRoot<MonoObject>): void {
if (is_exception) {
receiveWorkerHeapViews();
setI32_unchecked(is_exception, 0);
}
if (result) {
result.clear();
}
}

export function assert_js_interop (): void {
loaderHelpers.assert_runtime_running();
if (WasmEnableThreads) {
Expand Down
13 changes: 11 additions & 2 deletions src/mono/browser/runtime/strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { MonoString, MonoStringNull, WasmRoot, WasmRootBuffer } from "./types/in
import { Module } from "./globals";
import cwraps from "./cwraps";
import { isSharedArrayBuffer, localHeapViewU8, getU32_local, setU16_local, localHeapViewU32, getU16_local, localHeapViewU16, _zero_region } from "./memory";
import { NativePointer, CharPtr } from "./types/emscripten";
import { NativePointer, CharPtr, VoidPtr } from "./types/emscripten";

export const interned_js_string_table = new Map<string, MonoString>();
export const mono_wasm_empty_string = "";
Expand Down Expand Up @@ -45,7 +45,7 @@ export function stringToUTF8 (str: string): Uint8Array {
}

export function stringToUTF8Ptr (str: string): CharPtr {
const bytes = str.length * 2;
const bytes = (str.length + 1) * 2;
pavelsavara marked this conversation as resolved.
Show resolved Hide resolved
const ptr = Module._malloc(bytes) as any;
_zero_region(ptr, str.length * 2);
const buffer = localHeapViewU8().subarray(ptr, ptr + bytes);
Expand Down Expand Up @@ -108,6 +108,15 @@ export function stringToUTF16 (dstPtr: number, endPtr: number, text: string) {
}
}

export function stringToUTF16Ptr (str: string): VoidPtr {
const bytes = (str.length + 1) * 2;
const ptr = Module._malloc(bytes) as any;
_zero_region(ptr, str.length * 2);
stringToUTF16(ptr, ptr + bytes, str);
return ptr;

}

export function monoStringToString (root: WasmRoot<MonoString>): string | null {
if (root.value === MonoStringNull)
return null;
Expand Down
Loading