diff --git a/packages/emnapi/src/core/memory.ts b/packages/emnapi/src/core/memory.ts new file mode 100644 index 00000000..de1ba663 --- /dev/null +++ b/packages/emnapi/src/core/memory.ts @@ -0,0 +1,35 @@ +/* eslint-disable @typescript-eslint/indent */ + +function _napi_adjust_external_memory ( + env: napi_env, + change_in_bytes: bigint, + adjusted_value: number +): napi_status { + $CHECK_ENV!(env) + const envObject = emnapiCtx.envStore.get(env)! + $CHECK_ARG!(envObject, adjusted_value) + + const change_in_bytes_number = Number(change_in_bytes) + + if (change_in_bytes_number < 0) { + return envObject.setLastError(napi_status.napi_invalid_arg) + } + + const old_size = wasmMemory.buffer.byteLength + let new_size = old_size + change_in_bytes_number + new_size = new_size + ((65536 - new_size % 65536) % 65536) + if (wasmMemory.grow((new_size - old_size + 65535) >> 16) === -1) { + return envObject.setLastError(napi_status.napi_generic_failure) + } + + $from64('adjusted_value') + if (emnapiCtx.feature.supportBigInt) { + $makeSetValue('adjusted_value', 0, 'wasmMemory.buffer.byteLength', 'i64') + } else { + emnapiSetValueI64(adjusted_value, wasmMemory.buffer.byteLength) + } + + return envObject.clearLastError() +} + +emnapiImplement('napi_adjust_external_memory', 'ipjp', _napi_adjust_external_memory, []) diff --git a/packages/emnapi/src/core/tsconfig.json b/packages/emnapi/src/core/tsconfig.json index c3526072..46d51a7a 100644 --- a/packages/emnapi/src/core/tsconfig.json +++ b/packages/emnapi/src/core/tsconfig.json @@ -20,6 +20,7 @@ "./miscellaneous.ts", "./string.ts", "./util.ts", + "./memory.ts", "../../../runtime/src/typings/**/*.d.ts", "../typings/**/*.d.ts", "../*.ts", diff --git a/packages/emnapi/src/emscripten/memory.ts b/packages/emnapi/src/emscripten/memory.ts new file mode 100644 index 00000000..61c1879c --- /dev/null +++ b/packages/emnapi/src/emscripten/memory.ts @@ -0,0 +1,57 @@ +/* eslint-disable @typescript-eslint/indent */ +declare function _emscripten_resize_heap (requested: number): boolean + +function _napi_adjust_external_memory ( + env: napi_env, + low: number, + high: number, + adjusted_value: number +): napi_status { + $CHECK_ENV!(env) + const envObject = emnapiCtx.envStore.get(env)! + + let change_in_bytes: number + +// #if WASM_BIGINT + if (!high) return envObject.setLastError(napi_status.napi_invalid_arg) + change_in_bytes = Number(low) +// #else + if (!adjusted_value) return envObject.setLastError(napi_status.napi_invalid_arg) + change_in_bytes = (low >>> 0) + (high * Math.pow(2, 32)) +// #endif + + if (change_in_bytes < 0) { + return envObject.setLastError(napi_status.napi_invalid_arg) + } + + if (change_in_bytes > 0) { + const old_size = wasmMemory.buffer.byteLength + const new_size = old_size + change_in_bytes + if (!_emscripten_resize_heap(new_size)) { + return envObject.setLastError(napi_status.napi_generic_failure) + } + } + +// #if WASM_BIGINT + $from64('high') + if (emnapiCtx.feature.supportBigInt) { + $makeSetValue('high', 0, 'wasmMemory.buffer.byteLength', 'i64') + } else { + emnapiSetValueI64(high, wasmMemory.buffer.byteLength) + } +// #else + $from64('adjusted_value') + $makeSetValue('adjusted_value', 0, 'wasmMemory.buffer.byteLength', 'i64') +// #endif + + return envObject.clearLastError() +} + +emnapiImplement('napi_adjust_external_memory', 'ipjp', _napi_adjust_external_memory, + [ +// #if WASM_BIGINT + '$emnapiSetValueI64', +// #endif + 'emscripten_resize_heap' + ] +) diff --git a/packages/emnapi/src/js_native_api.c b/packages/emnapi/src/js_native_api.c index 60b6aab9..b3cd8e2d 100644 --- a/packages/emnapi/src/js_native_api.c +++ b/packages/emnapi/src/js_native_api.c @@ -1,9 +1,5 @@ #include "emnapi_internal.h" -#ifdef __EMSCRIPTEN__ -#include -#endif - EXTERN_C_START static const char* emnapi_error_messages[] = { @@ -67,34 +63,4 @@ napi_status napi_get_last_error_info( return napi_ok; } -#define PAGESIZE 65536 - -napi_status napi_adjust_external_memory(napi_env env, - int64_t change_in_bytes, - int64_t* adjusted_value) { - CHECK_ENV(env); - CHECK_ARG(env, adjusted_value); - - if (change_in_bytes < 0) { - return napi_set_last_error(env, napi_invalid_arg, 0, NULL); - } - - size_t old_size = __builtin_wasm_memory_size(0) << 16; - size_t new_size = old_size + (size_t) change_in_bytes; -#ifdef __EMSCRIPTEN__ - if (!emscripten_resize_heap(new_size)) { - return napi_set_last_error(env, napi_generic_failure, 0, NULL); - } -#else - new_size = new_size + (PAGESIZE - new_size % PAGESIZE) % PAGESIZE; - if (-1 == __builtin_wasm_memory_grow(0, (new_size - old_size + 65535) >> 16)) { - return napi_set_last_error(env, napi_generic_failure, 0, NULL); - } -#endif - - *adjusted_value = (int64_t) (__builtin_wasm_memory_size(0) << 16); - - return napi_clear_last_error(env); -} - EXTERN_C_END diff --git a/packages/emnapi/src/value/convert2c.ts b/packages/emnapi/src/value/convert2c.ts index 732c8a3a..eb92a23e 100644 --- a/packages/emnapi/src/value/convert2c.ts +++ b/packages/emnapi/src/value/convert2c.ts @@ -416,6 +416,17 @@ function napi_get_value_int32 (env: napi_env, value: napi_value, result: Pointer return envObject.clearLastError() } +function emnapiSetValueI64 (result: Pointer, numberValue: number): void { + let tempDouble: number + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const tempI64 = [ + numberValue >>> 0, + (tempDouble = numberValue, +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil((tempDouble - +(~~tempDouble >>> 0)) / 4294967296) >>> 0 : 0) + ] + $makeSetValue('result', 0, 'tempI64[0]', 'i32') + $makeSetValue('result', 4, 'tempI64[1]', 'i32') +} + function napi_get_value_int64 (env: napi_env, value: napi_value, result: Pointer): napi_status { $CHECK_ENV!(env) const envObject = emnapiCtx.envStore.get(env)! @@ -428,7 +439,7 @@ function napi_get_value_int64 (env: napi_env, value: napi_value, result: Pointer } const numberValue = handle.value $from64('result') - let tempI64: any + if (numberValue === Number.POSITIVE_INFINITY || numberValue === Number.NEGATIVE_INFINITY || isNaN(numberValue)) { $makeSetValue('result', 0, '0', 'i32') $makeSetValue('result', 4, '0', 'i32') @@ -439,11 +450,7 @@ function napi_get_value_int64 (env: napi_env, value: napi_value, result: Pointer $makeSetValue('result', 0, '0xffffffff', 'u32') $makeSetValue('result', 4, '0x7fffffff', 'u32') } else { - let tempDouble - // eslint-disable-next-line @typescript-eslint/no-unused-vars - tempI64 = [numberValue >>> 0, (tempDouble = numberValue, +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil((tempDouble - +(~~tempDouble >>> 0)) / 4294967296) >>> 0 : 0)] - $makeSetValue('result', 0, 'tempI64[0]', 'i32') - $makeSetValue('result', 4, 'tempI64[1]', 'i32') + emnapiSetValueI64(result, numberValue) } return envObject.clearLastError() } @@ -560,6 +567,8 @@ function napi_get_value_uint32 (env: napi_env, value: napi_value, result: Pointe return envObject.clearLastError() } +emnapiImplementHelper('$emnapiSetValueI64', undefined, emnapiSetValueI64, undefined) + emnapiImplement('napi_get_array_length', 'ippp', napi_get_array_length) emnapiImplement('napi_get_arraybuffer_info', 'ipppp', napi_get_arraybuffer_info, ['$emnapiExternalMemory']) emnapiImplement('napi_get_prototype', 'ippp', napi_get_prototype) @@ -574,7 +583,7 @@ emnapiImplement('napi_get_value_bigint_uint64', 'ipppp', napi_get_value_bigint_u emnapiImplement('napi_get_value_bigint_words', 'ippppp', napi_get_value_bigint_words) emnapiImplement('napi_get_value_external', 'ippp', napi_get_value_external) emnapiImplement('napi_get_value_int32', 'ippp', napi_get_value_int32) -emnapiImplement('napi_get_value_int64', 'ippp', napi_get_value_int64) +emnapiImplement('napi_get_value_int64', 'ippp', napi_get_value_int64, ['$emnapiSetValueI64']) emnapiImplement('napi_get_value_string_latin1', 'ippppp', napi_get_value_string_latin1) emnapiImplement('napi_get_value_string_utf8', 'ippppp', napi_get_value_string_utf8, ['$emnapiString']) diff --git a/packages/test/emnapitest/binding.c b/packages/test/emnapitest/binding.c index 3ca482ca..1c7b0739 100644 --- a/packages/test/emnapitest/binding.c +++ b/packages/test/emnapitest/binding.c @@ -83,15 +83,26 @@ static napi_value External(napi_env env, napi_callback_info info) { } static napi_value GrowMemory(napi_env env, napi_callback_info info) { - napi_value result; - int64_t adjustedValue; + size_t argc = 1; + napi_value result, change_in_bytes_value; + int64_t adjustedValue, change_in_bytes; - NODE_API_CALL(env, napi_adjust_external_memory(env, 1, &adjustedValue)); + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &change_in_bytes_value, NULL, NULL)); + NODE_API_CALL(env, napi_get_value_int64(env, change_in_bytes_value, &change_in_bytes)); + NODE_API_CALL(env, napi_adjust_external_memory(env, change_in_bytes, &adjustedValue)); NODE_API_CALL(env, napi_create_double(env, (double)adjustedValue, &result)); return result; } +static napi_value GetWasmMemorySize(napi_env env, napi_callback_info info) { + napi_value result; + size_t wasm_memory_size = __builtin_wasm_memory_size(0) << 16; + NODE_API_CALL(env, napi_create_bigint_uint64(env, wasm_memory_size, &result)); + + return result; +} + static napi_value NullArrayBuffer(napi_env env, napi_callback_info info) { static void* data = NULL; napi_value output_view; @@ -116,6 +127,7 @@ napi_value Init(napi_env env, napi_value exports) { DECLARE_NODE_API_PROPERTY("External", External), DECLARE_NODE_API_PROPERTY("NullArrayBuffer", NullArrayBuffer), DECLARE_NODE_API_PROPERTY("GrowMemory", GrowMemory), + DECLARE_NODE_API_PROPERTY("GetWasmMemorySize", GetWasmMemorySize), }; NODE_API_CALL(env, napi_define_properties( diff --git a/packages/test/emnapitest/emnapi.test.js b/packages/test/emnapitest/emnapi.test.js index 1c585758..e6e6f595 100644 --- a/packages/test/emnapitest/emnapi.test.js +++ b/packages/test/emnapitest/emnapi.test.js @@ -20,7 +20,10 @@ module.exports = promise.then(test_typedarray => { let externalResult = test_typedarray.External() assert.ok(externalResult instanceof Uint8Array) assert.deepStrictEqual([...externalResult], [0, 1, 2]) - test_typedarray.GrowMemory() + const oldSize = test_typedarray.GetWasmMemorySize() + test_typedarray.GrowMemory(1) + const newSize = test_typedarray.GetWasmMemorySize() + console.log(`memory grow: ${oldSize} --> ${newSize} (+${newSize - oldSize})`) if (process.env.EMNAPI_TEST_WASI || process.env.EMNAPI_TEST_WASM32) { console.log(promise.Module.emnapi) externalResult = promise.Module.emnapi.syncMemory(false, externalResult)