• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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