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

inspector: add inspector console support for native Console #7988

Closed
wants to merge 1 commit into from
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion lib/internal/bootstrap_node.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,15 +226,52 @@
}

function setupGlobalConsole() {
var inspectorConsole;
var wrapConsoleCall;
if (process.inspector) {
inspectorConsole = global.console;
wrapConsoleCall = process.inspector.wrapConsoleCall;
delete process.inspector;
}
var console;
Object.defineProperty(global, 'console', {
configurable: true,
enumerable: true,
get: function() {
return NativeModule.require('console');
if (!console) {
console = NativeModule.require('console');
installInspectorConsoleIfNeeded(console,
inspectorConsole,
wrapConsoleCall);
}
return console;
}
});
}

function installInspectorConsoleIfNeeded(console,
inspectorConsole,
wrapConsoleCall) {
if (!inspectorConsole)
return;
var config = {};
for (const key of Object.keys(console)) {
if (!inspectorConsole.hasOwnProperty(key))
continue;
// If node console has the same method as inspector console,
// then wrap these two methods into one. Native wrapper will preserve
// the original stack.
console[key] = wrapConsoleCall(inspectorConsole[key],
console[key],
config);
}
for (const key of Object.keys(inspectorConsole)) {
if (console.hasOwnProperty(key))
continue;
console[key] = inspectorConsole[key];
}
}

function setupProcessFatal() {

process._fatalException = function(er) {
Expand Down
92 changes: 92 additions & 0 deletions src/inspector_agent.cc
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ class AgentImpl {
const std::string& path);
static void WriteCbIO(uv_async_t* async);

void InstallInspectorOnProcess();

void WorkerRunIO();
void OnInspectorConnectionIO(inspector_socket_t* socket);
void OnRemoteDataIO(inspector_socket_t* stream, ssize_t read,
Expand Down Expand Up @@ -276,6 +278,9 @@ class ChannelImpl final : public blink::protocol::FrontendChannel {
AgentImpl* const agent_;
};

// Used in V8NodeInspector::currentTimeMS() below.
#define NANOS_PER_MSEC 1000000

class V8NodeInspector : public blink::V8InspectorClient {
public:
V8NodeInspector(AgentImpl* agent, node::Environment* env,
Expand Down Expand Up @@ -308,6 +313,10 @@ class V8NodeInspector : public blink::V8InspectorClient {
running_nested_loop_ = false;
}

double currentTimeMS() override {
return uv_hrtime() * 1.0 / NANOS_PER_MSEC;
}

void quitMessageLoopOnPause() override {
terminated_ = true;
}
Expand Down Expand Up @@ -361,11 +370,78 @@ AgentImpl::~AgentImpl() {
data_written_ = nullptr;
}

void InspectorConsoleCall(const v8::FunctionCallbackInfo<v8::Value>& info) {
v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Context> context = isolate->GetCurrentContext();

CHECK(info.Data()->IsArray());
v8::Local<v8::Array> args = info.Data().As<v8::Array>();
CHECK_EQ(args->Length(), 3);

v8::Local<v8::Value> inspector_method =
args->Get(context, 0).ToLocalChecked();
CHECK(inspector_method->IsFunction());
v8::Local<v8::Value> node_method =
args->Get(context, 1).ToLocalChecked();
CHECK(node_method->IsFunction());
v8::Local<v8::Value> config_value =
args->Get(context, 2).ToLocalChecked();
CHECK(config_value->IsObject());
v8::Local<v8::Object> config_object = config_value.As<v8::Object>();

std::vector<v8::Local<v8::Value>> call_args(info.Length());
for (int i = 0; i < info.Length(); ++i) {
call_args[i] = info[i];
}

v8::Local<v8::String> in_call_key = OneByteString(isolate, "in_call");
bool in_call = config_object->Has(context, in_call_key).FromMaybe(false);
if (!in_call) {
CHECK(config_object->Set(context,
in_call_key,
v8::True(isolate)).FromJust());
CHECK(!inspector_method.As<v8::Function>()->Call(
context,
info.Holder(),
call_args.size(),
call_args.data()).IsEmpty());
}

v8::TryCatch try_catch(info.GetIsolate());
node_method.As<v8::Function>()->Call(context,
info.Holder(),
call_args.size(),
call_args.data());
CHECK(config_object->Delete(context, in_call_key).FromJust());
if (try_catch.HasCaught())
try_catch.ReThrow();
}

void InspectorWrapConsoleCall(const v8::FunctionCallbackInfo<v8::Value>& args) {
Environment* env = Environment::GetCurrent(args);

if (args.Length() != 3 || !args[0]->IsFunction() ||
!args[1]->IsFunction() || !args[2]->IsObject()) {
return env->ThrowError("inspector.wrapConsoleCall takes exactly 3 "
"arguments: two functions and an object.");
}

v8::Local<v8::Array> array = v8::Array::New(env->isolate(), args.Length());
CHECK(array->Set(env->context(), 0, args[0]).FromJust());
CHECK(array->Set(env->context(), 1, args[1]).FromJust());
CHECK(array->Set(env->context(), 2, args[2]).FromJust());
args.GetReturnValue().Set(v8::Function::New(env->context(),
InspectorConsoleCall,
array).ToLocalChecked());
}

bool AgentImpl::Start(v8::Platform* platform, int port, bool wait) {
auto env = parent_env_;
inspector_ = new V8NodeInspector(this, env, platform);
platform_ = platform;

InstallInspectorOnProcess();

int err = uv_loop_init(&child_loop_);
CHECK_EQ(err, 0);

Expand Down Expand Up @@ -403,6 +479,22 @@ void AgentImpl::WaitForDisconnect() {
inspector_->runMessageLoopOnPause(0);
}

#define READONLY_PROPERTY(obj, str, var) \
do { \
obj->DefineOwnProperty(env->context(), \
OneByteString(env->isolate(), str), \
var, \
v8::ReadOnly).FromJust(); \
} while (0)

void AgentImpl::InstallInspectorOnProcess() {
auto env = parent_env_;
v8::Local<v8::Object> process = env->process_object();
v8::Local<v8::Object> inspector = v8::Object::New(env->isolate());
READONLY_PROPERTY(process, "inspector", inspector);
env->SetMethod(inspector, "wrapConsoleCall", InspectorWrapConsoleCall);
}

// static
void AgentImpl::ThreadCbIO(void* agent) {
static_cast<AgentImpl*>(agent)->WorkerRunIO();
Expand Down