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