diff --git a/src/mono/wasm/runtime/cs-to-js.ts b/src/mono/wasm/runtime/cs-to-js.ts index 070f2c498453e3074ef6b0ee9e7478b7afcd0f64..68e231edfe785317dd0fff3aa7e29f7b4d751758 100644 --- a/src/mono/wasm/runtime/cs-to-js.ts +++ b/src/mono/wasm/runtime/cs-to-js.ts @@ -15,6 +15,7 @@ import { get_js_owned_object_by_gc_handle, js_owned_gc_handle_symbol, mono_wasm_ import { mono_method_get_call_signature, call_method, wrap_error } from "./method-calls"; import { _js_to_mono_obj } from "./js-to-cs"; import { _are_promises_supported, _create_cancelable_promise } from "./cancelable-promise"; +import { getU32, getI32, getF32, getF64 } from "./memory"; // see src/mono/wasm/driver.c MARSHAL_TYPE_xxx and Runtime.cs MarshalType export enum MarshalType { @@ -132,7 +133,7 @@ export function _unbox_mono_obj_root_with_known_nonprimitive_type(root: WasmRoot let typePtr = MonoTypeNull; if ((type === MarshalType.VT) || (type == MarshalType.OBJECT)) { - typePtr = Module.HEAPU32[unbox_buffer >>> 2]; + typePtr = getU32(unbox_buffer); if (typePtr < 1024) throw new Error(`Got invalid MonoType ${typePtr} for object at address ${root.value} (root located at ${root.get_address()})`); } @@ -148,20 +149,20 @@ export function _unbox_mono_obj_root(root: WasmRoot): any { const type = cwraps.mono_wasm_try_unbox_primitive_and_get_type(root.value, unbox_buffer, runtimeHelpers._unbox_buffer_size); switch (type) { case MarshalType.INT: - return Module.HEAP32[unbox_buffer >>> 2]; + return getI32(unbox_buffer); case MarshalType.UINT32: - return Module.HEAPU32[unbox_buffer >>> 2]; + return getU32(unbox_buffer); case MarshalType.POINTER: // FIXME: Is this right? - return Module.HEAPU32[unbox_buffer >>> 2]; + return getU32(unbox_buffer); case MarshalType.FP32: - return Module.HEAPF32[unbox_buffer >>> 2]; + return getF32(unbox_buffer); case MarshalType.FP64: - return Module.HEAPF64[unbox_buffer >>> 3]; + return getF64(unbox_buffer); case MarshalType.BOOL: - return (Module.HEAP32[unbox_buffer >>> 2]) !== 0; + return (getI32(unbox_buffer)) !== 0; case MarshalType.CHAR: - return String.fromCharCode(Module.HEAP32[unbox_buffer >>> 2]); + return String.fromCharCode(getI32(unbox_buffer)); case MarshalType.NULL: return null; default: diff --git a/src/mono/wasm/runtime/exports.ts b/src/mono/wasm/runtime/exports.ts index bafde4cec98e95a94fe1a2224ad4927550e14f44..3dd3c123623095405debf74114ecb0bba5d38ff6 100644 --- a/src/mono/wasm/runtime/exports.ts +++ b/src/mono/wasm/runtime/exports.ts @@ -56,6 +56,12 @@ import { mono_wasm_release_cs_owned_object } from "./gc-handles"; import { mono_wasm_web_socket_open, mono_wasm_web_socket_send, mono_wasm_web_socket_receive, mono_wasm_web_socket_close, mono_wasm_web_socket_abort } from "./web-socket"; import cwraps from "./cwraps"; import { ArgsMarshalString } from "./method-binding"; +import { + setI8, setI16, setI32, setI64, + setU8, setU16, setU32, setF32, setF64, + getI8, getI16, getI32, getI64, + getU8, getU16, getU32, getF32, getF64, +} from "./memory"; export const MONO: MONO = { // current "public" MONO API @@ -251,6 +257,26 @@ export const INTERNAL: any = { mono_wasm_detach_debugger, mono_wasm_raise_debug_event, mono_wasm_runtime_is_ready: runtimeHelpers.mono_wasm_runtime_is_ready, + + // memory accessors + setI8, + setI16, + setI32, + setI64, + setU8, + setU16, + setU32, + setF32, + setF64, + getI8, + getI16, + getI32, + getI64, + getU8, + getU16, + getU32, + getF32, + getF64, }; // this represents visibility in the javascript diff --git a/src/mono/wasm/runtime/js-to-cs.ts b/src/mono/wasm/runtime/js-to-cs.ts index e9807e6033affe6f1d57763ee105bfaf90b7eebe..6914e6cfbc3636d4f568cd928bb01a50bb940131 100644 --- a/src/mono/wasm/runtime/js-to-cs.ts +++ b/src/mono/wasm/runtime/js-to-cs.ts @@ -15,6 +15,7 @@ import { js_string_to_mono_string, js_string_to_mono_string_interned } from "./s import { isThenable } from "./cancelable-promise"; import { has_backing_array_buffer } from "./buffers"; import { Int32Ptr, JSHandle, MonoArray, MonoMethod, MonoObject, MonoObjectNull, MonoString, wasm_type_symbol } from "./types"; +import { setI32, setU32, setF64 } from "./memory"; // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types export function _js_to_mono_uri(should_add_in_flight: boolean, js_obj: any): MonoObject { @@ -109,22 +110,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 >>> 2] = js_obj; + setI32(runtimeHelpers._box_buffer, js_obj); return cwraps.mono_wasm_box_primitive(runtimeHelpers._class_int32, runtimeHelpers._box_buffer, 4); } function _box_js_uint(js_obj: number) { - Module.HEAPU32[runtimeHelpers._box_buffer >>> 2] = js_obj; + setU32(runtimeHelpers._box_buffer, js_obj); return cwraps.mono_wasm_box_primitive(runtimeHelpers._class_uint32, runtimeHelpers._box_buffer, 4); } function _box_js_double(js_obj: number) { - Module.HEAPF64[runtimeHelpers._box_buffer >>> 3] = js_obj; + setF64(runtimeHelpers._box_buffer, js_obj); return cwraps.mono_wasm_box_primitive(runtimeHelpers._class_double, runtimeHelpers._box_buffer, 8); } export function _box_js_bool(js_obj: boolean): MonoObject { - Module.HEAP32[runtimeHelpers._box_buffer >>> 2] = js_obj ? 1 : 0; + setI32(runtimeHelpers._box_buffer, 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 index f56322ba4b3787d02a0f4dab9949b4d14df85d40..becf3fe3ff4d7669984be8503ae5ab246e8bbc5b 100644 --- a/src/mono/wasm/runtime/memory.ts +++ b/src/mono/wasm/runtime/memory.ts @@ -28,3 +28,80 @@ export function _release_temp_frame(): void { for (let i = 0, l = frame.length; i < l; i++) Module._free(frame[i]); } + +type _MemOffset = number | VoidPtr | NativePointer; + +export function setU8 (offset: _MemOffset, value: number) : void { + Module.HEAPU8[offset] = value; +} + +export function setU16 (offset: _MemOffset, value: number) : void { + Module.HEAPU16[offset >>> 1] = value; +} + +export function setU32 (offset: _MemOffset, value: number) : void { + Module.HEAPU32[offset >>> 2] = value; +} + +export function setI8 (offset: _MemOffset, value: number) : void { + Module.HEAP8[offset] = value; +} + +export function setI16 (offset: _MemOffset, value: number) : void { + Module.HEAP16[offset >>> 1] = value; +} + +export function setI32 (offset: _MemOffset, value: number) : void { + Module.HEAP32[offset >>> 2] = value; +} + +// NOTE: Accepts a number, not a BigInt, so values over Number.MAX_SAFE_INTEGER will be corrupted +export function setI64 (offset: _MemOffset, value: number) : void { + Module.setValue(offset, value, "i64"); +} + +export function setF32 (offset: _MemOffset, value: number) : void { + Module.HEAPF32[offset >>> 2] = value; +} + +export function setF64 (offset: _MemOffset, value: number) : void { + Module.HEAPF64[offset >>> 3] = value; +} + + +export function getU8 (offset: _MemOffset) : number { + return Module.HEAPU8[offset]; +} + +export function getU16 (offset: _MemOffset) : number { + return Module.HEAPU16[offset >>> 1]; +} + +export function getU32 (offset: _MemOffset) : number { + return Module.HEAPU32[offset >>> 2]; +} + +export function getI8 (offset: _MemOffset) : number { + return Module.HEAP8[offset]; +} + +export function getI16 (offset: _MemOffset) : number { + return Module.HEAP16[offset >>> 1]; +} + +export function getI32 (offset: _MemOffset) : number { + return Module.HEAP32[offset >>> 2]; +} + +// NOTE: Returns a number, not a BigInt. This means values over Number.MAX_SAFE_INTEGER will be corrupted +export function getI64 (offset: _MemOffset) : number { + return Module.getValue(offset, "i64"); +} + +export function getF32 (offset: _MemOffset) : number { + return Module.HEAPF32[offset >>> 2]; +} + +export function getF64 (offset: _MemOffset) : number { + return Module.HEAPF64[offset >>> 3]; +} diff --git a/src/mono/wasm/runtime/method-binding.ts b/src/mono/wasm/runtime/method-binding.ts index 57944e357453d13d18bef01437a283b3d0bd9e5a..a9b2ebb00d35ab09ba1b103d8c24720718779c29 100644 --- a/src/mono/wasm/runtime/method-binding.ts +++ b/src/mono/wasm/runtime/method-binding.ts @@ -7,7 +7,11 @@ import { BINDING, 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 { + _create_temp_frame, + getI32, getU32, getF32, getF64, + setI32, setU32, setF32, setF64, setI64, +} from "./memory"; import { _get_args_root_buffer_for_method_call, _get_buffer_for_method_call, _handle_exception_for_call, _teardown_after_call @@ -213,6 +217,11 @@ export function _compile_converter_for_marshal_string(args_marshal: ArgsMarshalS Module, _malloc: Module._malloc, mono_wasm_unbox_rooted: cwraps.mono_wasm_unbox_rooted, + setI32, + setU32, + setF32, + setF64, + setI64 }; let indirectLocalOffset = 0; @@ -220,8 +229,6 @@ export function _compile_converter_for_marshal_string(args_marshal: ArgsMarshalS "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;", "" ); @@ -253,37 +260,35 @@ export function _compile_converter_for_marshal_string(args_marshal: ArgsMarshalS body.push(`${valueKey} = mono_wasm_unbox_rooted (${valueKey});`); if (step.indirect) { - let heapArrayName = null; + const offsetText = `(indirectStart + ${indirectLocalOffset})`; switch (step.indirect) { case "u32": - heapArrayName = "HEAPU32"; + body.push(`setU32(${offsetText}, ${valueKey});`); break; case "i32": - heapArrayName = "HEAP32"; + body.push(`setI32(${offsetText}, ${valueKey});`); break; case "float": - heapArrayName = "HEAPF32"; + body.push(`setF32(${offsetText}, ${valueKey});`); break; case "double": - body.push(`Module.HEAPF64[indirect64 + ${(indirectLocalOffset >>> 3)}] = ${valueKey};`); + body.push(`setF64(${offsetText}, ${valueKey});`); break; case "i64": - body.push(`Module.setValue (indirectStart + ${indirectLocalOffset}, ${valueKey}, 'i64');`); + body.push(`setI64(${offsetText}, ${valueKey});`); break; default: throw new Error("Unimplemented indirect type: " + step.indirect); } - if (heapArrayName) - body.push(`Module.${heapArrayName}[indirect32 + ${(indirectLocalOffset >>> 2)}] = ${valueKey};`); - - body.push(`Module.HEAP32[buffer32 + ${i}] = indirectStart + ${indirectLocalOffset};`, ""); + body.push(`setU32(buffer + (${i} * 4), ${offsetText});`); indirectLocalOffset += step.size!; } else { - body.push(`Module.HEAP32[buffer32 + ${i}] = ${valueKey};`, ""); + body.push(`setI32(buffer + (${i} * 4), ${valueKey});`); indirectLocalOffset += 4; } + body.push(""); } body.push("return buffer;"); @@ -404,7 +409,11 @@ export function mono_bind_method(method: MonoMethod, this_arg: MonoObject | null this_arg, token, unbox_buffer, - unbox_buffer_size + unbox_buffer_size, + getI32, + getU32, + getF32, + getF64 }; const converterKey = converter ? "converter_" + converter.name : ""; @@ -493,18 +502,18 @@ export function mono_bind_method(method: MonoMethod, this_arg: MonoObject | null " let resultType = mono_wasm_try_unbox_primitive_and_get_type (resultPtr, unbox_buffer, unbox_buffer_size);", " switch (resultType) {", ` case ${MarshalType.INT}:`, - " result = Module.HEAP32[unbox_buffer >>> 2]; break;", + " result = getI32(unbox_buffer); break;", ` case ${MarshalType.POINTER}:`, // FIXME: Is this right? ` case ${MarshalType.UINT32}:`, - " result = Module.HEAPU32[unbox_buffer >>> 2]; break;", + " result = getU32(unbox_buffer); break;", ` case ${MarshalType.FP32}:`, - " result = Module.HEAPF32[unbox_buffer >>> 2]; break;", + " result = getF32(unbox_buffer); break;", ` case ${MarshalType.FP64}:`, - " result = Module.HEAPF64[unbox_buffer >>> 3]; break;", + " result = getF64(unbox_buffer); break;", ` case ${MarshalType.BOOL}:`, - " result = (Module.HEAP32[unbox_buffer >>> 2]) !== 0; break;", + " result = getI32(unbox_buffer) !== 0; break;", ` case ${MarshalType.CHAR}:`, - " result = String.fromCharCode(Module.HEAP32[unbox_buffer >>> 2]); break;", + " result = String.fromCharCode(getI32(unbox_buffer)); break;", " default:", " result = _unbox_mono_obj_root_with_known_nonprimitive_type (resultRoot, resultType, unbox_buffer); break;", " }", diff --git a/src/mono/wasm/runtime/roots.ts b/src/mono/wasm/runtime/roots.ts index 6d16379d914604a614dbf92b57c3fe4f4b5ee4ec..baf847dff3b942b6850abd9f822f23576d822566 100644 --- a/src/mono/wasm/runtime/roots.ts +++ b/src/mono/wasm/runtime/roots.ts @@ -196,13 +196,18 @@ export class WasmRootBuffer { return this.__offset32 + index; } + // NOTE: These functions do not use the helpers from memory.ts because WasmRoot.get and WasmRoot.set + // are hot-spots when you profile any application that uses the bindings extensively. + get(index: number): ManagedPointer { this._check_in_range(index); - return Module.HEAP32[this.get_address_32(index)]; + const offset = this.get_address_32(index); + return Module.HEAP32[offset]; } set(index: number, value: ManagedPointer): ManagedPointer { - Module.HEAP32[this.get_address_32(index)] = value; + const offset = this.get_address_32(index); + Module.HEAP32[offset] = value; return value; } diff --git a/src/mono/wasm/runtime/strings.ts b/src/mono/wasm/runtime/strings.ts index 18871e42599f655b962a4d5345138ce4c3ea18a5..feb0f671574419e38b35b0ea332af905c8d415ba 100644 --- a/src/mono/wasm/runtime/strings.ts +++ b/src/mono/wasm/runtime/strings.ts @@ -6,6 +6,7 @@ import { CharPtr, MonoString, MonoStringNull, NativePointer } from "./types"; import { Module } from "./modules"; import cwraps from "./cwraps"; import { mono_wasm_new_root } from "./roots"; +import { getI32 } from "./memory"; export class StringDecoder { @@ -31,9 +32,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 >>> 2], - pChars = Module.HEAP32[ppChars >>> 2], - isInterned = Module.HEAP32[pIsInterned >>> 2]; + const lengthBytes = getI32(pLengthBytes), + pChars = getI32(ppChars), + isInterned = getI32(pIsInterned); if (pLengthBytes && pChars) { if (