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 #include "../entry_point.h"
8
9 typedef struct {
10 int32_t finalize_count;
11 napi_ref js_func;
12 } FinalizerData;
13
finalizerOnlyCallback(node_api_nogc_env env,void * finalize_data,void * finalize_hint)14 static void finalizerOnlyCallback(node_api_nogc_env env,
15 void* finalize_data,
16 void* finalize_hint) {
17 FinalizerData* data = (FinalizerData*)finalize_data;
18 int32_t count = ++data->finalize_count;
19
20 // It is safe to access instance data
21 NODE_API_NOGC_CALL_RETURN_VOID(env,
22 napi_get_instance_data(env, (void**)&data));
23 NODE_API_NOGC_ASSERT_RETURN_VOID(count = data->finalize_count,
24 "Expected to be the same FinalizerData");
25 }
26
finalizerCallingJSCallback(napi_env env,void * finalize_data,void * finalize_hint)27 static void finalizerCallingJSCallback(napi_env env,
28 void* finalize_data,
29 void* finalize_hint) {
30 napi_value js_func, undefined;
31 FinalizerData* data = (FinalizerData*)finalize_data;
32 NODE_API_CALL_RETURN_VOID(
33 env, napi_get_reference_value(env, data->js_func, &js_func));
34 NODE_API_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined));
35 NODE_API_CALL_RETURN_VOID(
36 env, napi_call_function(env, undefined, js_func, 0, NULL, NULL));
37 NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, data->js_func));
38 data->js_func = NULL;
39 ++data->finalize_count;
40 }
41
42 // Schedule async finalizer to run JavaScript-touching code.
finalizerWithJSCallback(node_api_nogc_env env,void * finalize_data,void * finalize_hint)43 static void finalizerWithJSCallback(node_api_nogc_env env,
44 void* finalize_data,
45 void* finalize_hint) {
46 NODE_API_NOGC_CALL_RETURN_VOID(
47 env,
48 node_api_post_finalizer(
49 env, finalizerCallingJSCallback, finalize_data, finalize_hint));
50 }
51
finalizerWithFailedJSCallback(node_api_nogc_env nogc_env,void * finalize_data,void * finalize_hint)52 static void finalizerWithFailedJSCallback(node_api_nogc_env nogc_env,
53 void* finalize_data,
54 void* finalize_hint) {
55 // Intentionally cast to a napi_env to test the fatal failure.
56 napi_env env = (napi_env)nogc_env;
57 napi_value obj;
58 FinalizerData* data = (FinalizerData*)finalize_data;
59 ++data->finalize_count;
60 NODE_API_CALL_RETURN_VOID(env, napi_create_object(env, &obj));
61 }
62
addFinalizer(napi_env env,napi_callback_info info)63 static napi_value addFinalizer(napi_env env, napi_callback_info info) {
64 size_t argc = 1;
65 napi_value argv[1] = {0};
66 FinalizerData* data;
67
68 NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
69 NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data));
70 NODE_API_CALL(env,
71 napi_add_finalizer(
72 env, argv[0], data, finalizerOnlyCallback, NULL, NULL));
73 return NULL;
74 }
75
76 // This finalizer is going to call JavaScript from finalizer and succeed.
addFinalizerWithJS(napi_env env,napi_callback_info info)77 static napi_value addFinalizerWithJS(napi_env env, napi_callback_info info) {
78 size_t argc = 2;
79 napi_value argv[2] = {0};
80 napi_valuetype arg_type;
81 FinalizerData* data;
82
83 NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
84 NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data));
85 NODE_API_CALL(env, napi_typeof(env, argv[1], &arg_type));
86 NODE_API_ASSERT(
87 env, arg_type == napi_function, "Expected function as the second arg");
88 NODE_API_CALL(env, napi_create_reference(env, argv[1], 1, &data->js_func));
89 NODE_API_CALL(env,
90 napi_add_finalizer(
91 env, argv[0], data, finalizerWithJSCallback, NULL, NULL));
92 return NULL;
93 }
94
95 // This finalizer is going to call JavaScript from finalizer and fail.
addFinalizerFailOnJS(napi_env env,napi_callback_info info)96 static napi_value addFinalizerFailOnJS(napi_env env, napi_callback_info info) {
97 size_t argc = 1;
98 napi_value argv[1] = {0};
99 FinalizerData* data;
100
101 NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
102 NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data));
103 NODE_API_CALL(
104 env,
105 napi_add_finalizer(
106 env, argv[0], data, finalizerWithFailedJSCallback, NULL, NULL));
107 return NULL;
108 }
109
getFinalizerCallCount(napi_env env,napi_callback_info info)110 static napi_value getFinalizerCallCount(napi_env env, napi_callback_info info) {
111 size_t argc = 1;
112 napi_value argv[1];
113 FinalizerData* data;
114 napi_value result;
115
116 NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
117 NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data));
118 NODE_API_CALL(env, napi_create_int32(env, data->finalize_count, &result));
119 return result;
120 }
121
finalizeData(napi_env env,void * data,void * hint)122 static void finalizeData(napi_env env, void* data, void* hint) {
123 free(data);
124 }
125
126 EXTERN_C_START
Init(napi_env env,napi_value exports)127 napi_value Init(napi_env env, napi_value exports) {
128 FinalizerData* data = (FinalizerData*)malloc(sizeof(FinalizerData));
129 NODE_API_ASSERT(env, data != NULL, "Failed to allocate memory");
130 memset(data, 0, sizeof(FinalizerData));
131 NODE_API_CALL(env, napi_set_instance_data(env, data, finalizeData, NULL));
132 napi_property_descriptor descriptors[] = {
133 DECLARE_NODE_API_PROPERTY("addFinalizer", addFinalizer),
134 DECLARE_NODE_API_PROPERTY("addFinalizerWithJS", addFinalizerWithJS),
135 DECLARE_NODE_API_PROPERTY("addFinalizerFailOnJS", addFinalizerFailOnJS),
136 DECLARE_NODE_API_PROPERTY("getFinalizerCallCount",
137 getFinalizerCallCount)};
138
139 NODE_API_CALL(
140 env,
141 napi_define_properties(env,
142 exports,
143 sizeof(descriptors) / sizeof(*descriptors),
144 descriptors));
145
146 return exports;
147 }
148 EXTERN_C_END
149