inspector: add support for uncaught exception
To output exception in DevTools console method exceptionThrown should be called on uncaught exception on V8Inspector object. Additionally we need to wait disconnect to provide user way to inspect exception. PR-URL: https://github.com/nodejs/node/pull/8043 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
cb2e83e573
commit
26cd48fb9b
@ -13,6 +13,7 @@
|
||||
#include "platform/v8_inspector/public/V8Inspector.h"
|
||||
#include "platform/v8_inspector/public/V8InspectorClient.h"
|
||||
#include "platform/v8_inspector/public/V8InspectorSession.h"
|
||||
#include "platform/v8_inspector/public/V8StackTrace.h"
|
||||
#include "platform/inspector_protocol/FrontendChannel.h"
|
||||
#include "platform/inspector_protocol/String16.h"
|
||||
#include "platform/inspector_protocol/Values.h"
|
||||
@ -175,6 +176,9 @@ class AgentImpl {
|
||||
bool IsConnected() { return state_ == State::kConnected; }
|
||||
void WaitForDisconnect();
|
||||
|
||||
void FatalException(v8::Local<v8::Value> error,
|
||||
v8::Local<v8::Message> message);
|
||||
|
||||
private:
|
||||
using MessageQueue = std::vector<std::pair<int, String16>>;
|
||||
enum class State { kNew, kAccepting, kConnected, kDone, kError };
|
||||
@ -334,6 +338,10 @@ class V8NodeInspector : public blink::V8InspectorClient {
|
||||
session_->dispatchProtocolMessage(message);
|
||||
}
|
||||
|
||||
blink::V8Inspector* inspector() {
|
||||
return inspector_.get();
|
||||
}
|
||||
|
||||
private:
|
||||
AgentImpl* agent_;
|
||||
v8::Isolate* isolate_;
|
||||
@ -495,6 +503,46 @@ void AgentImpl::InstallInspectorOnProcess() {
|
||||
env->SetMethod(inspector, "wrapConsoleCall", InspectorWrapConsoleCall);
|
||||
}
|
||||
|
||||
String16 ToProtocolString(v8::Local<v8::Value> value) {
|
||||
if (value.IsEmpty() || value->IsNull() || value->IsUndefined() ||
|
||||
!value->IsString()) {
|
||||
return String16();
|
||||
}
|
||||
v8::Local<v8::String> string_value = v8::Local<v8::String>::Cast(value);
|
||||
wstring buffer(string_value->Length(), '\0');
|
||||
string_value->Write(&buffer[0], 0, string_value->Length());
|
||||
return String16(buffer);
|
||||
}
|
||||
|
||||
void AgentImpl::FatalException(v8::Local<v8::Value> error,
|
||||
v8::Local<v8::Message> message) {
|
||||
if (!IsStarted())
|
||||
return;
|
||||
auto env = parent_env_;
|
||||
v8::Local<v8::Context> context = env->context();
|
||||
|
||||
int script_id = message->GetScriptOrigin().ScriptID()->Value();
|
||||
std::unique_ptr<blink::V8StackTrace> stack_trace =
|
||||
inspector_->inspector()->createStackTrace(message->GetStackTrace());
|
||||
|
||||
if (stack_trace && !stack_trace->isEmpty() &&
|
||||
String16::fromInteger(script_id) == stack_trace->topScriptId()) {
|
||||
script_id = 0;
|
||||
}
|
||||
|
||||
inspector_->inspector()->exceptionThrown(
|
||||
context,
|
||||
"Uncaught",
|
||||
error,
|
||||
ToProtocolString(message->Get()),
|
||||
ToProtocolString(message->GetScriptResourceName()),
|
||||
message->GetLineNumber(context).FromMaybe(0),
|
||||
message->GetStartColumn(context).FromMaybe(0),
|
||||
std::move(stack_trace),
|
||||
script_id);
|
||||
WaitForDisconnect();
|
||||
}
|
||||
|
||||
// static
|
||||
void AgentImpl::ThreadCbIO(void* agent) {
|
||||
static_cast<AgentImpl*>(agent)->WorkerRunIO();
|
||||
@ -714,5 +762,11 @@ void Agent::WaitForDisconnect() {
|
||||
impl->WaitForDisconnect();
|
||||
}
|
||||
|
||||
void Agent::FatalException(v8::Local<v8::Value> error,
|
||||
v8::Local<v8::Message> message) {
|
||||
impl->FatalException(error, message);
|
||||
}
|
||||
|
||||
|
||||
} // namespace inspector
|
||||
} // namespace node
|
||||
|
@ -12,6 +12,10 @@ class Environment;
|
||||
|
||||
namespace v8 {
|
||||
class Platform;
|
||||
template<typename T>
|
||||
class Local;
|
||||
class Value;
|
||||
class Message;
|
||||
} // namespace v8
|
||||
|
||||
namespace node {
|
||||
@ -32,6 +36,9 @@ class Agent {
|
||||
bool IsStarted();
|
||||
bool IsConnected();
|
||||
void WaitForDisconnect();
|
||||
|
||||
void FatalException(v8::Local<v8::Value> error,
|
||||
v8::Local<v8::Message> message);
|
||||
private:
|
||||
AgentImpl* impl;
|
||||
};
|
||||
|
40
src/node.cc
40
src/node.cc
@ -2464,31 +2464,43 @@ void FatalException(Isolate* isolate,
|
||||
Local<Function> fatal_exception_function =
|
||||
process_object->Get(fatal_exception_string).As<Function>();
|
||||
|
||||
int exit_code = 0;
|
||||
if (!fatal_exception_function->IsFunction()) {
|
||||
// failed before the process._fatalException function was added!
|
||||
// this is probably pretty bad. Nothing to do but report and exit.
|
||||
ReportException(env, error, message);
|
||||
exit(6);
|
||||
exit_code = 6;
|
||||
}
|
||||
|
||||
TryCatch fatal_try_catch(isolate);
|
||||
if (exit_code == 0) {
|
||||
TryCatch fatal_try_catch(isolate);
|
||||
|
||||
// Do not call FatalException when _fatalException handler throws
|
||||
fatal_try_catch.SetVerbose(false);
|
||||
// Do not call FatalException when _fatalException handler throws
|
||||
fatal_try_catch.SetVerbose(false);
|
||||
|
||||
// this will return true if the JS layer handled it, false otherwise
|
||||
Local<Value> caught =
|
||||
fatal_exception_function->Call(process_object, 1, &error);
|
||||
// this will return true if the JS layer handled it, false otherwise
|
||||
Local<Value> caught =
|
||||
fatal_exception_function->Call(process_object, 1, &error);
|
||||
|
||||
if (fatal_try_catch.HasCaught()) {
|
||||
// the fatal exception function threw, so we must exit
|
||||
ReportException(env, fatal_try_catch);
|
||||
exit(7);
|
||||
if (fatal_try_catch.HasCaught()) {
|
||||
// the fatal exception function threw, so we must exit
|
||||
ReportException(env, fatal_try_catch);
|
||||
exit_code = 7;
|
||||
}
|
||||
|
||||
if (exit_code == 0 && false == caught->BooleanValue()) {
|
||||
ReportException(env, error, message);
|
||||
exit_code = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (false == caught->BooleanValue()) {
|
||||
ReportException(env, error, message);
|
||||
exit(1);
|
||||
if (exit_code) {
|
||||
#if HAVE_INSPECTOR
|
||||
if (use_inspector) {
|
||||
env->inspector_agent()->FatalException(error, message);
|
||||
}
|
||||
#endif
|
||||
exit(exit_code);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user