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] Make JSExport worker thread friendlier #83680

Merged
merged 2 commits into from
Apr 19, 2023
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 @@ -246,7 +246,9 @@ private static NamespaceDeclarationSyntax GenerateRegSource(
FieldDeclarationSyntax field = FieldDeclaration(VariableDeclaration(PredefinedType(Token(SyntaxKind.BoolKeyword)))
.WithVariables(SingletonSeparatedList(
VariableDeclarator(Identifier("initialized")))))
.WithModifiers(TokenList(Token(SyntaxKind.StaticKeyword)));
.WithModifiers(TokenList(Token(SyntaxKind.StaticKeyword)))
.WithAttributeLists(SingletonList(AttributeList(SingletonSeparatedList(
Attribute(IdentifierName(Constants.ThreadStaticGlobal))))));

MemberDeclarationSyntax method = MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), Identifier(initializerName))
.WithAttributeLists(List(attributes))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ internal static unsafe JSFunctionBinding BindJSFunctionImpl(string functionName,

signature.FnHandle = jsFunctionHandle;

JSHostImplementation.FreeMethodSignatureBuffer(signature);

return signature;
}

Expand All @@ -220,6 +222,9 @@ internal static unsafe JSFunctionBinding BindManagedFunctionImpl(string fullyQua
{
throw new JSException((string)exceptionMessage);
}

JSHostImplementation.FreeMethodSignatureBuffer(signature);

return signature;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,13 @@ public static unsafe JSFunctionBinding GetMethodSignature(ReadOnlySpan<JSMarshal
return signature;
}

public static unsafe void FreeMethodSignatureBuffer(JSFunctionBinding signature)
{
Marshal.FreeHGlobal((nint)signature.Header);
signature.Header = null;
signature.Sigs = null;
}

public static JSObject CreateCSOwnedProxy(nint jsHandle)
{
JSObject? res = null;
Expand Down
2 changes: 2 additions & 0 deletions src/mono/wasm/runtime/invoke-cs.ts
Original file line number Diff line number Diff line change
@@ -1,6 +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 MonoWasmThreads from "consts:monoWasmThreads";
import { Module, runtimeHelpers } from "./imports";
import { bind_arg_marshal_to_cs } from "./marshal-to-cs";
import { marshal_exception_to_js, bind_arg_marshal_to_js } from "./marshal-to-js";
Expand Down Expand Up @@ -299,6 +300,7 @@ export async function mono_wasm_get_assembly_exports(assembly: string): Promise<
}
}
} else {
mono_assert(!MonoWasmThreads, "JSExport is not supported with assemblies generated with Net7 SDK and multi-threading");
// this needs to stay here for compatibility with assemblies generated in Net7
// it doesn't have the __GeneratedInitializer class
cwraps.mono_wasm_runtime_run_module_cctor(asm);
Expand Down
4 changes: 2 additions & 2 deletions src/mono/wasm/runtime/invoke-js.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license.

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, MarshalerType, get_signature_type, imported_js_function_symbol } from "./marshal";
import { get_signature_argument_count, bound_js_function_symbol, get_sig, get_signature_version, get_signature_type, imported_js_function_symbol } from "./marshal";
import { setI32_unchecked } from "./memory";
import { conv_string_root, js_string_to_mono_string_root } from "./strings";
import { mono_assert, MonoObject, MonoObjectRef, MonoString, MonoStringRef, JSFunctionSignature, JSMarshalerArguments, WasmRoot, BoundMarshalerToJs, JSFnHandle, BoundMarshalerToCs, JSHandle } from "./types";
import { mono_assert, MonoObject, MonoObjectRef, MonoString, MonoStringRef, JSFunctionSignature, JSMarshalerArguments, WasmRoot, BoundMarshalerToJs, JSFnHandle, BoundMarshalerToCs, JSHandle, MarshalerType } from "./types";
import { Int32Ptr } from "./types/emscripten";
import { IMPORTS, INTERNAL, Module, runtimeHelpers } from "./imports";
import { bind_arg_marshal_to_js } from "./marshal-to-js";
Expand Down
4 changes: 2 additions & 2 deletions src/mono/wasm/runtime/managed-exports.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

