You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
cocos_lib/cocos/bindings/jswrapper/v8/debugger/inspector_agent.cpp

822 lines
27 KiB

#include "inspector_agent.h"
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
#include "env.h"
#include "inspector_io.h"
#include "node.h"
#include "util.h"
#include "v8-inspector.h"
#include "v8-platform.h"
#include "zlib.h"
#include "libplatform/libplatform.h"
#include <string.h>
#include <unordered_map>
#include <vector>
#ifdef __POSIX__
#include <unistd.h> // setuid, getuid
#endif // __POSIX__
namespace node {
namespace inspector {
namespace {
using v8::Context;
using v8::External;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::HandleScope;
using v8::Isolate;
using v8::Local;
using v8::Maybe;
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;
using v8_inspector::V8Inspector;
using v8_inspector::V8InspectorClient;
static uv_sem_t start_io_thread_semaphore;
static uv_async_t start_io_thread_async;
class StartIoTask : public v8::Task {
public:
explicit StartIoTask(Agent *agent) : agent(agent) {}
void Run() override {
agent->StartIoThread(false);
}
private:
Agent *agent;
};
std::unique_ptr<StringBuffer> ToProtocolString(Isolate *isolate,
Local<Value> value) {
TwoByteValue buffer(isolate, value);
return StringBuffer::create(StringView(*buffer, buffer.length()));
}
// Called on the main thread.
void StartIoThreadAsyncCallback(uv_async_t *handle) {
static_cast<Agent *>(handle->data)->StartIoThread(false);
}
void StartIoInterrupt(Isolate *isolate, void *agent) {
static_cast<Agent *>(agent)->StartIoThread(false);
}
#ifdef __POSIX__
static void StartIoThreadWakeup(int signo) {
uv_sem_post(&start_io_thread_semaphore);
}
inline void *StartIoThreadMain(void *unused) {
for (;;) {
uv_sem_wait(&start_io_thread_semaphore);
Agent *agent = static_cast<Agent *>(start_io_thread_async.data);
if (agent != nullptr)
agent->RequestIoThreadStart();
}
return nullptr;
}
static int StartDebugSignalHandler() {
// Start a watchdog thread for calling v8::Debug::DebugBreak() because
// it's not safe to call directly from the signal handler, it can
// deadlock with the thread it interrupts.
CHECK_EQ(0, uv_sem_init(&start_io_thread_semaphore, 0));
pthread_attr_t attr;
CHECK_EQ(0, pthread_attr_init(&attr));
// Don't shrink the thread's stack on FreeBSD. Said platform decided to
// follow the pthreads specification to the letter rather than in spirit:
// https://lists.freebsd.org/pipermail/freebsd-current/2014-March/048885.html
#ifndef __FreeBSD__
CHECK_EQ(0, pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN));
#endif // __FreeBSD__
CHECK_EQ(0, pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
sigset_t sigmask;
// Mask all signals.
sigfillset(&sigmask);
CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, &sigmask));
pthread_t thread;
const int err = pthread_create(&thread, &attr,
StartIoThreadMain, nullptr);
// Restore original mask
CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, nullptr));
CHECK_EQ(0, pthread_attr_destroy(&attr));
if (err != 0) {
SE_LOGE("node[%d]: pthread_create: %s\n", getpid(), strerror(err));
// Leave SIGUSR1 blocked. We don't install a signal handler,
// receiving the signal would terminate the process.
return -err;
}
RegisterSignalHandler(SIGUSR1, StartIoThreadWakeup);
// Unblock SIGUSR1. A pending SIGUSR1 signal will now be delivered.
sigemptyset(&sigmask);
sigaddset(&sigmask, SIGUSR1);
CHECK_EQ(0, pthread_sigmask(SIG_UNBLOCK, &sigmask, nullptr));
return 0;
}
#endif // __POSIX__
#ifdef _WIN32
DWORD WINAPI StartIoThreadProc(void *arg) {
Agent *agent = static_cast<Agent *>(start_io_thread_async.data);
if (agent != nullptr)
agent->RequestIoThreadStart();
return 0;
}
static int GetDebugSignalHandlerMappingName(DWORD pid, wchar_t *buf,
size_t buf_len) {
return _snwprintf(buf, buf_len, L"node-debug-handler-%u", pid);
}
static int StartDebugSignalHandler() {
wchar_t mapping_name[32];
HANDLE mapping_handle;
DWORD pid;
LPTHREAD_START_ROUTINE *handler;
pid = GetCurrentProcessId();
if (GetDebugSignalHandlerMappingName(pid,
mapping_name,
arraysize(mapping_name)) < 0) {
return -1;
}
mapping_handle = CreateFileMappingW(INVALID_HANDLE_VALUE,
nullptr,
PAGE_READWRITE,
0,
sizeof *handler,
mapping_name);
if (mapping_handle == nullptr) {
return -1;
}
handler = reinterpret_cast<LPTHREAD_START_ROUTINE *>(
MapViewOfFile(mapping_handle,
FILE_MAP_ALL_ACCESS,
0,
0,
sizeof *handler));
if (handler == nullptr) {
CloseHandle(mapping_handle);
return -1;
}
*handler = StartIoThreadProc;
UnmapViewOfFile(static_cast<void *>(handler));
return 0;
}
#endif // _WIN32
class JsBindingsSessionDelegate : public InspectorSessionDelegate {
public:
JsBindingsSessionDelegate(Environment *env,
Local<Object> session,
Local<Object> receiver,
Local<Function> callback)
: env_(env),
session_(env->isolate(), session),
receiver_(env->isolate(), receiver),
callback_(env->isolate(), callback) {
session_.SetWeak(this, JsBindingsSessionDelegate::Release,
v8::WeakCallbackType::kParameter);
}
~JsBindingsSessionDelegate() override {
session_.Reset();
receiver_.Reset();
callback_.Reset();
}
bool WaitForFrontendMessageWhilePaused() override {
return false;
}
void SendMessageToFrontend(const v8_inspector::StringView &message) override {
Isolate *isolate = env_->isolate();
v8::HandleScope handle_scope(isolate);
Context::Scope context_scope(env_->context());
MaybeLocal<String> v8string =
String::NewFromTwoByte(isolate, message.characters16(),
NewStringType::kNormal, static_cast<int>(message.length()));
Local<Value> argument = v8string.ToLocalChecked().As<Value>();
Local<Function> callback = callback_.Get(isolate);
Local<Object> receiver = receiver_.Get(isolate);
callback->Call(env_->context(), receiver, 1, &argument)
.FromMaybe(Local<Value>());
}
void Disconnect() {
Agent *agent = env_->inspector_agent();
if (agent->delegate() == this)
agent->Disconnect();
}
private:
static void Release(
const v8::WeakCallbackInfo<JsBindingsSessionDelegate> &info) {
info.SetSecondPassCallback(ReleaseSecondPass);
info.GetParameter()->session_.Reset();
}
static void ReleaseSecondPass(
const v8::WeakCallbackInfo<JsBindingsSessionDelegate> &info) {
JsBindingsSessionDelegate *delegate = info.GetParameter();
delegate->Disconnect();
delete delegate;
}
Environment *env_;
Persistent<Object> session_;
Persistent<Object> receiver_;
Persistent<Function> callback_;
};
void SetDelegate(Environment *env, Local<Object> inspector,
JsBindingsSessionDelegate *delegate) {
inspector->SetPrivate(env->context(),
env->inspector_delegate_private_symbol(),
v8::External::New(env->isolate(), delegate));
}
Maybe<JsBindingsSessionDelegate *> GetDelegate(
const FunctionCallbackInfo<Value> &info) {
Environment *env = Environment::GetCurrent(info);
Local<Value> delegate;
MaybeLocal<Value> maybe_delegate =
info.This()->GetPrivate(env->context(),
env->inspector_delegate_private_symbol());
if (maybe_delegate.ToLocal(&delegate)) {
CHECK(delegate->IsExternal());
void *value = delegate.As<External>()->Value();
if (value != nullptr) {
return v8::Just(static_cast<JsBindingsSessionDelegate *>(value));
}
}
env->ThrowError("Inspector is not connected");
return v8::Nothing<JsBindingsSessionDelegate *>();
}
void Dispatch(const FunctionCallbackInfo<Value> &info) {
Environment *env = Environment::GetCurrent(info);
if (!info[0]->IsString()) {
env->ThrowError("Inspector message must be a string");
return;
}
Maybe<JsBindingsSessionDelegate *> maybe_delegate = GetDelegate(info);
if (maybe_delegate.IsNothing())
return;
Agent *inspector = env->inspector_agent();
CHECK_EQ(maybe_delegate.ToChecked(), inspector->delegate());
inspector->Dispatch(ToProtocolString(env->isolate(), info[0])->string());
}
void Disconnect(const FunctionCallbackInfo<Value> &info) {
Environment *env = Environment::GetCurrent(info);
Maybe<JsBindingsSessionDelegate *> delegate = GetDelegate(info);
if (delegate.IsNothing()) {
return;
}
delegate.ToChecked()->Disconnect();
SetDelegate(env, info.This(), nullptr);
delete delegate.ToChecked();
}
void ConnectJSBindingsSession(const FunctionCallbackInfo<Value> &info) {
Environment *env = Environment::GetCurrent(info);
if (!info[0]->IsFunction()) {
env->ThrowError("Message callback is required");
return;
}
Agent *inspector = env->inspector_agent();
if (inspector->delegate() != nullptr) {
env->ThrowError("Session is already attached");
return;
}
Local<Object> session = Object::New(env->isolate());
env->SetMethod(session, "dispatch", Dispatch);
env->SetMethod(session, "disconnect", Disconnect);
info.GetReturnValue().Set(session);
JsBindingsSessionDelegate *delegate =
new JsBindingsSessionDelegate(env, session, info.Holder(),
info[0].As<Function>());
inspector->Connect(delegate);
SetDelegate(env, session, delegate);
}
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(),
static_cast<int>(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(),
static_cast<int>(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],
static_cast<int>(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;
const int CONTEXT_GROUP_ID = 1;
class ChannelImpl final : public v8_inspector::V8Inspector::Channel {
public:
explicit ChannelImpl(V8Inspector *inspector,
InspectorSessionDelegate *delegate)
: delegate_(delegate) {
#if V8_MAJOR_VERSION > 10 || (V8_MAJOR_VERSION == 10 && V8_MINOR_VERSION > 3)
session_ = inspector->connect(1, this, StringView(), v8_inspector::V8Inspector::ClientTrustLevel::kFullyTrusted);
#else
session_ = inspector->connect(1, this, StringView());
#endif
}
virtual ~ChannelImpl() {}
void dispatchProtocolMessage(const StringView &message) {
session_->dispatchProtocolMessage(message);
}
bool waitForFrontendMessage() {
return delegate_->WaitForFrontendMessageWhilePaused();
}
void schedulePauseOnNextStatement(const std::string &reason) {
std::unique_ptr<StringBuffer> buffer = Utf8ToStringView(reason);
session_->schedulePauseOnNextStatement(buffer->string(), buffer->string());
}
InspectorSessionDelegate *delegate() {
return delegate_;
}
private:
void sendResponse(
int callId,
std::unique_ptr<v8_inspector::StringBuffer> message) override {
sendMessageToFrontend(message->string());
}
void sendNotification(
std::unique_ptr<v8_inspector::StringBuffer> message) override {
sendMessageToFrontend(message->string());
}
void flushProtocolNotifications() override {}
void sendMessageToFrontend(const StringView &message) {
delegate_->SendMessageToFrontend(message);
}
InspectorSessionDelegate *const delegate_;
std::unique_ptr<v8_inspector::V8InspectorSession> session_;
};
class InspectorTimer {
public:
InspectorTimer(uv_loop_t *loop,
double interval_s,
V8InspectorClient::TimerCallback callback,
void *data) : timer_(),
callback_(callback),
data_(data) {
uv_timer_init(loop, &timer_);
int64_t interval_ms = 1000 * interval_s;
uv_timer_start(&timer_, OnTimer, interval_ms, interval_ms);
}
InspectorTimer(const InspectorTimer &) = delete;
void Stop() {
uv_timer_stop(&timer_);
uv_close(reinterpret_cast<uv_handle_t *>(&timer_), TimerClosedCb);
}
private:
static void OnTimer(uv_timer_t *uvtimer) {
InspectorTimer *timer = node::ContainerOf(&InspectorTimer::timer_, uvtimer);
timer->callback_(timer->data_);
}
static void TimerClosedCb(uv_handle_t *uvtimer) {
InspectorTimer *timer =
node::ContainerOf(&InspectorTimer::timer_,
reinterpret_cast<uv_timer_t *>(uvtimer));
delete timer;
}
~InspectorTimer() {}
uv_timer_t timer_;
V8InspectorClient::TimerCallback callback_;
void *data_;
};
class InspectorTimerHandle {
public:
InspectorTimerHandle(uv_loop_t *loop, double interval_s,
V8InspectorClient::TimerCallback callback, void *data) {
timer_ = new InspectorTimer(loop, interval_s, callback, data);
}
InspectorTimerHandle(const InspectorTimerHandle &) = delete;
~InspectorTimerHandle() {
CHECK_NE(timer_, nullptr);
timer_->Stop();
timer_ = nullptr;
}
private:
InspectorTimer *timer_;
};
} // namespace
class NodeInspectorClient : public V8InspectorClient {
public:
NodeInspectorClient(node::Environment *env,
v8::Platform *platform) : env_(env),
platform_(platform),
terminated_(false),
running_nested_loop_(false) {
client_ = V8Inspector::create(env->isolate(), this);
}
void runMessageLoopOnPause(int context_group_id) override {
CHECK_NE(channel_, nullptr);
if (running_nested_loop_)
return;
terminated_ = false;
running_nested_loop_ = true;
while (!terminated_ && channel_->waitForFrontendMessage()) {
while (v8::platform::PumpMessageLoop(platform_, env_->isolate())) {
}
}
terminated_ = false;
running_nested_loop_ = false;
}
double currentTimeMS() override {
return uv_hrtime() * 1.0 / NANOS_PER_MSEC;
}
void contextCreated(Local<Context> context, const std::string &name) {
std::unique_ptr<StringBuffer> name_buffer = Utf8ToStringView(name);
v8_inspector::V8ContextInfo info(context, CONTEXT_GROUP_ID,
name_buffer->string());
client_->contextCreated(info);
}
void contextDestroyed(Local<Context> context) {
client_->contextDestroyed(context);
}
void quitMessageLoopOnPause() override {
terminated_ = true;
}
void connectFrontend(InspectorSessionDelegate *delegate) {
CHECK_EQ(channel_, nullptr);
channel_ = std::unique_ptr<ChannelImpl>(
new ChannelImpl(client_.get(), delegate));
}
void disconnectFrontend() {
quitMessageLoopOnPause();
channel_.reset();
}
void dispatchMessageFromFrontend(const StringView &message) {
if (channel_ == nullptr) return;
CHECK_NE(channel_, nullptr);
channel_->dispatchProtocolMessage(message);
}
Local<Context> ensureDefaultContextInGroup(int contextGroupId) override {
return env_->context();
}
void FatalException(Local<Value> error, Local<v8::Message> message) {
Local<Context> context = env_->context();
int script_id = message->GetScriptOrigin().ScriptId();
Local<v8::StackTrace> stack_trace = message->GetStackTrace();
if (!stack_trace.IsEmpty() &&
stack_trace->GetFrameCount() > 0 &&
script_id == stack_trace->GetFrame(env_->isolate(), 0)->GetScriptId()) {
script_id = 0;
}
const uint8_t DETAILS[] = "Uncaught";
Isolate *isolate = context->GetIsolate();
client_->exceptionThrown(
context,
StringView(DETAILS, sizeof(DETAILS) - 1),
error,
ToProtocolString(isolate, message->Get())->string(),
ToProtocolString(isolate, message->GetScriptResourceName())->string(),
message->GetLineNumber(context).FromMaybe(0),
message->GetStartColumn(context).FromMaybe(0),
client_->createStackTrace(stack_trace),
script_id);
}
ChannelImpl *channel() {
return channel_.get();
}
void startRepeatingTimer(double interval_s,
TimerCallback callback,
void *data) override {
timers_.emplace(std::piecewise_construct, std::make_tuple(data),
std::make_tuple(env_->event_loop(), interval_s, callback,
data));
}
void cancelTimer(void *data) override {
timers_.erase(data);
}
private:
node::Environment *env_;
v8::Platform *platform_;
bool terminated_;
bool running_nested_loop_;
std::unique_ptr<V8Inspector> client_;
std::unique_ptr<ChannelImpl> channel_;
std::unordered_map<void *, InspectorTimerHandle> timers_;
};
Agent::Agent(Environment *env) : parent_env_(env),
client_(nullptr),
platform_(nullptr),
enabled_(false) {}
// Destructor needs to be defined here in implementation file as the header
// does not have full definition of some classes.
Agent::~Agent() {
}
bool Agent::Start(v8::Platform *platform, const char *path,
const DebugOptions &options) {
path_ = path == nullptr ? "" : path;
debug_options_ = options;
client_ =
std::unique_ptr<NodeInspectorClient>(
new NodeInspectorClient(parent_env_, platform));
client_->contextCreated(parent_env_->context(), "Node.js Main Context");
platform_ = platform;
CHECK_EQ(0, uv_async_init(uv_default_loop(),
&start_io_thread_async,
StartIoThreadAsyncCallback));
start_io_thread_async.data = this;
uv_unref(reinterpret_cast<uv_handle_t *>(&start_io_thread_async));
// Ignore failure, SIGUSR1 won't work, but that should not block node start.
StartDebugSignalHandler();
if (options.inspector_enabled()) {
// This will return false if listen failed on the inspector port.
return StartIoThread(options.wait_for_connect());
}
return true;
}
bool Agent::StartIoThread(bool wait_for_connect) {
if (io_ != nullptr)
return true;
CHECK_NE(client_, nullptr);
enabled_ = true;
io_ = std::unique_ptr<InspectorIo>(
new InspectorIo(parent_env_, platform_, path_, debug_options_,
wait_for_connect));
if (!io_->Start()) {
client_.reset();
return false;
}
v8::Isolate *isolate = parent_env_->isolate();
// Send message to enable debug in workers
HandleScope handle_scope(isolate);
Local<Object> process_object = parent_env_->process_object();
Local<Value> emit_fn =
process_object->Get(parent_env_->context(), FIXED_ONE_BYTE_STRING(isolate, "emit")).ToLocalChecked();
// In case the thread started early during the startup
if (!emit_fn->IsFunction())
return true;
Local<Object> message = Object::New(isolate);
message->Set(parent_env_->context(), FIXED_ONE_BYTE_STRING(isolate, "cmd"),
FIXED_ONE_BYTE_STRING(isolate, "NODE_DEBUG_ENABLED"))
.Check();
Local<Value> argv[] = {
FIXED_ONE_BYTE_STRING(isolate, "internalMessage"),
message};
MakeCallback(parent_env_->isolate(), process_object, emit_fn.As<Function>(),
arraysize(argv), argv, {0, 0});
return true;
}
void Agent::Stop() {
if (io_ != nullptr) {
io_->Stop();
io_.reset();
}
}
void Agent::Connect(InspectorSessionDelegate *delegate) {
enabled_ = true;
client_->connectFrontend(delegate);
}
bool Agent::IsConnected() {
return io_ && io_->IsConnected();
}
void Agent::WaitForDisconnect() {
CHECK_NE(client_, nullptr);
client_->contextDestroyed(parent_env_->context());
if (io_ != nullptr) {
io_->WaitForDisconnect();
}
}
void Agent::FatalException(Local<Value> error, Local<v8::Message> message) {
if (!IsStarted())
return;
client_->FatalException(error, message);
WaitForDisconnect();
}
void Agent::Dispatch(const StringView &message) {
if (client_ == nullptr) return;
CHECK_NE(client_, nullptr);
client_->dispatchMessageFromFrontend(message);
}
void Agent::Disconnect() {
CHECK_NE(client_, nullptr);
client_->disconnectFrontend();
}
void Agent::RunMessageLoop() {
CHECK_NE(client_, nullptr);
client_->runMessageLoopOnPause(CONTEXT_GROUP_ID);
}
InspectorSessionDelegate *Agent::delegate() {
CHECK_NE(client_, nullptr);
ChannelImpl *channel = client_->channel();
if (channel == nullptr)
return nullptr;
return channel->delegate();
}
void Agent::PauseOnNextJavascriptStatement(const std::string &reason) {
ChannelImpl *channel = client_->channel();
if (channel != nullptr)
channel->schedulePauseOnNextStatement(reason);
}
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(env->context()).ToChecked();
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(env->isolate());
}
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 Agent::InitInspector(Local<Object> target, Local<Value> unused,
Local<Context> context, void *priv) {
Environment *env = Environment::GetCurrent(context);
Agent *agent = env->inspector_agent();
env->SetMethod(target, "consoleCall", InspectorConsoleCall);
if (agent->debug_options_.wait_for_connect())
env->SetMethod(target, "callAndPauseOnStart", CallAndPauseOnStart);
env->SetMethod(target, "connect", ConnectJSBindingsSession);
env->SetMethod(target, "open", Open);
env->SetMethod(target, "url", Url);
}
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
// for IO events)
uv_async_send(&start_io_thread_async);
v8::Isolate *isolate = parent_env_->isolate();
platform_->GetForegroundTaskRunner(isolate)->PostTask(std::make_unique<StartIoTask>(this));
isolate->RequestInterrupt(StartIoInterrupt, this);
uv_async_send(&start_io_thread_async);
}
} // namespace inspector
} // namespace node
//cjh NODE_MODULE_CONTEXT_AWARE_BUILTIN(inspector,
// node::inspector::Agent::InitInspector);
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR