inspector: do not add 'inspector' property

'inspector' property is not an official API and should not be published
on process object, where the user may discover it.

This change was extracted from https://github.com/nodejs/node/pull/12263
that will be focused on creating JS bindings.

PR-URL: https://github.com/nodejs/node/pull/12656
Reviewed-By: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Timothy Gu <timothygu99@gmail.com>
This commit is contained in:
Eugene Ostroukhov 2017-04-25 14:55:55 -07:00
parent 6ade7f3601
commit 3f48ab3042
6 changed files with 115 additions and 161 deletions

View File

@ -254,52 +254,41 @@
} }
function setupGlobalConsole() { function setupGlobalConsole() {
var inspectorConsole; const originalConsole = global.console;
var wrapConsoleCall; let console;
if (process.inspector) {
inspectorConsole = global.console;
wrapConsoleCall = process.inspector.wrapConsoleCall;
delete process.inspector.wrapConsoleCall;
if (Object.keys(process.inspector).length === 0)
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() {
if (!console) { if (!console) {
console = NativeModule.require('console'); console = installInspectorConsole(originalConsole);
installInspectorConsoleIfNeeded(console,
inspectorConsole,
wrapConsoleCall);
} }
return console; return console;
} }
}); });
} }
function installInspectorConsoleIfNeeded(console, function installInspectorConsole(globalConsole) {
inspectorConsole, const wrappedConsole = NativeModule.require('console');
wrapConsoleCall) { const inspector = process.binding('inspector');
if (!inspectorConsole)
return;
const config = {}; const config = {};
for (const key of Object.keys(console)) { for (const key of Object.keys(wrappedConsole)) {
if (!inspectorConsole.hasOwnProperty(key)) if (!globalConsole.hasOwnProperty(key))
continue; continue;
// If node console has the same method as inspector console, // If global console has the same method as inspector console,
// then wrap these two methods into one. Native wrapper will preserve // then wrap these two methods into one. Native wrapper will preserve
// the original stack. // the original stack.
console[key] = wrapConsoleCall(inspectorConsole[key], wrappedConsole[key] = inspector.consoleCall.bind(wrappedConsole,
console[key], globalConsole[key],
config); wrappedConsole[key],
config);
} }
for (const key of Object.keys(inspectorConsole)) { for (const key of Object.keys(globalConsole)) {
if (console.hasOwnProperty(key)) if (wrappedConsole.hasOwnProperty(key))
continue; continue;
console[key] = inspectorConsole[key]; wrappedConsole[key] = globalConsole[key];
} }
return wrappedConsole;
} }
function setupProcessFatal() { function setupProcessFatal() {

View File

@ -472,19 +472,6 @@ function tryModuleLoad(module, filename) {
} }
} }
function getInspectorCallWrapper() {
var inspector = process.inspector;
if (!inspector || !inspector.callAndPauseOnStart) {
return null;
}
var wrapper = inspector.callAndPauseOnStart.bind(inspector);
delete inspector.callAndPauseOnStart;
if (Object.keys(process.inspector).length === 0) {
delete process.inspector;
}
return wrapper;
}
Module._resolveFilename = function(request, parent, isMain) { Module._resolveFilename = function(request, parent, isMain) {
if (NativeModule.nonInternalExists(request)) { if (NativeModule.nonInternalExists(request)) {
return request; return request;
@ -563,7 +550,7 @@ Module.prototype._compile = function(content, filename) {
// Set breakpoint on module start // Set breakpoint on module start
if (filename === resolvedArgv) { if (filename === resolvedArgv) {
delete process._debugWaitConnect; delete process._debugWaitConnect;
inspectorWrapper = getInspectorCallWrapper(); inspectorWrapper = process.binding('inspector').callAndPauseOnStart;
if (!inspectorWrapper) { if (!inspectorWrapper) {
const Debug = vm.runInDebugContext('Debug'); const Debug = vm.runInDebugContext('Debug');
Debug.setBreakPoint(compiledWrapper, 0, 0); Debug.setBreakPoint(compiledWrapper, 0, 0);

View File

@ -72,6 +72,7 @@ namespace node {
V(arrow_message_private_symbol, "node:arrowMessage") \ V(arrow_message_private_symbol, "node:arrowMessage") \
V(contextify_context_private_symbol, "node:contextify:context") \ V(contextify_context_private_symbol, "node:contextify:context") \
V(contextify_global_private_symbol, "node:contextify:global") \ V(contextify_global_private_symbol, "node:contextify:global") \
V(inspector_delegate_private_symbol, "node:inspector:delegate") \
V(decorated_private_symbol, "node:decorated") \ V(decorated_private_symbol, "node:decorated") \
V(npn_buffer_private_symbol, "node:npnBuffer") \ V(npn_buffer_private_symbol, "node:npnBuffer") \
V(processed_private_symbol, "node:processed") \ V(processed_private_symbol, "node:processed") \

View File

@ -154,8 +154,61 @@ static int RegisterDebugSignalHandler() {
return 0; return 0;
} }
#endif // _WIN32 #endif // _WIN32
} // namespace
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());
static_cast<void>(node_method.As<Function>()->Call(context,
info.Holder(),
call_args.size(),
call_args.data()));
}
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());
args.GetReturnValue().Set(retval.ToLocalChecked());
}
} // namespace
// Used in NodeInspectorClient::currentTimeMS() below. // Used in NodeInspectorClient::currentTimeMS() below.
const int NANOS_PER_MSEC = 1000000; const int NANOS_PER_MSEC = 1000000;
@ -263,12 +316,6 @@ class NodeInspectorClient : public v8_inspector::V8InspectorClient {
channel_->dispatchProtocolMessage(message); channel_->dispatchProtocolMessage(message);
} }
void schedulePauseOnNextStatement(const std::string& reason) {
if (channel_ != nullptr) {
channel_->schedulePauseOnNextStatement(reason);
}
}
Local<Context> ensureDefaultContextInGroup(int contextGroupId) override { Local<Context> ensureDefaultContextInGroup(int contextGroupId) override {
return env_->context(); return env_->context();
} }
@ -302,10 +349,8 @@ class NodeInspectorClient : public v8_inspector::V8InspectorClient {
script_id); script_id);
} }
InspectorSessionDelegate* delegate() { ChannelImpl* channel() {
if (channel_ == nullptr) return channel_.get();
return nullptr;
return channel_->delegate();
} }
private: private:
@ -320,98 +365,22 @@ class NodeInspectorClient : public v8_inspector::V8InspectorClient {
Agent::Agent(Environment* env) : parent_env_(env), Agent::Agent(Environment* env) : parent_env_(env),
inspector_(nullptr), inspector_(nullptr),
platform_(nullptr), platform_(nullptr),
inspector_console_(false) {} enabled_(false) {}
// Header has unique_ptr to some incomplete types - this definition tells // Destructor needs to be defined here in implementation file as the header
// the compiler to figure out destruction here, were those types are complete // does not have full definition of some classes.
Agent::~Agent() { Agent::~Agent() {
} }
// static
void Agent::InspectorConsoleCall(const v8::FunctionCallbackInfo<Value>& info) {
Isolate* isolate = info.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
CHECK(info.Data()->IsArray());
Local<v8::Array> args = info.Data().As<v8::Array>();
CHECK_EQ(args->Length(), 3);
std::vector<Local<Value>> call_args(info.Length());
for (int i = 0; i < info.Length(); ++i) {
call_args[i] = info[i];
}
Environment* env = Environment::GetCurrent(isolate);
if (env->inspector_agent()->inspector_console_) {
Local<Value> inspector_method = args->Get(context, 0).ToLocalChecked();
CHECK(inspector_method->IsFunction());
Local<Value> config_value = args->Get(context, 2).ToLocalChecked();
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 =
args->Get(context, 1).ToLocalChecked();
CHECK(node_method->IsFunction());
static_cast<void>(node_method.As<Function>()->Call(context,
info.Holder(),
call_args.size(),
call_args.data()));
}
// static
void Agent::InspectorWrapConsoleCall(const FunctionCallbackInfo<Value>& info) {
Environment* env = Environment::GetCurrent(info);
if (info.Length() != 3 || !info[0]->IsFunction() ||
!info[1]->IsFunction() || !info[2]->IsObject()) {
return env->ThrowError("inspector.wrapConsoleCall takes exactly 3 "
"arguments: two functions and an object.");
}
Local<v8::Array> array = v8::Array::New(env->isolate(), info.Length());
CHECK(array->Set(env->context(), 0, info[0]).FromJust());
CHECK(array->Set(env->context(), 1, info[1]).FromJust());
CHECK(array->Set(env->context(), 2, info[2]).FromJust());
info.GetReturnValue().Set(Function::New(env->context(),
InspectorConsoleCall,
array).ToLocalChecked());
}
bool Agent::Start(v8::Platform* platform, const char* path, bool Agent::Start(v8::Platform* platform, const char* path,
const DebugOptions& options) { const DebugOptions& options) {
path_ = path == nullptr ? "" : path; path_ = path == nullptr ? "" : path;
debug_options_ = options; debug_options_ = options;
inspector_console_ = false;
inspector_ = inspector_ =
std::unique_ptr<NodeInspectorClient>( std::unique_ptr<NodeInspectorClient>(
new NodeInspectorClient(parent_env_, platform)); new NodeInspectorClient(parent_env_, platform));
platform_ = platform; platform_ = platform;
Local<Object> process = parent_env_->process_object();
Local<Object> inspector = Object::New(parent_env_->isolate());
Local<String> name =
FIXED_ONE_BYTE_STRING(parent_env_->isolate(), "inspector");
process->DefineOwnProperty(parent_env_->context(),
name,
inspector,
v8::ReadOnly).FromJust();
parent_env_->SetMethod(inspector, "wrapConsoleCall",
InspectorWrapConsoleCall);
if (options.inspector_enabled()) { if (options.inspector_enabled()) {
if (options.wait_for_connect()) {
parent_env_->SetMethod(inspector, "callAndPauseOnStart",
CallAndPauseOnStart);
}
return StartIoThread(); return StartIoThread();
} else { } else {
CHECK_EQ(0, uv_async_init(uv_default_loop(), CHECK_EQ(0, uv_async_init(uv_default_loop(),
@ -431,7 +400,7 @@ bool Agent::StartIoThread() {
CHECK_NE(inspector_, nullptr); CHECK_NE(inspector_, nullptr);
inspector_console_ = true; enabled_ = true;
io_ = std::unique_ptr<InspectorIo>( io_ = std::unique_ptr<InspectorIo>(
new InspectorIo(parent_env_, platform_, path_, debug_options_)); new InspectorIo(parent_env_, platform_, path_, debug_options_));
if (!io_->Start()) { if (!io_->Start()) {
@ -469,7 +438,7 @@ void Agent::Stop() {
} }
void Agent::Connect(InspectorSessionDelegate* delegate) { void Agent::Connect(InspectorSessionDelegate* delegate) {
inspector_console_ = true; enabled_ = true;
inspector_->connectFrontend(delegate); inspector_->connectFrontend(delegate);
} }
@ -481,26 +450,6 @@ bool Agent::IsStarted() {
return !!inspector_; return !!inspector_;
} }
// static
void Agent::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]);
}
Agent* agent = env->inspector_agent();
agent->inspector_->schedulePauseOnNextStatement("Break on start");
v8::MaybeLocal<v8::Value> retval =
args[0].As<v8::Function>()->Call(env->context(), args[1],
call_args.size(), call_args.data());
args.GetReturnValue().Set(retval.ToLocalChecked());
}
void Agent::WaitForDisconnect() { void Agent::WaitForDisconnect() {
if (io_ != nullptr) { if (io_ != nullptr) {
io_->WaitForDisconnect(); io_->WaitForDisconnect();
@ -529,5 +478,23 @@ void Agent::RunMessageLoop() {
inspector_->runMessageLoopOnPause(CONTEXT_GROUP_ID); inspector_->runMessageLoopOnPause(CONTEXT_GROUP_ID);
} }
void Agent::PauseOnNextJavascriptStatement(const std::string& reason) {
ChannelImpl* channel = inspector_->channel();
if (channel != nullptr)
channel->schedulePauseOnNextStatement(reason);
}
// static
void Agent::InitJSBindings(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);
}
} // namespace inspector } // namespace inspector
} // namespace node } // namespace node
NODE_MODULE_CONTEXT_AWARE_BUILTIN(inspector,
node::inspector::Agent::InitJSBindings);

View File

@ -17,11 +17,13 @@ class Environment;
} // namespace node } // namespace node
namespace v8 { namespace v8 {
class Context;
template <typename V> template <typename V>
class FunctionCallbackInfo; class FunctionCallbackInfo;
template<typename T> template<typename T>
class Local; class Local;
class Message; class Message;
class Object;
class Platform; class Platform;
class Value; class Value;
} // namespace v8 } // namespace v8
@ -61,19 +63,21 @@ class Agent {
void Disconnect(); void Disconnect();
void Dispatch(const v8_inspector::StringView& message); void Dispatch(const v8_inspector::StringView& message);
void RunMessageLoop(); void RunMessageLoop();
bool enabled() {
return enabled_;
}
void PauseOnNextJavascriptStatement(const std::string& reason);
static void InitJSBindings(v8::Local<v8::Object> target,
v8::Local<v8::Value> unused,
v8::Local<v8::Context> context,
void* priv);
private: private:
static void CallAndPauseOnStart(const v8::FunctionCallbackInfo<v8::Value>&);
static void InspectorConsoleCall(
const v8::FunctionCallbackInfo<v8::Value>& info);
static void InspectorWrapConsoleCall(
const v8::FunctionCallbackInfo<v8::Value>& info);
node::Environment* parent_env_; node::Environment* parent_env_;
std::unique_ptr<NodeInspectorClient> inspector_; std::unique_ptr<NodeInspectorClient> inspector_;
std::unique_ptr<InspectorIo> io_; std::unique_ptr<InspectorIo> io_;
v8::Platform* platform_; v8::Platform* platform_;
bool inspector_console_; bool enabled_;
std::string path_; std::string path_;
DebugOptions debug_options_; DebugOptions debug_options_;
}; };

View File

@ -4624,3 +4624,9 @@ int Start(int argc, char** argv) {
} // namespace node } // namespace node
#if !HAVE_INSPECTOR
static void InitEmptyBindings() {}
NODE_MODULE_CONTEXT_AWARE_BUILTIN(inspector, InitEmptyBindings);
#endif // !HAVE_INSPECTOR