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