1 #include "node_buffer.h"
2 #include "node_internals.h"
3 #include "libplatform/libplatform.h"
4 #include "util.h"
5
6 #include <string>
7 #include "gtest/gtest.h"
8 #include "node_test_fixture.h"
9 #include <stdio.h>
10 #include <cstdio>
11
12 using node::AtExit;
13 using node::RunAtExit;
14 using node::USE;
15 using v8::Context;
16 using v8::Local;
17
18 static bool called_cb_1 = false;
19 static bool called_cb_2 = false;
20 static bool called_cb_ordered_1 = false;
21 static bool called_cb_ordered_2 = false;
22 static bool called_at_exit_js = false;
23 static void at_exit_callback1(void* arg);
24 static void at_exit_callback2(void* arg);
25 static void at_exit_callback_ordered1(void* arg);
26 static void at_exit_callback_ordered2(void* arg);
27 static void at_exit_js(void* arg);
28 static std::string cb_1_arg; // NOLINT(runtime/string)
29
30 class EnvironmentTest : public EnvironmentTestFixture {
31 private:
TearDown()32 void TearDown() override {
33 EnvironmentTestFixture::TearDown();
34 called_cb_1 = false;
35 called_cb_2 = false;
36 called_cb_ordered_1 = false;
37 called_cb_ordered_2 = false;
38 }
39 };
40
TEST_F(EnvironmentTest,EnvironmentWithESMLoader)41 TEST_F(EnvironmentTest, EnvironmentWithESMLoader) {
42 const v8::HandleScope handle_scope(isolate_);
43 Argv argv;
44 Env env {handle_scope, argv};
45
46 node::Environment* envi = *env;
47 envi->options()->experimental_vm_modules = true;
48
49 SetProcessExitHandler(*env, [&](node::Environment* env_, int exit_code) {
50 EXPECT_EQ(*env, env_);
51 EXPECT_EQ(exit_code, 0);
52 node::Stop(*env);
53 });
54
55 node::LoadEnvironment(
56 *env,
57 "const { SourceTextModule } = require('vm');"
58 "(async () => {"
59 "const stmString = 'globalThis.importResult = import(\"\")';"
60 "const m = new SourceTextModule(stmString, {"
61 "importModuleDynamically: (async () => {"
62 "const m = new SourceTextModule('');"
63 "await m.link(() => 0);"
64 "await m.evaluate();"
65 "return m.namespace;"
66 "}),"
67 "});"
68 "await m.link(() => 0);"
69 "await m.evaluate();"
70 "delete globalThis.importResult;"
71 "process.exit(0);"
72 "})()");
73 }
74
75 class RedirectStdErr {
76 public:
RedirectStdErr(const char * filename)77 explicit RedirectStdErr(const char* filename) : filename_(filename) {
78 fflush(stderr);
79 fgetpos(stderr, &pos_);
80 fd_ = dup(fileno(stderr));
81 USE(freopen(filename_, "w", stderr));
82 }
83
~RedirectStdErr()84 ~RedirectStdErr() {
85 fflush(stderr);
86 dup2(fd_, fileno(stderr));
87 close(fd_);
88 remove(filename_);
89 clearerr(stderr);
90 fsetpos(stderr, &pos_);
91 }
92
93 private:
94 int fd_;
95 fpos_t pos_;
96 const char* filename_;
97 };
98
TEST_F(EnvironmentTest,EnvironmentWithNoESMLoader)99 TEST_F(EnvironmentTest, EnvironmentWithNoESMLoader) {
100 // The following line will cause stderr to get redirected to avoid the
101 // error that would otherwise be printed to the console by this test.
102 RedirectStdErr redirect_scope("environment_test.log");
103 const v8::HandleScope handle_scope(isolate_);
104 Argv argv;
105 Env env {handle_scope, argv, node::EnvironmentFlags::kNoRegisterESMLoader};
106
107 node::Environment* envi = *env;
108 envi->options()->experimental_vm_modules = true;
109
110 SetProcessExitHandler(*env, [&](node::Environment* env_, int exit_code) {
111 EXPECT_EQ(*env, env_);
112 EXPECT_EQ(exit_code, 1);
113 node::Stop(*env);
114 });
115
116 node::LoadEnvironment(
117 *env,
118 "const { SourceTextModule } = require('vm');"
119 "(async () => {"
120 "const stmString = 'globalThis.importResult = import(\"\")';"
121 "const m = new SourceTextModule(stmString, {"
122 "importModuleDynamically: (async () => {"
123 "const m = new SourceTextModule('');"
124 "await m.link(() => 0);"
125 "await m.evaluate();"
126 "return m.namespace;"
127 "}),"
128 "});"
129 "await m.link(() => 0);"
130 "await m.evaluate();"
131 "delete globalThis.importResult;"
132 "})()");
133 }
134
TEST_F(EnvironmentTest,PreExecutionPreparation)135 TEST_F(EnvironmentTest, PreExecutionPreparation) {
136 const v8::HandleScope handle_scope(isolate_);
137 const Argv argv;
138 Env env {handle_scope, argv};
139
140 node::LoadEnvironment(*env, [&](const node::StartExecutionCallbackInfo& info)
141 -> v8::MaybeLocal<v8::Value> {
142 return v8::Null(isolate_);
143 });
144
145 v8::Local<v8::Context> context = isolate_->GetCurrentContext();
146 const char* run_script = "process.argv0";
147 v8::Local<v8::Script> script = v8::Script::Compile(
148 context,
149 v8::String::NewFromOneByte(isolate_,
150 reinterpret_cast<const uint8_t*>(run_script))
151 .ToLocalChecked())
152 .ToLocalChecked();
153 v8::Local<v8::Value> result = script->Run(context).ToLocalChecked();
154 CHECK(result->IsString());
155 }
156
TEST_F(EnvironmentTest,LoadEnvironmentWithCallback)157 TEST_F(EnvironmentTest, LoadEnvironmentWithCallback) {
158 const v8::HandleScope handle_scope(isolate_);
159 const Argv argv;
160 Env env {handle_scope, argv};
161
162 v8::Local<v8::Context> context = isolate_->GetCurrentContext();
163 bool called_cb = false;
164 node::LoadEnvironment(*env,
165 [&](const node::StartExecutionCallbackInfo& info)
166 -> v8::MaybeLocal<v8::Value> {
167 called_cb = true;
168
169 CHECK(info.process_object->IsObject());
170 CHECK(info.native_require->IsFunction());
171
172 v8::Local<v8::Value> argv0 = info.process_object->Get(
173 context,
174 v8::String::NewFromOneByte(
175 isolate_,
176 reinterpret_cast<const uint8_t*>("argv0"))
177 .ToLocalChecked()).ToLocalChecked();
178 CHECK(argv0->IsString());
179
180 return info.process_object;
181 });
182
183 CHECK(called_cb);
184 }
185
TEST_F(EnvironmentTest,LoadEnvironmentWithSource)186 TEST_F(EnvironmentTest, LoadEnvironmentWithSource) {
187 const v8::HandleScope handle_scope(isolate_);
188 const Argv argv;
189 Env env {handle_scope, argv};
190
191 v8::Local<v8::Context> context = isolate_->GetCurrentContext();
192 v8::Local<v8::Value> main_ret =
193 node::LoadEnvironment(*env,
194 "return { process, require };").ToLocalChecked();
195
196 CHECK(main_ret->IsObject());
197 CHECK(main_ret.As<v8::Object>()->Get(
198 context,
199 v8::String::NewFromOneByte(
200 isolate_,
201 reinterpret_cast<const uint8_t*>("process"))
202 .ToLocalChecked())
203 .ToLocalChecked()->IsObject());
204 CHECK(main_ret.As<v8::Object>()->Get(
205 context,
206 v8::String::NewFromOneByte(
207 isolate_,
208 reinterpret_cast<const uint8_t*>("require"))
209 .ToLocalChecked())
210 .ToLocalChecked()->IsFunction());
211 }
212
TEST_F(EnvironmentTest,AtExitWithEnvironment)213 TEST_F(EnvironmentTest, AtExitWithEnvironment) {
214 const v8::HandleScope handle_scope(isolate_);
215 const Argv argv;
216 Env env {handle_scope, argv};
217
218 AtExit(*env, at_exit_callback1, nullptr);
219 RunAtExit(*env);
220 EXPECT_TRUE(called_cb_1);
221 }
222
TEST_F(EnvironmentTest,AtExitOrder)223 TEST_F(EnvironmentTest, AtExitOrder) {
224 const v8::HandleScope handle_scope(isolate_);
225 const Argv argv;
226 Env env {handle_scope, argv};
227
228 // Test that callbacks are run in reverse order.
229 AtExit(*env, at_exit_callback_ordered1, nullptr);
230 AtExit(*env, at_exit_callback_ordered2, nullptr);
231 RunAtExit(*env);
232 EXPECT_TRUE(called_cb_ordered_1);
233 EXPECT_TRUE(called_cb_ordered_2);
234 }
235
TEST_F(EnvironmentTest,AtExitWithArgument)236 TEST_F(EnvironmentTest, AtExitWithArgument) {
237 const v8::HandleScope handle_scope(isolate_);
238 const Argv argv;
239 Env env {handle_scope, argv};
240
241 std::string arg{"some args"};
242 AtExit(*env, at_exit_callback1, static_cast<void*>(&arg));
243 RunAtExit(*env);
244 EXPECT_EQ(arg, cb_1_arg);
245 }
246
TEST_F(EnvironmentTest,AtExitRunsJS)247 TEST_F(EnvironmentTest, AtExitRunsJS) {
248 const v8::HandleScope handle_scope(isolate_);
249 const Argv argv;
250 Env env {handle_scope, argv};
251
252 AtExit(*env, at_exit_js, static_cast<void*>(isolate_));
253 EXPECT_FALSE(called_at_exit_js);
254 RunAtExit(*env);
255 EXPECT_TRUE(called_at_exit_js);
256 }
257
TEST_F(EnvironmentTest,MultipleEnvironmentsPerIsolate)258 TEST_F(EnvironmentTest, MultipleEnvironmentsPerIsolate) {
259 const v8::HandleScope handle_scope(isolate_);
260 const Argv argv;
261 // Only one of the Environments can have default flags and own the inspector.
262 Env env1 {handle_scope, argv};
263 Env env2 {handle_scope, argv, node::EnvironmentFlags::kNoFlags};
264
265 AtExit(*env1, at_exit_callback1, nullptr);
266 AtExit(*env2, at_exit_callback2, nullptr);
267 RunAtExit(*env1);
268 EXPECT_TRUE(called_cb_1);
269 EXPECT_FALSE(called_cb_2);
270
271 RunAtExit(*env2);
272 EXPECT_TRUE(called_cb_2);
273 }
274
TEST_F(EnvironmentTest,NoEnvironmentSanity)275 TEST_F(EnvironmentTest, NoEnvironmentSanity) {
276 const v8::HandleScope handle_scope(isolate_);
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_), nullptr);
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
TEST_F(EnvironmentTest,NonNodeJSContext)288 TEST_F(EnvironmentTest, NonNodeJSContext) {
289 const v8::HandleScope handle_scope(isolate_);
290 const Argv argv;
291 Env test_env {handle_scope, argv};
292
293 EXPECT_EQ(node::Environment::GetCurrent(v8::Local<v8::Context>()), nullptr);
294
295 node::Environment* env = *test_env;
296 EXPECT_EQ(node::Environment::GetCurrent(isolate_), env);
297 EXPECT_EQ(node::Environment::GetCurrent(env->context()), env);
298 EXPECT_EQ(node::GetCurrentEnvironment(env->context()), env);
299
300 v8::Local<v8::Context> context = v8::Context::New(isolate_);
301 EXPECT_EQ(node::Environment::GetCurrent(context), nullptr);
302 EXPECT_EQ(node::GetCurrentEnvironment(context), nullptr);
303 EXPECT_EQ(node::Environment::GetCurrent(isolate_), env);
304
305 v8::Context::Scope context_scope(context);
306 EXPECT_EQ(node::Environment::GetCurrent(context), nullptr);
307 EXPECT_EQ(node::GetCurrentEnvironment(context), nullptr);
308 EXPECT_EQ(node::Environment::GetCurrent(isolate_), nullptr);
309 }
310
at_exit_callback1(void * arg)311 static void at_exit_callback1(void* arg) {
312 called_cb_1 = true;
313 if (arg) {
314 cb_1_arg = *static_cast<std::string*>(arg);
315 }
316 }
317
at_exit_callback2(void * arg)318 static void at_exit_callback2(void* arg) {
319 called_cb_2 = true;
320 }
321
at_exit_callback_ordered1(void * arg)322 static void at_exit_callback_ordered1(void* arg) {
323 EXPECT_TRUE(called_cb_ordered_2);
324 called_cb_ordered_1 = true;
325 }
326
at_exit_callback_ordered2(void * arg)327 static void at_exit_callback_ordered2(void* arg) {
328 EXPECT_FALSE(called_cb_ordered_1);
329 called_cb_ordered_2 = true;
330 }
331
at_exit_js(void * arg)332 static void at_exit_js(void* arg) {
333 v8::Isolate* isolate = static_cast<v8::Isolate*>(arg);
334 v8::HandleScope handle_scope(isolate);
335 v8::Local<v8::Object> obj = v8::Object::New(isolate);
336 EXPECT_FALSE(obj.IsEmpty()); // Assert VM is still alive.
337 EXPECT_TRUE(obj->IsObject());
338 called_at_exit_js = true;
339 }
340
TEST_F(EnvironmentTest,SetImmediateCleanup)341 TEST_F(EnvironmentTest, SetImmediateCleanup) {
342 int called = 0;
343 int called_unref = 0;
344
345 {
346 const v8::HandleScope handle_scope(isolate_);
347 const Argv argv;
348 Env env {handle_scope, argv};
349
350 node::LoadEnvironment(*env,
351 [&](const node::StartExecutionCallbackInfo& info)
352 -> v8::MaybeLocal<v8::Value> {
353 return v8::Object::New(isolate_);
354 });
355
356 (*env)->SetImmediate([&](node::Environment* env_arg) {
357 EXPECT_EQ(env_arg, *env);
358 called++;
359 }, node::CallbackFlags::kRefed);
360 (*env)->SetImmediate([&](node::Environment* env_arg) {
361 EXPECT_EQ(env_arg, *env);
362 called_unref++;
363 }, node::CallbackFlags::kUnrefed);
364 }
365
366 EXPECT_EQ(called, 1);
367 EXPECT_EQ(called_unref, 0);
368 }
369
370 static char hello[] = "hello";
371
TEST_F(EnvironmentTest,BufferWithFreeCallbackIsDetached)372 TEST_F(EnvironmentTest, BufferWithFreeCallbackIsDetached) {
373 // Test that a Buffer allocated with a free callback is detached after
374 // its callback has been called.
375 const v8::HandleScope handle_scope(isolate_);
376 const Argv argv;
377
378 int callback_calls = 0;
379
380 v8::Local<v8::ArrayBuffer> ab;
381 {
382 Env env {handle_scope, argv};
383 v8::Local<v8::Object> buf_obj = node::Buffer::New(
384 isolate_,
385 hello,
386 sizeof(hello),
387 [](char* data, void* hint) {
388 CHECK_EQ(data, hello);
389 ++*static_cast<int*>(hint);
390 },
391 &callback_calls).ToLocalChecked();
392 CHECK(buf_obj->IsUint8Array());
393 ab = buf_obj.As<v8::Uint8Array>()->Buffer();
394 CHECK_EQ(ab->ByteLength(), sizeof(hello));
395 }
396
397 CHECK_EQ(callback_calls, 1);
398 CHECK_EQ(ab->ByteLength(), 0);
399 }
400
401 #if HAVE_INSPECTOR
TEST_F(EnvironmentTest,InspectorMultipleEmbeddedEnvironments)402 TEST_F(EnvironmentTest, InspectorMultipleEmbeddedEnvironments) {
403 // Tests that child Environments can be created through the public API
404 // that are accessible by the inspector.
405 // This test sets a global variable in the child Environment, and reads it
406 // back both through the inspector and inside the child Environment, and
407 // makes sure that those correspond to the value that was originally set.
408 const v8::HandleScope handle_scope(isolate_);
409 const Argv argv;
410 Env env {handle_scope, argv};
411
412 v8::Local<v8::Context> context = isolate_->GetCurrentContext();
413 node::LoadEnvironment(*env,
414 "'use strict';\n"
415 "const { Worker } = require('worker_threads');\n"
416 "const { Session } = require('inspector');\n"
417
418 "const session = new Session();\n"
419 "session.connect();\n"
420 "session.on('NodeWorker.attachedToWorker', (\n"
421 " ({ params: { workerInfo, sessionId } }) => {\n"
422 " session.post('NodeWorker.sendMessageToWorker', {\n"
423 " sessionId,\n"
424 " message: JSON.stringify({\n"
425 " id: 1,\n"
426 " method: 'Runtime.evaluate',\n"
427 " params: {\n"
428 " expression: 'globalThis.variableFromParent = 42;'\n"
429 " }\n"
430 " })\n"
431 " });\n"
432 " session.on('NodeWorker.receivedMessageFromWorker',\n"
433 " ({ params: { message } }) => {\n"
434 " global.messageFromWorker = \n"
435 " JSON.parse(message).result.result.value;\n"
436 " });\n"
437 " }));\n"
438 "session.post('NodeWorker.enable', { waitForDebuggerOnStart: false });\n")
439 .ToLocalChecked();
440
441 struct ChildEnvironmentData {
442 node::ThreadId thread_id;
443 std::unique_ptr<node::InspectorParentHandle> inspector_parent_handle;
444 node::MultiIsolatePlatform* platform;
445 int32_t extracted_value = -1;
446 uv_async_t thread_stopped_async;
447 };
448
449 ChildEnvironmentData data;
450 data.thread_id = node::AllocateEnvironmentThreadId();
451 data.inspector_parent_handle =
452 GetInspectorParentHandle(*env, data.thread_id, "file:///embedded.js");
453 CHECK(data.inspector_parent_handle);
454 data.platform = GetMultiIsolatePlatform(*env);
455 CHECK_NOT_NULL(data.platform);
456
457 bool thread_stopped = false;
458 int err = uv_async_init(
459 ¤t_loop, &data.thread_stopped_async, [](uv_async_t* async) {
460 *static_cast<bool*>(async->data) = true;
461 uv_close(reinterpret_cast<uv_handle_t*>(async), nullptr);
462 });
463 CHECK_EQ(err, 0);
464 data.thread_stopped_async.data = &thread_stopped;
465
466 uv_thread_t thread;
467 err = uv_thread_create(&thread, [](void* arg) {
468 ChildEnvironmentData* data = static_cast<ChildEnvironmentData*>(arg);
469 std::shared_ptr<node::ArrayBufferAllocator> aba =
470 node::ArrayBufferAllocator::Create();
471 uv_loop_t loop;
472 uv_loop_init(&loop);
473 v8::Isolate* isolate = NewIsolate(aba, &loop, data->platform);
474 CHECK_NOT_NULL(isolate);
475
476 {
477 v8::Isolate::Scope isolate_scope(isolate);
478 v8::HandleScope handle_scope(isolate);
479
480 v8::Local<v8::Context> context = node::NewContext(isolate);
481 CHECK(!context.IsEmpty());
482 v8::Context::Scope context_scope(context);
483
484 node::IsolateData* isolate_data = node::CreateIsolateData(
485 isolate,
486 &loop,
487 data->platform);
488 CHECK_NOT_NULL(isolate_data);
489 node::Environment* environment = node::CreateEnvironment(
490 isolate_data,
491 context,
492 { "dummy" },
493 {},
494 node::EnvironmentFlags::kNoFlags,
495 data->thread_id,
496 std::move(data->inspector_parent_handle));
497 CHECK_NOT_NULL(environment);
498
499 v8::Local<v8::Value> extracted_value = LoadEnvironment(
500 environment,
501 "while (!global.variableFromParent) {}\n"
502 "return global.variableFromParent;").ToLocalChecked();
503
504 uv_run(&loop, UV_RUN_DEFAULT);
505 CHECK(extracted_value->IsInt32());
506 data->extracted_value = extracted_value.As<v8::Int32>()->Value();
507
508 node::FreeEnvironment(environment);
509 node::FreeIsolateData(isolate_data);
510 }
511
512 data->platform->UnregisterIsolate(isolate);
513 isolate->Dispose();
514 uv_run(&loop, UV_RUN_DEFAULT);
515 CHECK_EQ(uv_loop_close(&loop), 0);
516
517 uv_async_send(&data->thread_stopped_async);
518 }, &data);
519 CHECK_EQ(err, 0);
520
521 bool more;
522 do {
523 uv_run(¤t_loop, UV_RUN_DEFAULT);
524 data.platform->DrainTasks(isolate_);
525 more = uv_loop_alive(¤t_loop);
526 } while (!thread_stopped || more);
527
528 uv_thread_join(&thread);
529
530 v8::Local<v8::Value> from_inspector =
531 context->Global()->Get(
532 context,
533 v8::String::NewFromOneByte(
534 isolate_,
535 reinterpret_cast<const uint8_t*>("messageFromWorker"))
536 .ToLocalChecked())
537 .ToLocalChecked();
538 CHECK_EQ(data.extracted_value, 42);
539 CHECK_EQ(from_inspector->IntegerValue(context).FromJust(), 42);
540 }
541 #endif // HAVE_INSPECTOR
542
TEST_F(EnvironmentTest,ExitHandlerTest)543 TEST_F(EnvironmentTest, ExitHandlerTest) {
544 const v8::HandleScope handle_scope(isolate_);
545 const Argv argv;
546
547 int callback_calls = 0;
548
549 Env env {handle_scope, argv};
550 SetProcessExitHandler(*env, [&](node::Environment* env_, int exit_code) {
551 EXPECT_EQ(*env, env_);
552 EXPECT_EQ(exit_code, 42);
553 callback_calls++;
554 node::Stop(*env);
555 });
556 // When terminating, v8 throws makes the current embedder call bail out
557 // with MaybeLocal<>()
558 EXPECT_TRUE(node::LoadEnvironment(*env, "process.exit(42)").IsEmpty());
559 EXPECT_EQ(callback_calls, 1);
560 }
561
TEST_F(EnvironmentTest,SetImmediateMicrotasks)562 TEST_F(EnvironmentTest, SetImmediateMicrotasks) {
563 int called = 0;
564
565 {
566 const v8::HandleScope handle_scope(isolate_);
567 const Argv argv;
568 Env env {handle_scope, argv};
569
570 node::LoadEnvironment(*env,
571 [&](const node::StartExecutionCallbackInfo& info)
572 -> v8::MaybeLocal<v8::Value> {
573 return v8::Object::New(isolate_);
574 });
575
576 (*env)->SetImmediate([&](node::Environment* env_arg) {
577 EXPECT_EQ(env_arg, *env);
578 isolate_->EnqueueMicrotask([](void* arg) {
579 ++*static_cast<int*>(arg);
580 }, &called);
581 }, node::CallbackFlags::kRefed);
582 uv_run(¤t_loop, UV_RUN_DEFAULT);
583 }
584
585 EXPECT_EQ(called, 1);
586 }
587
588 #ifndef _WIN32 // No SIGINT on Windows.
TEST_F(NodeZeroIsolateTestFixture,CtrlCWithOnlySafeTerminationTest)589 TEST_F(NodeZeroIsolateTestFixture, CtrlCWithOnlySafeTerminationTest) {
590 // We need to go through the whole setup dance here because we want to
591 // set only_terminate_in_safe_scope.
592 // Allocate and initialize Isolate.
593 v8::Isolate::CreateParams create_params;
594 create_params.array_buffer_allocator = allocator.get();
595 create_params.only_terminate_in_safe_scope = true;
596 v8::Isolate* isolate = v8::Isolate::Allocate();
597 CHECK_NOT_NULL(isolate);
598 platform->RegisterIsolate(isolate, ¤t_loop);
599 v8::Isolate::Initialize(isolate, create_params);
600
601 // Try creating Context + IsolateData + Environment.
602 {
603 v8::Isolate::Scope isolate_scope(isolate);
604 v8::HandleScope handle_scope(isolate);
605
606 auto context = node::NewContext(isolate);
607 CHECK(!context.IsEmpty());
608 v8::Context::Scope context_scope(context);
609
610 std::unique_ptr<node::IsolateData, decltype(&node::FreeIsolateData)>
611 isolate_data{node::CreateIsolateData(isolate,
612 ¤t_loop,
613 platform.get()),
614 node::FreeIsolateData};
615 CHECK(isolate_data);
616
617 std::unique_ptr<node::Environment, decltype(&node::FreeEnvironment)>
618 environment{node::CreateEnvironment(isolate_data.get(),
619 context,
620 {},
621 {}),
622 node::FreeEnvironment};
623 CHECK(environment);
624 EXPECT_EQ(node::GetEnvironmentIsolateData(environment.get()),
625 isolate_data.get());
626 EXPECT_EQ(node::GetArrayBufferAllocator(isolate_data.get()), nullptr);
627
628 v8::Local<v8::Value> main_ret =
629 node::LoadEnvironment(environment.get(),
630 "'use strict';\n"
631 "const { runInThisContext } = require('vm');\n"
632 "try {\n"
633 " runInThisContext("
634 " `process.kill(process.pid, 'SIGINT'); while(true){}`, "
635 " { breakOnSigint: true });\n"
636 " return 'unreachable';\n"
637 "} catch (err) {\n"
638 " return err.code;\n"
639 "}").ToLocalChecked();
640 node::Utf8Value main_ret_str(isolate, main_ret);
641 EXPECT_EQ(std::string(*main_ret_str), "ERR_SCRIPT_EXECUTION_INTERRUPTED");
642 }
643
644 // Cleanup.
645 platform->UnregisterIsolate(isolate);
646 isolate->Dispose();
647 }
648 #endif // _WIN32
649
TEST_F(EnvironmentTest,NestedMicrotaskQueue)650 TEST_F(EnvironmentTest, NestedMicrotaskQueue) {
651 const v8::HandleScope handle_scope(isolate_);
652 const Argv argv;
653
654 std::unique_ptr<v8::MicrotaskQueue> queue = v8::MicrotaskQueue::New(
655 isolate_, v8::MicrotasksPolicy::kExplicit);
656 v8::Local<v8::Context> context = v8::Context::New(
657 isolate_, nullptr, {}, {}, {}, queue.get());
658 node::InitializeContext(context);
659 v8::Context::Scope context_scope(context);
660
661 using IntVec = std::vector<int>;
662 IntVec callback_calls;
663 v8::Local<v8::Function> must_call = v8::Function::New(
664 context,
665 [](const v8::FunctionCallbackInfo<v8::Value>& info) {
666 IntVec* callback_calls = static_cast<IntVec*>(
667 info.Data().As<v8::External>()->Value());
668 callback_calls->push_back(info[0].As<v8::Int32>()->Value());
669 },
670 v8::External::New(isolate_, static_cast<void*>(&callback_calls)))
671 .ToLocalChecked();
672 context->Global()->Set(
673 context,
674 v8::String::NewFromUtf8Literal(isolate_, "mustCall"),
675 must_call).Check();
676
677 node::Environment* env =
678 node::CreateEnvironment(isolate_data_, context, {}, {});
679 CHECK_NE(nullptr, env);
680
681 v8::Local<v8::Function> eval_in_env = node::LoadEnvironment(
682 env,
683 "mustCall(1);\n"
684 "Promise.resolve().then(() => mustCall(2));\n"
685 "require('vm').runInNewContext("
686 " 'Promise.resolve().then(() => mustCall(3))',"
687 " { mustCall },"
688 " { microtaskMode: 'afterEvaluate' }"
689 ");\n"
690 "require('vm').runInNewContext("
691 " 'Promise.resolve().then(() => mustCall(4))',"
692 " { mustCall }"
693 ");\n"
694 "setTimeout(() => {"
695 " Promise.resolve().then(() => mustCall(5));"
696 "}, 10);\n"
697 "mustCall(6);\n"
698 "return eval;\n").ToLocalChecked().As<v8::Function>();
699 EXPECT_EQ(callback_calls, (IntVec { 1, 3, 6, 2, 4 }));
700 v8::Local<v8::Value> queue_microtask_code = v8::String::NewFromUtf8Literal(
701 isolate_, "queueMicrotask(() => mustCall(7));");
702 eval_in_env->Call(context,
703 v8::Null(isolate_),
704 1,
705 &queue_microtask_code).ToLocalChecked();
706 EXPECT_EQ(callback_calls, (IntVec { 1, 3, 6, 2, 4 }));
707 isolate_->PerformMicrotaskCheckpoint();
708 EXPECT_EQ(callback_calls, (IntVec { 1, 3, 6, 2, 4 }));
709 queue->PerformCheckpoint(isolate_);
710 EXPECT_EQ(callback_calls, (IntVec { 1, 3, 6, 2, 4, 7 }));
711
712 int exit_code = SpinEventLoop(env).FromJust();
713 EXPECT_EQ(exit_code, 0);
714 EXPECT_EQ(callback_calls, (IntVec { 1, 3, 6, 2, 4, 7, 5 }));
715
716 node::FreeEnvironment(env);
717 }
718
719 static bool interrupted = false;
OnInterrupt(void * arg)720 static void OnInterrupt(void* arg) {
721 interrupted = true;
722 }
TEST_F(EnvironmentTest,RequestInterruptAtExit)723 TEST_F(EnvironmentTest, RequestInterruptAtExit) {
724 const v8::HandleScope handle_scope(isolate_);
725 const Argv argv;
726
727 Local<Context> context = node::NewContext(isolate_);
728 CHECK(!context.IsEmpty());
729 context->Enter();
730
731 std::vector<std::string> args(*argv, *argv + 1);
732 std::vector<std::string> exec_args(*argv, *argv + 1);
733 node::Environment* environment =
734 node::CreateEnvironment(isolate_data_, context, args, exec_args);
735 CHECK_NE(nullptr, environment);
736
737 node::RequestInterrupt(environment, OnInterrupt, nullptr);
738 node::FreeEnvironment(environment);
739 EXPECT_TRUE(interrupted);
740
741 context->Exit();
742 }
743