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