summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/inspector_agent.cc334
-rw-r--r--src/inspector_agent.h19
-rw-r--r--src/inspector_js_api.cc353
3 files changed, 363 insertions, 343 deletions
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 <vector>
#ifdef __POSIX__
+#include <limits.h>
#include <unistd.h> // 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<String> v8string =
- String::NewFromTwoByte(isolate, message.characters16(),
- NewStringType::kNormal, message.length());
- Local<Value> argument = v8string.ToLocalChecked().As<Value>();
- 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<Object> wrap,
- Local<Function> 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> 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<Value>& info) {
- Environment* env = Environment::GetCurrent(info);
- if (!info[0]->IsFunction()) {
- env->ThrowTypeError("Message callback is required");
- return;
- }
- Local<Function> callback = info[0].As<Function>();
- new JSBindingsConnection(env, info.This(), callback);
- }
-
- void Disconnect() {
- delegate_.Disconnect();
- if (!persistent().IsEmpty()) {
- ClearWrap(object());
- persistent().Reset();
- }
- delete this;
- }
-
- static void Disconnect(const FunctionCallbackInfo<Value>& info) {
- JSBindingsConnection* session;
- ASSIGN_OR_RETURN_UNWRAP(&session, info.Holder());
- session->Disconnect();
- }
-
- static void Dispatch(const FunctionCallbackInfo<Value>& 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<Function> callback_;
-};
-
-void InspectorConsoleCall(const v8::FunctionCallbackInfo<Value>& info) {
- Isolate* isolate = info.GetIsolate();
- HandleScope handle_scope(isolate);
- Local<Context> context = isolate->GetCurrentContext();
- CHECK_LT(2, info.Length());
- std::vector<Local<Value>> 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<Value> inspector_method = info[0];
- CHECK(inspector_method->IsFunction());
- Local<Value> config_value = info[2];
- CHECK(config_value->IsObject());
- Local<Object> config_object = config_value.As<Object>();
- Local<String> 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<Function>()->Call(context,
- info.Holder(),
- call_args.size(),
- call_args.data()).IsEmpty());
- }
- CHECK(config_object->Delete(context, in_call_key).FromJust());
- }
-
- Local<Value> node_method = info[1];
- CHECK(node_method->IsFunction());
- node_method.As<Function>()->Call(context,
- info.Holder(),
- call_args.size(),
- call_args.data()).FromMaybe(Local<Value>());
-}
-
-void CallAndPauseOnStart(
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- Environment* env = Environment::GetCurrent(args);
- CHECK_GT(args.Length(), 1);
- CHECK(args[0]->IsFunction());
- std::vector<v8::Local<v8::Value>> 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<v8::Value> retval =
- args[0].As<v8::Function>()->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<Value>& info) {
- auto env = Environment::GetCurrent(info);
- Local<Context> 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<Object> 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<Value>& 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<int>(port));
- }
-
- if (args.Length() > 1 && args[1]->IsString()) {
- node::Utf8Value host(env->isolate(), args[1].As<String>());
- 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<Value>& args) {
- Environment* env = Environment::GetCurrent(args);
- inspector::Agent* agent = env->inspector_agent();
- inspector::InspectorIo* io = agent->io();
-
- if (!io) return;
-
- std::vector<std::string> 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<void*>(asyncId << 1);
-}
-
-template<void (Agent::*asyncTaskFn)(void*)>
-static void InvokeAsyncTaskFnWithId(const FunctionCallbackInfo<Value>& 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<Value>& args) {
- Environment* env = Environment::GetCurrent(args);
-
- CHECK(args[0]->IsString());
- Local<String> task_name = args[0].As<String>();
- 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<Value>& args) {
- Environment* env = Environment::GetCurrent(args);
-
- CHECK(args[0]->IsFunction());
- v8::Local<v8::Function> enable_function = args[0].As<Function>();
- CHECK(args[1]->IsFunction());
- v8::Local<v8::Function> disable_function = args[1].As<Function>();
- env->inspector_agent()->RegisterAsyncHook(env->isolate(),
- enable_function, disable_function);
-}
-
-static void IsEnabled(const FunctionCallbackInfo<Value>& args) {
- Environment* env = Environment::GetCurrent(args);
- args.GetReturnValue().Set(env->inspector_agent()->enabled());
-}
-
-// static
-void Agent::InitInspector(Local<Object> target, Local<Value> unused,
- Local<Context> 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<FunctionTemplate> 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> 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<v8::Value> 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<v8::Object> target,
- v8::Local<v8::Value> unused,
- v8::Local<v8::Context> 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<StringBuffer> ToProtocolString(Isolate* isolate,
+ Local<Value> 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<String> v8string =
+ String::NewFromTwoByte(isolate, message.characters16(),
+ NewStringType::kNormal, message.length());
+ Local<Value> argument = v8string.ToLocalChecked().As<Value>();
+ 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<Object> wrap,
+ Local<Function> 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> 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<Value>& info) {
+ Environment* env = Environment::GetCurrent(info);
+ if (!info[0]->IsFunction()) {
+ env->ThrowTypeError("Message callback is required");
+ return;
+ }
+ Local<Function> callback = info[0].As<Function>();
+ new JSBindingsConnection(env, info.This(), callback);
+ }
+
+ void Disconnect() {
+ delegate_.Disconnect();
+ if (!persistent().IsEmpty()) {
+ ClearWrap(object());
+ persistent().Reset();
+ }
+ delete this;
+ }
+
+ static void Disconnect(const FunctionCallbackInfo<Value>& info) {
+ JSBindingsConnection* session;
+ ASSIGN_OR_RETURN_UNWRAP(&session, info.Holder());
+ session->Disconnect();
+ }
+
+ static void Dispatch(const FunctionCallbackInfo<Value>& 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<Function> callback_;
+};
+
+
+void AddCommandLineAPI(const FunctionCallbackInfo<Value>& info) {
+ auto env = Environment::GetCurrent(info);
+ Local<Context> 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<Object> console_api = env->inspector_console_api_object();
+ console_api->Set(context, info[0], info[1]).FromJust();
+}
+
+void CallAndPauseOnStart(const FunctionCallbackInfo<v8::Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ CHECK_GT(args.Length(), 1);
+ CHECK(args[0]->IsFunction());
+ std::vector<v8::Local<v8::Value>> 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<v8::Value> retval =
+ args[0].As<v8::Function>()->Call(env->context(), args[1],
+ call_args.size(), call_args.data());
+ if (!retval.IsEmpty()) {
+ args.GetReturnValue().Set(retval.ToLocalChecked());
+ }
+}
+
+void InspectorConsoleCall(const FunctionCallbackInfo<Value>& info) {
+ Isolate* isolate = info.GetIsolate();
+ HandleScope handle_scope(isolate);
+ Local<Context> context = isolate->GetCurrentContext();
+ CHECK_LT(2, info.Length());
+ std::vector<Local<Value>> 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<Value> inspector_method = info[0];
+ CHECK(inspector_method->IsFunction());
+ Local<Value> config_value = info[2];
+ CHECK(config_value->IsObject());
+ Local<Object> config_object = config_value.As<Object>();
+ Local<String> 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<Function>()->Call(context,
+ info.Holder(),
+ call_args.size(),
+ call_args.data()).IsEmpty());
+ }
+ CHECK(config_object->Delete(context, in_call_key).FromJust());
+ }
+
+ Local<Value> node_method = info[1];
+ CHECK(node_method->IsFunction());
+ node_method.As<Function>()->Call(context,
+ info.Holder(),
+ call_args.size(),
+ call_args.data()).FromMaybe(Local<Value>());
+}
+
+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<void*>(asyncId << 1);
+}
+
+template<void (Agent::*asyncTaskFn)(void*)>
+static void InvokeAsyncTaskFnWithId(const FunctionCallbackInfo<Value>& 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<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+
+ CHECK(args[0]->IsString());
+ Local<String> task_name = args[0].As<String>();
+ 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<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+
+ CHECK(args[0]->IsFunction());
+ v8::Local<v8::Function> enable_function = args[0].As<Function>();
+ CHECK(args[1]->IsFunction());
+ v8::Local<v8::Function> disable_function = args[1].As<Function>();
+ env->inspector_agent()->RegisterAsyncHook(env->isolate(),
+ enable_function, disable_function);
+}
+
+void IsEnabled(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ args.GetReturnValue().Set(env->inspector_agent()->enabled());
+}
+
+void Open(const FunctionCallbackInfo<Value>& 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<int>(port));
+ }
+
+ if (args.Length() > 1 && args[1]->IsString()) {
+ Utf8Value host(env->isolate(), args[1].As<String>());
+ 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<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ Agent* agent = env->inspector_agent();
+ InspectorIo* io = agent->io();
+
+ if (!io) return;
+
+ std::vector<std::string> 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<Object> target, Local<Value> unused,
+ Local<Context> 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<FunctionTemplate> 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);