Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

src: initialize inspector before RunBootstrapping() #32672

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
56 changes: 28 additions & 28 deletions src/api/environment.cc
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,19 @@ void FreeIsolateData(IsolateData* isolate_data) {
delete isolate_data;
}

InspectorParentHandle::~InspectorParentHandle() {}

// Hide the internal handle class from the public API.
#if HAVE_INSPECTOR
struct InspectorParentHandleImpl : public InspectorParentHandle {
std::unique_ptr<inspector::ParentInspectorHandle> impl;

explicit InspectorParentHandleImpl(
std::unique_ptr<inspector::ParentInspectorHandle>&& impl)
: impl(std::move(impl)) {}
};
#endif

Environment* CreateEnvironment(IsolateData* isolate_data,
Local<Context> context,
int argc,
Expand All @@ -348,7 +361,8 @@ Environment* CreateEnvironment(
const std::vector<std::string>& args,
const std::vector<std::string>& exec_args,
EnvironmentFlags::Flags flags,
ThreadId thread_id) {
ThreadId thread_id,
std::unique_ptr<InspectorParentHandle> inspector_parent_handle) {
Isolate* isolate = context->GetIsolate();
HandleScope handle_scope(isolate);
Context::Scope context_scope(context);
Expand All @@ -365,6 +379,16 @@ Environment* CreateEnvironment(
env->set_abort_on_uncaught_exception(false);
}

#if HAVE_INSPECTOR
if (inspector_parent_handle) {
env->InitializeInspector(
std::move(static_cast<InspectorParentHandleImpl*>(
inspector_parent_handle.get())->impl));
} else {
env->InitializeInspector({});
}
#endif

if (env->RunBootstrapping().IsEmpty()) {
FreeEnvironment(env);
return nullptr;
Expand Down Expand Up @@ -394,19 +418,6 @@ void FreeEnvironment(Environment* env) {
delete env;
}

InspectorParentHandle::~InspectorParentHandle() {}

// Hide the internal handle class from the public API.
#if HAVE_INSPECTOR
struct InspectorParentHandleImpl : public InspectorParentHandle {
std::unique_ptr<inspector::ParentInspectorHandle> impl;

explicit InspectorParentHandleImpl(
std::unique_ptr<inspector::ParentInspectorHandle>&& impl)
: impl(std::move(impl)) {}
};
#endif

NODE_EXTERN std::unique_ptr<InspectorParentHandle> GetInspectorParentHandle(
Environment* env,
ThreadId thread_id,
Expand All @@ -430,27 +441,17 @@ void LoadEnvironment(Environment* env) {
MaybeLocal<Value> LoadEnvironment(
Environment* env,
StartExecutionCallback cb,
std::unique_ptr<InspectorParentHandle> inspector_parent_handle) {
std::unique_ptr<InspectorParentHandle> removeme) {
env->InitializeLibuv(per_process::v8_is_profiling);
env->InitializeDiagnostics();

#if HAVE_INSPECTOR
if (inspector_parent_handle) {
env->InitializeInspector(
std::move(static_cast<InspectorParentHandleImpl*>(
inspector_parent_handle.get())->impl));
} else {
env->InitializeInspector({});
}
#endif

return StartExecution(env, cb);
}

MaybeLocal<Value> LoadEnvironment(
Environment* env,
const char* main_script_source_utf8,
std::unique_ptr<InspectorParentHandle> inspector_parent_handle) {
std::unique_ptr<InspectorParentHandle> removeme) {
CHECK_NOT_NULL(main_script_source_utf8);
return LoadEnvironment(
env,
Expand All @@ -475,8 +476,7 @@ MaybeLocal<Value> LoadEnvironment(
env->process_object(),
env->native_module_require()};
return ExecuteBootstrapper(env, name.c_str(), &params, &args);
},
std::move(inspector_parent_handle));
});
}

Environment* GetCurrentEnvironment(Local<Context> context) {
Expand Down
18 changes: 11 additions & 7 deletions src/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,10 @@ enum Flags : uint64_t {
};
} // namespace EnvironmentFlags

struct InspectorParentHandle {
virtual ~InspectorParentHandle();
};

// TODO(addaleax): Maybe move per-Environment options parsing here.
// Returns nullptr when the Environment cannot be created e.g. there are
// pending JavaScript exceptions.
Expand All @@ -436,16 +440,14 @@ NODE_EXTERN Environment* CreateEnvironment(
const std::vector<std::string>& args,
const std::vector<std::string>& exec_args,
EnvironmentFlags::Flags flags = EnvironmentFlags::kDefaultFlags,
ThreadId thread_id = {} /* allocates a thread id automatically */);
ThreadId thread_id = {} /* allocates a thread id automatically */,
std::unique_ptr<InspectorParentHandle> inspector_parent_handle = {});

struct InspectorParentHandle {
virtual ~InspectorParentHandle();
};
// Returns a handle that can be passed to `LoadEnvironment()`, making the
// child Environment accessible to the inspector as if it were a Node.js Worker.
// `child_thread_id` can be created using `AllocateEnvironmentThreadId()`
// and then later passed on to `CreateEnvironment()` to create the child
// Environment.
// Environment, together with the inspector handle.
// This method should not be called while the parent Environment is active
// on another thread.
NODE_EXTERN std::unique_ptr<InspectorParentHandle> GetInspectorParentHandle(
Expand All @@ -463,14 +465,16 @@ using StartExecutionCallback =

// TODO(addaleax): Deprecate this in favour of the MaybeLocal<> overload.
NODE_EXTERN void LoadEnvironment(Environment* env);
// The `InspectorParentHandle` arguments here are ignored and not used.
// For passing `InspectorParentHandle`, use `CreateEnvironment()`.
addaleax marked this conversation as resolved.
Show resolved Hide resolved
NODE_EXTERN v8::MaybeLocal<v8::Value> LoadEnvironment(
Environment* env,
StartExecutionCallback cb,
std::unique_ptr<InspectorParentHandle> inspector_parent_handle = {});
std::unique_ptr<InspectorParentHandle> ignored_donotuse_removeme = {});
NODE_EXTERN v8::MaybeLocal<v8::Value> LoadEnvironment(
Environment* env,
const char* main_script_source_utf8,
std::unique_ptr<InspectorParentHandle> inspector_parent_handle = {});
std::unique_ptr<InspectorParentHandle> ignored_donotuse_removeme = {});
NODE_EXTERN void FreeEnvironment(Environment* env);

