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