Skip to content

Commit a0f9aee

Browse files
kraenhansenclaude
andcommitted
feat: port test_cannot_run_js to CTS
Builds two addons from the same C source with different NAPI_VERSION defines (10 and 9) to verify that finalizers attempting to access JS during shutdown receive napi_cannot_run_js or napi_pending_exception (respectively), or napi_ok if the event loop is still running. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent a8f4477 commit a0f9aee

File tree

4 files changed

+93
-1
lines changed

4 files changed

+93
-1
lines changed

PORTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Tests covering the engine-specific part of Node-API, defined in `js_native_api.h
4949
| `8_passing_wrapped` | Ported ✅ | Easy |
5050
| `test_array` | Ported ✅ | Easy |
5151
| `test_bigint` | Ported ✅ | Easy |
52-
| `test_cannot_run_js` | Not ported | Medium |
52+
| `test_cannot_run_js` | Ported ✅ | Medium |
5353
| `test_constructor` | Not ported | Medium |
5454
| `test_conversions` | Not ported | Medium |
5555
| `test_dataview` | Not ported | Medium |
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
add_node_api_cts_addon(test_cannot_run_js test_cannot_run_js.c)
2+
target_compile_definitions(test_cannot_run_js PRIVATE NAPI_VERSION=10)
3+
4+
add_node_api_cts_addon(test_pending_exception test_cannot_run_js.c)
5+
target_compile_definitions(test_pending_exception PRIVATE NAPI_VERSION=9)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Test that `napi_get_named_property()` returns `napi_cannot_run_js` in
2+
// experimental mode and `napi_pending_exception` otherwise. This test calls
3+
// the add-on's `createRef()` method, which creates a strong reference to a JS
4+
// function. When the process exits, it calls all reference finalizers. The
5+
// finalizer for the strong reference created herein will attempt to call
6+
// `napi_get_property()` on a property of the global object and will abort the
7+
// process if the API doesn't return the correct status.
8+
9+
const addon_v8 = loadAddon('test_pending_exception');
10+
const addon_new = loadAddon('test_cannot_run_js');
11+
12+
function runTests(addon) {
13+
addon.createRef(function() { throw new Error('function should not have been called'); });
14+
}
15+
16+
function runAllTests() {
17+
runTests(addon_v8);
18+
runTests(addon_new);
19+
}
20+
21+
runAllTests();
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#include <js_native_api.h>
2+
#include "../common.h"
3+
#include "../entry_point.h"
4+
#include "stdlib.h"
5+
6+
static void Finalize(napi_env env, void* data, void* hint) {
7+
napi_value global, set_timeout;
8+
napi_ref* ref = data;
9+
10+
NODE_API_BASIC_ASSERT_RETURN_VOID(
11+
napi_delete_reference(env, *ref) == napi_ok,
12+
"deleting reference in finalizer should succeed");
13+
NODE_API_BASIC_ASSERT_RETURN_VOID(
14+
napi_get_global(env, &global) == napi_ok,
15+
"getting global reference in finalizer should succeed");
16+
napi_status result =
17+
napi_get_named_property(env, global, "setTimeout", &set_timeout);
18+
19+
// The finalizer could be invoked either from check callbacks (as native
20+
// immediates) if the event loop is still running (where napi_ok is returned)
21+
// or during environment shutdown (where napi_cannot_run_js or
22+
// napi_pending_exception is returned). This is not deterministic from
23+
// the point of view of the addon.
24+
25+
#if NAPI_VERSION > 9
26+
NODE_API_BASIC_ASSERT_RETURN_VOID(
27+
result == napi_cannot_run_js || result == napi_ok,
28+
"getting named property from global in finalizer should succeed "
29+
"or return napi_cannot_run_js");
30+
#else
31+
NODE_API_BASIC_ASSERT_RETURN_VOID(
32+
result == napi_pending_exception || result == napi_ok,
33+
"getting named property from global in finalizer should succeed "
34+
"or return napi_pending_exception");
35+
#endif // NAPI_VERSION > 9
36+
free(ref);
37+
}
38+
39+
static napi_value CreateRef(napi_env env, napi_callback_info info) {
40+
size_t argc = 1;
41+
napi_value cb;
42+
napi_valuetype value_type;
43+
napi_ref* ref = malloc(sizeof(*ref));
44+
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &cb, NULL, NULL));
45+
NODE_API_ASSERT(env, argc == 1, "Function takes only one argument");
46+
NODE_API_CALL(env, napi_typeof(env, cb, &value_type));
47+
NODE_API_ASSERT(
48+
env, value_type == napi_function, "argument must be function");
49+
NODE_API_CALL(env, napi_add_finalizer(env, cb, ref, Finalize, NULL, ref));
50+
return cb;
51+
}
52+
53+
EXTERN_C_START
54+
napi_value Init(napi_env env, napi_value exports) {
55+
napi_property_descriptor properties[] = {
56+
DECLARE_NODE_API_PROPERTY("createRef", CreateRef),
57+
};
58+
59+
NODE_API_CALL(
60+
env,
61+
napi_define_properties(
62+
env, exports, sizeof(properties) / sizeof(*properties), properties));
63+
64+
return exports;
65+
}
66+
EXTERN_C_END

0 commit comments

Comments
 (0)