diff --git a/node.gypi b/node.gypi index e9905ab443..6ef47849a7 100644 --- a/node.gypi +++ b/node.gypi @@ -82,6 +82,7 @@ 'sources': [ 'src/inspector_agent.cc', 'src/inspector_io.cc', + 'src/inspector_js_api.cc', 'src/inspector_socket.cc', 'src/inspector_socket_server.cc', 'src/inspector_agent.h', diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index 9680fe620c..4828fa3071 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -1,12 +1,9 @@ #include "inspector_agent.h" #include "inspector_io.h" -#include "base-object.h" -#include "base-object-inl.h" #include "node_internals.h" #include "v8-inspector.h" #include "v8-platform.h" -#include "zlib.h" #include "libplatform/libplatform.h" @@ -16,6 +13,7 @@ #include #ifdef __POSIX__ +#include #include // setuid, getuid #endif // __POSIX__ @@ -26,23 +24,12 @@ namespace { using node::FatalError; using v8::Array; -using v8::Boolean; using v8::Context; using v8::Function; -using v8::FunctionCallbackInfo; -using v8::FunctionTemplate; using v8::HandleScope; -using v8::Integer; using v8::Isolate; using v8::Local; -using v8::Maybe; -using v8::MaybeLocal; -using v8::Name; -using v8::NewStringType; using v8::Object; -using v8::Persistent; -using v8::String; -using v8::Undefined; using v8::Value; using v8_inspector::StringBuffer; @@ -193,173 +180,6 @@ static int StartDebugSignalHandler() { } #endif // _WIN32 -class JSBindingsConnection : public AsyncWrap { - public: - class JSBindingsSessionDelegate : public InspectorSessionDelegate { - public: - JSBindingsSessionDelegate(Environment* env, - JSBindingsConnection* connection) - : env_(env), - connection_(connection) { - } - - bool WaitForFrontendMessageWhilePaused() override { - return false; - } - - void SendMessageToFrontend(const v8_inspector::StringView& message) - override { - Isolate* isolate = env_->isolate(); - HandleScope handle_scope(isolate); - Context::Scope context_scope(env_->context()); - MaybeLocal v8string = - String::NewFromTwoByte(isolate, message.characters16(), - NewStringType::kNormal, message.length()); - Local argument = v8string.ToLocalChecked().As(); - connection_->OnMessage(argument); - } - - void Disconnect() { - Agent* agent = env_->inspector_agent(); - if (agent->delegate() == this) - agent->Disconnect(); - } - - private: - Environment* env_; - JSBindingsConnection* connection_; - }; - - JSBindingsConnection(Environment* env, - Local wrap, - Local callback) - : AsyncWrap(env, wrap, PROVIDER_INSPECTORJSBINDING), - delegate_(env, this), - callback_(env->isolate(), callback) { - Wrap(wrap, this); - - Agent* inspector = env->inspector_agent(); - if (inspector->delegate() != nullptr) { - env->ThrowTypeError("Session is already attached"); - return; - } - inspector->Connect(&delegate_); - } - - ~JSBindingsConnection() override { - callback_.Reset(); - } - - void OnMessage(Local value) { - MakeCallback(callback_.Get(env()->isolate()), 1, &value); - } - - void CheckIsCurrent() { - Agent* inspector = env()->inspector_agent(); - CHECK_EQ(&delegate_, inspector->delegate()); - } - - static void New(const FunctionCallbackInfo& info) { - Environment* env = Environment::GetCurrent(info); - if (!info[0]->IsFunction()) { - env->ThrowTypeError("Message callback is required"); - return; - } - Local callback = info[0].As(); - new JSBindingsConnection(env, info.This(), callback); - } - - void Disconnect() { - delegate_.Disconnect(); - if (!persistent().IsEmpty()) { - ClearWrap(object()); - persistent().Reset(); - } - delete this; - } - - static void Disconnect(const FunctionCallbackInfo& info) { - JSBindingsConnection* session; - ASSIGN_OR_RETURN_UNWRAP(&session, info.Holder()); - session->Disconnect(); - } - - static void Dispatch(const FunctionCallbackInfo& info) { - Environment* env = Environment::GetCurrent(info); - JSBindingsConnection* session; - ASSIGN_OR_RETURN_UNWRAP(&session, info.Holder()); - if (!info[0]->IsString()) { - env->ThrowTypeError("Inspector message must be a string"); - return; - } - - session->CheckIsCurrent(); - Agent* inspector = env->inspector_agent(); - inspector->Dispatch(ToProtocolString(env->isolate(), info[0])->string()); - } - - size_t self_size() const override { return sizeof(*this); } - - private: - JSBindingsSessionDelegate delegate_; - Persistent callback_; -}; - -void InspectorConsoleCall(const v8::FunctionCallbackInfo& info) { - Isolate* isolate = info.GetIsolate(); - HandleScope handle_scope(isolate); - Local context = isolate->GetCurrentContext(); - CHECK_LT(2, info.Length()); - std::vector> call_args; - for (int i = 3; i < info.Length(); ++i) { - call_args.push_back(info[i]); - } - Environment* env = Environment::GetCurrent(isolate); - if (env->inspector_agent()->enabled()) { - Local inspector_method = info[0]; - CHECK(inspector_method->IsFunction()); - Local config_value = info[2]; - CHECK(config_value->IsObject()); - Local config_object = config_value.As(); - Local in_call_key = FIXED_ONE_BYTE_STRING(isolate, "in_call"); - if (!config_object->Has(context, in_call_key).FromMaybe(false)) { - CHECK(config_object->Set(context, - in_call_key, - v8::True(isolate)).FromJust()); - CHECK(!inspector_method.As()->Call(context, - info.Holder(), - call_args.size(), - call_args.data()).IsEmpty()); - } - CHECK(config_object->Delete(context, in_call_key).FromJust()); - } - - Local node_method = info[1]; - CHECK(node_method->IsFunction()); - node_method.As()->Call(context, - info.Holder(), - call_args.size(), - call_args.data()).FromMaybe(Local()); -} - -void 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()->PauseOnNextJavascriptStatement("Break on start"); - v8::MaybeLocal retval = - args[0].As()->Call(env->context(), args[1], - call_args.size(), call_args.data()); - if (!retval.IsEmpty()) { - args.GetReturnValue().Set(retval.ToLocalChecked()); - } -} // Used in NodeInspectorClient::currentTimeMS() below. const int NANOS_PER_MSEC = 1000000; @@ -710,20 +530,6 @@ bool Agent::StartIoThread(bool wait_for_connect) { return true; } -static void AddCommandLineAPI( - const FunctionCallbackInfo& info) { - auto env = Environment::GetCurrent(info); - Local context = env->context(); - - if (info.Length() != 2 || !info[0]->IsString()) { - return env->ThrowTypeError("inspector.addCommandLineAPI takes " - "exactly 2 arguments: a string and a value."); - } - - Local console_api = env->inspector_console_api_object(); - console_api->Set(context, info[0], info[1]).FromJust(); -} - void Agent::Stop() { if (io_ != nullptr) { io_->Stop(); @@ -842,138 +648,6 @@ void Agent::AllAsyncTasksCanceled() { client_->AllAsyncTasksCanceled(); } -void Open(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - inspector::Agent* agent = env->inspector_agent(); - bool wait_for_connect = false; - - if (args.Length() > 0 && args[0]->IsUint32()) { - uint32_t port = args[0]->Uint32Value(); - agent->options().set_port(static_cast(port)); - } - - if (args.Length() > 1 && args[1]->IsString()) { - node::Utf8Value host(env->isolate(), args[1].As()); - agent->options().set_host_name(*host); - } - - if (args.Length() > 2 && args[2]->IsBoolean()) { - wait_for_connect = args[2]->BooleanValue(); - } - - agent->StartIoThread(wait_for_connect); -} - -void Url(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - inspector::Agent* agent = env->inspector_agent(); - inspector::InspectorIo* io = agent->io(); - - if (!io) return; - - std::vector ids = io->GetTargetIds(); - - if (ids.empty()) return; - - std::string url = FormatWsAddress(io->host(), io->port(), ids[0], true); - args.GetReturnValue().Set(OneByteString(env->isolate(), url.c_str())); -} - -static void* GetAsyncTask(int64_t asyncId) { - // The inspector assumes that when other clients use its asyncTask* API, - // they use real pointers, or at least something aligned like real pointer. - // In general it means that our task_id should always be even. - // - // On 32bit platforms, the 64bit asyncId would get truncated when converted - // to a 32bit pointer. However, the javascript part will never enable - // the async_hook on 32bit platforms, therefore the truncation will never - // happen in practice. - return reinterpret_cast(asyncId << 1); -} - -template -static void InvokeAsyncTaskFnWithId(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - CHECK(args[0]->IsNumber()); - int64_t task_id = args[0]->IntegerValue(env->context()).FromJust(); - (env->inspector_agent()->*asyncTaskFn)(GetAsyncTask(task_id)); -} - -static void AsyncTaskScheduledWrapper(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - CHECK(args[0]->IsString()); - Local task_name = args[0].As(); - String::Value task_name_value(task_name); - StringView task_name_view(*task_name_value, task_name_value.length()); - - CHECK(args[1]->IsNumber()); - int64_t task_id = args[1]->IntegerValue(env->context()).FromJust(); - void* task = GetAsyncTask(task_id); - - CHECK(args[2]->IsBoolean()); - bool recurring = args[2]->BooleanValue(env->context()).FromJust(); - - env->inspector_agent()->AsyncTaskScheduled(task_name_view, task, recurring); -} - -static void RegisterAsyncHookWrapper(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - CHECK(args[0]->IsFunction()); - v8::Local enable_function = args[0].As(); - CHECK(args[1]->IsFunction()); - v8::Local disable_function = args[1].As(); - env->inspector_agent()->RegisterAsyncHook(env->isolate(), - enable_function, disable_function); -} - -static void IsEnabled(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - args.GetReturnValue().Set(env->inspector_agent()->enabled()); -} - -// static -void Agent::InitInspector(Local target, Local unused, - Local context, void* priv) { - Environment* env = Environment::GetCurrent(context); - { - auto obj = Object::New(env->isolate()); - auto null = Null(env->isolate()); - CHECK(obj->SetPrototype(context, null).FromJust()); - env->set_inspector_console_api_object(obj); - } - - Agent* agent = env->inspector_agent(); - env->SetMethod(target, "consoleCall", InspectorConsoleCall); - env->SetMethod(target, "addCommandLineAPI", AddCommandLineAPI); - if (agent->debug_options_.wait_for_connect()) - env->SetMethod(target, "callAndPauseOnStart", CallAndPauseOnStart); - env->SetMethod(target, "open", Open); - env->SetMethod(target, "url", Url); - - env->SetMethod(target, "asyncTaskScheduled", AsyncTaskScheduledWrapper); - env->SetMethod(target, "asyncTaskCanceled", - InvokeAsyncTaskFnWithId<&Agent::AsyncTaskCanceled>); - env->SetMethod(target, "asyncTaskStarted", - InvokeAsyncTaskFnWithId<&Agent::AsyncTaskStarted>); - env->SetMethod(target, "asyncTaskFinished", - InvokeAsyncTaskFnWithId<&Agent::AsyncTaskFinished>); - - env->SetMethod(target, "registerAsyncHook", RegisterAsyncHookWrapper); - env->SetMethod(target, "isEnabled", IsEnabled); - - auto conn_str = FIXED_ONE_BYTE_STRING(env->isolate(), "Connection"); - Local tmpl = - env->NewFunctionTemplate(JSBindingsConnection::New); - tmpl->InstanceTemplate()->SetInternalFieldCount(1); - tmpl->SetClassName(conn_str); - AsyncWrap::AddWrapMethods(env, tmpl); - env->SetProtoMethod(tmpl, "dispatch", JSBindingsConnection::Dispatch); - env->SetProtoMethod(tmpl, "disconnect", JSBindingsConnection::Disconnect); - target->Set(env->context(), conn_str, tmpl->GetFunction()).ToChecked(); -} - void Agent::RequestIoThreadStart() { // We need to attempt to interrupt V8 flow (in case Node is running // continuous JS code) and to wake up libuv thread (in case Node is waiting @@ -993,8 +667,10 @@ void Agent::ContextCreated(Local context) { client_->contextCreated(context, name.str()); } +bool Agent::IsWaitingForConnect() { + return debug_options_.wait_for_connect(); +} + } // namespace inspector } // namespace node -NODE_MODULE_CONTEXT_AWARE_BUILTIN(inspector, - node::inspector::Agent::InitInspector); diff --git a/src/inspector_agent.h b/src/inspector_agent.h index 8195e001c2..9119e7263b 100644 --- a/src/inspector_agent.h +++ b/src/inspector_agent.h @@ -10,13 +10,6 @@ #endif #include "node_debug_options.h" - -// Forward declaration to break recursive dependency chain with src/env.h. -namespace node { -class Environment; -class NodePlatform; -} // namespace node - #include "v8.h" namespace v8_inspector { @@ -24,6 +17,10 @@ class StringView; } // namespace v8_inspector namespace node { +// Forward declaration to break recursive dependency chain with src/env.h. +class Environment; +class NodePlatform; + namespace inspector { class InspectorSessionDelegate { @@ -52,7 +49,7 @@ class Agent { // IO thread started, and client connected bool IsConnected(); - + bool IsWaitingForConnect(); void WaitForDisconnect(); void FatalException(v8::Local error, @@ -82,12 +79,6 @@ class Agent { bool enabled() { return enabled_; } void PauseOnNextJavascriptStatement(const std::string& reason); - // Initialize 'inspector' module bindings - static void InitInspector(v8::Local target, - v8::Local unused, - v8::Local context, - void* priv); - InspectorIo* io() { return io_.get(); } diff --git a/src/inspector_js_api.cc b/src/inspector_js_api.cc new file mode 100644 index 0000000000..110f1363aa --- /dev/null +++ b/src/inspector_js_api.cc @@ -0,0 +1,353 @@ +#include "base-object.h" +#include "base-object-inl.h" +#include "inspector_agent.h" +#include "inspector_io.h" +#include "node_internals.h" +#include "v8.h" +#include "v8-inspector.h" + +namespace node { +namespace inspector { +namespace { + +using v8::Context; +using v8::Function; +using v8::FunctionCallbackInfo; +using v8::FunctionTemplate; +using v8::HandleScope; +using v8::Isolate; +using v8::Local; +using v8::MaybeLocal; +using v8::NewStringType; +using v8::Object; +using v8::Persistent; +using v8::String; +using v8::Value; + +using v8_inspector::StringBuffer; +using v8_inspector::StringView; + +std::unique_ptr ToProtocolString(Isolate* isolate, + Local value) { + TwoByteValue buffer(isolate, value); + return StringBuffer::create(StringView(*buffer, buffer.length())); +} + +class JSBindingsConnection : public AsyncWrap { + public: + class JSBindingsSessionDelegate : public InspectorSessionDelegate { + public: + JSBindingsSessionDelegate(Environment* env, + JSBindingsConnection* connection) + : env_(env), + connection_(connection) { + } + + bool WaitForFrontendMessageWhilePaused() override { + return false; + } + + void SendMessageToFrontend(const v8_inspector::StringView& message) + override { + Isolate* isolate = env_->isolate(); + HandleScope handle_scope(isolate); + Context::Scope context_scope(env_->context()); + MaybeLocal v8string = + String::NewFromTwoByte(isolate, message.characters16(), + NewStringType::kNormal, message.length()); + Local argument = v8string.ToLocalChecked().As(); + connection_->OnMessage(argument); + } + + void Disconnect() { + Agent* agent = env_->inspector_agent(); + if (agent->delegate() == this) + agent->Disconnect(); + } + + private: + Environment* env_; + JSBindingsConnection* connection_; + }; + + JSBindingsConnection(Environment* env, + Local wrap, + Local callback) + : AsyncWrap(env, wrap, PROVIDER_INSPECTORJSBINDING), + delegate_(env, this), + callback_(env->isolate(), callback) { + Wrap(wrap, this); + + Agent* inspector = env->inspector_agent(); + if (inspector->delegate() != nullptr) { + env->ThrowTypeError("Session is already attached"); + return; + } + inspector->Connect(&delegate_); + } + + ~JSBindingsConnection() override { + callback_.Reset(); + } + + void OnMessage(Local value) { + MakeCallback(callback_.Get(env()->isolate()), 1, &value); + } + + void CheckIsCurrent() { + Agent* inspector = env()->inspector_agent(); + CHECK_EQ(&delegate_, inspector->delegate()); + } + + static void New(const FunctionCallbackInfo& info) { + Environment* env = Environment::GetCurrent(info); + if (!info[0]->IsFunction()) { + env->ThrowTypeError("Message callback is required"); + return; + } + Local callback = info[0].As(); + new JSBindingsConnection(env, info.This(), callback); + } + + void Disconnect() { + delegate_.Disconnect(); + if (!persistent().IsEmpty()) { + ClearWrap(object()); + persistent().Reset(); + } + delete this; + } + + static void Disconnect(const FunctionCallbackInfo& info) { + JSBindingsConnection* session; + ASSIGN_OR_RETURN_UNWRAP(&session, info.Holder()); + session->Disconnect(); + } + + static void Dispatch(const FunctionCallbackInfo& info) { + Environment* env = Environment::GetCurrent(info); + JSBindingsConnection* session; + ASSIGN_OR_RETURN_UNWRAP(&session, info.Holder()); + if (!info[0]->IsString()) { + env->ThrowTypeError("Inspector message must be a string"); + return; + } + + session->CheckIsCurrent(); + Agent* inspector = env->inspector_agent(); + inspector->Dispatch(ToProtocolString(env->isolate(), info[0])->string()); + } + + size_t self_size() const override { return sizeof(*this); } + + private: + JSBindingsSessionDelegate delegate_; + Persistent callback_; +}; + + +void AddCommandLineAPI(const FunctionCallbackInfo& info) { + auto env = Environment::GetCurrent(info); + Local context = env->context(); + + if (info.Length() != 2 || !info[0]->IsString()) { + return env->ThrowTypeError("inspector.addCommandLineAPI takes " + "exactly 2 arguments: a string and a value."); + } + + Local console_api = env->inspector_console_api_object(); + console_api->Set(context, info[0], info[1]).FromJust(); +} + +void CallAndPauseOnStart(const 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()->PauseOnNextJavascriptStatement("Break on start"); + v8::MaybeLocal retval = + args[0].As()->Call(env->context(), args[1], + call_args.size(), call_args.data()); + if (!retval.IsEmpty()) { + args.GetReturnValue().Set(retval.ToLocalChecked()); + } +} + +void InspectorConsoleCall(const FunctionCallbackInfo& info) { + Isolate* isolate = info.GetIsolate(); + HandleScope handle_scope(isolate); + Local context = isolate->GetCurrentContext(); + CHECK_LT(2, info.Length()); + std::vector> call_args; + for (int i = 3; i < info.Length(); ++i) { + call_args.push_back(info[i]); + } + Environment* env = Environment::GetCurrent(isolate); + if (env->inspector_agent()->enabled()) { + Local inspector_method = info[0]; + CHECK(inspector_method->IsFunction()); + Local config_value = info[2]; + CHECK(config_value->IsObject()); + Local config_object = config_value.As(); + Local in_call_key = FIXED_ONE_BYTE_STRING(isolate, "in_call"); + if (!config_object->Has(context, in_call_key).FromMaybe(false)) { + CHECK(config_object->Set(context, + in_call_key, + v8::True(isolate)).FromJust()); + CHECK(!inspector_method.As()->Call(context, + info.Holder(), + call_args.size(), + call_args.data()).IsEmpty()); + } + CHECK(config_object->Delete(context, in_call_key).FromJust()); + } + + Local node_method = info[1]; + CHECK(node_method->IsFunction()); + node_method.As()->Call(context, + info.Holder(), + call_args.size(), + call_args.data()).FromMaybe(Local()); +} + +static void* GetAsyncTask(int64_t asyncId) { + // The inspector assumes that when other clients use its asyncTask* API, + // they use real pointers, or at least something aligned like real pointer. + // In general it means that our task_id should always be even. + // + // On 32bit platforms, the 64bit asyncId would get truncated when converted + // to a 32bit pointer. However, the javascript part will never enable + // the async_hook on 32bit platforms, therefore the truncation will never + // happen in practice. + return reinterpret_cast(asyncId << 1); +} + +template +static void InvokeAsyncTaskFnWithId(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(args[0]->IsNumber()); + int64_t task_id = args[0]->IntegerValue(env->context()).FromJust(); + (env->inspector_agent()->*asyncTaskFn)(GetAsyncTask(task_id)); +} + +static void AsyncTaskScheduledWrapper(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + CHECK(args[0]->IsString()); + Local task_name = args[0].As(); + String::Value task_name_value(task_name); + StringView task_name_view(*task_name_value, task_name_value.length()); + + CHECK(args[1]->IsNumber()); + int64_t task_id = args[1]->IntegerValue(env->context()).FromJust(); + void* task = GetAsyncTask(task_id); + + CHECK(args[2]->IsBoolean()); + bool recurring = args[2]->BooleanValue(env->context()).FromJust(); + + env->inspector_agent()->AsyncTaskScheduled(task_name_view, task, recurring); +} + +static void RegisterAsyncHookWrapper(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + CHECK(args[0]->IsFunction()); + v8::Local enable_function = args[0].As(); + CHECK(args[1]->IsFunction()); + v8::Local disable_function = args[1].As(); + env->inspector_agent()->RegisterAsyncHook(env->isolate(), + enable_function, disable_function); +} + +void IsEnabled(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + args.GetReturnValue().Set(env->inspector_agent()->enabled()); +} + +void Open(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + Agent* agent = env->inspector_agent(); + bool wait_for_connect = false; + + if (args.Length() > 0 && args[0]->IsUint32()) { + uint32_t port = args[0]->Uint32Value(); + agent->options().set_port(static_cast(port)); + } + + if (args.Length() > 1 && args[1]->IsString()) { + Utf8Value host(env->isolate(), args[1].As()); + agent->options().set_host_name(*host); + } + + if (args.Length() > 2 && args[2]->IsBoolean()) { + wait_for_connect = args[2]->BooleanValue(); + } + + agent->StartIoThread(wait_for_connect); +} + +void Url(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + Agent* agent = env->inspector_agent(); + InspectorIo* io = agent->io(); + + if (!io) return; + + std::vector ids = io->GetTargetIds(); + + if (ids.empty()) return; + + std::string url = FormatWsAddress(io->host(), io->port(), ids[0], true); + args.GetReturnValue().Set(OneByteString(env->isolate(), url.c_str())); +} + +void InitInspectorBindings(Local target, Local unused, + Local context, void* priv) { + Environment* env = Environment::GetCurrent(context); + { + auto obj = Object::New(env->isolate()); + auto null = Null(env->isolate()); + CHECK(obj->SetPrototype(context, null).FromJust()); + env->set_inspector_console_api_object(obj); + } + + Agent* agent = env->inspector_agent(); + env->SetMethod(target, "consoleCall", InspectorConsoleCall); + env->SetMethod(target, "addCommandLineAPI", AddCommandLineAPI); + if (agent->IsWaitingForConnect()) + env->SetMethod(target, "callAndPauseOnStart", CallAndPauseOnStart); + env->SetMethod(target, "open", Open); + env->SetMethod(target, "url", Url); + + env->SetMethod(target, "asyncTaskScheduled", AsyncTaskScheduledWrapper); + env->SetMethod(target, "asyncTaskCanceled", + InvokeAsyncTaskFnWithId<&Agent::AsyncTaskCanceled>); + env->SetMethod(target, "asyncTaskStarted", + InvokeAsyncTaskFnWithId<&Agent::AsyncTaskStarted>); + env->SetMethod(target, "asyncTaskFinished", + InvokeAsyncTaskFnWithId<&Agent::AsyncTaskFinished>); + + env->SetMethod(target, "registerAsyncHook", RegisterAsyncHookWrapper); + env->SetMethod(target, "isEnabled", IsEnabled); + + auto conn_str = FIXED_ONE_BYTE_STRING(env->isolate(), "Connection"); + Local tmpl = + env->NewFunctionTemplate(JSBindingsConnection::New); + tmpl->InstanceTemplate()->SetInternalFieldCount(1); + tmpl->SetClassName(conn_str); + AsyncWrap::AddWrapMethods(env, tmpl); + env->SetProtoMethod(tmpl, "dispatch", JSBindingsConnection::Dispatch); + env->SetProtoMethod(tmpl, "disconnect", JSBindingsConnection::Disconnect); + target->Set(env->context(), conn_str, tmpl->GetFunction()).ToChecked(); +} + +} // namespace +} // namespace inspector +} // namespace node + +NODE_MODULE_CONTEXT_AWARE_BUILTIN(inspector, + node::inspector::InitInspectorBindings);