• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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       &current_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(&current_loop, UV_RUN_DEFAULT);
524     data.platform->DrainTasks(isolate_);
525     more = uv_loop_alive(&current_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(&current_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, &current_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                                            &current_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