diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index 78fc3719a6972f..5b6a8f01bb321e 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -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 error, + v8::Local message); + private: using MessageQueue = std::vector>; 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 value) { + if (value.IsEmpty() || value->IsNull() || value->IsUndefined() || + !value->IsString()) { + return String16(); + } + v8::Local string_value = v8::Local::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 error, + v8::Local message) { + if (!IsStarted()) + return; + auto env = parent_env_; + v8::Local context = env->context(); + + int script_id = message->GetScriptOrigin().ScriptID()->Value(); + std::unique_ptr 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(agent)->WorkerRunIO(); @@ -714,5 +762,11 @@ void Agent::WaitForDisconnect() { impl->WaitForDisconnect(); } +void Agent::FatalException(v8::Local error, + v8::Local message) { + impl->FatalException(error, message); +} + + } // namespace inspector } // namespace node diff --git a/src/inspector_agent.h b/src/inspector_agent.h index f2b2c1a187bd91..43433fdc6e69f0 100644 --- a/src/inspector_agent.h +++ b/src/inspector_agent.h @@ -12,6 +12,10 @@ class Environment; namespace v8 { class Platform; +template +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 error, + v8::Local message); private: AgentImpl* impl; }; diff --git a/src/node.cc b/src/node.cc index af35308047b2ee..8ba5a678d1138b 100644 --- a/src/node.cc +++ b/src/node.cc @@ -2520,31 +2520,43 @@ void FatalException(Isolate* isolate, Local fatal_exception_function = process_object->Get(fatal_exception_string).As(); + 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 caught = - fatal_exception_function->Call(process_object, 1, &error); + // this will return true if the JS layer handled it, false otherwise + Local 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); } }