diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index 479f06e5db0d4c..aea83fc331e771 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -262,7 +262,9 @@ if (process.inspector) { inspectorConsole = global.console; wrapConsoleCall = process.inspector.wrapConsoleCall; - delete process.inspector; + delete process.inspector.wrapConsoleCall; + if (Object.keys(process.inspector).length === 0) + delete process.inspector; } var console; Object.defineProperty(global, 'console', { diff --git a/lib/module.js b/lib/module.js index d9442a62b7c372..fe83cd0ecb8a26 100644 --- a/lib/module.js +++ b/lib/module.js @@ -472,6 +472,19 @@ function tryModuleLoad(module, filename) { } } +function getInspectorCallWrapper() { + var inspector = process.inspector; + if (!inspector || !inspector.callAndPauseOnStart) { + return null; + } + var wrapper = inspector.callAndPauseOnStart.bind(inspector); + delete inspector.callAndPauseOnStart; + if (Object.keys(process.inspector).length === 0) { + delete process.inspector; + } + return wrapper; +} + Module._resolveFilename = function(request, parent, isMain) { if (NativeModule.nonInternalExists(request)) { return request; @@ -561,6 +574,7 @@ Module.prototype._compile = function(content, filename) { displayErrors: true }); + var inspectorWrapper = null; if (process._debugWaitConnect && process._eval == null) { if (!resolvedArgv) { // we enter the repl if we're not given a filename argument. @@ -574,16 +588,25 @@ Module.prototype._compile = function(content, filename) { // Set breakpoint on module start if (filename === resolvedArgv) { delete process._debugWaitConnect; - const Debug = vm.runInDebugContext('Debug'); - Debug.setBreakPoint(compiledWrapper, 0, 0); + inspectorWrapper = getInspectorCallWrapper(); + if (!inspectorWrapper) { + const Debug = vm.runInDebugContext('Debug'); + Debug.setBreakPoint(compiledWrapper, 0, 0); + } } } var dirname = path.dirname(filename); var require = internalModule.makeRequireFunction(this); var depth = internalModule.requireDepth; if (depth === 0) stat.cache = new Map(); - var result = compiledWrapper.call(this.exports, this.exports, require, this, - filename, dirname); + var result; + if (inspectorWrapper) { + result = inspectorWrapper(compiledWrapper, this.exports, this.exports, + require, this, filename, dirname); + } else { + result = compiledWrapper.call(this.exports, this.exports, require, this, + filename, dirname); + } if (depth === 0) stat.cache = null; return result; }; diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index 1574beb673eb1c..34ba5a7fc9d4be 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -94,7 +94,6 @@ std::unique_ptr Utf8ToStringView(const std::string& message) { utf16.length()); return StringBuffer::create(view); } - } // namespace class V8NodeInspector; @@ -145,6 +144,8 @@ class AgentImpl { void FatalException(v8::Local error, v8::Local message); + void SchedulePauseOnNextStatement(const std::string& reason); + void PostIncomingMessage(InspectorAction action, int session_id, const std::string& message); void ResumeStartup() { @@ -160,6 +161,8 @@ class AgentImpl { static void ThreadCbIO(void* agent); static void WriteCbIO(uv_async_t* async); static void MainThreadAsyncCb(uv_async_t* req); + static void CallAndPauseOnStart( + const v8::FunctionCallbackInfo& args); void InstallInspectorOnProcess(); @@ -310,6 +313,14 @@ class V8NodeInspector : public v8_inspector::V8InspectorClient { session_->dispatchProtocolMessage(message); } + void schedulePauseOnNextStatement(const std::string& reason) { + if (session_ != nullptr) { + std::unique_ptr buffer = Utf8ToStringView(reason); + session_->schedulePauseOnNextStatement(buffer->string(), + buffer->string()); + } + } + v8::Local ensureDefaultContextInGroup(int contextGroupId) override { return env_->context(); @@ -477,6 +488,28 @@ void AgentImpl::InstallInspectorOnProcess() { v8::Local inspector = v8::Object::New(env->isolate()); READONLY_PROPERTY(process, "inspector", inspector); env->SetMethod(inspector, "wrapConsoleCall", InspectorWrapConsoleCall); + if (options_.wait_for_connect()) { + env->SetMethod(inspector, "callAndPauseOnStart", CallAndPauseOnStart); + } +} + +// static +void AgentImpl::CallAndPauseOnStart( + const v8::FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK_GT(args.Length(), 1); + CHECK(args[0]->IsFunction()); + std::vector> call_args; + for (int i = 2; i < args.Length(); i++) { + call_args.push_back(args[i]); + } + + env->inspector_agent()->SchedulePauseOnNextStatement("Break on start"); + + v8::MaybeLocal retval = + args[0].As()->Call(env->context(), args[1], + call_args.size(), call_args.data()); + args.GetReturnValue().Set(retval.ToLocalChecked()); } std::unique_ptr ToProtocolString(v8::Local value) { @@ -682,6 +715,10 @@ void AgentImpl::Write(TransportAction action, int session_id, CHECK_EQ(0, err); } +void AgentImpl::SchedulePauseOnNextStatement(const std::string& reason) { + inspector_->schedulePauseOnNextStatement(reason); +} + // Exported class Agent Agent::Agent(node::Environment* env) : impl(new AgentImpl(env)) {} @@ -715,6 +752,10 @@ void Agent::FatalException(v8::Local error, impl->FatalException(error, message); } +void Agent::SchedulePauseOnNextStatement(const std::string& reason) { + impl->SchedulePauseOnNextStatement(reason); +} + InspectorAgentDelegate::InspectorAgentDelegate(AgentImpl* agent, const std::string& script_path, const std::string& script_name, diff --git a/src/inspector_agent.h b/src/inspector_agent.h index 9cc2fa676d4d13..50575d7c282d45 100644 --- a/src/inspector_agent.h +++ b/src/inspector_agent.h @@ -43,6 +43,7 @@ class Agent { void WaitForDisconnect(); void FatalException(v8::Local error, v8::Local message); + void SchedulePauseOnNextStatement(const std::string& reason); private: AgentImpl* impl; };