import { GCHandle, MarshalerToCs, MarshalerToJs, MonoMethod, mono_assert } from "./types";
import { GCHandle, MarshalerToCs, MarshalerToJs, MarshalerType, MonoMethod, mono_assert } from "./types";
import cwraps from "./cwraps";
import { runtimeHelpers, ENVIRONMENT_IS_PTHREAD, Module } from "./imports";
import { alloc_stack_frame, get_arg, get_arg_gc_handle, MarshalerType, set_arg_type, set_gc_handle } from "./marshal";
import { alloc_stack_frame, get_arg, get_arg_gc_handle, set_arg_type, set_gc_handle } from "./marshal";
import { invoke_method_and_handle_exception } from "./invoke-cs";
import { marshal_array_to_cs_impl, marshal_exception_to_cs, marshal_intptr_to_cs } from "./marshal-to-cs";
import { marshal_int32_to_js, marshal_string_to_js, marshal_task_to_js } from "./marshal-to-js";
Expand Down
31 changes: 15 additions & 16 deletions src/mono/wasm/runtime/marshal-to-cs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ import {
set_gc_handle, set_js_handle, set_arg_type, set_arg_i32, set_arg_f64, set_arg_i52, set_arg_f32, set_arg_i16, set_arg_u8, set_arg_b8, set_arg_date,
set_arg_length, get_arg, get_signature_arg1_type, get_signature_arg2_type, js_to_cs_marshalers,
get_signature_res_type, bound_js_function_symbol, set_arg_u16, array_element_size,
get_string_root, Span, ArraySegment, MemoryViewType, get_signature_arg3_type, MarshalerType, set_arg_i64_big, set_arg_intptr, IDisposable,
get_string_root, Span, ArraySegment, MemoryViewType, get_signature_arg3_type, set_arg_i64_big, set_arg_intptr, IDisposable,
set_arg_element_type, ManagedObject, JavaScriptMarshalerArgSize
} from "./marshal";
import { get_marshaler_to_js_by_type } from "./marshal-to-js";
import { _zero_region } from "./memory";
import { js_string_to_mono_string_root } from "./strings";
import { mono_assert, GCHandle, GCHandleNull, JSMarshalerArgument, JSMarshalerArguments, JSMarshalerType, MarshalerToCs, MarshalerToJs, BoundMarshalerToCs } from "./types";
import { mono_assert, GCHandle, GCHandleNull, JSMarshalerArgument, JSMarshalerArguments, JSMarshalerType, MarshalerToCs, MarshalerToJs, BoundMarshalerToCs, MarshalerType } from "./types";
import { TypedArray } from "./types/emscripten";

export function initialize_marshalers_to_cs(): void {
Expand Down Expand Up @@ -69,10 +69,11 @@ export function bind_arg_marshal_to_cs(sig: JSMarshalerType, marshaler_type: Mar
marshaler_type = marshaler_type_res;
}
const converter = get_marshaler_to_cs_by_type(marshaler_type)!;
const element_type = get_signature_arg1_type(sig);

const arg_offset = index * JavaScriptMarshalerArgSize;
return (args: JSMarshalerArguments, value: any) => {
converter(<any>args + arg_offset, value, sig, res_marshaler, arg1_marshaler, arg2_marshaler, arg3_marshaler);
converter(<any>args + arg_offset, value, element_type, res_marshaler, arg1_marshaler, arg2_marshaler, arg3_marshaler);
};
}

Expand Down Expand Up @@ -232,7 +233,7 @@ function _marshal_null_to_cs(arg: JSMarshalerArgument) {
set_arg_type(arg, MarshalerType.None);
}

