@@ -329,3 +329,144 @@ TEST_F(EnvironmentTest, SetImmediateCleanup) {
329329 EXPECT_EQ (called, 1 );
330330 EXPECT_EQ (called_unref, 0 );
331331}
332+
333+ #if HAVE_INSPECTOR
334+ TEST_F (EnvironmentTest, InspectorMultipleEmbeddedEnvironments) {
335+ // Tests that child Environments can be created through the public API
336+ // that are accessible by the inspector.
337+ // This test sets a global variable in the child Environment, and reads it
338+ // back both through the inspector and inside the child Environment, and
339+ // makes sure that those correspond to the value that was originally set.
340+ const v8::HandleScope handle_scope (isolate_);
341+ const Argv argv;
342+ Env env {handle_scope, argv};
343+
344+ v8::Local<v8::Context> context = isolate_->GetCurrentContext ();
345+ node::LoadEnvironment (*env,
346+ " 'use strict';\n "
347+ " const { Worker } = require('worker_threads');\n "
348+ " const { Session } = require('inspector');\n "
349+
350+ " const session = new Session();\n "
351+ " session.connect();\n "
352+ " session.on('NodeWorker.attachedToWorker', (\n "
353+ " ({ params: { workerInfo, sessionId } }) => {\n "
354+ " session.post('NodeWorker.sendMessageToWorker', {\n "
355+ " sessionId,\n "
356+ " message: JSON.stringify({\n "
357+ " id: 1,\n "
358+ " method: 'Runtime.evaluate',\n "
359+ " params: {\n "
360+ " expression: 'global.variableFromParent = 42;'\n "
361+ " }\n "
362+ " })\n "
363+ " });\n "
364+ " session.on('NodeWorker.receivedMessageFromWorker',\n "
365+ " ({ params: { message } }) => {\n "
366+ " global.messageFromWorker = \n "
367+ " JSON.parse(message).result.result.value;\n "
368+ " });\n "
369+ " }));\n "
370+ " session.post('NodeWorker.enable', { waitForDebuggerOnStart: false });\n " )
371+ .ToLocalChecked ();
372+
373+ struct ChildEnvironmentData {
374+ node::ThreadId thread_id;
375+ std::unique_ptr<node::InspectorParentHandle> inspector_parent_handle;
376+ node::MultiIsolatePlatform* platform;
377+ int32_t extracted_value = -1 ;
378+ uv_async_t thread_stopped_async;
379+ };
380+
381+ ChildEnvironmentData data;
382+ data.thread_id = node::AllocateEnvironmentThreadId ();
383+ data.inspector_parent_handle =
384+ GetInspectorParentHandle (*env, data.thread_id , " file:///embedded.js" );
385+ CHECK (data.inspector_parent_handle );
386+ data.platform = GetMultiIsolatePlatform (*env);
387+ CHECK_NOT_NULL (data.platform );
388+
389+ bool thread_stopped = false ;
390+ int err = uv_async_init (
391+ ¤t_loop, &data.thread_stopped_async , [](uv_async_t * async) {
392+ *static_cast <bool *>(async->data ) = true ;
393+ uv_close (reinterpret_cast <uv_handle_t *>(async), nullptr );
394+ });
395+ CHECK_EQ (err, 0 );
396+ data.thread_stopped_async .data = &thread_stopped;
397+
398+ uv_thread_t thread;
399+ err = uv_thread_create (&thread, [](void * arg) {
400+ ChildEnvironmentData* data = static_cast <ChildEnvironmentData*>(arg);
401+ std::shared_ptr<node::ArrayBufferAllocator> aba =
402+ node::ArrayBufferAllocator::Create ();
403+ uv_loop_t loop;
404+ uv_loop_init (&loop);
405+ v8::Isolate* isolate = NewIsolate (aba, &loop, data->platform );
406+ CHECK_NOT_NULL (isolate);
407+
408+ {
409+ v8::Isolate::Scope isolate_scope (isolate);
410+ v8::HandleScope handle_scope (isolate);
411+
412+ v8::Local<v8::Context> context = node::NewContext (isolate);
413+ CHECK (!context.IsEmpty ());
414+ v8::Context::Scope context_scope (context);
415+
416+ node::IsolateData* isolate_data = node::CreateIsolateData (
417+ isolate,
418+ &loop,
419+ data->platform );
420+ CHECK_NOT_NULL (isolate_data);
421+ node::Environment* environment = node::CreateEnvironment (
422+ isolate_data,
423+ context,
424+ { " dummy" },
425+ {},
426+ node::EnvironmentFlags::kNoFlags ,
427+ data->thread_id );
428+ CHECK_NOT_NULL (environment);
429+
430+ v8::Local<v8::Value> extracted_value = LoadEnvironment (
431+ environment,
432+ " return global.variableFromParent;" ,
433+ std::move (data->inspector_parent_handle )).ToLocalChecked ();
434+
435+ uv_run (&loop, UV_RUN_DEFAULT);
436+ CHECK (extracted_value->IsInt32 ());
437+ data->extracted_value = extracted_value.As <v8::Int32>()->Value ();
438+
439+ node::FreeEnvironment (environment);
440+ node::FreeIsolateData (isolate_data);
441+ }
442+
443+ data->platform ->UnregisterIsolate (isolate);
444+ isolate->Dispose ();
445+ uv_run (&loop, UV_RUN_DEFAULT);
446+ CHECK_EQ (uv_loop_close (&loop), 0 );
447+
448+ uv_async_send (&data->thread_stopped_async );
449+ }, &data);
450+ CHECK_EQ (err, 0 );
451+
452+ bool more;
453+ do {
454+ uv_run (¤t_loop, UV_RUN_DEFAULT);
455+ data.platform ->DrainTasks (isolate_);
456+ more = uv_loop_alive (¤t_loop);
457+ } while (!thread_stopped || more);
458+
459+ uv_thread_join (&thread);
460+
461+ v8::Local<v8::Value> from_inspector =
462+ context->Global ()->Get (
463+ context,
464+ v8::String::NewFromOneByte (
465+ isolate_,
466+ reinterpret_cast <const uint8_t *>(" messageFromWorker" ),
467+ v8::NewStringType::kNormal ).ToLocalChecked ())
468+ .ToLocalChecked ();
469+ CHECK_EQ (data.extracted_value , 42 );
470+ CHECK_EQ (from_inspector->IntegerValue (context).FromJust (), 42 );
471+ }
472+ #endif // HAVE_INSPECTOR
0 commit comments