// Set a callback that is called when process.exit() is called from JS,
Expand Down
9 changes: 3 additions & 6 deletions src/node_worker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,8 @@ void Worker::Run() {
std::move(argv_),
std::move(exec_argv_),
EnvironmentFlags::kNoFlags,
thread_id_));
thread_id_,
std::move(inspector_parent_handle_)));
if (is_stopped()) return;
CHECK_NOT_NULL(env_);
env_->set_env_vars(std::move(env_vars_));
Expand All @@ -328,12 +329,8 @@ void Worker::Run() {
{
CreateEnvMessagePort(env_.get());
Debug(this, "Created message port for worker %llu", thread_id_.id);
if (LoadEnvironment(env_.get(),
StartExecutionCallback{},
std::move(inspector_parent_handle_))
.IsEmpty()) {
if (LoadEnvironment(env_.get(), StartExecutionCallback{}).IsEmpty())
return;
}

Debug(this, "Loaded environment for worker %llu", thread_id_.id);
}
Expand Down
12 changes: 9 additions & 3 deletions test/cctest/node_test_fixture.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,10 @@ class EnvironmentTestFixture : public NodeTestFixture {
public:
class Env {
public:
Env(const v8::HandleScope& handle_scope, const Argv& argv) {
Env(const v8::HandleScope& handle_scope,
const Argv& argv,
node::EnvironmentFlags::Flags flags =
node::EnvironmentFlags::kDefaultFlags) {
auto isolate = handle_scope.GetIsolate();
context_ = node::NewContext(isolate);
CHECK(!context_.IsEmpty());
Expand All @@ -145,10 +148,13 @@ class EnvironmentTestFixture : public NodeTestFixture {
&NodeTestFixture::current_loop,
platform.get());
CHECK_NE(nullptr, isolate_data_);
std::vector<std::string> args(*argv, *argv + 1);
std::vector<std::string> exec_args(*argv, *argv + 1);
environment_ = node::CreateEnvironment(isolate_data_,
context_,
1, *argv,
argv.nr_args(), *argv);
args,
exec_args,
flags);
CHECK_NE(nullptr, environment_);
}

Expand Down
11 changes: 6 additions & 5 deletions test/cctest/test_environment.cc
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,9 @@ TEST_F(EnvironmentTest, AtExitRunsJS) {
TEST_F(EnvironmentTest, MultipleEnvironmentsPerIsolate) {
const v8::HandleScope handle_scope(isolate_);
const Argv argv;
// Only one of the Environments can have default flags and own the inspector.
Env env1 {handle_scope, argv};
Env env2 {handle_scope, argv};
Env env2 {handle_scope, argv, node::EnvironmentFlags::kNoFlags};

AtExit(*env1, at_exit_callback1);
AtExit(*env2, at_exit_callback2);
Expand Down Expand Up @@ -334,7 +335,7 @@ TEST_F(EnvironmentTest, InspectorMultipleEmbeddedEnvironments) {
" id: 1,\n"
" method: 'Runtime.evaluate',\n"
" params: {\n"
" expression: 'global.variableFromParent = 42;'\n"
" expression: 'globalThis.variableFromParent = 42;'\n"
" }\n"
" })\n"
" });\n"
Expand Down Expand Up @@ -401,14 +402,14 @@ TEST_F(EnvironmentTest, InspectorMultipleEmbeddedEnvironments) {
{ "dummy" },
{},
node::EnvironmentFlags::kNoFlags,
data->thread_id);
data->thread_id,
std::move(data->inspector_parent_handle));
CHECK_NOT_NULL(environment);

v8::Local<v8::Value> extracted_value = LoadEnvironment(
environment,
"while (!global.variableFromParent) {}\n"
"return global.variableFromParent;",
std::move(data->inspector_parent_handle)).ToLocalChecked();
"return global.variableFromParent;").ToLocalChecked();

uv_run(&loop, UV_RUN_DEFAULT);
CHECK(extracted_value->IsInt32());
Expand Down
28 changes: 28 additions & 0 deletions test/parallel/test-inspector-inspect-brk-node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use strict';
const common = require('../common');

// Regression test for https://github.com/nodejs/node/issues/32648

common.skipIfInspectorDisabled();

const { NodeInstance } = require('../common/inspector-helper.js');

async function runTest() {
const child = new NodeInstance(['--inspect-brk-node=0', '-p', '42']);
const session = await child.connectInspectorSession();
await session.send({ method: 'Runtime.enable' });
await session.send({ method: 'Debugger.enable' });
await session.send({ method: 'Runtime.runIfWaitingForDebugger' });
await session.waitForNotification((notification) => {
// The main assertion here is that we do hit the loader script first.
return notification.method === 'Debugger.scriptParsed' &&
notification.params.url === 'internal/bootstrap/loaders.js';
});

await session.waitForNotification('Debugger.paused');
await session.send({ method: 'Debugger.resume' });
await session.waitForNotification('Debugger.paused');
await session.runToCompletion();
}

runTest().then(common.mustCall());