function _marshal_function_to_cs(arg: JSMarshalerArgument, value: Function, _?: JSMarshalerType, res_converter?: MarshalerToCs, arg1_converter?: MarshalerToJs, arg2_converter?: MarshalerToJs, arg3_converter?: MarshalerToJs): void {
function _marshal_function_to_cs(arg: JSMarshalerArgument, value: Function, _?: MarshalerType, res_converter?: MarshalerToCs, arg1_converter?: MarshalerToJs, arg2_converter?: MarshalerToJs, arg3_converter?: MarshalerToJs): void {
if (value === null || value === undefined) {
set_arg_type(arg, MarshalerType.None);
return;
Expand Down Expand Up @@ -292,7 +293,7 @@ export class TaskCallbackHolder implements IDisposable {
}
}

function _marshal_task_to_cs(arg: JSMarshalerArgument, value: Promise<any>, _?: JSMarshalerType, res_converter?: MarshalerToCs) {
function _marshal_task_to_cs(arg: JSMarshalerArgument, value: Promise<any>, _?: MarshalerType, res_converter?: MarshalerToCs) {
if (value === null || value === undefined) {
set_arg_type(arg, MarshalerType.None);
return;
Expand Down Expand Up @@ -443,9 +444,8 @@ function _marshal_cs_object_to_cs(arg: JSMarshalerArgument, value: any): void {
}
}

function _marshal_array_to_cs(arg: JSMarshalerArgument, value: Array<any> | TypedArray, sig?: JSMarshalerType): void {
mono_assert(!!sig, "Expected valid sig parameter");
const element_type = get_signature_arg1_type(sig);
function _marshal_array_to_cs(arg: JSMarshalerArgument, value: Array<any> | TypedArray, element_type?: MarshalerType): void {
mono_assert(!!element_type, "Expected valid element_type parameter");
marshal_array_to_cs_impl(arg, value, element_type);
}

Expand Down Expand Up @@ -510,30 +510,29 @@ export function marshal_array_to_cs_impl(arg: JSMarshalerArgument, value: Array<
}
}

function _marshal_span_to_cs(arg: JSMarshalerArgument, value: Span, sig?: JSMarshalerType): void {
mono_assert(!!sig, "Expected valid sig parameter");
function _marshal_span_to_cs(arg: JSMarshalerArgument, value: Span, element_type?: MarshalerType): void {
mono_assert(!!element_type, "Expected valid element_type parameter");
mono_assert(!value.isDisposed, "ObjectDisposedException");
checkViewType(sig, value._viewType);
checkViewType(element_type, value._viewType);

set_arg_type(arg, MarshalerType.Span);
set_arg_intptr(arg, value._pointer);
set_arg_length(arg, value.length);
}

// this only supports round-trip
function _marshal_array_segment_to_cs(arg: JSMarshalerArgument, value: ArraySegment, sig?: JSMarshalerType): void {
mono_assert(!!sig, "Expected valid sig parameter");
function _marshal_array_segment_to_cs(arg: JSMarshalerArgument, value: ArraySegment, element_type?: MarshalerType): void {
mono_assert(!!element_type, "Expected valid element_type parameter");
const gc_handle = assert_not_disposed(value);
mono_assert(gc_handle, "Only roundtrip of ArraySegment instance created by C#");
checkViewType(sig, value._viewType);
checkViewType(element_type, value._viewType);
set_arg_type(arg, MarshalerType.ArraySegment);
set_arg_intptr(arg, value._pointer);
set_arg_length(arg, value.length);
set_gc_handle(arg, gc_handle);
}

function checkViewType(sig: JSMarshalerType, viewType: MemoryViewType) {
const element_type = get_signature_arg1_type(sig);
function checkViewType(element_type: MarshalerType, viewType: MemoryViewType) {
if (element_type == MarshalerType.Byte) {
mono_assert(MemoryViewType.Byte == viewType, "Expected MemoryViewType.Byte");
}
Expand Down
26 changes: 12 additions & 14 deletions src/mono/wasm/runtime/marshal-to-js.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import {
get_arg_b8, get_arg_date, get_arg_length, set_js_handle, get_arg, set_arg_type,
get_signature_arg2_type, get_signature_arg1_type, cs_to_js_marshalers,
get_signature_res_type, get_arg_u16, array_element_size, get_string_root,
ArraySegment, Span, MemoryViewType, get_signature_arg3_type, MarshalerType, get_arg_i64_big, get_arg_intptr, get_arg_element_type, JavaScriptMarshalerArgSize
ArraySegment, Span, MemoryViewType, get_signature_arg3_type, get_arg_i64_big, get_arg_intptr, get_arg_element_type, JavaScriptMarshalerArgSize
} from "./marshal";
import { conv_string_root } from "./strings";
import { mono_assert, JSHandleNull, GCHandleNull, JSMarshalerArgument, JSMarshalerArguments, JSMarshalerType, MarshalerToCs, MarshalerToJs, BoundMarshalerToJs } from "./types";
import { mono_assert, JSHandleNull, GCHandleNull, JSMarshalerArgument, JSMarshalerArguments, JSMarshalerType, MarshalerToCs, MarshalerToJs, BoundMarshalerToJs, MarshalerType } from "./types";
import { TypedArray } from "./types/emscripten";
import { get_marshaler_to_cs_by_type } from "./marshal-to-cs";

Expand Down Expand Up @@ -69,10 +69,11 @@ export function bind_arg_marshal_to_js(sig: JSMarshalerType, marshaler_type: Mar
marshaler_type = marshaler_type_res;
}
const converter = get_marshaler_to_js_by_type(marshaler_type)!;
const element_type = get_signature_arg1_type(sig);

const arg_offset = index * JavaScriptMarshalerArgSize;
return (args: JSMarshalerArguments) => {
return converter(<any>args + arg_offset, sig, res_marshaler, arg1_marshaler, arg2_marshaler, arg3_marshaler);
return converter(<any>args + arg_offset, element_type, res_marshaler, arg1_marshaler, arg2_marshaler, arg3_marshaler);
};
}

Expand Down Expand Up @@ -177,7 +178,7 @@ function _marshal_datetime_to_js(arg: JSMarshalerArgument): Date | null {
return get_arg_date(arg);
}

function _marshal_delegate_to_js(arg: JSMarshalerArgument, _?: JSMarshalerType, res_converter?: MarshalerToJs, arg1_converter?: MarshalerToCs, arg2_converter?: MarshalerToCs, arg3_converter?: MarshalerToCs): Function | null {
function _marshal_delegate_to_js(arg: JSMarshalerArgument, _?: MarshalerType, res_converter?: MarshalerToJs, arg1_converter?: MarshalerToCs, arg2_converter?: MarshalerToCs, arg3_converter?: MarshalerToCs): Function | null {
const type = get_arg_type(arg);
if (type === MarshalerType.None) {
return null;
Expand All @@ -197,7 +198,7 @@ function _marshal_delegate_to_js(arg: JSMarshalerArgument, _?: JSMarshalerType,
return result;
}

export function marshal_task_to_js(arg: JSMarshalerArgument, _?: JSMarshalerType, res_converter?: MarshalerToJs): Promise<any> | null {
export function marshal_task_to_js(arg: JSMarshalerArgument, _?: MarshalerType, res_converter?: MarshalerToJs): Promise<any> | null {
const type = get_arg_type(arg);
if (type === MarshalerType.None) {
return null;
Expand Down Expand Up @@ -385,9 +386,8 @@ function _marshal_cs_object_to_js(arg: JSMarshalerArgument): any {
return converter(arg);
}

function _marshal_array_to_js(arg: JSMarshalerArgument, sig?: JSMarshalerType): Array<any> | TypedArray | null {
mono_assert(!!sig, "Expected valid sig parameter");
const element_type = get_signature_arg1_type(sig);
function _marshal_array_to_js(arg: JSMarshalerArgument, element_type?: MarshalerType): Array<any> | TypedArray | null {
mono_assert(!!element_type, "Expected valid element_type parameter");
return _marshal_array_to_js_impl(arg, element_type);
}

Expand Down Expand Up @@ -443,10 +443,9 @@ function _marshal_array_to_js_impl(arg: JSMarshalerArgument, element_type: Marsh
return result;
}

function _marshal_span_to_js(arg: JSMarshalerArgument, sig?: JSMarshalerType): Span {
mono_assert(!!sig, "Expected valid sig parameter");
function _marshal_span_to_js(arg: JSMarshalerArgument, element_type?: MarshalerType): Span {
mono_assert(!!element_type, "Expected valid element_type parameter");

const element_type = get_signature_arg1_type(sig);
const buffer_ptr = get_arg_intptr(arg);
const length = get_arg_length(arg);
let result: Span | null = null;
Expand All @@ -465,10 +464,9 @@ function _marshal_span_to_js(arg: JSMarshalerArgument, sig?: JSMarshalerType): S
return result;
}

function _marshal_array_segment_to_js(arg: JSMarshalerArgument, sig?: JSMarshalerType): ArraySegment {
mono_assert(!!sig, "Expected valid sig parameter");
function _marshal_array_segment_to_js(arg: JSMarshalerArgument, element_type?: MarshalerType): ArraySegment {
mono_assert(!!element_type, "Expected valid element_type parameter");

const element_type = get_signature_arg1_type(sig);
const buffer_ptr = get_arg_intptr(arg);
const length = get_arg_length(arg);
let result: ArraySegment | null = null;
Expand Down
36 changes: 1 addition & 35 deletions src/mono/wasm/runtime/marshal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { js_owned_gc_handle_symbol, teardown_managed_proxy } from "./gc-handles"
import { Module, runtimeHelpers } from "./imports";
import { getF32, getF64, getI16, getI32, getI64Big, getU16, getU32, getU8, setF32, setF64, setI16, setI32, setI64Big, setU16, setU32, setU8 } from "./memory";
import { mono_wasm_new_external_root } from "./roots";
import { mono_assert, GCHandle, JSHandle, MonoObject, MonoString, GCHandleNull, JSMarshalerArguments, JSFunctionSignature, JSMarshalerType, JSMarshalerArgument, MarshalerToJs, MarshalerToCs, WasmRoot } from "./types";
import { mono_assert, GCHandle, JSHandle, MonoObject, MonoString, GCHandleNull, JSMarshalerArguments, JSFunctionSignature, JSMarshalerType, JSMarshalerArgument, MarshalerToJs, MarshalerToCs, WasmRoot, MarshalerType } from "./types";
import { CharPtr, TypedArray, VoidPtr } from "./types/emscripten";

export const cs_to_js_marshalers = new Map<MarshalerType, MarshalerToJs>();
Expand Down Expand Up @@ -479,37 +479,3 @@ export class ArraySegment extends MemoryView {
return (<any>this)[js_owned_gc_handle_symbol] === GCHandleNull;
}
}

// please keep in sync with src\libraries\System.Runtime.InteropServices.JavaScript\src\System\Runtime\InteropServices\JavaScript\MarshalerType.cs
export enum MarshalerType {
None = 0,
Void = 1,
Discard,
Boolean,
Byte,
Char,
Int16,
Int32,
Int52,
BigInt64,
Double,
Single,
IntPtr,
JSObject,
Object,
String,
Exception,
DateTime,
DateTimeOffset,

Nullable,
Task,
Array,
ArraySegment,
Span,
Action,
Function,

// only on runtime
JSException,
}
37 changes: 35 additions & 2 deletions src/mono/wasm/runtime/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -435,10 +435,43 @@ export interface JavaScriptExports {
get_managed_stack_trace(exception_gc_handle: GCHandle): string | null
}

export type MarshalerToJs = (arg: JSMarshalerArgument, sig?: JSMarshalerType, res_converter?: MarshalerToJs, arg1_converter?: MarshalerToCs, arg2_converter?: MarshalerToCs, arg3_converter?: MarshalerToCs) => any;
export type MarshalerToCs = (arg: JSMarshalerArgument, value: any, sig?: JSMarshalerType, res_converter?: MarshalerToCs, arg1_converter?: MarshalerToJs, arg2_converter?: MarshalerToJs, arg3_converter?: MarshalerToJs) => void;
export type MarshalerToJs = (arg: JSMarshalerArgument, element_type?: MarshalerType, res_converter?: MarshalerToJs, arg1_converter?: MarshalerToCs, arg2_converter?: MarshalerToCs, arg3_converter?: MarshalerToCs) => any;
export type MarshalerToCs = (arg: JSMarshalerArgument, value: any, element_type?: MarshalerType, res_converter?: MarshalerToCs, arg1_converter?: MarshalerToJs, arg2_converter?: MarshalerToJs, arg3_converter?: MarshalerToJs) => void;
export type BoundMarshalerToJs = (args: JSMarshalerArguments) => any;
export type BoundMarshalerToCs = (args: JSMarshalerArguments, value: any) => void;
// please keep in sync with src\libraries\System.Runtime.InteropServices.JavaScript\src\System\Runtime\InteropServices\JavaScript\MarshalerType.cs
export enum MarshalerType {
None = 0,
Void = 1,
Discard,
Boolean,
Byte,
Char,
Int16,
Int32,
Int52,
BigInt64,
Double,
Single,
IntPtr,
JSObject,
Object,
String,
Exception,
DateTime,
DateTimeOffset,

Nullable,
Task,
Array,
ArraySegment,
Span,
Action,
Function,

// only on runtime
JSException,
}

export interface JSMarshalerArguments extends NativePointer {
__brand: "JSMarshalerArguments"
Expand Down