• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Include node.h first to define NAPI_VERSION built with the
2 // binary.
3 // The node.h should also be included first in embedder's use case.
4 #include "node.h"
5 #include "node_api.h"
6 #include "node_test_fixture.h"
7 
InitializeBinding(v8::Local<v8::Object> exports,v8::Local<v8::Value> module,v8::Local<v8::Context> context,void * priv)8 void InitializeBinding(v8::Local<v8::Object> exports,
9                        v8::Local<v8::Value> module,
10                        v8::Local<v8::Context> context,
11                        void* priv) {
12   v8::Isolate* isolate = context->GetIsolate();
13   exports
14       ->Set(context,
15             v8::String::NewFromOneByte(isolate,
16                                        reinterpret_cast<const uint8_t*>("key"))
17                 .ToLocalChecked(),
18             v8::String::NewFromOneByte(
19                 isolate, reinterpret_cast<const uint8_t*>("value"))
20                 .ToLocalChecked())
21       .FromJust();
22 }
23 
24 NODE_MODULE_LINKED(cctest_linkedbinding, InitializeBinding)
25 
26 class LinkedBindingTest : public EnvironmentTestFixture {};
27 
TEST_F(LinkedBindingTest,SimpleTest)28 TEST_F(LinkedBindingTest, SimpleTest) {
29   const v8::HandleScope handle_scope(isolate_);
30   const Argv argv;
31   Env test_env{handle_scope, argv};
32 
33   v8::Local<v8::Context> context = isolate_->GetCurrentContext();
34 
35   const char* run_script = "process._linkedBinding('cctest_linkedbinding').key";
36   v8::Local<v8::Script> script =
37       v8::Script::Compile(
38           context,
39           v8::String::NewFromOneByte(
40               isolate_, reinterpret_cast<const uint8_t*>(run_script))
41               .ToLocalChecked())
42           .ToLocalChecked();
43   v8::Local<v8::Value> completion_value = script->Run(context).ToLocalChecked();
44   v8::String::Utf8Value utf8val(isolate_, completion_value);
45   CHECK_NOT_NULL(*utf8val);
46   CHECK_EQ(strcmp(*utf8val, "value"), 0);
47 }
48 
InitializeLocalBinding(v8::Local<v8::Object> exports,v8::Local<v8::Value> module,v8::Local<v8::Context> context,void * priv)49 void InitializeLocalBinding(v8::Local<v8::Object> exports,
50                             v8::Local<v8::Value> module,
51                             v8::Local<v8::Context> context,
52                             void* priv) {
53   ++*static_cast<int*>(priv);
54   v8::Isolate* isolate = context->GetIsolate();
55   exports
56       ->Set(context,
57             v8::String::NewFromOneByte(isolate,
58                                        reinterpret_cast<const uint8_t*>("key"))
59                 .ToLocalChecked(),
60             v8::String::NewFromOneByte(
61                 isolate, reinterpret_cast<const uint8_t*>("value"))
62                 .ToLocalChecked())
63       .FromJust();
64 }
65 
TEST_F(LinkedBindingTest,LocallyDefinedLinkedBindingTest)66 TEST_F(LinkedBindingTest, LocallyDefinedLinkedBindingTest) {
67   const v8::HandleScope handle_scope(isolate_);
68   const Argv argv;
69   Env test_env{handle_scope, argv};
70 
71   int calls = 0;
72   AddLinkedBinding(*test_env, "local_linked", InitializeLocalBinding, &calls);
73 
74   v8::Local<v8::Context> context = isolate_->GetCurrentContext();
75 
76   const char* run_script = "process._linkedBinding('local_linked').key";
77   v8::Local<v8::Script> script =
78       v8::Script::Compile(
79           context,
80           v8::String::NewFromOneByte(
81               isolate_, reinterpret_cast<const uint8_t*>(run_script))
82               .ToLocalChecked())
83           .ToLocalChecked();
84   v8::Local<v8::Value> completion_value = script->Run(context).ToLocalChecked();
85   v8::String::Utf8Value utf8val(isolate_, completion_value);
86   CHECK_NOT_NULL(*utf8val);
87   CHECK_EQ(strcmp(*utf8val, "value"), 0);
88   CHECK_EQ(calls, 1);
89 }
90 
InitializeLocalNapiBinding(napi_env env,napi_value exports)91 napi_value InitializeLocalNapiBinding(napi_env env, napi_value exports) {
92   napi_value key, value;
93   CHECK_EQ(napi_create_string_utf8(env, "hello", NAPI_AUTO_LENGTH, &key),
94            napi_ok);
95   CHECK_EQ(napi_create_string_utf8(env, "world", NAPI_AUTO_LENGTH, &value),
96            napi_ok);
97   CHECK_EQ(napi_set_property(env, exports, key, value), napi_ok);
98   return nullptr;
99 }
100 
101 static napi_module local_linked_napi = {
102     NAPI_MODULE_VERSION,
103     node::ModuleFlags::kLinked,
104     nullptr,
105     InitializeLocalNapiBinding,
106     "local_linked_napi",
107     nullptr,
108     {0},
109 };
110 
TEST_F(LinkedBindingTest,LocallyDefinedLinkedBindingNapiTest)111 TEST_F(LinkedBindingTest, LocallyDefinedLinkedBindingNapiTest) {
112   const v8::HandleScope handle_scope(isolate_);
113   const Argv argv;
114   Env test_env{handle_scope, argv};
115 
116   AddLinkedBinding(*test_env, local_linked_napi);
117 
118   v8::Local<v8::Context> context = isolate_->GetCurrentContext();
119 
120   const char* run_script = "process._linkedBinding('local_linked_napi').hello";
121   v8::Local<v8::Script> script =
122       v8::Script::Compile(
123           context,
124           v8::String::NewFromOneByte(
125               isolate_, reinterpret_cast<const uint8_t*>(run_script))
126               .ToLocalChecked())
127           .ToLocalChecked();
128   v8::Local<v8::Value> completion_value = script->Run(context).ToLocalChecked();
129   v8::String::Utf8Value utf8val(isolate_, completion_value);
130   CHECK_NOT_NULL(*utf8val);
131   CHECK_EQ(strcmp(*utf8val, "world"), 0);
132 }
133 
TEST_F(LinkedBindingTest,LocallyDefinedLinkedBindingNapiCallbackTest)134 TEST_F(LinkedBindingTest, LocallyDefinedLinkedBindingNapiCallbackTest) {
135   const v8::HandleScope handle_scope(isolate_);
136   const Argv argv;
137   Env test_env{handle_scope, argv};
138 
139   AddLinkedBinding(*test_env,
140                    "local_linked_napi_cb",
141                    InitializeLocalNapiBinding,
142                    NAPI_VERSION);
143 
144   v8::Local<v8::Context> context = isolate_->GetCurrentContext();
145 
146   const char* run_script =
147       "process._linkedBinding('local_linked_napi_cb').hello";
148   v8::Local<v8::Script> script =
149       v8::Script::Compile(
150           context,
151           v8::String::NewFromOneByte(
152               isolate_, reinterpret_cast<const uint8_t*>(run_script))
153               .ToLocalChecked())
154           .ToLocalChecked();
155   v8::Local<v8::Value> completion_value = script->Run(context).ToLocalChecked();
156   v8::String::Utf8Value utf8val(isolate_, completion_value);
157   CHECK_NOT_NULL(*utf8val);
158   CHECK_EQ(strcmp(*utf8val, "world"), 0);
159 }
160 
161 static int32_t node_api_version = NODE_API_DEFAULT_MODULE_API_VERSION;
162 
InitializeLocalNapiRefBinding(napi_env env,napi_value exports)163 napi_value InitializeLocalNapiRefBinding(napi_env env, napi_value exports) {
164   napi_value key, value;
165   CHECK_EQ(
166       napi_create_string_utf8(env, "napi_ref_created", NAPI_AUTO_LENGTH, &key),
167       napi_ok);
168 
169   // In experimental Node-API version we can create napi_ref to any value type.
170   // Here we are trying to create a reference to the key string.
171   napi_ref ref{};
172   if (node_api_version == NAPI_VERSION_EXPERIMENTAL) {
173     CHECK_EQ(napi_create_reference(env, key, 1, &ref), napi_ok);
174     CHECK_EQ(napi_delete_reference(env, ref), napi_ok);
175   } else {
176     CHECK_EQ(napi_create_reference(env, key, 1, &ref), napi_invalid_arg);
177   }
178   CHECK_EQ(napi_get_boolean(env, ref != nullptr, &value), napi_ok);
179   CHECK_EQ(napi_set_property(env, exports, key, value), napi_ok);
180   return nullptr;
181 }
182 
183 // napi_ref in Node-API version 8 cannot accept strings.
TEST_F(LinkedBindingTest,LocallyDefinedLinkedBindingNapiRefVersion8Test)184 TEST_F(LinkedBindingTest, LocallyDefinedLinkedBindingNapiRefVersion8Test) {
185   node_api_version = NODE_API_DEFAULT_MODULE_API_VERSION;
186   const v8::HandleScope handle_scope(isolate_);
187   const Argv argv;
188   Env test_env{handle_scope, argv};
189 
190   AddLinkedBinding(*test_env,
191                    "local_linked_napi_ref_v8",
192                    InitializeLocalNapiRefBinding,
193                    node_api_version);
194 
195   v8::Local<v8::Context> context = isolate_->GetCurrentContext();
196 
197   const char* run_script =
198       "process._linkedBinding('local_linked_napi_ref_v8').napi_ref_created";
199   v8::Local<v8::Script> script =
200       v8::Script::Compile(
201           context,
202           v8::String::NewFromOneByte(
203               isolate_, reinterpret_cast<const uint8_t*>(run_script))
204               .ToLocalChecked())
205           .ToLocalChecked();
206   v8::Local<v8::Value> completion_value = script->Run(context).ToLocalChecked();
207   CHECK(completion_value->IsBoolean());
208   CHECK(!completion_value.As<v8::Boolean>()->Value());
209 }
210 
211 // Experimental version of napi_ref in Node-API can accept strings.
TEST_F(LinkedBindingTest,LocallyDefinedLinkedBindingNapiRefExperimentalTest)212 TEST_F(LinkedBindingTest, LocallyDefinedLinkedBindingNapiRefExperimentalTest) {
213   node_api_version = NAPI_VERSION_EXPERIMENTAL;
214   const v8::HandleScope handle_scope(isolate_);
215   const Argv argv;
216   Env test_env{handle_scope, argv};
217 
218   AddLinkedBinding(*test_env,
219                    "local_linked_napi_ref_experimental",
220                    InitializeLocalNapiRefBinding,
221                    node_api_version);
222 
223   v8::Local<v8::Context> context = isolate_->GetCurrentContext();
224 
225   const char* run_script = "process._linkedBinding('local_linked_napi_ref_"
226                            "experimental').napi_ref_created";
227   v8::Local<v8::Script> script =
228       v8::Script::Compile(
229           context,
230           v8::String::NewFromOneByte(
231               isolate_, reinterpret_cast<const uint8_t*>(run_script))
232               .ToLocalChecked())
233           .ToLocalChecked();
234   v8::Local<v8::Value> completion_value = script->Run(context).ToLocalChecked();
235   CHECK(completion_value->IsBoolean());
236   CHECK(completion_value.As<v8::Boolean>()->Value());
237 }
238 
NapiLinkedWithInstanceData(napi_env env,napi_value exports)239 napi_value NapiLinkedWithInstanceData(napi_env env, napi_value exports) {
240   int* instance_data = new int(0);
241   CHECK_EQ(napi_set_instance_data(
242                env,
243                instance_data,
244                [](napi_env env, void* data, void* hint) {
245                  ++*static_cast<int*>(data);
246                },
247                nullptr),
248            napi_ok);
249 
250   napi_value key, value;
251   CHECK_EQ(napi_create_string_utf8(env, "hello", NAPI_AUTO_LENGTH, &key),
252            napi_ok);
253   CHECK_EQ(napi_create_external(env, instance_data, nullptr, nullptr, &value),
254            napi_ok);
255   CHECK_EQ(napi_set_property(env, exports, key, value), napi_ok);
256   return nullptr;
257 }
258 
259 static napi_module local_linked_napi_id = {
260     NAPI_MODULE_VERSION,
261     node::ModuleFlags::kLinked,
262     nullptr,
263     NapiLinkedWithInstanceData,
264     "local_linked_napi_id",
265     nullptr,
266     {0},
267 };
268 
TEST_F(LinkedBindingTest,LocallyDefinedLinkedBindingNapiInstanceDataTest)269 TEST_F(LinkedBindingTest, LocallyDefinedLinkedBindingNapiInstanceDataTest) {
270   const v8::HandleScope handle_scope(isolate_);
271   int* instance_data = nullptr;
272 
273   {
274     const Argv argv;
275     Env test_env{handle_scope, argv};
276 
277     AddLinkedBinding(*test_env, local_linked_napi_id);
278 
279     v8::Local<v8::Context> context = isolate_->GetCurrentContext();
280 
281     const char* run_script =
282         "process._linkedBinding('local_linked_napi_id').hello";
283     v8::Local<v8::Script> script =
284         v8::Script::Compile(
285             context,
286             v8::String::NewFromOneByte(
287                 isolate_, reinterpret_cast<const uint8_t*>(run_script))
288                 .ToLocalChecked())
289             .ToLocalChecked();
290     v8::Local<v8::Value> completion_value =
291         script->Run(context).ToLocalChecked();
292     CHECK(completion_value->IsExternal());
293     instance_data =
294         static_cast<int*>(completion_value.As<v8::External>()->Value());
295     CHECK_NE(instance_data, nullptr);
296     CHECK_EQ(*instance_data, 0);
297   }
298 
299   CHECK_EQ(*instance_data, 1);
300   delete instance_data;
301 }
302 
TEST_F(LinkedBindingTest,LocallyDefinedLinkedBindingNapiCallbackInstanceDataTest)303 TEST_F(LinkedBindingTest,
304        LocallyDefinedLinkedBindingNapiCallbackInstanceDataTest) {
305   const v8::HandleScope handle_scope(isolate_);
306   int* instance_data = nullptr;
307 
308   {
309     const Argv argv;
310     Env test_env{handle_scope, argv};
311 
312     AddLinkedBinding(*test_env,
313                      "local_linked_napi_id_cb",
314                      NapiLinkedWithInstanceData,
315                      NAPI_VERSION);
316 
317     v8::Local<v8::Context> context = isolate_->GetCurrentContext();
318 
319     const char* run_script =
320         "process._linkedBinding('local_linked_napi_id_cb').hello";
321     v8::Local<v8::Script> script =
322         v8::Script::Compile(
323             context,
324             v8::String::NewFromOneByte(
325                 isolate_, reinterpret_cast<const uint8_t*>(run_script))
326                 .ToLocalChecked())
327             .ToLocalChecked();
328     v8::Local<v8::Value> completion_value =
329         script->Run(context).ToLocalChecked();
330     CHECK(completion_value->IsExternal());
331     instance_data =
332         static_cast<int*>(completion_value.As<v8::External>()->Value());
333     CHECK_NE(instance_data, nullptr);
334     CHECK_EQ(*instance_data, 0);
335   }
336 
337   CHECK_EQ(*instance_data, 1);
338   delete instance_data;
339 }
340 
TEST_F(LinkedBindingTest,ManyBindingsTest)341 TEST_F(LinkedBindingTest, ManyBindingsTest) {
342   const v8::HandleScope handle_scope(isolate_);
343   const Argv argv;
344   Env test_env{handle_scope, argv};
345 
346   int calls = 0;
347   AddLinkedBinding(*test_env, "local_linked1", InitializeLocalBinding, &calls);
348   AddLinkedBinding(*test_env, "local_linked2", InitializeLocalBinding, &calls);
349   AddLinkedBinding(*test_env, "local_linked3", InitializeLocalBinding, &calls);
350   AddLinkedBinding(*test_env, local_linked_napi);  // Add a N-API addon as well.
351   AddLinkedBinding(*test_env, "local_linked4", InitializeLocalBinding, &calls);
352   AddLinkedBinding(*test_env, "local_linked5", InitializeLocalBinding, &calls);
353 
354   v8::Local<v8::Context> context = isolate_->GetCurrentContext();
355 
356   const char* run_script =
357       "for (let i = 1; i <= 5; i++)process._linkedBinding(`local_linked${i}`);"
358       "process._linkedBinding('local_linked_napi').hello";
359   v8::Local<v8::Script> script =
360       v8::Script::Compile(
361           context,
362           v8::String::NewFromOneByte(
363               isolate_, reinterpret_cast<const uint8_t*>(run_script))
364               .ToLocalChecked())
365           .ToLocalChecked();
366   v8::Local<v8::Value> completion_value = script->Run(context).ToLocalChecked();
367   v8::String::Utf8Value utf8val(isolate_, completion_value);
368   CHECK_NOT_NULL(*utf8val);
369   CHECK_EQ(strcmp(*utf8val, "world"), 0);
370   CHECK_EQ(calls, 5);
371 }
372