Skip to content

Commit

Permalink
represent napi_callback_info with non-zero number (#78)
Browse files Browse the repository at this point in the history
* add cbinfo test

* implement real napi_callback_info pointer
  • Loading branch information
toyobayashi authored Aug 25, 2023
1 parent 9bc69c5 commit 9373df1
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 13 deletions.
8 changes: 4 additions & 4 deletions packages/emnapi/src/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ function napi_create_function (env: napi_env, utf8name: Pointer<const_char>, len
})
}

function napi_get_cb_info (env: napi_env, _cbinfo: napi_callback_info, argc: Pointer<size_t>, argv: Pointer<napi_value>, this_arg: Pointer<napi_value>, data: void_pp): napi_status {
function napi_get_cb_info (env: napi_env, cbinfo: napi_callback_info, argc: Pointer<size_t>, argv: Pointer<napi_value>, this_arg: Pointer<napi_value>, data: void_pp): napi_status {
$CHECK_ENV!(env)
const envObject = emnapiCtx.envStore.get(env)!

const cbinfoValue = emnapiCtx.cbinfoStack.current!
const cbinfoValue = emnapiCtx.cbinfoStack.get(cbinfo)!

$from64('argc')
$from64('argv')
Expand Down Expand Up @@ -160,7 +160,7 @@ function napi_new_instance (

function napi_get_new_target (
env: napi_env,
_cbinfo: napi_callback_info,
cbinfo: napi_callback_info,
result: Pointer<napi_value>
): napi_status {
$CHECK_ENV!(env)
Expand All @@ -170,7 +170,7 @@ function napi_get_new_target (

$from64('result')

const cbinfoValue = emnapiCtx.cbinfoStack.current!
const cbinfoValue = emnapiCtx.cbinfoStack.get(cbinfo)!
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const value = cbinfoValue.getNewTarget(envObject)
$makeSetValue('result', 0, 'value', '*')
Expand Down
4 changes: 2 additions & 2 deletions packages/emnapi/src/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ function emnapiCreateFunction<F extends (...args: any[]) => any> (envObject: Env

const makeFunction = () => function (this: any): any {
'use strict'
emnapiCtx.cbinfoStack.push(this, data, arguments, f)
const cbinfo = emnapiCtx.cbinfoStack.push(this, data, arguments, f)
const scope = emnapiCtx.openScope(envObject)
try {
return envObject.callIntoModule((envObject) => {
const napiValue = $makeDynCall('ppp', 'cb')(envObject.id, 0)
const napiValue = $makeDynCall('ppp', 'cb')(envObject.id, cbinfo)
return (!napiValue) ? undefined : emnapiCtx.handleStore.get(napiValue)!.value
})
} finally {
Expand Down
47 changes: 40 additions & 7 deletions packages/runtime/src/CallbackInfo.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import type { Env } from './env'

const EMPTY_ARGS = [] as const

export class CallbackInfo {
public constructor (
public parent: CallbackInfo | null,
public id: number,
public parent: CallbackInfo,
public child: CallbackInfo | null,
public thiz: any,
public data: void_p,
public args: ArrayLike<any>,
Expand All @@ -14,29 +18,58 @@ export class CallbackInfo {
if (thiz == null || thiz.constructor == null) return 0
return thiz instanceof this.fn ? envObject.ensureHandleId(thiz.constructor) : 0
}

public dispose (): void {
if (this.thiz !== undefined) this.thiz = undefined
this.args = EMPTY_ARGS
this.fn = null!
}
}

const ROOT_CBINFO = new CallbackInfo(0, null!, null, null, 0, null!, null!)

export class CallbackInfoStack {
public current: CallbackInfo | null = null
public current: CallbackInfo = ROOT_CBINFO

public get (id: number): CallbackInfo | null {
if (id === 1) return ROOT_CBINFO.child!

let info = ROOT_CBINFO
for (let i = 0; i < id; ++i) {
info = info.child!
if (info === null) return null
}
return info === ROOT_CBINFO ? null : info
}

public pop (): void {
const current = this.current
if (current === null) return
if (current === ROOT_CBINFO) return
this.current = current.parent
current.dispose()
}

public push (
thiz: any,
data: void_p,
args: ArrayLike<any>,
fn: Function
): CallbackInfo {
const info = new CallbackInfo(this.current, thiz, data, args, fn)
): number {
let info = this.current.child
if (info) {
info.thiz = thiz
info.data = data
info.args = args
info.fn = fn
} else {
info = new CallbackInfo(this.current.id + 1, this.current, null, thiz, data, args, fn)
this.current.child = info
}
this.current = info
return info
return info.id
}

public dispose (): void {
this.current = null
this.current = null!
}
}
1 change: 1 addition & 0 deletions packages/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ if((NOT IS_WASM) OR IS_EMSCRIPTEN OR IS_WASI_THREADS)
endif()

add_test("arg" "./arg/binding.c" ON OFF "")
add_test("cbinfo" "./cbinfo/binding.c" ON OFF "")
add_test("callback" "./callback/binding.c" ON OFF "")
add_test("objfac" "./objfac/binding.c" ON OFF "")
add_test("fnfac" "./fnfac/binding.c" ON OFF "")
Expand Down
69 changes: 69 additions & 0 deletions packages/test/cbinfo/binding.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#include <js_native_api.h>
#include "../common.h"

static napi_value Test1(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));

NAPI_ASSERT(env, argc == 1,
"Test1: Wrong number of arguments. Expects a single argument.");

napi_valuetype valuetype0;
NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
NAPI_ASSERT(env, valuetype0 == napi_function,
"Test1: Wrong type of arguments. Expects a function as first argument.");

napi_value argv[1];
NAPI_CALL(env, napi_create_bigint_int64(env, (int64_t) info, argv));

napi_value global;
NAPI_CALL(env, napi_get_global(env, &global));

napi_value cb = args[0];
NAPI_CALL(env, napi_call_function(env, global, cb, 1, argv, NULL));

return NULL;
}

static napi_value Test2(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));

NAPI_ASSERT(env, argc == 1,
"Test2: Wrong number of arguments. Expects a single argument.");

napi_valuetype valuetype0;
NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
NAPI_ASSERT(env, valuetype0 == napi_bigint,
"Test2: Wrong type of arguments. Expects a bigint as first argument.");

int64_t prev_info;
bool lossless;
NAPI_CALL(env, napi_get_value_bigint_int64(env, args[0], &prev_info, &lossless));

size_t prev_argc = 1;
napi_value prev_args[1];
NAPI_CALL(env, napi_get_cb_info(env, (napi_callback_info) prev_info, &prev_argc, prev_args, NULL, NULL));

NAPI_ASSERT(env, prev_argc == 1,
"Test2: Wrong number of arguments. Expects a single argument.");

napi_valuetype t;
NAPI_CALL(env, napi_typeof(env, prev_args[0], &t));
NAPI_ASSERT(env, t == napi_function,
"Test2: Wrong type of arguments. Expects a function as first argument.");
return NULL;
}

EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor desc[2] = {
DECLARE_NAPI_PROPERTY("test1", Test1),
DECLARE_NAPI_PROPERTY("test2", Test2),
};
NAPI_CALL(env, napi_define_properties(env, exports, 2, desc));
return exports;
}
EXTERN_C_END
6 changes: 6 additions & 0 deletions packages/test/cbinfo/cbinfo.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
'use strict'
const { load } = require('../util')

module.exports = load('cbinfo').then(addon => {
addon.test1(addon.test2)
})

0 comments on commit 9373df1

Please sign in to comment.