1 #include <stdlib.h>
2 #include <assert.h>
3 #include <js_native_api.h>
4 #include "../common.h"
5
6 static int test_value = 1;
7 static int finalize_count = 0;
8 static napi_ref test_reference = NULL;
9
GetFinalizeCount(napi_env env,napi_callback_info info)10 static napi_value GetFinalizeCount(napi_env env, napi_callback_info info) {
11 napi_value result;
12 NAPI_CALL(env, napi_create_int32(env, finalize_count, &result));
13 return result;
14 }
15
FinalizeExternal(napi_env env,void * data,void * hint)16 static void FinalizeExternal(napi_env env, void* data, void* hint) {
17 int *actual_value = data;
18 NAPI_ASSERT_RETURN_VOID(env, actual_value == &test_value,
19 "The correct pointer was passed to the finalizer");
20 finalize_count++;
21 }
22
CreateExternal(napi_env env,napi_callback_info info)23 static napi_value CreateExternal(napi_env env, napi_callback_info info) {
24 int* data = &test_value;
25
26 napi_value result;
27 NAPI_CALL(env,
28 napi_create_external(env,
29 data,
30 NULL, /* finalize_cb */
31 NULL, /* finalize_hint */
32 &result));
33
34 finalize_count = 0;
35 return result;
36 }
37
CreateSymbol(napi_env env,napi_callback_info info)38 static napi_value CreateSymbol(napi_env env, napi_callback_info info) {
39
40 size_t argc = 1;
41 napi_value args[1];
42
43 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL,NULL));
44 NAPI_ASSERT(env, argc == 1, "Expect one argument only (symbol description)");
45
46 napi_value result_symbol;
47
48 NAPI_CALL(env, napi_create_symbol(env, args[0], &result_symbol));
49 return result_symbol;
50 }
51
52 static napi_value
CreateExternalWithFinalize(napi_env env,napi_callback_info info)53 CreateExternalWithFinalize(napi_env env, napi_callback_info info) {
54 napi_value result;
55 NAPI_CALL(env,
56 napi_create_external(env,
57 &test_value,
58 FinalizeExternal,
59 NULL, /* finalize_hint */
60 &result));
61
62 finalize_count = 0;
63 return result;
64 }
65
CheckExternal(napi_env env,napi_callback_info info)66 static napi_value CheckExternal(napi_env env, napi_callback_info info) {
67 size_t argc = 1;
68 napi_value arg;
69 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &arg, NULL, NULL));
70
71 NAPI_ASSERT(env, argc == 1, "Expected one argument.");
72
73 napi_valuetype argtype;
74 NAPI_CALL(env, napi_typeof(env, arg, &argtype));
75
76 NAPI_ASSERT(env, argtype == napi_external, "Expected an external value.");
77
78 void* data;
79 NAPI_CALL(env, napi_get_value_external(env, arg, &data));
80
81 NAPI_ASSERT(env, data != NULL && *(int*)data == test_value,
82 "An external data value of 1 was expected.");
83
84 return NULL;
85 }
86
CreateReference(napi_env env,napi_callback_info info)87 static napi_value CreateReference(napi_env env, napi_callback_info info) {
88 NAPI_ASSERT(env, test_reference == NULL,
89 "The test allows only one reference at a time.");
90
91 size_t argc = 2;
92 napi_value args[2];
93 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
94 NAPI_ASSERT(env, argc == 2, "Expected two arguments.");
95
96 uint32_t initial_refcount;
97 NAPI_CALL(env, napi_get_value_uint32(env, args[1], &initial_refcount));
98
99 NAPI_CALL(env,
100 napi_create_reference(env, args[0], initial_refcount, &test_reference));
101
102 NAPI_ASSERT(env, test_reference != NULL,
103 "A reference should have been created.");
104
105 return NULL;
106 }
107
DeleteReference(napi_env env,napi_callback_info info)108 static napi_value DeleteReference(napi_env env, napi_callback_info info) {
109 NAPI_ASSERT(env, test_reference != NULL,
110 "A reference must have been created.");
111
112 NAPI_CALL(env, napi_delete_reference(env, test_reference));
113 test_reference = NULL;
114 return NULL;
115 }
116
IncrementRefcount(napi_env env,napi_callback_info info)117 static napi_value IncrementRefcount(napi_env env, napi_callback_info info) {
118 NAPI_ASSERT(env, test_reference != NULL,
119 "A reference must have been created.");
120
121 uint32_t refcount;
122 NAPI_CALL(env, napi_reference_ref(env, test_reference, &refcount));
123
124 napi_value result;
125 NAPI_CALL(env, napi_create_uint32(env, refcount, &result));
126 return result;
127 }
128
DecrementRefcount(napi_env env,napi_callback_info info)129 static napi_value DecrementRefcount(napi_env env, napi_callback_info info) {
130 NAPI_ASSERT(env, test_reference != NULL,
131 "A reference must have been created.");
132
133 uint32_t refcount;
134 NAPI_CALL(env, napi_reference_unref(env, test_reference, &refcount));
135
136 napi_value result;
137 NAPI_CALL(env, napi_create_uint32(env, refcount, &result));
138 return result;
139 }
140
GetReferenceValue(napi_env env,napi_callback_info info)141 static napi_value GetReferenceValue(napi_env env, napi_callback_info info) {
142 NAPI_ASSERT(env, test_reference != NULL,
143 "A reference must have been created.");
144
145 napi_value result;
146 NAPI_CALL(env, napi_get_reference_value(env, test_reference, &result));
147 return result;
148 }
149
DeleteBeforeFinalizeFinalizer(napi_env env,void * finalize_data,void * finalize_hint)150 static void DeleteBeforeFinalizeFinalizer(
151 napi_env env, void* finalize_data, void* finalize_hint) {
152 napi_ref* ref = (napi_ref*)finalize_data;
153 napi_value value;
154 assert(napi_get_reference_value(env, *ref, &value) == napi_ok);
155 assert(value == NULL);
156 napi_delete_reference(env, *ref);
157 free(ref);
158 }
159
ValidateDeleteBeforeFinalize(napi_env env,napi_callback_info info)160 static napi_value ValidateDeleteBeforeFinalize(napi_env env, napi_callback_info info) {
161 napi_value wrapObject;
162 size_t argc = 1;
163 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &wrapObject, NULL, NULL));
164
165 napi_ref* ref_t = malloc(sizeof(napi_ref));
166 NAPI_CALL(env, napi_wrap(env,
167 wrapObject,
168 ref_t,
169 DeleteBeforeFinalizeFinalizer,
170 NULL,
171 NULL));
172
173 // Create a reference that will be eligible for collection at the same
174 // time as the wrapped object by passing in the same wrapObject.
175 // This means that the FinalizeOrderValidation callback may be run
176 // before the finalizer for the newly created reference (there is a finalizer
177 // behind the scenes even though it cannot be passed to napi_create_reference)
178 // The Finalizer for the wrap (which is different than the finalizer
179 // for the reference) calls napi_delete_reference validating that
180 // napi_delete_reference can be called before the finalizer for the
181 // reference runs.
182 NAPI_CALL(env, napi_create_reference(env, wrapObject, 0, ref_t));
183 return wrapObject;
184 }
185
186 EXTERN_C_START
Init(napi_env env,napi_value exports)187 napi_value Init(napi_env env, napi_value exports) {
188 napi_property_descriptor descriptors[] = {
189 DECLARE_NAPI_GETTER("finalizeCount", GetFinalizeCount),
190 DECLARE_NAPI_PROPERTY("createExternal", CreateExternal),
191 DECLARE_NAPI_PROPERTY("createExternalWithFinalize",
192 CreateExternalWithFinalize),
193 DECLARE_NAPI_PROPERTY("checkExternal", CheckExternal),
194 DECLARE_NAPI_PROPERTY("createReference", CreateReference),
195 DECLARE_NAPI_PROPERTY("createSymbol", CreateSymbol),
196 DECLARE_NAPI_PROPERTY("deleteReference", DeleteReference),
197 DECLARE_NAPI_PROPERTY("incrementRefcount", IncrementRefcount),
198 DECLARE_NAPI_PROPERTY("decrementRefcount", DecrementRefcount),
199 DECLARE_NAPI_GETTER("referenceValue", GetReferenceValue),
200 DECLARE_NAPI_PROPERTY("validateDeleteBeforeFinalize",
201 ValidateDeleteBeforeFinalize),
202 };
203
204 NAPI_CALL(env, napi_define_properties(
205 env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
206
207 return exports;
208 }
209 EXTERN_C_END
210