• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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       &current_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(&current_loop, UV_RUN_DEFAULT);
501     data.platform->DrainTasks(isolate_);
502     more = uv_loop_alive(&current_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(&current_loop, UV_RUN_DEFAULT);
558   }
559 
560   EXPECT_EQ(called, 1);
561 }
562