Skip to content

Commit fee7037

Browse files
committed
add unit test for pure finalizers
1 parent f4e3bd2 commit fee7037

File tree

3 files changed

+158
-0
lines changed

3 files changed

+158
-0
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"targets": [
3+
{
4+
"target_name": "test_finalizer",
5+
"defines": [ "NAPI_EXPERIMENTAL" ],
6+
"sources": [
7+
"../entry_point.c",
8+
"test_finalizer.c"
9+
]
10+
}
11+
]
12+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
'use strict';
2+
// Flags: --expose-gc
3+
4+
const common = require('../../common');
5+
const test_finalizer = require(`./build/${common.buildType}/test_finalizer`);
6+
const assert = require('assert');
7+
8+
function addFinalizer() {
9+
// Define obj in a function context to let it GC-collected.
10+
const obj = {};
11+
test_finalizer.addFinalizer(obj);
12+
}
13+
14+
addFinalizer();
15+
16+
for (let i = 0; i < 1000; ++i) {
17+
global.gc();
18+
if (test_finalizer.getFinalizerCallCount() === 1) {
19+
break;
20+
}
21+
}
22+
23+
assert(test_finalizer.getFinalizerCallCount() === 1);
24+
25+
async function runAsyncTests() {
26+
let js_is_called = false;
27+
(() => {
28+
const obj = {};
29+
test_finalizer.addFinalizerWithJS(obj, () => { js_is_called = true; });
30+
})();
31+
await common.gcUntil('test JS finalizer',
32+
() => (test_finalizer.getFinalizerCallCount() === 2));
33+
assert(js_is_called);
34+
}
35+
runAsyncTests();
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#include <js_native_api.h>
2+
#include <stdint.h>
3+
#include <stdio.h>
4+
#include <stdlib.h>
5+
#include <string.h>
6+
#include "../common.h"
7+
8+
typedef struct {
9+
int32_t finalize_count;
10+
napi_ref js_func;
11+
} FinalizerData;
12+
13+
static void finalizerOnlyCallback(napi_env env,
14+
void* finalize_data,
15+
void* finalize_hint) {
16+
FinalizerData* data = (FinalizerData*)finalize_data;
17+
++data->finalize_count;
18+
}
19+
20+
static void finalizerCallingJSCallback(napi_env env,
21+
void* finalize_data,
22+
void* finalize_hint) {
23+
napi_value js_func, undefined;
24+
FinalizerData* data = (FinalizerData*)finalize_data;
25+
NODE_API_CALL_RETURN_VOID(env, napi_get_reference_value(env, data->js_func, &js_func));
26+
NODE_API_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined));
27+
NODE_API_CALL_RETURN_VOID(env, napi_call_function(env, undefined, js_func, 0, NULL, NULL));
28+
NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, data->js_func));
29+
data->js_func = NULL;
30+
++data->finalize_count;
31+
}
32+
33+
// Schedule async finalizer to run JavaScript-touching code.
34+
static void finalizerWithJSCallback(napi_env env,
35+
void* finalize_data,
36+
void* finalize_hint) {
37+
FinalizerData* data = (FinalizerData*)finalize_data;
38+
NODE_API_CALL_RETURN_VOID(env, node_api_post_finalizer(
39+
env, finalizerCallingJSCallback, finalize_data, finalize_hint));
40+
}
41+
42+
static napi_value addFinalizer(napi_env env, napi_callback_info info) {
43+
size_t argc = 1;
44+
napi_value argv[1];
45+
FinalizerData* data;
46+
47+
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
48+
NODE_API_CALL(env, napi_get_instance_data(env, &data));
49+
NODE_API_CALL(env,
50+
napi_add_finalizer(
51+
env, argv[0], data, finalizerOnlyCallback, NULL, NULL));
52+
return NULL;
53+
}
54+
55+
// This finalizer is going to call JavaScript from finalizer
56+
static napi_value addFinalizerWithJS(napi_env env, napi_callback_info info) {
57+
size_t argc = 2;
58+
napi_value argv[2] = {0};
59+
napi_valuetype arg_type;
60+
FinalizerData* data;
61+
62+
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
63+
NODE_API_CALL(env, napi_get_instance_data(env, &data));
64+
NODE_API_CALL(env, napi_typeof(env, argv[1], &arg_type));
65+
NODE_API_ASSERT(
66+
env, arg_type == napi_function, "Expected function as the second arg");
67+
NODE_API_CALL(env, napi_create_reference(env, argv[1], 1, &data->js_func));
68+
NODE_API_CALL(env,
69+
napi_add_finalizer(
70+
env, argv[0], data, finalizerWithJSCallback, NULL, NULL));
71+
return NULL;
72+
}
73+
74+
static napi_value getFinalizerCallCount(napi_env env, napi_callback_info info) {
75+
size_t argc = 1;
76+
napi_value argv[1];
77+
FinalizerData* data;
78+
napi_value result;
79+
80+
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
81+
NODE_API_CALL(env, napi_get_instance_data(env, &data));
82+
NODE_API_CALL(env, napi_create_int32(env, data->finalize_count, &result));
83+
return result;
84+
}
85+
86+
static void finalizeData(napi_env env, void* data, void* hint) {
87+
free(data);
88+
}
89+
90+
EXTERN_C_START
91+
napi_value Init(napi_env env, napi_value exports) {
92+
FinalizerData* data = (FinalizerData*)malloc(sizeof(FinalizerData));
93+
NODE_API_ASSERT(env, data != NULL, "Failed to allocate memory");
94+
memset(data, 0, sizeof(FinalizerData));
95+
NODE_API_CALL(env, napi_set_instance_data(env, data, finalizeData, NULL));
96+
napi_property_descriptor descriptors[] = {
97+
DECLARE_NODE_API_PROPERTY("addFinalizer", addFinalizer),
98+
DECLARE_NODE_API_PROPERTY("addFinalizerWithJS", addFinalizerWithJS),
99+
DECLARE_NODE_API_PROPERTY("getFinalizerCallCount",
100+
getFinalizerCallCount)};
101+
102+
NODE_API_CALL(
103+
env,
104+
napi_define_properties(env,
105+
exports,
106+
sizeof(descriptors) / sizeof(*descriptors),
107+
descriptors));
108+
109+
return exports;
110+
}
111+
EXTERN_C_END

0 commit comments

Comments
 (0)