Skip to content

Commit

Permalink
move napi_adjust_external_memory to JS (#87)
Browse files Browse the repository at this point in the history
  • Loading branch information
toyobayashi authored Nov 12, 2023
1 parent 9614ccb commit 6f0398f
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 45 deletions.
35 changes: 35 additions & 0 deletions packages/emnapi/src/core/memory.ts
Original file line number Diff line number Diff line change
@@ -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, [])
1 change: 1 addition & 0 deletions packages/emnapi/src/core/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"./miscellaneous.ts",
"./string.ts",
"./util.ts",
"./memory.ts",
"../../../runtime/src/typings/**/*.d.ts",
"../typings/**/*.d.ts",
"../*.ts",
Expand Down
57 changes: 57 additions & 0 deletions packages/emnapi/src/emscripten/memory.ts
Original file line number Diff line number Diff line change
@@ -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'
]
)
34 changes: 0 additions & 34 deletions packages/emnapi/src/js_native_api.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
#include "emnapi_internal.h"

#ifdef __EMSCRIPTEN__
#include <emscripten/heap.h>
#endif

EXTERN_C_START

static const char* emnapi_error_messages[] = {
Expand Down Expand Up @@ -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
23 changes: 16 additions & 7 deletions packages/emnapi/src/value/convert2c.ts
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,17 @@ function napi_get_value_int32 (env: napi_env, value: napi_value, result: Pointer
return envObject.clearLastError()
}

function emnapiSetValueI64 (result: Pointer<int64_t>, 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<int64_t>): napi_status {
$CHECK_ENV!(env)
const envObject = emnapiCtx.envStore.get(env)!
Expand All @@ -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')
Expand All @@ -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()
}
Expand Down Expand Up @@ -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)
Expand All @@ -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'])

Expand Down
18 changes: 15 additions & 3 deletions packages/test/emnapitest/binding.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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(
Expand Down
5 changes: 4 additions & 1 deletion packages/test/emnapitest/emnapi.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 6f0398f

Please sign in to comment.