diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Runtime.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Runtime.cs index 60e6e4d4df676a..60f694e2b7cf39 100644 --- a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Runtime.cs +++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Runtime.cs @@ -12,11 +12,11 @@ public static partial class Runtime private const string TaskGetResultName = "get_Result"; private static readonly MethodInfo _taskGetResultMethodInfo = typeof(Task<>).GetMethod(TaskGetResultName)!; - // - // Execute the provided string in the JavaScript context - // - // The js. - // String. + /// + /// Execute the provided string in the JavaScript context + /// + /// The js. + /// String. public static string InvokeJS(string str) { return Interop.Runtime.InvokeJS(str); @@ -50,6 +50,52 @@ private struct IntPtrAndHandle [FieldOffset(0)] internal RuntimeMethodHandle handle; + + [FieldOffset(0)] + internal RuntimeTypeHandle typeHandle; + } + + // see src/mono/wasm/driver.c MARSHAL_TYPE_xxx + public enum MarshalType : int { + NULL = 0, + INT = 1, + FP64 = 2, + STRING = 3, + VT = 4, + DELEGATE = 5, + TASK = 6, + OBJECT = 7, + BOOL = 8, + ENUM = 9, + URI = 22, + SAFEHANDLE = 23, + ARRAY_BYTE = 10, + ARRAY_UBYTE = 11, + ARRAY_UBYTE_C = 12, + ARRAY_SHORT = 13, + ARRAY_USHORT = 14, + ARRAY_INT = 15, + ARRAY_UINT = 16, + ARRAY_FLOAT = 17, + ARRAY_DOUBLE = 18, + FP32 = 24, + UINT32 = 25, + INT64 = 26, + UINT64 = 27, + CHAR = 28, + STRING_INTERNED = 29, + VOID = 30, + ENUM64 = 31, + POINTER = 32 + } + + // see src/mono/wasm/driver.c MARSHAL_ERROR_xxx + public enum MarshalError : int { + BUFFER_TOO_SMALL = 512, + NULL_CLASS_POINTER = 513, + NULL_TYPE_POINTER = 514, + UNSUPPORTED_TYPE = 515, + FIRST = BUFFER_TOO_SMALL } public static string GetCallSignature(IntPtr methodHandle, object objForRuntimeType) diff --git a/src/mono/wasm/runtime/class-loader.ts b/src/mono/wasm/runtime/class-loader.ts new file mode 100644 index 00000000000000..63581a8e6b1369 --- /dev/null +++ b/src/mono/wasm/runtime/class-loader.ts @@ -0,0 +1,78 @@ +import { MonoAssembly, MonoClass, MonoType, MonoTypeNull, MonoAssemblyNull } from "./types"; +import cwraps from "./cwraps"; + +const _assembly_cache_by_name = new Map(); +const _class_cache_by_assembly = new Map>>(); +let _corlib : MonoAssembly = MonoAssemblyNull; + +export function assembly_load (name : string) : MonoAssembly { + if (_assembly_cache_by_name.has(name)) + return _assembly_cache_by_name.get(name); + + const result = cwraps.mono_wasm_assembly_load(name); + _assembly_cache_by_name.set(name, result); + return result; +} + +function _find_cached_class (assembly : MonoAssembly, namespace : string, name : string) : MonoClass | undefined { + let namespaces = _class_cache_by_assembly.get(assembly); + if (!namespaces) + _class_cache_by_assembly.set(assembly, namespaces = new Map()); + + let classes = namespaces.get(namespace); + if (!classes) { + classes = new Map(); + namespaces.set(namespace, classes); + } + + return classes.get(name); +} + +function _set_cached_class (assembly : MonoAssembly, namespace : string, name : string, ptr : MonoClass) { + const namespaces = _class_cache_by_assembly.get(assembly); + if (!namespaces) + throw new Error("internal error"); + const classes = namespaces.get(namespace); + if (!classes) + throw new Error("internal error"); + classes.set(name, ptr); +} + +export function find_corlib_class (namespace : string, name : string, throw_on_failure? : boolean | undefined) : MonoClass { + if (!_corlib) + _corlib = cwraps.mono_wasm_get_corlib(); + let result = _find_cached_class (_corlib, namespace, name); + if (result !== undefined) + return result; + result = cwraps.mono_wasm_assembly_find_class(_corlib, namespace, name); + if (throw_on_failure && !result) + throw new Error(`Failed to find corlib class ${namespace}.${name}`); + _set_cached_class(_corlib, namespace, name, result); + return result; +} + +export function find_class_in_assembly (assembly_name : string, namespace : string, name : string, throw_on_failure? : boolean | undefined) : MonoClass { + const assembly = cwraps.mono_wasm_assembly_load(assembly_name); + let result = _find_cached_class(assembly, namespace, name); + if (result !== undefined) + return result; + result = cwraps.mono_wasm_assembly_find_class(assembly, namespace, name); + if (throw_on_failure && !result) + throw new Error(`Failed to find class ${namespace}.${name} in ${assembly_name}`); + _set_cached_class(assembly, namespace, name, result); + return result; +} + +export function find_corlib_type (namespace : string, name : string, throw_on_failure? : boolean | undefined) : MonoType { + const classPtr = find_corlib_class(namespace, name, throw_on_failure); + if (!classPtr) + return MonoTypeNull; + return cwraps.mono_wasm_class_get_type(classPtr); +} + +export function find_type_in_assembly (assembly_name : string, namespace : string, name : string, throw_on_failure? : boolean | undefined) : MonoType { + const classPtr = find_class_in_assembly(assembly_name, namespace, name, throw_on_failure); + if (!classPtr) + return MonoTypeNull; + return cwraps.mono_wasm_class_get_type(classPtr); +} diff --git a/src/mono/wasm/runtime/cs-to-js.ts b/src/mono/wasm/runtime/cs-to-js.ts index 749961621cb982..2a81c822c1d018 100644 --- a/src/mono/wasm/runtime/cs-to-js.ts +++ b/src/mono/wasm/runtime/cs-to-js.ts @@ -3,8 +3,9 @@ import { mono_wasm_new_root, WasmRoot } from "./roots"; import { - GCHandle, Int32Ptr, JSHandleDisposed, MonoArray, - MonoArrayNull, MonoObject, MonoObjectNull, MonoString + GCHandle, Int32Ptr, JSHandle, JSHandleDisposed, MonoArray, + MonoArrayNull, MonoObject, MonoObjectNull, MonoString, MonoClass, + MonoClassNull, MonoType, MonoTypeNull } from "./types"; import { Module, runtimeHelpers } from "./modules"; import { conv_string } from "./strings"; @@ -15,6 +16,49 @@ import { mono_method_get_call_signature, call_method, wrap_error } from "./metho import { _js_to_mono_obj } from "./js-to-cs"; import { _are_promises_supported, _create_cancelable_promise } from "./cancelable-promise"; +// see src/mono/wasm/driver.c MARSHAL_TYPE_xxx and Runtime.cs MarshalType +export enum MarshalType { + NULL = 0, + INT = 1, + FP64 = 2, + STRING = 3, + VT = 4, + DELEGATE = 5, + TASK = 6, + OBJECT = 7, + BOOL = 8, + ENUM = 9, + URI = 22, + SAFEHANDLE = 23, + ARRAY_BYTE = 10, + ARRAY_UBYTE = 11, + ARRAY_UBYTE_C = 12, + ARRAY_SHORT = 13, + ARRAY_USHORT = 14, + ARRAY_INT = 15, + ARRAY_UINT = 16, + ARRAY_FLOAT = 17, + ARRAY_DOUBLE = 18, + FP32 = 24, + UINT32 = 25, + INT64 = 26, + UINT64 = 27, + CHAR = 28, + STRING_INTERNED = 29, + VOID = 30, + ENUM64 = 31, + POINTER = 32 +} + +// see src/mono/wasm/driver.c MARSHAL_ERROR_xxx and Runtime.cs +export enum MarshalError { + BUFFER_TOO_SMALL = 512, + NULL_CLASS_POINTER = 513, + NULL_TYPE_POINTER = 514, + UNSUPPORTED_TYPE = 515, + FIRST = BUFFER_TOO_SMALL +} + const delegate_invoke_symbol = Symbol.for("wasm delegate_invoke"); const delegate_invoke_signature_symbol = Symbol.for("wasm delegate_invoke_signature"); @@ -38,72 +82,89 @@ function _unbox_cs_owned_root_as_js_object(root: WasmRoot) { return js_obj; } -export function _unbox_mono_obj_root_with_known_nonprimitive_type(root: WasmRoot, type: number): any { - if (root.value === undefined) - throw new Error(`Expected a root but got ${root}`); - +function _unbox_mono_obj_root_with_known_nonprimitive_type_impl(root: WasmRoot, type: MarshalType, typePtr: MonoType, unbox_buffer: VoidPtr) : any { //See MARSHAL_TYPE_ defines in driver.c switch (type) { - case 26: // int64 - case 27: // uint64 + 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 3: // string - case 29: // interned string + case MarshalType.STRING: + case MarshalType.STRING_INTERNED: return conv_string(root.value); - case 4: //vts + case MarshalType.VT: throw new Error("no idea on how to unbox value types"); - case 5: // delegate + case MarshalType.DELEGATE: return _wrap_delegate_root_as_function(root); - case 6: // Task + case MarshalType.TASK: return _unbox_task_root_as_promise(root); - case 7: // ref type + case MarshalType.OBJECT: return _unbox_ref_type_root_as_js_object(root); - case 10: // arrays - case 11: - case 12: - case 13: - case 14: - case 15: - case 16: - case 17: - case 18: + case MarshalType.ARRAY_BYTE: + case MarshalType.ARRAY_UBYTE: + case MarshalType.ARRAY_UBYTE_C: + case MarshalType.ARRAY_SHORT: + case MarshalType.ARRAY_USHORT: + case MarshalType.ARRAY_INT: + case MarshalType.ARRAY_UINT: + case MarshalType.ARRAY_FLOAT: + case MarshalType.ARRAY_DOUBLE: throw new Error("Marshalling of primitive arrays are not supported. Use the corresponding TypedArray instead."); - case 20: // clr .NET DateTime + case 20: // clr .NET DateTime return new Date(corebindings._get_date_value(root.value)); - case 21: // clr .NET DateTimeOffset + case 21: // clr .NET DateTimeOffset return corebindings._object_to_string(root.value); - case 22: // clr .NET Uri + case MarshalType.URI: return corebindings._object_to_string(root.value); - case 23: // clr .NET SafeHandle/JSObject + case MarshalType.SAFEHANDLE: return _unbox_cs_owned_root_as_js_object(root); - case 30: + case MarshalType.VOID: return undefined; default: - throw new Error(`no idea on how to unbox object kind ${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.get_address()})`); + } +} + +export function _unbox_mono_obj_root_with_known_nonprimitive_type(root: WasmRoot, 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()})`); + + let typePtr = MonoTypeNull; + if ((type === MarshalType.VT) || (type == MarshalType.OBJECT)) { + typePtr = Module.HEAPU32[unbox_buffer >>> 2]; + if (typePtr < 1024) + throw new Error(`Got invalid MonoType ${typePtr} for object at address ${root.value} (root located at ${root.get_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 { +export function _unbox_mono_obj_root(root: WasmRoot) : any { if (root.value === 0) return undefined; - const type = cwraps.mono_wasm_try_unbox_primitive_and_get_type(root.value, runtimeHelpers._unbox_buffer); + 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); switch (type) { - case 1: // int - return Module.HEAP32[runtimeHelpers._unbox_buffer / 4]; - case 25: // uint32 - return Module.HEAPU32[runtimeHelpers._unbox_buffer / 4]; - case 24: // float32 - return Module.HEAPF32[runtimeHelpers._unbox_buffer / 4]; - case 2: // float64 - return Module.HEAPF64[runtimeHelpers._unbox_buffer / 8]; - case 8: // boolean - return (Module.HEAP32[runtimeHelpers._unbox_buffer / 4]) !== 0; - case 28: // char - return String.fromCharCode(Module.HEAP32[runtimeHelpers._unbox_buffer / 4]); + case MarshalType.INT: + return Module.HEAP32[unbox_buffer >>> 2]; + case MarshalType.UINT32: + return Module.HEAPU32[unbox_buffer >>> 2]; + case MarshalType.POINTER: + // FIXME: Is this right? + return Module.HEAPU32[unbox_buffer >>> 2]; + case MarshalType.FP32: + return Module.HEAPF32[unbox_buffer >>> 2]; + case MarshalType.FP64: + return Module.HEAPF64[unbox_buffer >>> 3]; + case MarshalType.BOOL: + return (Module.HEAP32[unbox_buffer >>> 2]) !== 0; + case MarshalType.CHAR: + return String.fromCharCode(Module.HEAP32[unbox_buffer >>> 2]); + case MarshalType.NULL: + return null; default: - return _unbox_mono_obj_root_with_known_nonprimitive_type(root, type); + return _unbox_mono_obj_root_with_known_nonprimitive_type(root, type, unbox_buffer); } } @@ -289,7 +350,7 @@ function _unbox_task_root_as_promise(root: WasmRoot) { return result; } -function _unbox_ref_type_root_as_js_object(root: WasmRoot) { +export function _unbox_ref_type_root_as_js_object(root: WasmRoot) { if (root.value === MonoObjectNull) return null; diff --git a/src/mono/wasm/runtime/cwraps.ts b/src/mono/wasm/runtime/cwraps.ts index adda829a587a57..723cb2ecc09260 100644 --- a/src/mono/wasm/runtime/cwraps.ts +++ b/src/mono/wasm/runtime/cwraps.ts @@ -1,7 +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 { CharPtr, CharPtrPtr, Int32Ptr, MonoArray, MonoAssembly, MonoClass, MonoMethod, MonoObject, MonoString, VoidPtr } from "./types"; +import { + CharPtr, CharPtrPtr, Int32Ptr, + MonoArray, MonoAssembly, MonoClass, + MonoMethod, MonoObject, MonoString, + MonoType, VoidPtr +} from "./types"; import { Module } from "./modules"; const fn_signatures: [ident: string, returnType: string | null, argTypes?: string[], opts?: any][] = [ @@ -25,9 +30,12 @@ const fn_signatures: [ident: string, returnType: string | null, argTypes?: strin ["mono_wasm_exit", null, ["number"]], // BINDING + ["mono_wasm_get_corlib", "number", []], ["mono_wasm_assembly_load", "number", ["string"]], ["mono_wasm_find_corlib_class", "number", ["string", "string"]], ["mono_wasm_assembly_find_class", "number", ["number", "string", "string"]], + ["mono_wasm_find_corlib_type", "number", ["string", "string"]], + ["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_string_get_utf8", "number", ["number"]], @@ -38,13 +46,18 @@ const fn_signatures: [ident: string, returnType: string | null, argTypes?: strin ["mono_wasm_obj_array_new", "number", ["number"]], ["mono_wasm_obj_array_set", "void", ["number", "number", "number"]], ["mono_wasm_register_bundled_satellite_assemblies", "void", []], - ["mono_wasm_try_unbox_primitive_and_get_type", "number", ["number", "number"]], + ["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_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_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"]], @@ -77,9 +90,12 @@ export interface t_Cwraps { mono_wasm_load_runtime(unused: string, debug_level: number): void; // BINDING - mono_wasm_assembly_load(name: string): MonoAssembly - mono_wasm_find_corlib_class(namespace: string, name: string): MonoClass + mono_wasm_get_corlib(): MonoAssembly; + mono_wasm_assembly_load(name: string): MonoAssembly; + mono_wasm_find_corlib_class(namespace: string, name: string): MonoClass; mono_wasm_assembly_find_class(assembly: MonoAssembly, namespace: string, name: string): MonoClass; + 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: MonoObject): MonoObject; mono_wasm_string_get_utf8(str: MonoString): CharPtr; @@ -90,17 +106,22 @@ export interface t_Cwraps { mono_wasm_obj_array_new(size: number): MonoArray; mono_wasm_obj_array_set(array: MonoArray, idx: number, obj: MonoObject): void; mono_wasm_register_bundled_satellite_assemblies(): void; - mono_wasm_try_unbox_primitive_and_get_type(obj: MonoObject, result: VoidPtr): number; + 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_assembly_get_entry_point(assembly: MonoAssembly): MonoMethod - mono_wasm_get_delegate_invoke(delegate: MonoObject): MonoMethod + mono_wasm_intern_string(str: MonoString): MonoString; + 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_class_get_type(klass: MonoClass): MonoType; + mono_wasm_type_get_class(ty: MonoType): MonoClass; + mono_wasm_get_type_name(ty: MonoType): string; + mono_wasm_get_type_aqn(ty: MonoType): string; + mono_wasm_unbox_rooted(obj: MonoObject): VoidPtr; //DOTNET - mono_wasm_string_from_js(str: string): MonoString - + mono_wasm_string_from_js(str: string): MonoString; + //INTERNAL mono_wasm_exit(exit_code: number): number; mono_wasm_enable_on_demand_gc(enable: number): void; diff --git a/src/mono/wasm/runtime/driver.c b/src/mono/wasm/runtime/driver.c index 45186acdbb76b0..e62d6428426249 100644 --- a/src/mono/wasm/runtime/driver.c +++ b/src/mono/wasm/runtime/driver.c @@ -38,6 +38,8 @@ extern void* mono_wasm_invoke_js_unmarshalled (MonoString **exceptionMessage, Mo void mono_wasm_enable_debugging (int); +int mono_wasm_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); @@ -54,6 +56,46 @@ void mono_free (void*); int32_t mini_parse_debug_option (const char *option); char *mono_method_get_full_name (MonoMethod *method); +#define MARSHAL_TYPE_NULL 0 +#define MARSHAL_TYPE_INT 1 +#define MARSHAL_TYPE_FP64 2 +#define MARSHAL_TYPE_STRING 3 +#define MARSHAL_TYPE_VT 4 +#define MARSHAL_TYPE_DELEGATE 5 +#define MARSHAL_TYPE_TASK 6 +#define MARSHAL_TYPE_OBJECT 7 +#define MARSHAL_TYPE_BOOL 8 +#define MARSHAL_TYPE_ENUM 9 +#define MARSHAL_TYPE_DATE 20 +#define MARSHAL_TYPE_DATEOFFSET 21 +#define MARSHAL_TYPE_URI 22 +#define MARSHAL_TYPE_SAFEHANDLE 23 + +// typed array marshalling +#define MARSHAL_ARRAY_BYTE 10 +#define MARSHAL_ARRAY_UBYTE 11 +#define MARSHAL_ARRAY_UBYTE_C 12 +#define MARSHAL_ARRAY_SHORT 13 +#define MARSHAL_ARRAY_USHORT 14 +#define MARSHAL_ARRAY_INT 15 +#define MARSHAL_ARRAY_UINT 16 +#define MARSHAL_ARRAY_FLOAT 17 +#define MARSHAL_ARRAY_DOUBLE 18 + +#define MARSHAL_TYPE_FP32 24 +#define MARSHAL_TYPE_UINT32 25 +#define MARSHAL_TYPE_INT64 26 +#define MARSHAL_TYPE_UINT64 27 +#define MARSHAL_TYPE_CHAR 28 +#define MARSHAL_TYPE_STRING_INTERNED 29 +#define MARSHAL_TYPE_VOID 30 +#define MARSHAL_TYPE_POINTER 32 + +// errors +#define MARSHAL_ERROR_BUFFER_TOO_SMALL 512 +#define MARSHAL_ERROR_NULL_CLASS_POINTER 513 +#define MARSHAL_ERROR_NULL_TYPE_POINTER 514 + static MonoClass* datetime_class; static MonoClass* datetimeoffset_class; static MonoClass* uri_class; @@ -611,10 +653,9 @@ mono_wasm_load_runtime (const char *unused, int debug_level) EMSCRIPTEN_KEEPALIVE MonoAssembly* mono_wasm_assembly_load (const char *name) { + assert (name); MonoImageOpenStatus status; MonoAssemblyName* aname = mono_assembly_name_new (name); - if (!name) - return NULL; MonoAssembly *res = mono_assembly_load (aname, NULL, &status); mono_assembly_name_free (aname); @@ -622,21 +663,23 @@ mono_wasm_assembly_load (const char *name) return res; } -EMSCRIPTEN_KEEPALIVE MonoClass* -mono_wasm_find_corlib_class (const char *namespace, const char *name) +EMSCRIPTEN_KEEPALIVE MonoAssembly* +mono_wasm_get_corlib () { - return mono_class_from_name (mono_get_corlib (), namespace, name); + return mono_image_get_assembly (mono_get_corlib()); } 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); } 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); } @@ -649,8 +692,7 @@ mono_wasm_get_delegate_invoke (MonoObject *delegate) EMSCRIPTEN_KEEPALIVE MonoObject* mono_wasm_box_primitive (MonoClass *klass, void *value, int value_size) { - if (!klass) - return NULL; + assert (klass); MonoType *type = mono_class_get_type (klass); int alignment; @@ -721,6 +763,7 @@ mono_wasm_assembly_get_entry_point (MonoAssembly *assembly) return method; MonoClass *klass = mono_method_get_class (method); + assert(klass); char *async_name = malloc (name_length + 2); snprintf (async_name, name_length + 2, "%s$", name); @@ -772,6 +815,9 @@ mono_wasm_string_from_utf16 (const mono_unichar2 * chars, int length) static int class_is_task (MonoClass *klass) { + if (!klass) + return 0; + if (!task_class && !resolved_task_class) { task_class = mono_class_from_name (mono_get_corlib(), "System.Threading.Tasks", "Task"); resolved_task_class = 1; @@ -792,39 +838,6 @@ MonoClass* mono_get_uri_class(MonoException** exc) return klass; } -#define MARSHAL_TYPE_INT 1 -#define MARSHAL_TYPE_FP64 2 -#define MARSHAL_TYPE_STRING 3 -#define MARSHAL_TYPE_VT 4 -#define MARSHAL_TYPE_DELEGATE 5 -#define MARSHAL_TYPE_TASK 6 -#define MARSHAL_TYPE_OBJECT 7 -#define MARSHAL_TYPE_BOOL 8 -#define MARSHAL_TYPE_ENUM 9 -#define MARSHAL_TYPE_DATE 20 -#define MARSHAL_TYPE_DATEOFFSET 21 -#define MARSHAL_TYPE_URI 22 -#define MARSHAL_TYPE_SAFEHANDLE 23 - -// typed array marshalling -#define MARSHAL_ARRAY_BYTE 10 -#define MARSHAL_ARRAY_UBYTE 11 -#define MARSHAL_ARRAY_UBYTE_C 12 -#define MARSHAL_ARRAY_SHORT 13 -#define MARSHAL_ARRAY_USHORT 14 -#define MARSHAL_ARRAY_INT 15 -#define MARSHAL_ARRAY_UINT 16 -#define MARSHAL_ARRAY_FLOAT 17 -#define MARSHAL_ARRAY_DOUBLE 18 - -#define MARSHAL_TYPE_FP32 24 -#define MARSHAL_TYPE_UINT32 25 -#define MARSHAL_TYPE_INT64 26 -#define MARSHAL_TYPE_UINT64 27 -#define MARSHAL_TYPE_CHAR 28 -#define MARSHAL_TYPE_STRING_INTERNED 29 -#define MARSHAL_TYPE_VOID 30 - void mono_wasm_ensure_classes_resolved () { if (!datetime_class && !resolved_datetime_class) { @@ -855,14 +868,18 @@ mono_wasm_marshal_type_from_mono_type (int mono_type, MonoClass *klass, MonoType { switch (mono_type) { // case MONO_TYPE_CHAR: prob should be done not as a number? + case MONO_TYPE_VOID: + return MARSHAL_TYPE_VOID; case MONO_TYPE_BOOLEAN: return MARSHAL_TYPE_BOOL; + case MONO_TYPE_I: // IntPtr + case MONO_TYPE_PTR: + return MARSHAL_TYPE_POINTER; case MONO_TYPE_I1: case MONO_TYPE_U1: case MONO_TYPE_I2: case MONO_TYPE_U2: case MONO_TYPE_I4: - case MONO_TYPE_I: // IntPtr return MARSHAL_TYPE_INT; case MONO_TYPE_CHAR: return MARSHAL_TYPE_CHAR; @@ -880,6 +897,7 @@ mono_wasm_marshal_type_from_mono_type (int mono_type, MonoClass *klass, MonoType case MONO_TYPE_STRING: return MARSHAL_TYPE_STRING; case MONO_TYPE_SZARRAY: { // simple zero based one-dim-array + if (klass) { MonoClass *eklass = mono_class_get_element_class (klass); MonoType *etype = mono_class_get_type (eklass); @@ -903,10 +921,14 @@ mono_wasm_marshal_type_from_mono_type (int mono_type, MonoClass *klass, MonoType default: return MARSHAL_TYPE_OBJECT; } + } else { + return MARSHAL_TYPE_OBJECT; + } } default: mono_wasm_ensure_classes_resolved (); + if (klass) { if (klass == datetime_class) return MARSHAL_TYPE_DATE; if (klass == datetimeoffset_class) @@ -917,13 +939,13 @@ mono_wasm_marshal_type_from_mono_type (int mono_type, MonoClass *klass, MonoType return MARSHAL_TYPE_VOID; if (mono_class_is_enum (klass)) return MARSHAL_TYPE_ENUM; - if (!mono_type_is_reference (type)) //vt + if (type && !mono_type_is_reference (type)) //vt return MARSHAL_TYPE_VT; if (mono_class_is_delegate (klass)) return MARSHAL_TYPE_DELEGATE; if (class_is_task(klass)) return MARSHAL_TYPE_TASK; - if (safehandle_class && (klass == safehandle_class || mono_class_is_subclass_of(klass, safehandle_class, 0))) { + if (safehandle_class && (klass == safehandle_class || mono_class_is_subclass_of(klass, safehandle_class, 0))) return MARSHAL_TYPE_SAFEHANDLE; } @@ -931,6 +953,15 @@ mono_wasm_marshal_type_from_mono_type (int mono_type, MonoClass *klass, MonoType } } +EMSCRIPTEN_KEEPALIVE MonoClass * +mono_wasm_get_obj_class (MonoObject *obj) +{ + if (!obj) + return NULL; + + return mono_object_get_class (obj); +} + EMSCRIPTEN_KEEPALIVE int mono_wasm_get_obj_type (MonoObject *obj) { @@ -939,11 +970,15 @@ mono_wasm_get_obj_type (MonoObject *obj) /* Process obj before calling into the runtime, class_from_name () can invoke managed code */ 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)) 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); @@ -952,33 +987,59 @@ mono_wasm_get_obj_type (MonoObject *obj) } EMSCRIPTEN_KEEPALIVE int -mono_wasm_try_unbox_primitive_and_get_type (MonoObject *obj, void *result) +mono_wasm_try_unbox_primitive_and_get_type (MonoObject *obj, void *result, int result_capacity) { + void **resultP = result; int *resultI = result; int64_t *resultL = result; float *resultF = result; double *resultD = result; - if (!obj) { + if (result_capacity >= sizeof (int64_t)) *resultL = 0; - return 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) + return MARSHAL_ERROR_NULL_CLASS_POINTER; + + MonoType *type = mono_class_get_type (klass), *original_type = type; + if (!type) + return MARSHAL_ERROR_NULL_TYPE_POINTER; + if ((klass == mono_get_string_class ()) && mono_string_instance_is_interned ((MonoString *)obj)) { *resultL = 0; + *resultP = type; return MARSHAL_TYPE_STRING_INTERNED; } - MonoType *type = mono_class_get_type (klass), *original_type = type; - if (mono_class_is_enum (klass)) type = mono_type_get_underlying_type (type); + if (!type) + return MARSHAL_ERROR_NULL_TYPE_POINTER; + int mono_type = mono_type_get_type (type); + if (mono_type == MONO_TYPE_GENERICINST) { + // HACK: While the 'any other type' fallback is valid for classes, it will do the + // wrong thing for structs, so we need to make sure the valuetype handler is used + 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) { @@ -1010,24 +1071,58 @@ mono_wasm_try_unbox_primitive_and_get_type (MonoObject *obj, void *result) case MONO_TYPE_R8: *resultD = *(double*)mono_object_unbox (obj); break; + case MONO_TYPE_PTR: + *resultL = (int64_t)(*(void**)mono_object_unbox (obj)); + break; case MONO_TYPE_I8: case MONO_TYPE_U8: // FIXME: At present the javascript side of things can't handle this, // but there's no reason not to future-proof this API *resultL = *(int64_t*)mono_object_unbox (obj); break; + case MONO_TYPE_VALUETYPE: + { + int obj_size = mono_object_get_size (obj), + required_size = (sizeof (int)) + (sizeof (MonoType *)) + obj_size; + + // 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); + if (marshal_type != MARSHAL_TYPE_VT) + return marshal_type; + + // Check whether the result buffer is big enough for the struct and padding + if (result_capacity < required_size) + return MARSHAL_ERROR_BUFFER_TOO_SMALL; + + // Store a header before the struct data with the size of the data and its MonoType + *resultP = type; + int * resultSize = (int *)(resultP + 1); + *resultSize = obj_size; + void * resultVoid = (resultP + 2); + void * unboxed = mono_object_unbox (obj); + memcpy (resultVoid, unboxed, obj_size); + return MARSHAL_TYPE_VT; + } + break; default: // If we failed to do a fast unboxing, return the original type information so // that the caller can do a proper, slow unboxing later - *resultL = 0; + // 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; - return mono_wasm_marshal_type_from_mono_type (mono_type, klass, original_type); + int fallbackResultType = mono_wasm_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; - return mono_wasm_marshal_type_from_mono_type (mono_type, klass, type); + int resultType = mono_wasm_marshal_type_from_mono_type (mono_type, klass, type); + assert (resultType != MARSHAL_TYPE_VT); + return resultType; } // FIXME: This function is retained specifically because runtime-test.js uses it @@ -1159,3 +1254,37 @@ mono_wasm_string_get_data ( *outIsInterned = mono_string_instance_is_interned (string); return; } + +EMSCRIPTEN_KEEPALIVE MonoType * +mono_wasm_class_get_type (MonoClass *klass) +{ + if (!klass) + return NULL; + return mono_class_get_type (klass); +} + +EMSCRIPTEN_KEEPALIVE MonoClass * +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); +} + +EMSCRIPTEN_KEEPALIVE char * +mono_wasm_get_type_name (MonoType * typePtr) { + return mono_type_get_name_full (typePtr, MONO_TYPE_NAME_FORMAT_REFLECTION); +} + +EMSCRIPTEN_KEEPALIVE char * +mono_wasm_get_type_aqn (MonoType * typePtr) { + return mono_type_get_name_full (typePtr, MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED); +} diff --git a/src/mono/wasm/runtime/js-to-cs.ts b/src/mono/wasm/runtime/js-to-cs.ts index 2e6f0c7a10ec45..e9807e6033affe 100644 --- a/src/mono/wasm/runtime/js-to-cs.ts +++ b/src/mono/wasm/runtime/js-to-cs.ts @@ -109,22 +109,22 @@ function _extract_mono_obj(should_add_in_flight: boolean, js_obj: any): MonoObje } function _box_js_int(js_obj: number) { - Module.HEAP32[runtimeHelpers._box_buffer / 4] = js_obj; + Module.HEAP32[runtimeHelpers._box_buffer >>> 2] = js_obj; return cwraps.mono_wasm_box_primitive(runtimeHelpers._class_int32, runtimeHelpers._box_buffer, 4); } function _box_js_uint(js_obj: number) { - Module.HEAPU32[runtimeHelpers._box_buffer / 4] = js_obj; + Module.HEAPU32[runtimeHelpers._box_buffer >>> 2] = js_obj; return cwraps.mono_wasm_box_primitive(runtimeHelpers._class_uint32, runtimeHelpers._box_buffer, 4); } function _box_js_double(js_obj: number) { - Module.HEAPF64[runtimeHelpers._box_buffer / 8] = js_obj; + Module.HEAPF64[runtimeHelpers._box_buffer >>> 3] = js_obj; return cwraps.mono_wasm_box_primitive(runtimeHelpers._class_double, runtimeHelpers._box_buffer, 8); } export function _box_js_bool(js_obj: boolean): MonoObject { - Module.HEAP32[runtimeHelpers._box_buffer / 4] = js_obj ? 1 : 0; + Module.HEAP32[runtimeHelpers._box_buffer >>> 2] = js_obj ? 1 : 0; return cwraps.mono_wasm_box_primitive(runtimeHelpers._class_boolean, runtimeHelpers._box_buffer, 4); } diff --git a/src/mono/wasm/runtime/memory.ts b/src/mono/wasm/runtime/memory.ts new file mode 100644 index 00000000000000..abfe17ae89744b --- /dev/null +++ b/src/mono/wasm/runtime/memory.ts @@ -0,0 +1,30 @@ +import { Module, MONO } from './modules' + +let _temp_mallocs : Array | null> = []; + +export function temp_malloc (size : number) : VoidPtr { + if (!_temp_mallocs || !_temp_mallocs.length) + throw new Error("No temp frames have been created at this point"); + + const frame = _temp_mallocs[_temp_mallocs.length - 1] || []; + const result = Module._malloc(size); + frame.push(result); + _temp_mallocs[_temp_mallocs.length - 1] = frame; + return result; +} + +export function _create_temp_frame () { + _temp_mallocs.push(null); +} + +export function _release_temp_frame () { + if (!_temp_mallocs.length) + throw new Error("No temp frames have been created at this point"); + + const frame = _temp_mallocs.pop(); + if (!frame) + return; + + for (let i = 0, l = frame.length; i < l; i++) + Module._free(frame[i]); +} diff --git a/src/mono/wasm/runtime/method-binding.ts b/src/mono/wasm/runtime/method-binding.ts index 7d0011d01f0e24..6e48288f81d50e 100644 --- a/src/mono/wasm/runtime/method-binding.ts +++ b/src/mono/wasm/runtime/method-binding.ts @@ -1,17 +1,43 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -import { WasmRootBuffer } from "./roots"; -import { MonoClass, MonoMethod, MonoObject, coerceNull, VoidPtrNull, VoidPtr } from "./types"; +import { WasmRoot, WasmRootBuffer, mono_wasm_new_root } from "./roots"; +import { MonoClass, MonoMethod, MonoObject, coerceNull, VoidPtrNull, VoidPtr, MonoType } from "./types"; import { BINDING, MONO, runtimeHelpers } from "./modules"; 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 { MarshalType, _unbox_mono_obj_root_with_known_nonprimitive_type } from "./cs-to-js"; +import { _create_temp_frame } from "./memory"; +import { + _get_args_root_buffer_for_method_call, _get_buffer_for_method_call, + _handle_exception_for_call, _teardown_after_call +} from "./method-calls"; import cwraps from "./cwraps"; +import cswraps from "./corebindings"; const primitiveConverters = new Map(); const _signature_converters = new Map(); const _method_descriptions = new Map(); + +export function _get_type_name (typePtr : MonoType) : string { + if (!typePtr) + return ""; + return cwraps.mono_wasm_get_type_name(typePtr); +} + +export function _get_type_aqn (typePtr : MonoType) : string { + if (!typePtr) + return ""; + return cwraps.mono_wasm_get_type_aqn(typePtr); +} + +export function _get_class_name (classPtr : MonoClass) : string { + if (!classPtr) + return ""; + return cwraps.mono_wasm_get_type_name(cwraps.mono_wasm_class_get_type(classPtr)); +} + export function find_method(klass: MonoClass, name: string, n: number): MonoMethod { const result = cwraps.mono_wasm_assembly_find_method(klass, name, n); if (result) { @@ -33,7 +59,7 @@ export function bind_runtime_method(method_name: string, signature: ArgsMarshalS } -function _create_named_function(name: string, argumentNames: string[], body: string, closure: any) { +export function _create_named_function(name: string, argumentNames: string[], body: string, closure: any) { let result = null; let closureArgumentList: any[] | null = null; let closureArgumentNames = null; @@ -52,7 +78,7 @@ function _create_named_function(name: string, argumentNames: string[], body: str return result; } -function _create_rebindable_named_function(name: string, argumentNames: string[], body: string, closureArgNames: string[] | null) { +export function _create_rebindable_named_function(name: string, argumentNames: string[], body: string, closureArgNames: string[] | null) { const strictPrefix = "\"use strict\";\r\n"; let uriPrefix = "", escapedFunctionIdentifier = ""; @@ -139,17 +165,17 @@ function _create_converter_for_marshal_string(args_marshal: ArgsMarshalString): if (conv.needs_root) needs_root_buffer = true; localStep.needs_root = conv.needs_root; - localStep.key = args_marshal[i]; + localStep.key = key; steps.push(localStep); size += conv.size; } return { - steps: steps, size: size, args_marshal: args_marshal, - is_result_definitely_unmarshaled: is_result_definitely_unmarshaled, - is_result_possibly_unmarshaled: is_result_possibly_unmarshaled, - result_unmarshaled_if_argc: result_unmarshaled_if_argc, - needs_root_buffer: needs_root_buffer + steps, size, args_marshal, + is_result_definitely_unmarshaled, + is_result_possibly_unmarshaled, + result_unmarshaled_if_argc, + needs_root_buffer }; } @@ -183,14 +209,19 @@ export function _compile_converter_for_marshal_string(args_marshal: ArgsMarshalS // ensure the indirect values are 8-byte aligned so that aligned loads and stores will work const indirectBaseOffset = ((((args_marshal.length * 4) + 7) / 8) | 0) * 8; - let closure: any = {}; + const closure: any = { + Module, + _malloc: Module._malloc, + mono_wasm_unbox_rooted: cwraps.mono_wasm_unbox_rooted, + }; let indirectLocalOffset = 0; body.push( - `if (!buffer) buffer = Module._malloc (${bufferSizeBytes});`, - `var indirectStart = buffer + ${indirectBaseOffset};`, - "var indirect32 = (indirectStart / 4) | 0, indirect64 = (indirectStart / 8) | 0;", - "var buffer32 = (buffer / 4) | 0;", + "if (!method) throw new Error('no method provided');", + `if (!buffer) buffer = _malloc (${bufferSizeBytes});`, + `let indirectStart = buffer + ${indirectBaseOffset};`, + "let indirect32 = indirectStart >>> 2, indirect64 = indirectStart >>> 3;", + "let buffer32 = buffer >>> 2;", "" ); @@ -204,13 +235,22 @@ export function _compile_converter_for_marshal_string(args_marshal: ArgsMarshalS if (step.convert) { closure[closureKey] = step.convert; - body.push(`var ${valueKey} = ${closureKey}(${argKey}, method, ${i});`); + body.push(`let ${valueKey} = ${closureKey}(${argKey}, method, ${i});`); } else { - body.push(`var ${valueKey} = ${argKey};`); + body.push(`let ${valueKey} = ${argKey};`); } - if (step.needs_root) + if (step.needs_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) { let heapArrayName = null; @@ -226,7 +266,7 @@ export function _compile_converter_for_marshal_string(args_marshal: ArgsMarshalS heapArrayName = "HEAPF32"; break; case "double": - body.push(`Module.HEAPF64[indirect64 + ${(indirectLocalOffset / 8)}] = ${valueKey};`); + body.push(`Module.HEAPF64[indirect64 + ${(indirectLocalOffset >>> 3)}] = ${valueKey};`); break; case "i64": body.push(`Module.setValue (indirectStart + ${indirectLocalOffset}, ${valueKey}, 'i64');`); @@ -236,7 +276,7 @@ export function _compile_converter_for_marshal_string(args_marshal: ArgsMarshalS } if (heapArrayName) - body.push(`Module.${heapArrayName}[indirect32 + ${(indirectLocalOffset / 4)}] = ${valueKey};`); + body.push(`Module.${heapArrayName}[indirect32 + ${(indirectLocalOffset >>> 2)}] = ${valueKey};`); body.push(`Module.HEAP32[buffer32 + ${i}] = indirectStart + ${indirectLocalOffset};`, ""); indirectLocalOffset += step.size!; @@ -253,13 +293,14 @@ export function _compile_converter_for_marshal_string(args_marshal: ArgsMarshalS compiledFunction = _create_named_function("converter_" + converterName, argumentNames, bodyJs, closure); converter.compiled_function = compiledFunction; } catch (exc) { - converter.compiled_function = undefined; + converter.compiled_function = null; console.warn("compiling converter failed for", bodyJs, "with error", exc); throw exc; } + argumentNames = ["existingBuffer", "rootBuffer", "method", "args"]; - closure = { + const variadicClosure = { converter: compiledFunction }; body = [ @@ -282,15 +323,15 @@ export function _compile_converter_for_marshal_string(args_marshal: ArgsMarshalS bodyJs = body.join("\r\n"); try { - compiledVariadicFunction = _create_named_function("variadic_converter_" + converterName, argumentNames, bodyJs, closure); + compiledVariadicFunction = _create_named_function("variadic_converter_" + converterName, argumentNames, bodyJs, variadicClosure); converter.compiled_variadic_function = compiledVariadicFunction; } catch (exc) { - converter.compiled_variadic_function = undefined; + converter.compiled_variadic_function = null; console.warn("compiling converter failed for", bodyJs, "with error", exc); throw exc; } - converter.scratchRootBuffer = undefined; + converter.scratchRootBuffer = null; converter.scratchBuffer = VoidPtrNull; return converter; @@ -331,37 +372,67 @@ export function mono_bind_method(method: MonoMethod, this_arg: MonoObject | null this_arg = coerceNull(this_arg); let converter: Converter | null = null; + if (typeof (args_marshal) === "string") { + converter = _compile_converter_for_marshal_string (args_marshal); + } - converter = _compile_converter_for_marshal_string(args_marshal); - + // FIXME + const unbox_buffer_size = 8192; + const unbox_buffer = Module._malloc(unbox_buffer_size); + + const token: BoundMethodToken = { + friendlyName: friendly_name, + method, + converter, + scratchRootBuffer: null, + scratchBuffer: VoidPtrNull, + scratchResultRoot: mono_wasm_new_root(), + scratchExceptionRoot: mono_wasm_new_root() + }; const closure: any = { - library_mono: MONO, - binding_support: BINDING, - method: method, - this_arg: this_arg + mono_wasm_new_root, + _create_temp_frame, + _get_args_root_buffer_for_method_call, + _get_buffer_for_method_call, + _handle_exception_for_call, + _teardown_after_call, + mono_wasm_try_unbox_primitive_and_get_type: cwraps.mono_wasm_try_unbox_primitive_and_get_type, + _unbox_mono_obj_root_with_known_nonprimitive_type, + invoke_method: cwraps.mono_wasm_invoke_method, + method, + this_arg, + token, + unbox_buffer, + unbox_buffer_size }; - const converterKey = "converter_" + converter.name; - + const converterKey = converter ? "converter_" + converter.name : ""; if (converter) closure[converterKey] = converter; const argumentNames = []; const body = [ - "var resultRoot = library_mono.mono_wasm_new_root (), exceptionRoot = library_mono.mono_wasm_new_root ();", + "_create_temp_frame();", + "let resultRoot = token.scratchResultRoot, exceptionRoot = token.scratchExceptionRoot;", + "token.scratchResultRoot = null;", + "token.scratchExceptionRoot = null;", + "if (resultRoot === null)", + " resultRoot = mono_wasm_new_root ();", + "if (exceptionRoot === null)", + " exceptionRoot = mono_wasm_new_root ();", "" ]; if (converter) { body.push( - `var argsRootBuffer = binding_support._get_args_root_buffer_for_method_call (${converterKey});`, - `var scratchBuffer = binding_support._get_buffer_for_method_call (${converterKey});`, - `var buffer = ${converterKey}.compiled_function (`, + `let argsRootBuffer = _get_args_root_buffer_for_method_call(${converterKey}, token);`, + `let scratchBuffer = _get_buffer_for_method_call(${converterKey}, token);`, + `let buffer = ${converterKey}.compiled_function(`, " scratchBuffer, argsRootBuffer, method," ); for (let i = 0; i < converter.steps.length; i++) { - const argName = "arg" + i; + let argName = "arg" + i; argumentNames.push(argName); body.push( " " + argName + @@ -376,15 +447,15 @@ export function mono_bind_method(method: MonoMethod, this_arg: MonoObject | null body.push(");"); } else { - body.push("var argsRootBuffer = null, buffer = 0;"); + body.push("let argsRootBuffer = null, buffer = 0;"); } - if (converter.is_result_definitely_unmarshaled) { - body.push("var is_result_marshaled = false;"); - } else if (converter.is_result_possibly_unmarshaled) { - body.push(`var is_result_marshaled = arguments.length !== ${converter.result_unmarshaled_if_argc};`); + if (converter && converter.is_result_definitely_unmarshaled) { + body.push ("let is_result_marshaled = false;"); + } else if (converter && converter.is_result_possibly_unmarshaled) { + body.push (`let is_result_marshaled = arguments.length !== ${converter.result_unmarshaled_if_argc};`); } else { - body.push("var is_result_marshaled = true;"); + body.push ("let is_result_marshaled = true;"); } // We inline a bunch of the invoke and marshaling logic here in order to eliminate the GC pressure normally @@ -398,53 +469,70 @@ 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 = binding_support.invoke_method (method, this_arg, buffer, exceptionRoot.get_address ());", - `binding_support._handle_exception_for_call (${converterKey}, buffer, resultRoot, exceptionRoot, argsRootBuffer);`, + "resultRoot.value = invoke_method (method, this_arg, buffer, exceptionRoot.get_address ());", + `_handle_exception_for_call (${converterKey}, token, buffer, resultRoot, exceptionRoot, argsRootBuffer);`, "", - "var result = undefined;", - "if (!is_result_marshaled) ", - " result = resultRoot.value;", - "else if (resultRoot.value !== 0) {", + "let resultPtr = resultRoot.value, result = undefined;" + ); + + if (converter) { + if (converter.is_result_possibly_unmarshaled) + body.push("if (!is_result_marshaled) "); + + if (converter.is_result_definitely_unmarshaled || converter.is_result_possibly_unmarshaled) + body.push(" result = resultPtr;"); + + if (!converter.is_result_definitely_unmarshaled) + body.push( + "if (is_result_marshaled && (resultPtr !== 0)) {", // 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). - " var resultType = binding_support.mono_wasm_try_unbox_primitive_and_get_type (resultRoot.value, buffer);", + " let resultType = mono_wasm_try_unbox_primitive_and_get_type (resultPtr, unbox_buffer, unbox_buffer_size);", " switch (resultType) {", - " case 1:", // int - " result = Module.HEAP32[buffer / 4]; break;", - " case 25:", // uint32 - " result = Module.HEAPU32[buffer / 4]; break;", - " case 24:", // float32 - " result = Module.HEAPF32[buffer / 4]; break;", - " case 2:", // float64 - " result = Module.HEAPF64[buffer / 8]; break;", - " case 8:", // boolean - " result = (Module.HEAP32[buffer / 4]) !== 0; break;", - " case 28:", // char - " result = String.fromCharCode(Module.HEAP32[buffer / 4]); break;", + ` case ${MarshalType.INT}:`, + " result = Module.HEAP32[unbox_buffer >>> 2]; break;", + ` case ${MarshalType.POINTER}:`, // FIXME: Is this right? + ` case ${MarshalType.UINT32}:`, + " result = Module.HEAPU32[unbox_buffer >>> 2]; break;", + ` case ${MarshalType.FP32}:`, + " result = Module.HEAPF32[unbox_buffer >>> 2]; break;", + ` case ${MarshalType.FP64}:`, + " result = Module.HEAPF64[unbox_buffer >>> 3]; break;", + ` case ${MarshalType.BOOL}:`, + " result = (Module.HEAP32[unbox_buffer >>> 2]) !== 0; break;", + ` case ${MarshalType.CHAR}:`, + " result = String.fromCharCode(Module.HEAP32[unbox_buffer >>> 2]); break;", " default:", - " result = binding_support._unbox_mono_obj_root_with_known_nonprimitive_type (resultRoot, resultType); break;", + " result = _unbox_mono_obj_root_with_known_nonprimitive_type (resultRoot, resultType, unbox_buffer); break;", " }", - "}", - "", - `binding_support._teardown_after_call (${converterKey}, buffer, resultRoot, exceptionRoot, argsRootBuffer);`, - "return result;" + "}" ); - - const bodyJs = body.join("\r\n"); + } else { + throw new Error("No converter"); + } if (friendly_name) { - const escapeRE = /[^A-Za-z0-9_]/g; + const escapeRE = /[^A-Za-z0-9_\$]/g; friendly_name = friendly_name.replace(escapeRE, "_"); } - let displayName = "managed_" + (friendly_name || method); + let displayName = friendly_name || ("clr_" + method); if (this_arg) - displayName += "_with_this_" + this_arg; + displayName += "_this" + this_arg; - return _create_named_function(displayName, argumentNames, bodyJs, closure); + body.push( + `_teardown_after_call (${converterKey}, token, buffer, resultRoot, exceptionRoot, argsRootBuffer);`, + "return result;" + ); + + let bodyJs = body.join ("\r\n"); + + let result = _create_named_function(displayName, argumentNames, bodyJs, closure); + + return result; } declare const enum ArgsMarshal { @@ -479,6 +567,7 @@ export type Converter = { steps: { convert?: boolean | Function; needs_root?: boolean; + needs_unbox?: boolean; indirect?: ConverterStepIndirects; size?: number; }[]; @@ -488,11 +577,25 @@ export type Converter = { is_result_possibly_unmarshaled?: boolean; result_unmarshaled_if_argc?: number; needs_root_buffer?: boolean; + key?: string; name?: string; needs_root?: boolean; - compiled_variadic_function?: Function; - compiled_function?: Function; - scratchRootBuffer?: WasmRootBuffer; + needs_unbox?: boolean; + compiled_variadic_function?: Function | null; + compiled_function?: Function | null; + scratchRootBuffer?: WasmRootBuffer | null; scratchBuffer?: VoidPtr; has_warned_about_signature?: boolean; + convert?: Function | null; + method?: MonoMethod | null; +} + +export type BoundMethodToken = { + friendlyName: string; + method: MonoMethod; + converter: Converter | null; + scratchRootBuffer: WasmRootBuffer | null; + scratchBuffer: VoidPtr; + scratchResultRoot: WasmRoot; + scratchExceptionRoot: WasmRoot; } \ No newline at end of file diff --git a/src/mono/wasm/runtime/method-calls.ts b/src/mono/wasm/runtime/method-calls.ts index 3847bc09c1a059..f3b5b223e05a69 100644 --- a/src/mono/wasm/runtime/method-calls.ts +++ b/src/mono/wasm/runtime/method-calls.ts @@ -2,15 +2,25 @@ // 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 { JSHandle, MonoArray, MonoMethod, MonoObject, MonoObjectNull, MonoString, coerceNull as coerceNull, VoidPtrNull, VoidPtr, Int32Ptr } from "./types"; +import { + JSHandle, MonoArray, MonoMethod, MonoObject, + MonoObjectNull, MonoString, coerceNull as coerceNull, + VoidPtr, VoidPtrNull, Int32Ptr +} from "./types"; import { Module, runtimeHelpers } from "./modules"; 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"; -import { ArgsMarshalString, mono_bind_method, Converter, _compile_converter_for_marshal_string, _decide_if_result_is_marshaled, find_method } from "./method-binding"; +import { + ArgsMarshalString, 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 cwraps from "./cwraps"; import { bindings_lazy_init } from "./startup"; +import { _create_temp_frame, _release_temp_frame } from "./memory"; function _verify_args_for_method_call(args_marshal: ArgsMarshalString, args: any) { const has_args = args && (typeof args === "object") && args.length > 0; @@ -26,43 +36,61 @@ function _verify_args_for_method_call(args_marshal: ArgsMarshalString, args: any return has_args_marshal && has_args; } -export function _get_buffer_for_method_call(converter: Converter): VoidPtr | undefined { +export function _get_buffer_for_method_call(converter: Converter, token: BoundMethodToken | null) : VoidPtr | undefined { if (!converter) return VoidPtrNull; - const result = converter.scratchBuffer; - converter.scratchBuffer = VoidPtrNull; + let result = VoidPtrNull; + if (token !== null) { + result = token.scratchBuffer || VoidPtrNull; + token.scratchBuffer = VoidPtrNull; + } else { + result = converter.scratchBuffer || VoidPtrNull; + converter.scratchBuffer = VoidPtrNull; + } return result; } -export function _get_args_root_buffer_for_method_call(converter: Converter): WasmRootBuffer | undefined { +export function _get_args_root_buffer_for_method_call(converter: Converter, token: BoundMethodToken | null) : WasmRootBuffer | undefined { if (!converter) return undefined; if (!converter.needs_root_buffer) return undefined; - let result; - if (converter.scratchRootBuffer) { - result = converter.scratchRootBuffer; - converter.scratchRootBuffer = undefined; + let result = null; + if (token !== null) { + result = token.scratchRootBuffer; + token.scratchRootBuffer = null; } else { + result = converter.scratchRootBuffer; + converter.scratchRootBuffer = null; + } + + if (result === null) { // TODO: Expand the converter's heap allocation and then use // mono_wasm_new_root_buffer_from_pointer instead. Not that important // at present because the scratch buffer will be reused unless we are // recursing through a re-entrant call - result = mono_wasm_new_root_buffer(converter.steps.length, converter.name); + result = mono_wasm_new_root_buffer (converter.steps.length); + // FIXME (result).converter = converter; } + return result; } -function _release_args_root_buffer_from_method_call(converter?: Converter, argsRootBuffer?: WasmRootBuffer) { +function _release_args_root_buffer_from_method_call( + converter?: Converter, token?: BoundMethodToken | null, argsRootBuffer?: WasmRootBuffer +) { if (!argsRootBuffer || !converter) return; // Store the arguments root buffer for re-use in later calls - if (!converter.scratchRootBuffer) { + if (token && (token.scratchRootBuffer === null)) { + argsRootBuffer.clear (); + token.scratchRootBuffer = argsRootBuffer; + } else if (!converter.scratchRootBuffer) { argsRootBuffer.clear(); converter.scratchRootBuffer = argsRootBuffer; } else { @@ -70,11 +98,15 @@ function _release_args_root_buffer_from_method_call(converter?: Converter, argsR } } -function _release_buffer_from_method_call(converter: Converter | undefined, buffer?: VoidPtr) { +function _release_buffer_from_method_call( + converter: Converter | undefined, token?: BoundMethodToken | null, buffer?: VoidPtr +) { if (!converter || !buffer) return; - if (!converter.scratchBuffer) + if (token && !token.scratchBuffer) + token.scratchBuffer = buffer; + else if (!converter.scratchBuffer) converter.scratchBuffer = coerceNull(buffer); else if (buffer) Module._free(buffer); @@ -122,59 +154,88 @@ export function call_method(method: MonoMethod, this_arg: MonoObject | undefined let buffer = VoidPtrNull, converter = undefined, argsRootBuffer = undefined; let is_result_marshaled = true; + // TODO: Only do this if the signature needs marshalling + _create_temp_frame(); + // check if the method signature needs argument mashalling if (needs_converter) { converter = _compile_converter_for_marshal_string(args_marshal); is_result_marshaled = _decide_if_result_is_marshaled(converter, args.length); - argsRootBuffer = _get_args_root_buffer_for_method_call(converter); + argsRootBuffer = _get_args_root_buffer_for_method_call(converter, null); - const scratchBuffer = _get_buffer_for_method_call(converter); + const scratchBuffer = _get_buffer_for_method_call(converter, null); buffer = converter.compiled_variadic_function!(scratchBuffer, argsRootBuffer, method, args); } - return _call_method_with_converted_args(method, this_arg!, converter, buffer, is_result_marshaled, argsRootBuffer); + return _call_method_with_converted_args(method, this_arg!, converter, null, buffer, is_result_marshaled, argsRootBuffer); } -export function _handle_exception_for_call(converter: Converter | undefined, buffer: VoidPtr, resultRoot: WasmRoot, exceptionRoot: WasmRoot, argsRootBuffer?: WasmRootBuffer -): void { +export function _handle_exception_for_call( + converter: Converter | undefined, token: BoundMethodToken | null, + buffer: VoidPtr, resultRoot: WasmRoot, + exceptionRoot: WasmRoot, argsRootBuffer?: WasmRootBuffer +) : void { const exc = _convert_exception_for_method_call(resultRoot.value, exceptionRoot.value); if (!exc) return; - _teardown_after_call(converter, buffer, resultRoot, exceptionRoot, argsRootBuffer); + _teardown_after_call(converter, token, buffer, resultRoot, exceptionRoot, argsRootBuffer); throw exc; } -function _handle_exception_and_produce_result_for_call(converter: Converter | undefined, buffer: VoidPtr, resultRoot: WasmRoot, exceptionRoot: WasmRoot, argsRootBuffer: WasmRootBuffer | undefined, is_result_marshaled: boolean -): any { - _handle_exception_for_call(converter, buffer, resultRoot, exceptionRoot, argsRootBuffer); +function _handle_exception_and_produce_result_for_call( + converter: Converter | undefined, token: BoundMethodToken | null, + buffer: VoidPtr, resultRoot: WasmRoot, + exceptionRoot: WasmRoot, argsRootBuffer: WasmRootBuffer | undefined, + is_result_marshaled: boolean +) : any { + _handle_exception_for_call(converter, token, buffer, resultRoot, exceptionRoot, argsRootBuffer); let result: any = resultRoot.value; if (is_result_marshaled) result = _unbox_mono_obj_root(resultRoot); - _teardown_after_call(converter, buffer, resultRoot, exceptionRoot, argsRootBuffer); + _teardown_after_call(converter, token, buffer, resultRoot, exceptionRoot, argsRootBuffer); return result; } -export function _teardown_after_call(converter: Converter | undefined, buffer: VoidPtr, resultRoot: WasmRoot, exceptionRoot: WasmRoot, argsRootBuffer?: WasmRootBuffer): void { - _release_args_root_buffer_from_method_call(converter, argsRootBuffer); - _release_buffer_from_method_call(converter, buffer); - - if (resultRoot) - resultRoot.release(); - if (exceptionRoot) - exceptionRoot.release(); +export function _teardown_after_call( + converter: Converter | undefined, token: BoundMethodToken | null, + buffer: VoidPtr, resultRoot: WasmRoot, + exceptionRoot: WasmRoot, argsRootBuffer?: WasmRootBuffer +) : void { + _release_temp_frame(); + _release_args_root_buffer_from_method_call(converter, token, argsRootBuffer); + _release_buffer_from_method_call(converter, token, buffer); + + if (resultRoot) { + resultRoot.value = 0; + if ((token !== null) && (token.scratchResultRoot === null)) + token.scratchResultRoot = resultRoot; + else + resultRoot.release (); + } + if (exceptionRoot) { + exceptionRoot.value = 0; + if ((token !== null) && (token.scratchExceptionRoot === null)) + token.scratchExceptionRoot = exceptionRoot; + else + exceptionRoot.release (); + } } -function _call_method_with_converted_args(method: MonoMethod, this_arg: MonoObject, converter: Converter | undefined, buffer: VoidPtr, is_result_marshaled: boolean, argsRootBuffer?: WasmRootBuffer) { +function _call_method_with_converted_args( + method: MonoMethod, this_arg: MonoObject, converter: Converter | undefined, + token: BoundMethodToken | null, buffer: VoidPtr, + is_result_marshaled: boolean, argsRootBuffer?: WasmRootBuffer +) : any { const resultRoot = mono_wasm_new_root(), exceptionRoot = mono_wasm_new_root(); resultRoot.value = cwraps.mono_wasm_invoke_method(method, this_arg, buffer, exceptionRoot.get_address()); - return _handle_exception_and_produce_result_for_call(converter, buffer, resultRoot, exceptionRoot, argsRootBuffer, is_result_marshaled); + return _handle_exception_and_produce_result_for_call(converter, token, buffer, resultRoot, exceptionRoot, argsRootBuffer, is_result_marshaled); } export function call_static_method(fqn: string, args: any[], signature: ArgsMarshalString): any { diff --git a/src/mono/wasm/runtime/roots.ts b/src/mono/wasm/runtime/roots.ts index f7ef2817ef0d2d..6d16379d914604 100644 --- a/src/mono/wasm/runtime/roots.ts +++ b/src/mono/wasm/runtime/roots.ts @@ -125,7 +125,7 @@ export function mono_wasm_release_roots(...args: WasmRoot[]): void { function _zero_region(byteOffset: VoidPtr, sizeBytes: number) { if (((byteOffset % 4) === 0) && ((sizeBytes % 4) === 0)) - Module.HEAP32.fill(0, byteOffset / 4, sizeBytes / 4); + Module.HEAP32.fill(0, byteOffset >>> 2, sizeBytes >>> 2); else Module.HEAP8.fill(0, byteOffset, sizeBytes); } @@ -170,7 +170,7 @@ export class WasmRootBuffer { const capacityBytes = capacity * 4; this.__offset = offset; - this.__offset32 = (offset / 4) | 0; + this.__offset32 = offset >>> 2; this.__count = capacity; this.length = capacity; this.__handle = cwraps.mono_wasm_register_root(offset, capacityBytes, name || "noname"); @@ -278,6 +278,9 @@ export class WasmRoot { } release(): void { + if (!this.__buffer) + throw new Error("No buffer"); + const maxPooledInstances = 128; if (_scratch_root_free_instances.length > maxPooledInstances) { _mono_wasm_release_scratch_index(this.__index); diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts index 821b197e9f8b21..9f09f750013377 100644 --- a/src/mono/wasm/runtime/startup.ts +++ b/src/mono/wasm/runtime/startup.ts @@ -11,6 +11,7 @@ import { mono_wasm_init_aot_profiler, mono_wasm_init_coverage_profiler } from ". import { mono_wasm_load_bytes_into_heap } from "./buffers"; import { bind_runtime_method, get_method, _create_primitive_converters } from "./method-binding"; import { conv_string } from "./strings"; +import { find_corlib_class } from "./class-loader"; // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types export function mono_wasm_invoke_js_blazor(exceptionMessage: Int32Ptr, callInfo: any, arg0: any, arg1: any, arg2: any): void | number { @@ -347,12 +348,14 @@ export function bindings_lazy_init(): void { (Float32Array.prototype)[wasm_type_symbol] = 17; (Float64Array.prototype)[wasm_type_symbol] = 18; - runtimeHelpers._box_buffer = Module._malloc(16); - runtimeHelpers._unbox_buffer = Module._malloc(16); - runtimeHelpers._class_int32 = cwraps.mono_wasm_find_corlib_class("System", "Int32"); - runtimeHelpers._class_uint32 = cwraps.mono_wasm_find_corlib_class("System", "UInt32"); - runtimeHelpers._class_double = cwraps.mono_wasm_find_corlib_class("System", "Double"); - runtimeHelpers._class_boolean = cwraps.mono_wasm_find_corlib_class("System", "Boolean"); + runtimeHelpers._box_buffer_size = 65536; + runtimeHelpers._unbox_buffer_size = 65536; + runtimeHelpers._box_buffer = Module._malloc(runtimeHelpers._box_buffer_size); + runtimeHelpers._unbox_buffer = Module._malloc(runtimeHelpers._unbox_buffer_size); + runtimeHelpers._class_int32 = find_corlib_class("System", "Int32"); + runtimeHelpers._class_uint32 = find_corlib_class("System", "UInt32"); + runtimeHelpers._class_double = find_corlib_class("System", "Double"); + runtimeHelpers._class_boolean = find_corlib_class("System", "Boolean"); runtimeHelpers.bind_runtime_method = bind_runtime_method; const bindingAssembly = INTERNAL.BINDING_ASM; @@ -417,9 +420,9 @@ function _load_assets_and_runtime(args: MonoConfig) { if (ctx.pending_count === 0) { try { _finalize_startup(args, ctx); - } catch (exc: any) { + } catch (exc : any) { console.error("Unhandled exception in _finalize_startup", exc); - console.log(exc.stack); + console.error(exc.stack); throw exc; } } diff --git a/src/mono/wasm/runtime/strings.ts b/src/mono/wasm/runtime/strings.ts index 0977591fdbab41..18871e42599f65 100644 --- a/src/mono/wasm/runtime/strings.ts +++ b/src/mono/wasm/runtime/strings.ts @@ -31,9 +31,9 @@ export class StringDecoder { cwraps.mono_wasm_string_get_data(mono_string, ppChars, pLengthBytes, pIsInterned); let result = mono_wasm_empty_string; - const lengthBytes = Module.HEAP32[pLengthBytes / 4], - pChars = Module.HEAP32[ppChars / 4], - isInterned = Module.HEAP32[pIsInterned / 4]; + const lengthBytes = Module.HEAP32[pLengthBytes >>> 2], + pChars = Module.HEAP32[ppChars >>> 2], + isInterned = Module.HEAP32[pIsInterned >>> 2]; if (pLengthBytes && pChars) { if ( @@ -186,9 +186,9 @@ export function js_string_to_mono_string(string: string): MonoString | null { return js_string_to_mono_string_new(string); } -function js_string_to_mono_string_new(string: string) { +export function js_string_to_mono_string_new(string: string) : MonoString { const buffer = Module._malloc((string.length + 1) * 2); - const buffer16 = (buffer / 2) | 0; + const buffer16 = (buffer >>> 1) | 0; for (let i = 0; i < string.length; i++) Module.HEAP16[buffer16 + i] = string.charCodeAt(i); Module.HEAP16[buffer16 + string.length] = 0; diff --git a/src/mono/wasm/runtime/types.ts b/src/mono/wasm/runtime/types.ts index 34a6584ccc02ef..cf9c65ee6b0ec9 100644 --- a/src/mono/wasm/runtime/types.ts +++ b/src/mono/wasm/runtime/types.ts @@ -44,7 +44,10 @@ export interface MonoString extends MonoObject { export interface MonoClass extends MonoObject { __brand: "MonoClass" } -export interface MonoMethod extends ManagedPointer { +export interface MonoType extends ManagedPointer{ + __brand: "MonoType" +} +export interface MonoMethod extends ManagedPointer{ __brand: "MonoMethod" } export interface MonoArray extends MonoObject { @@ -58,6 +61,7 @@ export const MonoObjectNull: MonoObject = 0; export const MonoArrayNull: MonoArray = 0; export const MonoAssemblyNull: MonoAssembly = 0; export const MonoClassNull: MonoClass = 0; +export const MonoTypeNull: MonoType = 0; export const MonoStringNull: MonoString = 0; export const JSHandleDisposed: JSHandle = -1; export const JSHandleNull: JSHandle = 0; @@ -137,6 +141,9 @@ export type RuntimeHelpers = { wasm_runtime_class: MonoClass; bind_runtime_method: typeof bind_runtime_method; + _box_buffer_size: number; + _unbox_buffer_size: number; + _box_buffer: VoidPtr; _unbox_buffer: VoidPtr; _class_int32: MonoClass;