Found while porting #437 to Android (NativeScript/android#2004) by an independent review of the port; the defect is shared with the iOS original.
CompleteAsyncWork in NativeScript/napi/NodeApiEmbed.mm calls env->CallIntoModule(...) with no v8::Context::Scope (and no HandleScope of its own) — the event-loop entry it runs in supplies only Locker + Isolate::Scope + HandleScope. Every sibling entry path opens the context scope itself (NapiEnv::CallFinalizer, the TSFN dispatch in NapiThreadSafeFunction.mm), but the async-work completion path does not.
Consequences:
- Node guarantees the
complete callback runs with the env's context entered; addon code that touches the engine directly (or any napi call that reads isolate->GetCurrentContext()) observes an empty current context.
- If the complete callback throws JS-side (e.g. the documented
napi_throw_error failure-reporting pattern), the exception handler runs against an empty current context — on the Android port this was a null-deref/CHECK crash in the error reporter; the iOS reporter may or may not survive it, but the contract violation is the same.
Fix is the same one-liner the Android port applied in review: open v8::HandleScope + v8::Context::Scope(env->context()) in CompleteAsyncWork before CallIntoModule, mirroring NapiEnv::CallFinalizer.
Android-side fix: NativeScript/android#2004 (CompleteAsyncWork in test-app/runtime/src/main/cpp/napi/NodeApiEmbed.cpp).
Found while porting #437 to Android (NativeScript/android#2004) by an independent review of the port; the defect is shared with the iOS original.
CompleteAsyncWorkinNativeScript/napi/NodeApiEmbed.mmcallsenv->CallIntoModule(...)with nov8::Context::Scope(and noHandleScopeof its own) — the event-loop entry it runs in supplies only Locker + Isolate::Scope + HandleScope. Every sibling entry path opens the context scope itself (NapiEnv::CallFinalizer, the TSFN dispatch inNapiThreadSafeFunction.mm), but the async-work completion path does not.Consequences:
completecallback runs with the env's context entered; addon code that touches the engine directly (or any napi call that readsisolate->GetCurrentContext()) observes an empty current context.napi_throw_errorfailure-reporting pattern), the exception handler runs against an empty current context — on the Android port this was a null-deref/CHECK crash in the error reporter; the iOS reporter may or may not survive it, but the contract violation is the same.Fix is the same one-liner the Android port applied in review: open
v8::HandleScope+v8::Context::Scope(env->context())inCompleteAsyncWorkbeforeCallIntoModule, mirroringNapiEnv::CallFinalizer.Android-side fix: NativeScript/android#2004 (
CompleteAsyncWorkintest-app/runtime/src/main/cpp/napi/NodeApiEmbed.cpp).