deps: v8_inspector: console support
When node is running with --inspect flag, default console.log, console.warn and other methods call inspector console methods in addition to current behaviour (dump formatted message to stderr and stdout). Inspector console methods forward message to DevTools and show up in DevTools Console with DevTools formatters. Inspector console methods not present on Node console will be added into it. Only own methods on global.console object will be changed while in a debugging session. User are still able to redefine it, use console.Console or change original methods on Console.prototype. PR-URL: https://github.com/nodejs/node/pull/7988 Reviewed-By: bnoordhuis - Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: jasnell - James M Snell <jasnell@gmail.com> Reviewed-By: ofrobots - Ali Ijaz Sheikh <ofrobots@google.com>
This commit is contained in:
parent
e40234d6e2
commit
f6070a1a02
39
lib/internal/bootstrap_node.js
vendored
39
lib/internal/bootstrap_node.js
vendored
@ -226,15 +226,52 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setupGlobalConsole() {
|
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', {
|
Object.defineProperty(global, 'console', {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
get: function() {
|
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() {
|
function setupProcessFatal() {
|
||||||
|
|
||||||
process._fatalException = function(er) {
|
process._fatalException = function(er) {
|
||||||
|
@ -186,6 +186,8 @@ class AgentImpl {
|
|||||||
const std::string& path);
|
const std::string& path);
|
||||||
static void WriteCbIO(uv_async_t* async);
|
static void WriteCbIO(uv_async_t* async);
|
||||||
|
|
||||||
|
void InstallInspectorOnProcess();
|
||||||
|
|
||||||
void WorkerRunIO();
|
void WorkerRunIO();
|
||||||
void OnInspectorConnectionIO(inspector_socket_t* socket);
|
void OnInspectorConnectionIO(inspector_socket_t* socket);
|
||||||
void OnRemoteDataIO(inspector_socket_t* stream, ssize_t read,
|
void OnRemoteDataIO(inspector_socket_t* stream, ssize_t read,
|
||||||
@ -276,6 +278,9 @@ class ChannelImpl final : public blink::protocol::FrontendChannel {
|
|||||||
AgentImpl* const agent_;
|
AgentImpl* const agent_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Used in V8NodeInspector::currentTimeMS() below.
|
||||||
|
#define NANOS_PER_MSEC 1000000
|
||||||
|
|
||||||
class V8NodeInspector : public blink::V8InspectorClient {
|
class V8NodeInspector : public blink::V8InspectorClient {
|
||||||
public:
|
public:
|
||||||
V8NodeInspector(AgentImpl* agent, node::Environment* env,
|
V8NodeInspector(AgentImpl* agent, node::Environment* env,
|
||||||
@ -308,6 +313,10 @@ class V8NodeInspector : public blink::V8InspectorClient {
|
|||||||
running_nested_loop_ = false;
|
running_nested_loop_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double currentTimeMS() override {
|
||||||
|
return uv_hrtime() * 1.0 / NANOS_PER_MSEC;
|
||||||
|
}
|
||||||
|
|
||||||
void quitMessageLoopOnPause() override {
|
void quitMessageLoopOnPause() override {
|
||||||
terminated_ = true;
|
terminated_ = true;
|
||||||
}
|
}
|
||||||
@ -361,11 +370,78 @@ AgentImpl::~AgentImpl() {
|
|||||||
data_written_ = nullptr;
|
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) {
|
bool AgentImpl::Start(v8::Platform* platform, int port, bool wait) {
|
||||||
auto env = parent_env_;
|
auto env = parent_env_;
|
||||||
inspector_ = new V8NodeInspector(this, env, platform);
|
inspector_ = new V8NodeInspector(this, env, platform);
|
||||||
platform_ = platform;
|
platform_ = platform;
|
||||||
|
|
||||||
|
InstallInspectorOnProcess();
|
||||||
|
|
||||||
int err = uv_loop_init(&child_loop_);
|
int err = uv_loop_init(&child_loop_);
|
||||||
CHECK_EQ(err, 0);
|
CHECK_EQ(err, 0);
|
||||||
|
|
||||||
@ -403,6 +479,22 @@ void AgentImpl::WaitForDisconnect() {
|
|||||||
inspector_->runMessageLoopOnPause(0);
|
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
|
// static
|
||||||
void AgentImpl::ThreadCbIO(void* agent) {
|
void AgentImpl::ThreadCbIO(void* agent) {
|
||||||
static_cast<AgentImpl*>(agent)->WorkerRunIO();
|
static_cast<AgentImpl*>(agent)->WorkerRunIO();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user