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