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] fix 4GB JS interop #109079

Merged
merged 10 commits into from
Oct 31, 2024
4 changes: 2 additions & 2 deletions src/mono/browser/runtime/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { toBase64StringImpl } from "./base64";
import cwraps from "./cwraps";
import { VoidPtr, CharPtr } from "./types/emscripten";
import { mono_log_warn } from "./logging";
import { forceThreadMemoryViewRefresh, localHeapViewU8 } from "./memory";
import { forceThreadMemoryViewRefresh, localHeapViewU8, malloc } from "./memory";
import { utf8ToString } from "./strings";
const commands_received: any = new Map<number, CommandResponse>();
commands_received.remove = function (key: number): CommandResponse {
Expand Down Expand Up @@ -66,7 +66,7 @@ function mono_wasm_malloc_and_set_debug_buffer (command_parameters: string) {
if (_debugger_buffer)
Module._free(_debugger_buffer);
_debugger_buffer_len = Math.max(command_parameters.length, _debugger_buffer_len, 256);
_debugger_buffer = Module._malloc(_debugger_buffer_len);
_debugger_buffer = malloc(_debugger_buffer_len);
}
const byteCharacters = atob(command_parameters);
const heapU8 = localHeapViewU8();
Expand Down
6 changes: 3 additions & 3 deletions src/mono/browser/runtime/interp-pgo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import WasmEnableThreads from "consts:wasmEnableThreads";

import { ENVIRONMENT_IS_WEB, Module, loaderHelpers, runtimeHelpers } from "./globals";
import { mono_log_info, mono_log_error, mono_log_warn } from "./logging";
import { localHeapViewU8 } from "./memory";
import { localHeapViewU8, malloc } from "./memory";
import cwraps from "./cwraps";
import { MonoConfigInternal } from "./types/internal";

Expand All @@ -31,7 +31,7 @@ export async function interp_pgo_save_data () {
return;
}

const pData = <any>Module._malloc(expectedSize);
const pData = <any>malloc(expectedSize);
const saved = cwraps.mono_interp_pgo_save_table(pData, expectedSize) === 0;
if (!saved) {
mono_log_error("Failed to save interp_pgo table (Unknown error)");
Expand Down Expand Up @@ -66,7 +66,7 @@ export async function interp_pgo_load_data () {
return;
}

const pData = <any>Module._malloc(data.byteLength);
const pData = <any>malloc(data.byteLength);
const u8 = localHeapViewU8();
u8.set(new Uint8Array(data), pData);

Expand Down
5 changes: 5 additions & 0 deletions src/mono/browser/runtime/invoke-js.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const js_import_wrapper_by_fn_handle: Function[] = <any>[null];// 0th slo
export function mono_wasm_bind_js_import_ST (signature: JSFunctionSignature): VoidPtr {
if (WasmEnableThreads) return VoidPtrNull;
assert_js_interop();
signature = ((signature as any) >>> 0) as any;
try {
bind_js_import(signature);
return VoidPtrNull;
Expand All @@ -35,6 +36,8 @@ export function mono_wasm_bind_js_import_ST (signature: JSFunctionSignature): Vo
export function mono_wasm_invoke_jsimport_MT (signature: JSFunctionSignature, args: JSMarshalerArguments) {
if (!WasmEnableThreads) return;
assert_js_interop();
signature = ((signature as any) >>> 0) as any;
args = ((args as any) >>> 0) as any;

const function_handle = get_signature_handle(signature);

Expand Down Expand Up @@ -73,6 +76,7 @@ export function mono_wasm_invoke_jsimport_MT (signature: JSFunctionSignature, ar
export function mono_wasm_invoke_jsimport_ST (function_handle: JSFnHandle, args: JSMarshalerArguments): void {
if (WasmEnableThreads) return;
loaderHelpers.assert_runtime_running();
args = ((args as any) >>> 0) as any;
const bound_fn = js_import_wrapper_by_fn_handle[<any>function_handle];
mono_assert(bound_fn, () => `Imported function handle expected ${function_handle}`);
bound_fn(args);
Expand Down Expand Up @@ -364,6 +368,7 @@ export function mono_wasm_invoke_js_function_impl (bound_function_js_handle: JSH
loaderHelpers.assert_runtime_running();
const bound_fn = mono_wasm_get_jsobj_from_js_handle(bound_function_js_handle);
mono_assert(bound_fn && typeof (bound_fn) === "function" && bound_fn[bound_js_function_symbol], () => `Bound function handle expected ${bound_function_js_handle}`);
args = ((args as any) >>> 0) as any;
bound_fn(args);
}

Expand Down
5 changes: 3 additions & 2 deletions src/mono/browser/runtime/jiterpreter-interp-entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { MonoMethod, MonoType } from "./types/internal";
import { NativePointer } from "./types/emscripten";
import { Module, mono_assert } from "./globals";
import {
setI32, getU32_unaligned, _zero_region
setI32, getU32_unaligned, _zero_region,
malloc
} from "./memory";
import { WasmOpcode } from "./jiterpreter-opcodes";
import cwraps from "./cwraps";
Expand Down Expand Up @@ -554,7 +555,7 @@ function generate_wasm_body (
// FIXME: Pre-allocate these buffers and their constant slots at the start before we
// generate function bodies, so that even if we run out of constant slots for MonoType we
// will always have put the buffers in a constant slot. This will be necessary for thread safety
const scratchBuffer = <any>Module._malloc(sizeOfJiterpEntryData);
const scratchBuffer = <any>malloc(sizeOfJiterpEntryData);
_zero_region(scratchBuffer, sizeOfJiterpEntryData);

// Initialize the parameter count in the data blob. This is used to calculate the new value of sp
Expand Down
11 changes: 6 additions & 5 deletions src/mono/browser/runtime/jiterpreter-support.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { WasmOpcode, WasmSimdOpcode, WasmAtomicOpcode, WasmValtype } from "./jit
import { MintOpcode } from "./mintops";
import cwraps from "./cwraps";
import { mono_log_error, mono_log_info } from "./logging";
import { localHeapViewU8, localHeapViewU32 } from "./memory";
import { localHeapViewU8, localHeapViewU32, malloc } from "./memory";
import {
JiterpNumberMode, BailoutReason, JiterpreterTable,
JiterpCounter, JiterpMember, OpcodeInfoType
Expand All @@ -20,7 +20,8 @@ export const maxFailures = 2,
shortNameBase = 36,
// NOTE: This needs to be big enough to hold the maximum module size since there's no auto-growth
// support yet. If that becomes a problem, we should just make it growable
blobBuilderCapacity = 24 * 1024;
blobBuilderCapacity = 24 * 1024,
INT32_MIN = -2147483648;

// uint16
export declare interface MintOpcodePtr extends NativePointer {
Expand Down Expand Up @@ -948,7 +949,7 @@ export class BlobBuilder {

constructor () {
this.capacity = blobBuilderCapacity;
this.buffer = <any>Module._malloc(this.capacity);
this.buffer = <any>malloc(this.capacity);
mono_assert(this.buffer, () => `Failed to allocate ${blobBuilderCapacity}b buffer for BlobBuilder`);
localHeapViewU8().fill(0, this.buffer, this.buffer + this.capacity);
this.size = 0;
Expand Down Expand Up @@ -1665,7 +1666,7 @@ export function append_exit (builder: WasmBuilder, ip: MintOpcodePtr, opcodeCoun

export function copyIntoScratchBuffer (src: NativePointer, size: number): NativePointer {
if (!scratchBuffer)
scratchBuffer = Module._malloc(64);
scratchBuffer = malloc(64);
if (size > 64)
throw new Error("Scratch buffer size is 64");

Expand Down Expand Up @@ -2106,7 +2107,7 @@ function updateOptions () {
optionTable = <any>{};
for (const k in optionNames) {
const value = cwraps.mono_jiterp_get_option_as_int(optionNames[k]);
if (value > -2147483647)
if (value !== INT32_MIN)
(<any>optionTable)[k] = value;
else
mono_log_info(`Failed to retrieve value of option ${optionNames[k]}`);
Expand Down
4 changes: 2 additions & 2 deletions src/mono/browser/runtime/managed-exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { marshal_int32_to_js, end_marshal_task_to_js, marshal_string_to_js, begi
import { do_not_force_dispose, is_gcv_handle } from "./gc-handles";
import { assert_c_interop, assert_js_interop } from "./invoke-js";
import { monoThreadInfo, mono_wasm_main_thread_ptr } from "./pthreads";
import { _zero_region, copyBytes } from "./memory";
import { _zero_region, copyBytes, malloc } from "./memory";
import { stringToUTF8Ptr } from "./strings";
import { mono_log_error } from "./logging";

Expand Down Expand Up @@ -285,7 +285,7 @@ export function invoke_async_jsexport (managedTID: PThreadPtr, method: MonoMetho
} else {
set_receiver_should_free(args);
const bytes = JavaScriptMarshalerArgSize * size;
const cpy = Module._malloc(bytes) as any;
const cpy = malloc(bytes) as any;
copyBytes(args as any, cpy, bytes);
twraps.mono_wasm_invoke_jsexport_async_post(managedTID, method, cpy);
}
Expand Down
12 changes: 6 additions & 6 deletions src/mono/browser/runtime/marshal-to-cs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import WasmEnableJsInteropByValue from "consts:wasmEnableJsInteropByValue";
import { PromiseHolder, isThenable } from "./cancelable-promise";
import cwraps from "./cwraps";
import { alloc_gcv_handle, assert_not_disposed, cs_owned_js_handle_symbol, js_owned_gc_handle_symbol, mono_wasm_get_js_handle, setup_managed_proxy } from "./gc-handles";
import { Module, mono_assert, runtimeHelpers } from "./globals";
import { mono_assert, runtimeHelpers } from "./globals";
import {
ManagedError,
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_bool, set_arg_date,
Expand All @@ -18,7 +18,7 @@ import {
set_arg_element_type, ManagedObject, JavaScriptMarshalerArgSize, proxy_debug_symbol, get_arg_gc_handle, get_arg_type, set_arg_proxy_context, get_arg_intptr
} from "./marshal";
import { get_marshaler_to_js_by_type } from "./marshal-to-js";
import { _zero_region, localHeapViewF64, localHeapViewI32, localHeapViewU8 } from "./memory";
import { _zero_region, localHeapViewF64, localHeapViewI32, localHeapViewU8, malloc } from "./memory";
import { stringToMonoStringRoot, stringToUTF16 } from "./strings";
import { JSMarshalerArgument, JSMarshalerArguments, JSMarshalerType, MarshalerToCs, MarshalerToJs, BoundMarshalerToCs, MarshalerType } from "./types/internal";
import { TypedArray } from "./types/emscripten";
Expand Down Expand Up @@ -218,7 +218,7 @@ export function marshal_string_to_cs (arg: JSMarshalerArgument, value: string) {
function _marshal_string_to_cs_impl (arg: JSMarshalerArgument, value: string) {
if (WasmEnableJsInteropByValue) {
const bufferLen = value.length * 2;
const buffer = Module._malloc(bufferLen);// together with Marshal.FreeHGlobal
const buffer = malloc(bufferLen);// together with Marshal.FreeHGlobal
stringToUTF16(buffer as any, buffer as any + bufferLen, value);
set_arg_intptr(arg, buffer);
set_arg_length(arg, value.length);
Expand Down Expand Up @@ -458,7 +458,7 @@ export function marshal_array_to_cs_impl (arg: JSMarshalerArgument, value: Array
mono_assert(element_size != -1, () => `Element type ${element_type} not supported`);
const length = value.length;
const buffer_length = element_size * length;
const buffer_ptr = <any>Module._malloc(buffer_length);
const buffer_ptr = malloc(buffer_length) as any;
if (element_type == MarshalerType.String) {
mono_check(Array.isArray(value), "Value is not an Array");
_zero_region(buffer_ptr, buffer_length);
Expand Down Expand Up @@ -494,11 +494,11 @@ export function marshal_array_to_cs_impl (arg: JSMarshalerArgument, value: Array
targetView.set(value);
} else if (element_type == MarshalerType.Int32) {
mono_check(Array.isArray(value) || value instanceof Int32Array, "Value is not an Array or Int32Array");
const targetView = localHeapViewI32().subarray(<any>buffer_ptr >> 2, (buffer_ptr >> 2) + length);
const targetView = localHeapViewI32().subarray(<any>buffer_ptr >>> 2, (buffer_ptr >>> 2) + length);
targetView.set(value);
} else if (element_type == MarshalerType.Double) {
mono_check(Array.isArray(value) || value instanceof Float64Array, "Value is not an Array or Float64Array");
const targetView = localHeapViewF64().subarray(<any>buffer_ptr >> 3, (buffer_ptr >> 3) + length);
const targetView = localHeapViewF64().subarray(<any>buffer_ptr >>> 3, (buffer_ptr >>> 3) + length);
targetView.set(value);
} else {
throw new Error("not implemented");
Expand Down
7 changes: 4 additions & 3 deletions src/mono/browser/runtime/marshal-to-js.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ export function mono_wasm_resolve_or_reject_promise_impl (args: JSMarshalerArgum
mono_log_debug("This promise resolution/rejection can't be propagated to managed code, mono runtime already exited.");
return;
}
args = ((args as any) >>> 0) as any;
const exc = get_arg(args, 0);
const receiver_should_free = WasmEnableThreads && is_receiver_should_free(args);
try {
Expand Down Expand Up @@ -524,13 +525,13 @@ function _marshal_array_to_js_impl (arg: JSMarshalerArgument, element_type: Mars
result[index] = _marshal_js_object_to_js(element_arg);
}
} else if (element_type == MarshalerType.Byte) {
const sourceView = localHeapViewU8().subarray(<any>buffer_ptr, buffer_ptr + length);
const sourceView = localHeapViewU8().subarray(buffer_ptr >>> 0, (buffer_ptr >>> 0) + length);
result = sourceView.slice();//copy
} else if (element_type == MarshalerType.Int32) {
const sourceView = localHeapViewI32().subarray(buffer_ptr >> 2, (buffer_ptr >> 2) + length);
const sourceView = localHeapViewI32().subarray(buffer_ptr >>> 2, (buffer_ptr >>> 2) + length);
result = sourceView.slice();//copy
} else if (element_type == MarshalerType.Double) {
const sourceView = localHeapViewF64().subarray(buffer_ptr >> 3, (buffer_ptr >> 3) + length);
const sourceView = localHeapViewF64().subarray(buffer_ptr >>> 3, (buffer_ptr >>> 3) + length);
result = sourceView.slice();//copy
} else {
throw new Error(`NotImplementedException ${element_type}. ${jsinteropDoc}`);
Expand Down
4 changes: 2 additions & 2 deletions src/mono/browser/runtime/marshal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ export function get_arg_i32 (arg: JSMarshalerArgument): number {

export function get_arg_intptr (arg: JSMarshalerArgument): number {
mono_assert(arg, "Null arg");
return getI32(<any>arg);
return getU32(<any>arg);
}

export function get_arg_i52 (arg: JSMarshalerArgument): number {
Expand Down Expand Up @@ -291,7 +291,7 @@ export function set_arg_i32 (arg: JSMarshalerArgument, value: number): void {

export function set_arg_intptr (arg: JSMarshalerArgument, value: VoidPtr): void {
mono_assert(arg, "Null arg");
setI32(<any>arg, <any>value);
setU32(<any>arg, <any>value);
}

export function set_arg_i52 (arg: JSMarshalerArgument, value: number): void {
Expand Down
9 changes: 7 additions & 2 deletions src/mono/browser/runtime/memory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ let alloca_base: VoidPtr, alloca_offset: VoidPtr, alloca_limit: VoidPtr;
function _ensure_allocated (): void {
if (alloca_base)
return;
alloca_base = Module._malloc(alloca_buffer_size);
alloca_base = malloc(alloca_buffer_size);
alloca_offset = alloca_base;
alloca_limit = <VoidPtr>(<any>alloca_base + alloca_buffer_size);
}
Expand All @@ -37,6 +37,11 @@ export function temp_malloc (size: number): VoidPtr {
return result;
}

// returns always uint32 (not negative Number)
export function malloc (size: number): VoidPtr {
return (Module._malloc(size) as any >>> 0) as any;
}

export function _create_temp_frame (): void {
_ensure_allocated();
alloca_stack.push(alloca_offset);
Expand Down Expand Up @@ -326,7 +331,7 @@ export function withStackAlloc<T1, T2, T3, TResult> (bytesWanted: number, f: (pt
// and it is copied to that location. returns the address of the allocation.
export function mono_wasm_load_bytes_into_heap (bytes: Uint8Array): VoidPtr {
// pad sizes by 16 bytes for simd
const memoryOffset = Module._malloc(bytes.length + 16);
const memoryOffset = malloc(bytes.length + 16);
if (<any>memoryOffset <= 0) {
mono_log_error(`malloc failed to allocate ${(bytes.length + 16)} bytes.`);
throw new Error("Out of memory");
Expand Down
4 changes: 2 additions & 2 deletions src/mono/browser/runtime/roots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import cwraps from "./cwraps";
import { Module, mono_assert, runtimeHelpers } from "./globals";
import { VoidPtr, ManagedPointer, NativePointer } from "./types/emscripten";
import { MonoObjectRef, MonoObjectRefNull, MonoObject, is_nullish, WasmRoot, WasmRootBuffer } from "./types/internal";
import { _zero_region, localHeapViewU32 } from "./memory";
import { _zero_region, localHeapViewU32, malloc } from "./memory";
import { gc_locked } from "./gc-lock";

const maxScratchRoots = 8192;
Expand All @@ -31,7 +31,7 @@ export function mono_wasm_new_root_buffer (capacity: number, name?: string): Was
capacity = capacity | 0;

const capacityBytes = capacity * 4;
const offset = Module._malloc(capacityBytes);
const offset = malloc(capacityBytes);
if ((<any>offset % 4) !== 0)
throw new Error("Malloc returned an unaligned offset");

Expand Down
Loading
Loading