Add initial Node-API (N-API) PoC scaffolding#1606
Open
ksh8281 wants to merge 11 commits into
Open
Conversation
Vendor node-api-headers and a sparse nodejs/node test corpus, implement enough js_native_api.h to pass test/js-native-api/2_function_arguments. Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
…ty/call_function Unlocks 3_callbacks, 4_object_factory, 5_function_factory TCs; cctest napi addon build list generalized into a FOREACH loop instead of per-TC blocks. Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
…object_wrap napi_define_class uses FunctionTemplateRef (auto-allocates `this` on construct, unlike plain FunctionObjectRef); napi_ref strong/weak refcounting maps onto PersistentValueRefMap::add()/remove(). Also generalizes the cctest napi addon build list to handle mixed .c/.cc TC sources, and auto-enables ESCARGOT_USE_EXTENDED_API when ESCARGOT_NAPI is on. Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
…zer env Add napi_create_uint32/napi_get_value_uint32. Generalize the cctest addon build list to support multi-source addons (7_factory_wrap needs two .cc files). Fix a segfault: ObjectWrap's wrapped-instance finalizers captured a napi_env__ that lived in a nested lambda's stack frame, so once that frame returned, a later test's GC pass could invoke the finalizer with a dangling env. Move envData to the test's own frame and force collection before it returns, matching the WeakPtr/Finalizer GC-forcing pattern already used elsewhere in cctest. Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
Move napi_env__ into a NapiEnv member (NapiEnv::env()) so a napi_callback captured by something long-lived (e.g. a napi_wrap finalizer) never captures a dangling env once the Evaluator::execute call that created it returns. Simplifies all cctest napi_env__ boilerplace down to napiEnv->env(). This is a distinct fix from the "flush wrapped objects via forced GC before a test ends" pattern already used in ObjectWrap/FactoryWrap - both are still needed, since leftover GC garbage can otherwise get finalized at an unsafe moment (verified via gdb: mid-construction of an unrelated test's VMInstance). Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
Flush wrap'd/finalizer-bearing garbage before a NapiEnv's Context/VMInstance tear down, so nothing lingers to be opportunistically finalized later against a torn-down env. Not exercised by current tests (none call delete on a NapiEnv yet - they intentionally leak like the rest of this codebase's cctest style), verified manually with a temporary delete call. Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
napi_wrap's always-weak ref and napi_create_reference(initial_refcount==0) now register a finalizer on the target that clears ref->value when it is GC'd, so napi_get_reference_value stops returning a dangling pointer. napi_delete_reference unregisters that finalizer before freeing ref, since ref is a plain (non-GC) allocation and a later collection would otherwise invoke the finalizer with a dangling data pointer. Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
napi_remove_wrap only cleared the object's extraData() before, leaving the napi_wrap finalizer registered; a real Node-API napi_remove_wrap must suppress it instead. NapiEnv now keeps a side table (HashMap<ObjectRef*, void*>) from wrapped object to its WrapFinalizeData*, since extraData() itself must stay exactly the caller's native_object for napi_unwrap. napi_remove_wrap looks up and unregisters the finalizer via this table; NapiWrapFinalizer itself also clears its own entry when the object is actually collected, so a later object reusing the same address never sees a stale entry. Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
Each call adds/removes exactly one PersistentValueRefMap rooting unit, keeping napi_ref's refcount field equal to how many add() calls it has made for its target - the same invariant napi_create_reference's initial loop and napi_delete_reference's teardown loop already rely on. The weak-staleness finalizer (NapiWeakRefFinalizer) is registered/unregistered only at the weak<->strong boundary, guarded by a new napi_ref__::weakFinalizerRegistered flag so repeated ref()/unref() calls don't register it more than once. napi_reference_ref errors on a weak ref whose target is already collected; napi_reference_unref errors when the refcount is already 0, matching js_native_api.h's documented contract. While writing the regression test for the weak-after-unref path, found that PersistentValueRefMap::remove() could leave a value rooted forever: tsl::robin_map's erase() only marks a bucket empty (its destructor call on a raw pointer key is a no-op) without clearing the bucket's memory, and since the map's backing array is itself GC-managed and thus conservatively scanned, the leftover ValueRef* bytes kept looking like a live reference until some unrelated future insert() happened to reuse that exact bucket. Fixed by forcing a rehash right after the erase, which allocates a fresh (GC_malloc-zeroed) backing array containing only the currently-valid entries. PersistentValueRefMap has no other consumer in the engine today, so this is scoped to napi. Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
~NapiEnv() is the only environment-teardown hook this PoC has, so run the instance data finalizer there, once, before the wrap/finalizer GC-flush safety net that already lives in that destructor. The finalizer pointer is cleared before invocation to avoid any re-entrant double-call. Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
napi_open_handle_scope/napi_close_handle_scope/ napi_open_escapable_handle_scope/napi_close_escapable_handle_scope/ napi_escape_handle are pure bookkeeping (LIFO nesting + escape-once enforcement) since napi_value is already a GC-rooted pointer here, unlike V8's buffered handles. While adding the real test_handle_scope TC as a regression test, found that napi_call_function let a JS exception escape as a raw C++ exception across the N-API call boundary instead of reporting napi_pending_exception. Fixed by running the call through Evaluator::execute's nested-SandBox overload. Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Vendor node-api-headers and a sparse nodejs/node test corpus, implement enough js_native_api.h to pass test/js-native-api/2_function_arguments.