mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 02:48:50 +00:00
## Summary Fixes a crash in `napi_is_exception_pending` that occurs during environment cleanup when finalizers call this function. The crash manifested as: ``` panic: Aborted - napi.h:192: napi_is_exception_pending - napi.h:516: wrap_cleanup - napi.h:273: napi_env__::cleanup ``` ## Root Cause Bun's implementation was using `DECLARE_THROW_SCOPE` during cleanup when JavaScript execution is not safe, and didn't follow Node.js's approach of avoiding `NAPI_PREAMBLE` for this function. ## Changes Made 1. **Remove `NAPI_PREAMBLE_NO_THROW_SCOPE`** - Node.js explicitly states this function "must execute when there is a pending exception" 2. **Use `DECLARE_CATCH_SCOPE`** instead of `DECLARE_THROW_SCOPE` for safety during cleanup 3. **Add safety check** `!env->isFinishingFinalizers()` before accessing VM 4. **Add `napi_clear_last_error` function** to match Node.js implementation 5. **Use `napi_clear_last_error`** instead of `napi_set_last_error` for consistent behavior ## Test Plan Created comprehensive test that: - ✅ **Reproduces the original crash scenario** (finalizers calling `napi_is_exception_pending`) - ✅ **Verifies it no longer crashes in Bun** - ✅ **Confirms behavior matches Node.js exactly** ### Test Results **Before fix:** Would crash with `panic: Aborted` during cleanup **After fix:** ``` Testing napi_is_exception_pending behavior... 1. Testing basic napi_is_exception_pending: Status: 0 (should be 0 for napi_ok) Result: false (should be false - no exception pending) 2. Testing with pending exception: Exception was thrown as expected: Test exception 3. Testing finalizer scenario (the crash case): Creating object with finalizer that calls napi_is_exception_pending... Objects created. Forcing garbage collection... Garbage collection completed. napi_is_exception_pending in finalizer: status=0, result=false [...5 finalizers ran successfully...] SUCCESS: napi_is_exception_pending works correctly in all scenarios! ``` **Node.js comparison:** Identical output and behavior confirmed. ## Impact - **Fixes crashes** in native addons that call `napi_is_exception_pending` in finalizers - **Improves Node.js compatibility** by aligning implementation approach - **No breaking changes** - only fixes crash scenario, normal usage unchanged The fix aligns Bun's NAPI implementation with Node.js's proven approach for safe exception checking during environment cleanup. 🤖 Generated with [Claude Code](https://claude.ai/code) --------- Co-authored-by: Claude Bot <claude-bot@bun.sh> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
73 lines
2.8 KiB
C++
73 lines
2.8 KiB
C++
#include <node_api.h>
|
|
#include <stdio.h>
|
|
|
|
// This reproduces the crash that was happening in napi_is_exception_pending
|
|
// when called during cleanup/finalizers
|
|
static void test_finalizer(napi_env env, void* finalize_data, void* finalize_hint) {
|
|
// This is what was causing the crash before the fix
|
|
bool result = false;
|
|
napi_status status = napi_is_exception_pending(env, &result);
|
|
|
|
// Print status for verification (should not crash and should return napi_ok)
|
|
printf("napi_is_exception_pending in finalizer: status=%d, result=%s\n",
|
|
status, result ? "true" : "false");
|
|
}
|
|
|
|
static napi_value create_object_with_finalizer(napi_env env, napi_callback_info info) {
|
|
napi_value obj;
|
|
napi_create_object(env, &obj);
|
|
|
|
// Add a finalizer that will call napi_is_exception_pending during cleanup
|
|
napi_add_finalizer(env, obj, nullptr, test_finalizer, nullptr, nullptr);
|
|
|
|
return obj;
|
|
}
|
|
|
|
static napi_value test_exception_pending_basic(napi_env env, napi_callback_info info) {
|
|
bool result = false;
|
|
napi_status status = napi_is_exception_pending(env, &result);
|
|
|
|
napi_value return_status, return_result;
|
|
napi_create_int32(env, status, &return_status);
|
|
napi_get_boolean(env, result, &return_result);
|
|
|
|
napi_value return_obj;
|
|
napi_create_object(env, &return_obj);
|
|
napi_set_named_property(env, return_obj, "status", return_status);
|
|
napi_set_named_property(env, return_obj, "result", return_result);
|
|
|
|
return return_obj;
|
|
}
|
|
|
|
static napi_value test_with_pending_exception(napi_env env, napi_callback_info info) {
|
|
// Create a pending exception
|
|
napi_throw_error(env, nullptr, "Test exception");
|
|
|
|
// Now test napi_is_exception_pending
|
|
bool result = false;
|
|
napi_status status = napi_is_exception_pending(env, &result);
|
|
|
|
napi_value return_status, return_result;
|
|
napi_create_int32(env, status, &return_status);
|
|
napi_get_boolean(env, result, &return_result);
|
|
|
|
napi_value return_obj;
|
|
napi_create_object(env, &return_obj);
|
|
napi_set_named_property(env, return_obj, "status", return_status);
|
|
napi_set_named_property(env, return_obj, "result", return_result);
|
|
|
|
return return_obj;
|
|
}
|
|
|
|
static napi_value init(napi_env env, napi_value exports) {
|
|
napi_property_descriptor desc[] = {
|
|
{ "createObjectWithFinalizer", nullptr, create_object_with_finalizer, nullptr, nullptr, nullptr, napi_default, nullptr },
|
|
{ "testExceptionPendingBasic", nullptr, test_exception_pending_basic, nullptr, nullptr, nullptr, napi_default, nullptr },
|
|
{ "testWithPendingException", nullptr, test_with_pending_exception, nullptr, nullptr, nullptr, napi_default, nullptr }
|
|
};
|
|
|
|
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
|
|
return exports;
|
|
}
|
|
|
|
NAPI_MODULE(NODE_GYP_MODULE_NAME, init) |