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