1 #include "node_buffer.h"
2 #include "node_internals.h"
3 #include "libplatform/libplatform.h"
4
5 #include <string>
6 #include "gtest/gtest.h"
7 #include "node_test_fixture.h"
8
9 using node::AtExit;
10 using node::RunAtExit;
11
12 static bool called_cb_1 = false;
13 static bool called_cb_2 = false;
14 static bool called_cb_ordered_1 = false;
15 static bool called_cb_ordered_2 = false;
16 static bool called_at_exit_js = false;
17 static void at_exit_callback1(void* arg);
18 static void at_exit_callback2(void* arg);
19 static void at_exit_callback_ordered1(void* arg);
20 static void at_exit_callback_ordered2(void* arg);
21 static void at_exit_js(void* arg);
22 static std::string cb_1_arg; // NOLINT(runtime/string)
23
24 class EnvironmentTest : public EnvironmentTestFixture {
25 private:
TearDown()26 void TearDown() override {
27 NodeTestFixture::TearDown();
28 called_cb_1 = false;
29 called_cb_2 = false;
30 called_cb_ordered_1 = false;
31 called_cb_ordered_2 = false;
32 }
33 };
34
TEST_F(EnvironmentTest,EnvironmentWithESMLoader)35 TEST_F(EnvironmentTest, EnvironmentWithESMLoader) {
36 const v8::HandleScope handle_scope(isolate_);
37 Argv argv;
38 Env env {handle_scope, argv};
39
40 node::Environment* envi = *env;
41 envi->options()->experimental_vm_modules = true;
42
43 SetProcessExitHandler(*env, [&](node::Environment* env_, int exit_code) {
44 EXPECT_EQ(*env, env_);
45 EXPECT_EQ(exit_code, 0);
46 node::Stop(*env);
47 });
48
49 node::LoadEnvironment(
50 *env,
51 "const { SourceTextModule } = require('vm');"
52 "(async () => {"
53 "const stmString = 'globalThis.importResult = import(\"\")';"
54 "const m = new SourceTextModule(stmString, {"
55 "importModuleDynamically: (async () => {"
56 "const m = new SourceTextModule('');"
57 "await m.link(() => 0);"
58 "await m.evaluate();"
59 "return m.namespace;"
60 "}),"
61 "});"
62 "await m.link(() => 0);"
63 "await m.evaluate();"
64 "delete globalThis.importResult;"
65 "process.exit(0);"
66 "})()");
67 }
68
TEST_F(EnvironmentTest,EnvironmentWithNoESMLoader)69 TEST_F(EnvironmentTest, EnvironmentWithNoESMLoader) {
70 const v8::HandleScope handle_scope(isolate_);
71 Argv argv;
72 Env env {handle_scope, argv, node::EnvironmentFlags::kNoRegisterESMLoader};
73
74 node::Environment* envi = *env;
75 envi->options()->experimental_vm_modules = true;
76
77 SetProcessExitHandler(*env, [&](node::Environment* env_, int exit_code) {
78 EXPECT_EQ(*env, env_);
79 EXPECT_EQ(exit_code, 1);
80 node::Stop(*env);
81 });
82
83 node::LoadEnvironment(
84 *env,
85 "const { SourceTextModule } = require('vm');"
86 "(async () => {"
87 "const stmString = 'globalThis.importResult = import(\"\")';"
88 "const m = new SourceTextModule(stmString, {"
89 "importModuleDynamically: (async () => {"
90 "const m = new SourceTextModule('');"
91 "await m.link(() => 0);"
92 "await m.evaluate();"
93 "return m.namespace;"
94 "}),"
95 "});"
96 "await m.link(() => 0);"
97 "await m.evaluate();"
98 "delete globalThis.importResult;"
99 "})()");
100 }
101
TEST_F(EnvironmentTest,PreExecutionPreparation)102 TEST_F(EnvironmentTest, PreExecutionPreparation) {
103 const v8::HandleScope handle_scope(isolate_);
104 const Argv argv;
105 Env env {handle_scope, argv};
106
107 node::LoadEnvironment(*env, [&](const node::StartExecutionCallbackInfo& info)
108 -> v8::MaybeLocal<v8::Value> {
109 return v8::Null(isolate_);
110 });
111
112 v8::Local<v8::Context> context = isolate_->GetCurrentContext();
113 const char* run_script = "process.argv0";
114 v8::Local<v8::Script> script = v8::Script::Compile(
115 context,
116 v8::String::NewFromOneByte(isolate_,
117 reinterpret_cast<const uint8_t*>(run_script),
118 v8::NewStringType::kNormal).ToLocalChecked())
119 .ToLocalChecked();
120 v8::Local<v8::Value> result = script->Run(context).ToLocalChecked();
121 CHECK(result->IsString());
122 }
123
TEST_F(EnvironmentTest,LoadEnvironmentWithCallback)124 TEST_F(EnvironmentTest, LoadEnvironmentWithCallback) {
125 const v8::HandleScope handle_scope(isolate_);
126 const Argv argv;
127 Env env {handle_scope, argv};
128
129 v8::Local<v8::Context> context = isolate_->GetCurrentContext();
130 bool called_cb = false;
131 node::LoadEnvironment(*env,
132 [&](const node::StartExecutionCallbackInfo& info)
133 -> v8::MaybeLocal<v8::Value> {
134 called_cb = true;
135
136 CHECK(info.process_object->IsObject());
137 CHECK(info.native_require->IsFunction());
138
139 v8::Local<v8::Value> argv0 = info.process_object->Get(
140 context,
141 v8::String::NewFromOneByte(
142 isolate_,
143 reinterpret_cast<const uint8_t*>("argv0"),
144 v8::NewStringType::kNormal).ToLocalChecked()).ToLocalChecked();
145 CHECK(argv0->IsString());
146
147 return info.process_object;
148 });
149
150 CHECK(called_cb);
151 }
152
TEST_F(EnvironmentTest,LoadEnvironmentWithSource)153 TEST_F(EnvironmentTest, LoadEnvironmentWithSource) {
154 const v8::HandleScope handle_scope(isolate_);
155 const Argv argv;
156 Env env {handle_scope, argv};
157
158 v8::Local<v8::Context> context = isolate_->GetCurrentContext();
159 v8::Local<v8::Value> main_ret =
160 node::LoadEnvironment(*env,
161 "return { process, require };").ToLocalChecked();
162
163 CHECK(main_ret->IsObject());
164 CHECK(main_ret.As<v8::Object>()->Get(
165 context,
166 v8::String::NewFromOneByte(
167 isolate_,
168 reinterpret_cast<const uint8_t*>("process"),
169 v8::NewStringType::kNormal).ToLocalChecked())
170 .ToLocalChecked()->IsObject());
171 CHECK(main_ret.As<v8::Object>()->Get(
172 context,
173 v8::String::NewFromOneByte(
174 isolate_,
175 reinterpret_cast<const uint8_t*>("require"),
176 v8::NewStringType::kNormal).ToLocalChecked())
177 .ToLocalChecked()->IsFunction());
178 }
179
TEST_F(EnvironmentTest,AtExitWithEnvironment)180 TEST_F(EnvironmentTest, AtExitWithEnvironment) {
181 const v8::HandleScope handle_scope(isolate_);
182 const Argv argv;
183 Env env {handle_scope, argv};
184
185 AtExit(*env, at_exit_callback1);
186 RunAtExit(*env);
187 EXPECT_TRUE(called_cb_1);
188 }
189
TEST_F(EnvironmentTest,AtExitWithoutEnvironment)190 TEST_F(EnvironmentTest, AtExitWithoutEnvironment) {
191 const v8::HandleScope handle_scope(isolate_);
192 const Argv argv;
193 Env env {handle_scope, argv};
194
195 AtExit(at_exit_callback1); // No Environment is passed to AtExit.
196 RunAtExit(*env);
197 EXPECT_TRUE(called_cb_1);
198 }
199
TEST_F(EnvironmentTest,AtExitOrder)200 TEST_F(EnvironmentTest, AtExitOrder) {
201 const v8::HandleScope handle_scope(isolate_);
202 const Argv argv;
203 Env env {handle_scope, argv};
204
205 // Test that callbacks are run in reverse order.
206 AtExit(*env, at_exit_callback_ordered1);
207 AtExit(*env, at_exit_callback_ordered2);
208 RunAtExit(*env);
209 EXPECT_TRUE(called_cb_ordered_1);
210 EXPECT_TRUE(called_cb_ordered_2);
211 }
212
TEST_F(EnvironmentTest,AtExitWithArgument)213 TEST_F(EnvironmentTest, AtExitWithArgument) {
214 const v8::HandleScope handle_scope(isolate_);
215 const Argv argv;
216 Env env {handle_scope, argv};
217
218 std::string arg{"some args"};
219 AtExit(*env, at_exit_callback1, static_cast<void*>(&arg));
220 RunAtExit(*env);
221 EXPECT_EQ(arg, cb_1_arg);
222 }
223
TEST_F(EnvironmentTest,AtExitRunsJS)224 TEST_F(EnvironmentTest, AtExitRunsJS) {
225 const v8::HandleScope handle_scope(isolate_);
226 const Argv argv;
227 Env env {handle_scope, argv};
228
229 AtExit(*env, at_exit_js, static_cast<void*>(isolate_));
230 EXPECT_FALSE(called_at_exit_js);
231 RunAtExit(*env);
232 EXPECT_TRUE(called_at_exit_js);
233 }
234
TEST_F(EnvironmentTest,MultipleEnvironmentsPerIsolate)235 TEST_F(EnvironmentTest, MultipleEnvironmentsPerIsolate) {
236 const v8::HandleScope handle_scope(isolate_);
237 const Argv argv;
238 // Only one of the Environments can have default flags and own the inspector.
239 Env env1 {handle_scope, argv};
240 Env env2 {handle_scope, argv, node::EnvironmentFlags::kNoFlags};
241
242 AtExit(*env1, at_exit_callback1);
243 AtExit(*env2, at_exit_callback2);
244 RunAtExit(*env1);
245 EXPECT_TRUE(called_cb_1);
246 EXPECT_FALSE(called_cb_2);
247
248 RunAtExit(*env2);
249 EXPECT_TRUE(called_cb_2);
250 }
251
TEST_F(EnvironmentTest,NoEnvironmentSanity)252 TEST_F(EnvironmentTest, NoEnvironmentSanity) {
253 const v8::HandleScope handle_scope(isolate_);
254 v8::Local<v8::Context> context = v8::Context::New(isolate_);
255 EXPECT_EQ(node::Environment::GetCurrent(context), nullptr);
256 EXPECT_EQ(node::GetCurrentEnvironment(context), nullptr);
257 EXPECT_EQ(node::Environment::GetCurrent(isolate_), nullptr);
258
259 v8::Context::Scope context_scope(context);
260 EXPECT_EQ(node::Environment::GetCurrent(context), nullptr);
261 EXPECT_EQ(node::GetCurrentEnvironment(context), nullptr);
262 EXPECT_EQ(node::Environment::GetCurrent(isolate_), nullptr);
263 }
264
TEST_F(EnvironmentTest,NonNodeJSContext)265 TEST_F(EnvironmentTest, NonNodeJSContext) {
266 const v8::HandleScope handle_scope(isolate_);
267 const Argv argv;
268 Env test_env {handle_scope, argv};
269
270 EXPECT_EQ(node::Environment::GetCurrent(v8::Local<v8::Context>()), nullptr);
271
272 node::Environment* env = *test_env;
273 EXPECT_EQ(node::Environment::GetCurrent(isolate_), env);
274 EXPECT_EQ(node::Environment::GetCurrent(env->context()), env);
275 EXPECT_EQ(node::GetCurrentEnvironment(env->context()), env);
276
277 v8::Local<v8::Context> context = v8::Context::New(isolate_);
278 EXPECT_EQ(node::Environment::GetCurrent(context), nullptr);
279 EXPECT_EQ(node::GetCurrentEnvironment(context), nullptr);
280 EXPECT_EQ(node::Environment::GetCurrent(isolate_), env);
281
282 v8::Context::Scope context_scope(context);
283 EXPECT_EQ(node::Environment::GetCurrent(context), nullptr);
284 EXPECT_EQ(node::GetCurrentEnvironment(context), nullptr);
285 EXPECT_EQ(node::Environment::GetCurrent(isolate_), nullptr);
286 }
287
at_exit_callback1(void * arg)288 static void at_exit_callback1(void* arg) {
289 called_cb_1 = true;
290 if (arg) {
291 cb_1_arg = *static_cast<std::string*>(arg);
292 }
293 }
294
at_exit_callback2(void * arg)295 static void at_exit_callback2(void* arg) {
296 called_cb_2 = true;
297 }
298
at_exit_callback_ordered1(void * arg)299 static void at_exit_callback_ordered1(void* arg) {
300 EXPECT_TRUE(called_cb_ordered_2);
301 called_cb_ordered_1 = true;
302 }
303
at_exit_callback_ordered2(void * arg)304 static void at_exit_callback_ordered2(void* arg) {
305 EXPECT_FALSE(called_cb_ordered_1);
306 called_cb_ordered_2 = true;
307 }
308
at_exit_js(void * arg)309 static void at_exit_js(void* arg) {
310 v8::Isolate* isolate = static_cast<v8::Isolate*>(arg);
311 v8::HandleScope handle_scope(isolate);
312 v8::Local<v8::Object> obj = v8::Object::New(isolate);
313 assert(!obj.IsEmpty()); // Assert VM is still alive.
314 assert(obj->IsObject());
315 called_at_exit_js = true;
316 }
317
TEST_F(EnvironmentTest,SetImmediateCleanup)318 TEST_F(EnvironmentTest, SetImmediateCleanup) {
319 int called = 0;
320 int called_unref = 0;
321
322 {
323 const v8::HandleScope handle_scope(isolate_);
324 const Argv argv;
325 Env env {handle_scope, argv};
326
327 node::LoadEnvironment(*env,
328 [&](const node::StartExecutionCallbackInfo& info)
329 -> v8::MaybeLocal<v8::Value> {
330 return v8::Object::New(isolate_);
331 });
332
333 (*env)->SetImmediate([&](node::Environment* env_arg) {
334 EXPECT_EQ(env_arg, *env);
335 called++;
336 });
337 (*env)->SetImmediate([&](node::Environment* env_arg) {
338 EXPECT_EQ(env_arg, *env);
339 called_unref++;
340 }, node::CallbackFlags::kUnrefed);
341 }
342
343 EXPECT_EQ(called, 1);
344 EXPECT_EQ(called_unref, 0);
345 }
346
347 static char hello[] = "hello";
348
TEST_F(EnvironmentTest,BufferWithFreeCallbackIsDetached)349 TEST_F(EnvironmentTest, BufferWithFreeCallbackIsDetached) {
350 // Test that a Buffer allocated with a free callback is detached after
351 // its callback has been called.
352 const v8::HandleScope handle_scope(isolate_);
353 const Argv argv;
354
355 int callback_calls = 0;
356
357 v8::Local<v8::ArrayBuffer> ab;
358 {
359 Env env {handle_scope, argv};
360 v8::Local<v8::Object> buf_obj = node::Buffer::New(
361 isolate_,
362 hello,
363 sizeof(hello),
364 [](char* data, void* hint) {
365 CHECK_EQ(data, hello);
366 ++*static_cast<int*>(hint);
367 },
368 &callback_calls).ToLocalChecked();
369 CHECK(buf_obj->IsUint8Array());
370 ab = buf_obj.As<v8::Uint8Array>()->Buffer();
371 CHECK_EQ(ab->ByteLength(), sizeof(hello));
372 }
373
374 CHECK_EQ(callback_calls, 1);
375 CHECK_EQ(ab->ByteLength(), 0);
376 }
377
378 #if HAVE_INSPECTOR
TEST_F(EnvironmentTest,InspectorMultipleEmbeddedEnvironments)379 TEST_F(EnvironmentTest, InspectorMultipleEmbeddedEnvironments) {
380 // Tests that child Environments can be created through the public API
381 // that are accessible by the inspector.
382 // This test sets a global variable in the child Environment, and reads it
383 // back both through the inspector and inside the child Environment, and
384 // makes sure that those correspond to the value that was originally set.
385 const v8::HandleScope handle_scope(isolate_);
386 const Argv argv;
387 Env env {handle_scope, argv};
388
389 v8::Local<v8::Context> context = isolate_->GetCurrentContext();
390 node::LoadEnvironment(*env,
391 "'use strict';\n"
392 "const { Worker } = require('worker_threads');\n"
393 "const { Session } = require('inspector');\n"
394
395 "const session = new Session();\n"
396 "session.connect();\n"
397 "session.on('NodeWorker.attachedToWorker', (\n"
398 " ({ params: { workerInfo, sessionId } }) => {\n"
399 " session.post('NodeWorker.sendMessageToWorker', {\n"
400 " sessionId,\n"
401 " message: JSON.stringify({\n"
402 " id: 1,\n"
403 " method: 'Runtime.evaluate',\n"
404 " params: {\n"
405 " expression: 'globalThis.variableFromParent = 42;'\n"
406 " }\n"
407 " })\n"
408 " });\n"
409 " session.on('NodeWorker.receivedMessageFromWorker',\n"
410 " ({ params: { message } }) => {\n"
411 " global.messageFromWorker = \n"
412 " JSON.parse(message).result.result.value;\n"
413 " });\n"
414 " }));\n"
415 "session.post('NodeWorker.enable', { waitForDebuggerOnStart: false });\n")
416 .ToLocalChecked();
417
418 struct ChildEnvironmentData {
419 node::ThreadId thread_id;
420 std::unique_ptr<node::InspectorParentHandle> inspector_parent_handle;
421 node::MultiIsolatePlatform* platform;
422 int32_t extracted_value = -1;
423 uv_async_t thread_stopped_async;
424 };
425
426 ChildEnvironmentData data;
427 data.thread_id = node::AllocateEnvironmentThreadId();
428 data.inspector_parent_handle =
429 GetInspectorParentHandle(*env, data.thread_id, "file:///embedded.js");
430 CHECK(data.inspector_parent_handle);
431 data.platform = GetMultiIsolatePlatform(*env);
432 CHECK_NOT_NULL(data.platform);
433
434 bool thread_stopped = false;
435 int err = uv_async_init(
436 ¤t_loop, &data.thread_stopped_async, [](uv_async_t* async) {
437 *static_cast<bool*>(async->data) = true;
438 uv_close(reinterpret_cast<uv_handle_t*>(async), nullptr);
439 });
440 CHECK_EQ(err, 0);
441 data.thread_stopped_async.data = &thread_stopped;
442
443 uv_thread_t thread;
444 err = uv_thread_create(&thread, [](void* arg) {
445 ChildEnvironmentData* data = static_cast<ChildEnvironmentData*>(arg);
446 std::shared_ptr<node::ArrayBufferAllocator> aba =
447 node::ArrayBufferAllocator::Create();
448 uv_loop_t loop;
449 uv_loop_init(&loop);
450 v8::Isolate* isolate = NewIsolate(aba.get(), &loop, data->platform);
451 CHECK_NOT_NULL(isolate);
452
453 {
454 v8::Isolate::Scope isolate_scope(isolate);
455 v8::HandleScope handle_scope(isolate);
456
457 v8::Local<v8::Context> context = node::NewContext(isolate);
458 CHECK(!context.IsEmpty());
459 v8::Context::Scope context_scope(context);
460
461 node::IsolateData* isolate_data = node::CreateIsolateData(
462 isolate,
463 &loop,
464 data->platform);
465 CHECK_NOT_NULL(isolate_data);
466 node::Environment* environment = node::CreateEnvironment(
467 isolate_data,
468 context,
469 { "dummy" },
470 {},
471 node::EnvironmentFlags::kNoFlags,
472 data->thread_id,
473 std::move(data->inspector_parent_handle));
474 CHECK_NOT_NULL(environment);
475
476 v8::Local<v8::Value> extracted_value = LoadEnvironment(
477 environment,
478 "while (!global.variableFromParent) {}\n"
479 "return global.variableFromParent;").ToLocalChecked();
480
481 uv_run(&loop, UV_RUN_DEFAULT);
482 CHECK(extracted_value->IsInt32());
483 data->extracted_value = extracted_value.As<v8::Int32>()->Value();
484
485 node::FreeEnvironment(environment);
486 node::FreeIsolateData(isolate_data);
487 }
488
489 data->platform->UnregisterIsolate(isolate);
490 isolate->Dispose();
491 uv_run(&loop, UV_RUN_DEFAULT);
492 CHECK_EQ(uv_loop_close(&loop), 0);
493
494 uv_async_send(&data->thread_stopped_async);
495 }, &data);
496 CHECK_EQ(err, 0);
497
498 bool more;
499 do {
500 uv_run(¤t_loop, UV_RUN_DEFAULT);
501 data.platform->DrainTasks(isolate_);
502 more = uv_loop_alive(¤t_loop);
503 } while (!thread_stopped || more);
504
505 uv_thread_join(&thread);
506
507 v8::Local<v8::Value> from_inspector =
508 context->Global()->Get(
509 context,
510 v8::String::NewFromOneByte(
511 isolate_,
512 reinterpret_cast<const uint8_t*>("messageFromWorker"),
513 v8::NewStringType::kNormal).ToLocalChecked())
514 .ToLocalChecked();
515 CHECK_EQ(data.extracted_value, 42);
516 CHECK_EQ(from_inspector->IntegerValue(context).FromJust(), 42);
517 }
518 #endif // HAVE_INSPECTOR
519
TEST_F(EnvironmentTest,ExitHandlerTest)520 TEST_F(EnvironmentTest, ExitHandlerTest) {
521 const v8::HandleScope handle_scope(isolate_);
522 const Argv argv;
523
524 int callback_calls = 0;
525
526 Env env {handle_scope, argv};
527 SetProcessExitHandler(*env, [&](node::Environment* env_, int exit_code) {
528 EXPECT_EQ(*env, env_);
529 EXPECT_EQ(exit_code, 42);
530 callback_calls++;
531 node::Stop(*env);
532 });
533 node::LoadEnvironment(*env, "process.exit(42)").ToLocalChecked();
534 EXPECT_EQ(callback_calls, 1);
535 }
536
TEST_F(EnvironmentTest,SetImmediateMicrotasks)537 TEST_F(EnvironmentTest, SetImmediateMicrotasks) {
538 int called = 0;
539
540 {
541 const v8::HandleScope handle_scope(isolate_);
542 const Argv argv;
543 Env env {handle_scope, argv};
544
545 node::LoadEnvironment(*env,
546 [&](const node::StartExecutionCallbackInfo& info)
547 -> v8::MaybeLocal<v8::Value> {
548 return v8::Object::New(isolate_);
549 });
550
551 (*env)->SetImmediate([&](node::Environment* env_arg) {
552 EXPECT_EQ(env_arg, *env);
553 isolate_->EnqueueMicrotask([](void* arg) {
554 ++*static_cast<int*>(arg);
555 }, &called);
556 });
557 uv_run(¤t_loop, UV_RUN_DEFAULT);
558 }
559
560 EXPECT_EQ(called, 1);
561 }
562