2019-01-08 23:08:42 +08:00
|
|
|
#include <errno.h>
|
2018-11-03 22:45:40 +08:00
|
|
|
#include <stdarg.h>
|
2019-01-08 23:08:42 +08:00
|
|
|
|
2018-11-03 22:45:40 +08:00
|
|
|
#include "node_errors.h"
|
|
|
|
|
#include "node_internals.h"
|
|
|
|
|
|
|
|
|
|
namespace node {
|
2018-11-30 08:13:45 -06:00
|
|
|
|
2018-11-03 22:45:40 +08:00
|
|
|
using v8::Context;
|
|
|
|
|
using v8::Exception;
|
|
|
|
|
using v8::Function;
|
|
|
|
|
using v8::FunctionCallbackInfo;
|
|
|
|
|
using v8::HandleScope;
|
|
|
|
|
using v8::Int32;
|
|
|
|
|
using v8::Isolate;
|
|
|
|
|
using v8::Just;
|
|
|
|
|
using v8::Local;
|
|
|
|
|
using v8::Maybe;
|
|
|
|
|
using v8::MaybeLocal;
|
|
|
|
|
using v8::Message;
|
|
|
|
|
using v8::NewStringType;
|
|
|
|
|
using v8::Number;
|
|
|
|
|
using v8::Object;
|
|
|
|
|
using v8::ScriptOrigin;
|
|
|
|
|
using v8::String;
|
|
|
|
|
using v8::Undefined;
|
|
|
|
|
using v8::Value;
|
|
|
|
|
|
|
|
|
|
bool IsExceptionDecorated(Environment* env, Local<Value> er) {
|
|
|
|
|
if (!er.IsEmpty() && er->IsObject()) {
|
|
|
|
|
Local<Object> err_obj = er.As<Object>();
|
|
|
|
|
auto maybe_value =
|
|
|
|
|
err_obj->GetPrivate(env->context(), env->decorated_private_symbol());
|
|
|
|
|
Local<Value> decorated;
|
|
|
|
|
return maybe_value.ToLocal(&decorated) && decorated->IsTrue();
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-01 13:56:53 +08:00
|
|
|
namespace per_process {
|
|
|
|
|
static Mutex tty_mutex;
|
|
|
|
|
} // namespace per_process
|
|
|
|
|
|
2018-11-03 22:45:40 +08:00
|
|
|
void AppendExceptionLine(Environment* env,
|
|
|
|
|
Local<Value> er,
|
|
|
|
|
Local<Message> message,
|
|
|
|
|
enum ErrorHandlingMode mode) {
|
|
|
|
|
if (message.IsEmpty()) return;
|
|
|
|
|
|
|
|
|
|
HandleScope scope(env->isolate());
|
|
|
|
|
Local<Object> err_obj;
|
|
|
|
|
if (!er.IsEmpty() && er->IsObject()) {
|
|
|
|
|
err_obj = er.As<Object>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Print (filename):(line number): (message).
|
|
|
|
|
ScriptOrigin origin = message->GetScriptOrigin();
|
|
|
|
|
node::Utf8Value filename(env->isolate(), message->GetScriptResourceName());
|
|
|
|
|
const char* filename_string = *filename;
|
|
|
|
|
int linenum = message->GetLineNumber(env->context()).FromJust();
|
|
|
|
|
// Print line of source code.
|
|
|
|
|
MaybeLocal<String> source_line_maybe = message->GetSourceLine(env->context());
|
|
|
|
|
node::Utf8Value sourceline(env->isolate(),
|
|
|
|
|
source_line_maybe.ToLocalChecked());
|
|
|
|
|
const char* sourceline_string = *sourceline;
|
|
|
|
|
if (strstr(sourceline_string, "node-do-not-add-exception-line") != nullptr)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Because of how node modules work, all scripts are wrapped with a
|
|
|
|
|
// "function (module, exports, __filename, ...) {"
|
|
|
|
|
// to provide script local variables.
|
|
|
|
|
//
|
|
|
|
|
// When reporting errors on the first line of a script, this wrapper
|
|
|
|
|
// function is leaked to the user. There used to be a hack here to
|
|
|
|
|
// truncate off the first 62 characters, but it caused numerous other
|
|
|
|
|
// problems when vm.runIn*Context() methods were used for non-module
|
|
|
|
|
// code.
|
|
|
|
|
//
|
|
|
|
|
// If we ever decide to re-instate such a hack, the following steps
|
|
|
|
|
// must be taken:
|
|
|
|
|
//
|
|
|
|
|
// 1. Pass a flag around to say "this code was wrapped"
|
|
|
|
|
// 2. Update the stack frame output so that it is also correct.
|
|
|
|
|
//
|
|
|
|
|
// It would probably be simpler to add a line rather than add some
|
|
|
|
|
// number of characters to the first line, since V8 truncates the
|
|
|
|
|
// sourceline to 78 characters, and we end up not providing very much
|
|
|
|
|
// useful debugging info to the user if we remove 62 characters.
|
|
|
|
|
|
|
|
|
|
int script_start = (linenum - origin.ResourceLineOffset()->Value()) == 1
|
|
|
|
|
? origin.ResourceColumnOffset()->Value()
|
|
|
|
|
: 0;
|
|
|
|
|
int start = message->GetStartColumn(env->context()).FromMaybe(0);
|
|
|
|
|
int end = message->GetEndColumn(env->context()).FromMaybe(0);
|
|
|
|
|
if (start >= script_start) {
|
|
|
|
|
CHECK_GE(end, start);
|
|
|
|
|
start -= script_start;
|
|
|
|
|
end -= script_start;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char arrow[1024];
|
|
|
|
|
int max_off = sizeof(arrow) - 2;
|
|
|
|
|
|
|
|
|
|
int off = snprintf(arrow,
|
|
|
|
|
sizeof(arrow),
|
|
|
|
|
"%s:%i\n%s\n",
|
|
|
|
|
filename_string,
|
|
|
|
|
linenum,
|
|
|
|
|
sourceline_string);
|
|
|
|
|
CHECK_GE(off, 0);
|
|
|
|
|
if (off > max_off) {
|
|
|
|
|
off = max_off;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Print wavy underline (GetUnderline is deprecated).
|
|
|
|
|
for (int i = 0; i < start; i++) {
|
|
|
|
|
if (sourceline_string[i] == '\0' || off >= max_off) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
CHECK_LT(off, max_off);
|
|
|
|
|
arrow[off++] = (sourceline_string[i] == '\t') ? '\t' : ' ';
|
|
|
|
|
}
|
|
|
|
|
for (int i = start; i < end; i++) {
|
|
|
|
|
if (sourceline_string[i] == '\0' || off >= max_off) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
CHECK_LT(off, max_off);
|
|
|
|
|
arrow[off++] = '^';
|
|
|
|
|
}
|
|
|
|
|
CHECK_LE(off, max_off);
|
|
|
|
|
arrow[off] = '\n';
|
|
|
|
|
arrow[off + 1] = '\0';
|
|
|
|
|
|
|
|
|
|
Local<String> arrow_str =
|
|
|
|
|
String::NewFromUtf8(env->isolate(), arrow, NewStringType::kNormal)
|
|
|
|
|
.ToLocalChecked();
|
|
|
|
|
|
|
|
|
|
const bool can_set_arrow = !arrow_str.IsEmpty() && !err_obj.IsEmpty();
|
|
|
|
|
// If allocating arrow_str failed, print it out. There's not much else to do.
|
|
|
|
|
// If it's not an error, but something needs to be printed out because
|
|
|
|
|
// it's a fatal exception, also print it out from here.
|
|
|
|
|
// Otherwise, the arrow property will be attached to the object and handled
|
|
|
|
|
// by the caller.
|
|
|
|
|
if (!can_set_arrow || (mode == FATAL_ERROR && !err_obj->IsNativeError())) {
|
|
|
|
|
if (env->printed_error()) return;
|
2019-01-01 13:56:53 +08:00
|
|
|
Mutex::ScopedLock lock(per_process::tty_mutex);
|
2018-11-03 22:45:40 +08:00
|
|
|
env->set_printed_error(true);
|
|
|
|
|
|
|
|
|
|
uv_tty_reset_mode();
|
|
|
|
|
PrintErrorString("\n%s", arrow);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CHECK(err_obj
|
|
|
|
|
->SetPrivate(
|
|
|
|
|
env->context(), env->arrow_message_private_symbol(), arrow_str)
|
|
|
|
|
.FromMaybe(false));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[noreturn]] void Abort() {
|
|
|
|
|
DumpBacktrace(stderr);
|
|
|
|
|
fflush(stderr);
|
|
|
|
|
ABORT_NO_BACKTRACE();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[noreturn]] void Assert(const char* const (*args)[4]) {
|
|
|
|
|
auto filename = (*args)[0];
|
|
|
|
|
auto linenum = (*args)[1];
|
|
|
|
|
auto message = (*args)[2];
|
|
|
|
|
auto function = (*args)[3];
|
|
|
|
|
|
|
|
|
|
char name[1024];
|
|
|
|
|
GetHumanReadableProcessName(&name);
|
|
|
|
|
|
|
|
|
|
fprintf(stderr,
|
|
|
|
|
"%s: %s:%s:%s%s Assertion `%s' failed.\n",
|
|
|
|
|
name,
|
|
|
|
|
filename,
|
|
|
|
|
linenum,
|
|
|
|
|
function,
|
|
|
|
|
*function ? ":" : "",
|
|
|
|
|
message);
|
|
|
|
|
fflush(stderr);
|
|
|
|
|
|
|
|
|
|
Abort();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ReportException(Environment* env,
|
|
|
|
|
Local<Value> er,
|
|
|
|
|
Local<Message> message) {
|
|
|
|
|
CHECK(!er.IsEmpty());
|
|
|
|
|
HandleScope scope(env->isolate());
|
|
|
|
|
|
|
|
|
|
if (message.IsEmpty()) message = Exception::CreateMessage(env->isolate(), er);
|
|
|
|
|
|
|
|
|
|
AppendExceptionLine(env, er, message, FATAL_ERROR);
|
|
|
|
|
|
|
|
|
|
Local<Value> trace_value;
|
|
|
|
|
Local<Value> arrow;
|
|
|
|
|
const bool decorated = IsExceptionDecorated(env, er);
|
|
|
|
|
|
|
|
|
|
if (er->IsUndefined() || er->IsNull()) {
|
|
|
|
|
trace_value = Undefined(env->isolate());
|
|
|
|
|
} else {
|
|
|
|
|
Local<Object> err_obj = er->ToObject(env->context()).ToLocalChecked();
|
|
|
|
|
|
2018-11-08 07:22:13 +01:00
|
|
|
trace_value = err_obj->Get(env->context(),
|
|
|
|
|
env->stack_string()).ToLocalChecked();
|
2018-11-03 22:45:40 +08:00
|
|
|
arrow =
|
|
|
|
|
err_obj->GetPrivate(env->context(), env->arrow_message_private_symbol())
|
|
|
|
|
.ToLocalChecked();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
node::Utf8Value trace(env->isolate(), trace_value);
|
|
|
|
|
|
|
|
|
|
// range errors have a trace member set to undefined
|
|
|
|
|
if (trace.length() > 0 && !trace_value->IsUndefined()) {
|
|
|
|
|
if (arrow.IsEmpty() || !arrow->IsString() || decorated) {
|
|
|
|
|
PrintErrorString("%s\n", *trace);
|
|
|
|
|
} else {
|
|
|
|
|
node::Utf8Value arrow_string(env->isolate(), arrow);
|
|
|
|
|
PrintErrorString("%s\n%s\n", *arrow_string, *trace);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// this really only happens for RangeErrors, since they're the only
|
|
|
|
|
// kind that won't have all this info in the trace, or when non-Error
|
|
|
|
|
// objects are thrown manually.
|
|
|
|
|
Local<Value> message;
|
|
|
|
|
Local<Value> name;
|
|
|
|
|
|
|
|
|
|
if (er->IsObject()) {
|
|
|
|
|
Local<Object> err_obj = er.As<Object>();
|
2018-11-08 07:22:13 +01:00
|
|
|
message = err_obj->Get(env->context(),
|
|
|
|
|
env->message_string()).ToLocalChecked();
|
|
|
|
|
name = err_obj->Get(env->context(),
|
|
|
|
|
FIXED_ONE_BYTE_STRING(env->isolate(), "name")).ToLocalChecked();
|
2018-11-03 22:45:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (message.IsEmpty() || message->IsUndefined() || name.IsEmpty() ||
|
|
|
|
|
name->IsUndefined()) {
|
|
|
|
|
// Not an error object. Just print as-is.
|
|
|
|
|
String::Utf8Value message(env->isolate(), er);
|
|
|
|
|
|
|
|
|
|
PrintErrorString("%s\n",
|
|
|
|
|
*message ? *message : "<toString() threw exception>");
|
|
|
|
|
} else {
|
|
|
|
|
node::Utf8Value name_string(env->isolate(), name);
|
|
|
|
|
node::Utf8Value message_string(env->isolate(), message);
|
|
|
|
|
|
|
|
|
|
if (arrow.IsEmpty() || !arrow->IsString() || decorated) {
|
|
|
|
|
PrintErrorString("%s: %s\n", *name_string, *message_string);
|
|
|
|
|
} else {
|
|
|
|
|
node::Utf8Value arrow_string(env->isolate(), arrow);
|
|
|
|
|
PrintErrorString(
|
|
|
|
|
"%s\n%s: %s\n", *arrow_string, *name_string, *message_string);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fflush(stderr);
|
|
|
|
|
|
|
|
|
|
#if HAVE_INSPECTOR
|
|
|
|
|
env->inspector_agent()->FatalException(er, message);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-30 08:13:45 -06:00
|
|
|
void ReportException(Environment* env, const v8::TryCatch& try_catch) {
|
2018-11-03 22:45:40 +08:00
|
|
|
ReportException(env, try_catch.Exception(), try_catch.Message());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PrintErrorString(const char* format, ...) {
|
|
|
|
|
va_list ap;
|
|
|
|
|
va_start(ap, format);
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE);
|
|
|
|
|
|
|
|
|
|
// Check if stderr is something other than a tty/console
|
|
|
|
|
if (stderr_handle == INVALID_HANDLE_VALUE || stderr_handle == nullptr ||
|
|
|
|
|
uv_guess_handle(_fileno(stderr)) != UV_TTY) {
|
|
|
|
|
vfprintf(stderr, format, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fill in any placeholders
|
|
|
|
|
int n = _vscprintf(format, ap);
|
|
|
|
|
std::vector<char> out(n + 1);
|
|
|
|
|
vsprintf(out.data(), format, ap);
|
|
|
|
|
|
|
|
|
|
// Get required wide buffer size
|
|
|
|
|
n = MultiByteToWideChar(CP_UTF8, 0, out.data(), -1, nullptr, 0);
|
|
|
|
|
|
|
|
|
|
std::vector<wchar_t> wbuf(n);
|
|
|
|
|
MultiByteToWideChar(CP_UTF8, 0, out.data(), -1, wbuf.data(), n);
|
|
|
|
|
|
|
|
|
|
// Don't include the null character in the output
|
|
|
|
|
CHECK_GT(n, 0);
|
|
|
|
|
WriteConsoleW(stderr_handle, wbuf.data(), n - 1, nullptr, nullptr);
|
|
|
|
|
#else
|
|
|
|
|
vfprintf(stderr, format, ap);
|
|
|
|
|
#endif
|
|
|
|
|
va_end(ap);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[noreturn]] void FatalError(const char* location, const char* message) {
|
|
|
|
|
OnFatalError(location, message);
|
|
|
|
|
// to suppress compiler warning
|
|
|
|
|
ABORT();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OnFatalError(const char* location, const char* message) {
|
|
|
|
|
if (location) {
|
|
|
|
|
PrintErrorString("FATAL ERROR: %s %s\n", location, message);
|
|
|
|
|
} else {
|
|
|
|
|
PrintErrorString("FATAL ERROR: %s\n", message);
|
|
|
|
|
}
|
|
|
|
|
fflush(stderr);
|
|
|
|
|
ABORT();
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-30 08:13:45 -06:00
|
|
|
namespace errors {
|
|
|
|
|
|
|
|
|
|
TryCatchScope::~TryCatchScope() {
|
2018-12-19 23:07:53 +01:00
|
|
|
if (HasCaught() && !HasTerminated() && mode_ == CatchMode::kFatal) {
|
2018-11-03 22:45:40 +08:00
|
|
|
HandleScope scope(env_->isolate());
|
2018-11-30 08:13:45 -06:00
|
|
|
ReportException(env_, Exception(), Message());
|
2018-11-03 22:45:40 +08:00
|
|
|
exit(7);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-08 23:08:42 +08:00
|
|
|
const char* errno_string(int errorno) {
|
|
|
|
|
#define ERRNO_CASE(e) \
|
|
|
|
|
case e: \
|
|
|
|
|
return #e;
|
|
|
|
|
switch (errorno) {
|
|
|
|
|
#ifdef EACCES
|
|
|
|
|
ERRNO_CASE(EACCES);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EADDRINUSE
|
|
|
|
|
ERRNO_CASE(EADDRINUSE);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EADDRNOTAVAIL
|
|
|
|
|
ERRNO_CASE(EADDRNOTAVAIL);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EAFNOSUPPORT
|
|
|
|
|
ERRNO_CASE(EAFNOSUPPORT);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EAGAIN
|
|
|
|
|
ERRNO_CASE(EAGAIN);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EWOULDBLOCK
|
|
|
|
|
#if EAGAIN != EWOULDBLOCK
|
|
|
|
|
ERRNO_CASE(EWOULDBLOCK);
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EALREADY
|
|
|
|
|
ERRNO_CASE(EALREADY);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EBADF
|
|
|
|
|
ERRNO_CASE(EBADF);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EBADMSG
|
|
|
|
|
ERRNO_CASE(EBADMSG);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EBUSY
|
|
|
|
|
ERRNO_CASE(EBUSY);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ECANCELED
|
|
|
|
|
ERRNO_CASE(ECANCELED);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ECHILD
|
|
|
|
|
ERRNO_CASE(ECHILD);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ECONNABORTED
|
|
|
|
|
ERRNO_CASE(ECONNABORTED);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ECONNREFUSED
|
|
|
|
|
ERRNO_CASE(ECONNREFUSED);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ECONNRESET
|
|
|
|
|
ERRNO_CASE(ECONNRESET);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EDEADLK
|
|
|
|
|
ERRNO_CASE(EDEADLK);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EDESTADDRREQ
|
|
|
|
|
ERRNO_CASE(EDESTADDRREQ);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EDOM
|
|
|
|
|
ERRNO_CASE(EDOM);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EDQUOT
|
|
|
|
|
ERRNO_CASE(EDQUOT);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EEXIST
|
|
|
|
|
ERRNO_CASE(EEXIST);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EFAULT
|
|
|
|
|
ERRNO_CASE(EFAULT);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EFBIG
|
|
|
|
|
ERRNO_CASE(EFBIG);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EHOSTUNREACH
|
|
|
|
|
ERRNO_CASE(EHOSTUNREACH);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EIDRM
|
|
|
|
|
ERRNO_CASE(EIDRM);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EILSEQ
|
|
|
|
|
ERRNO_CASE(EILSEQ);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EINPROGRESS
|
|
|
|
|
ERRNO_CASE(EINPROGRESS);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EINTR
|
|
|
|
|
ERRNO_CASE(EINTR);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EINVAL
|
|
|
|
|
ERRNO_CASE(EINVAL);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EIO
|
|
|
|
|
ERRNO_CASE(EIO);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EISCONN
|
|
|
|
|
ERRNO_CASE(EISCONN);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EISDIR
|
|
|
|
|
ERRNO_CASE(EISDIR);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ELOOP
|
|
|
|
|
ERRNO_CASE(ELOOP);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EMFILE
|
|
|
|
|
ERRNO_CASE(EMFILE);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EMLINK
|
|
|
|
|
ERRNO_CASE(EMLINK);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EMSGSIZE
|
|
|
|
|
ERRNO_CASE(EMSGSIZE);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EMULTIHOP
|
|
|
|
|
ERRNO_CASE(EMULTIHOP);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENAMETOOLONG
|
|
|
|
|
ERRNO_CASE(ENAMETOOLONG);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENETDOWN
|
|
|
|
|
ERRNO_CASE(ENETDOWN);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENETRESET
|
|
|
|
|
ERRNO_CASE(ENETRESET);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENETUNREACH
|
|
|
|
|
ERRNO_CASE(ENETUNREACH);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENFILE
|
|
|
|
|
ERRNO_CASE(ENFILE);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENOBUFS
|
|
|
|
|
ERRNO_CASE(ENOBUFS);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENODATA
|
|
|
|
|
ERRNO_CASE(ENODATA);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENODEV
|
|
|
|
|
ERRNO_CASE(ENODEV);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENOENT
|
|
|
|
|
ERRNO_CASE(ENOENT);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENOEXEC
|
|
|
|
|
ERRNO_CASE(ENOEXEC);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENOLINK
|
|
|
|
|
ERRNO_CASE(ENOLINK);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENOLCK
|
|
|
|
|
#if ENOLINK != ENOLCK
|
|
|
|
|
ERRNO_CASE(ENOLCK);
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENOMEM
|
|
|
|
|
ERRNO_CASE(ENOMEM);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENOMSG
|
|
|
|
|
ERRNO_CASE(ENOMSG);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENOPROTOOPT
|
|
|
|
|
ERRNO_CASE(ENOPROTOOPT);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENOSPC
|
|
|
|
|
ERRNO_CASE(ENOSPC);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENOSR
|
|
|
|
|
ERRNO_CASE(ENOSR);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENOSTR
|
|
|
|
|
ERRNO_CASE(ENOSTR);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENOSYS
|
|
|
|
|
ERRNO_CASE(ENOSYS);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENOTCONN
|
|
|
|
|
ERRNO_CASE(ENOTCONN);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENOTDIR
|
|
|
|
|
ERRNO_CASE(ENOTDIR);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENOTEMPTY
|
|
|
|
|
#if ENOTEMPTY != EEXIST
|
|
|
|
|
ERRNO_CASE(ENOTEMPTY);
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENOTSOCK
|
|
|
|
|
ERRNO_CASE(ENOTSOCK);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENOTSUP
|
|
|
|
|
ERRNO_CASE(ENOTSUP);
|
|
|
|
|
#else
|
|
|
|
|
#ifdef EOPNOTSUPP
|
|
|
|
|
ERRNO_CASE(EOPNOTSUPP);
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENOTTY
|
|
|
|
|
ERRNO_CASE(ENOTTY);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ENXIO
|
|
|
|
|
ERRNO_CASE(ENXIO);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EOVERFLOW
|
|
|
|
|
ERRNO_CASE(EOVERFLOW);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EPERM
|
|
|
|
|
ERRNO_CASE(EPERM);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EPIPE
|
|
|
|
|
ERRNO_CASE(EPIPE);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EPROTO
|
|
|
|
|
ERRNO_CASE(EPROTO);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EPROTONOSUPPORT
|
|
|
|
|
ERRNO_CASE(EPROTONOSUPPORT);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EPROTOTYPE
|
|
|
|
|
ERRNO_CASE(EPROTOTYPE);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ERANGE
|
|
|
|
|
ERRNO_CASE(ERANGE);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EROFS
|
|
|
|
|
ERRNO_CASE(EROFS);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ESPIPE
|
|
|
|
|
ERRNO_CASE(ESPIPE);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ESRCH
|
|
|
|
|
ERRNO_CASE(ESRCH);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ESTALE
|
|
|
|
|
ERRNO_CASE(ESTALE);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ETIME
|
|
|
|
|
ERRNO_CASE(ETIME);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ETIMEDOUT
|
|
|
|
|
ERRNO_CASE(ETIMEDOUT);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef ETXTBSY
|
|
|
|
|
ERRNO_CASE(ETXTBSY);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef EXDEV
|
|
|
|
|
ERRNO_CASE(EXDEV);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-30 08:13:45 -06:00
|
|
|
} // namespace errors
|
|
|
|
|
|
|
|
|
|
void DecorateErrorStack(Environment* env,
|
|
|
|
|
const errors::TryCatchScope& try_catch) {
|
|
|
|
|
Local<Value> exception = try_catch.Exception();
|
|
|
|
|
|
|
|
|
|
if (!exception->IsObject()) return;
|
|
|
|
|
|
|
|
|
|
Local<Object> err_obj = exception.As<Object>();
|
|
|
|
|
|
|
|
|
|
if (IsExceptionDecorated(env, err_obj)) return;
|
|
|
|
|
|
|
|
|
|
AppendExceptionLine(env, exception, try_catch.Message(), CONTEXTIFY_ERROR);
|
|
|
|
|
Local<Value> stack =
|
|
|
|
|
err_obj->Get(env->context(), env->stack_string()).ToLocalChecked();
|
|
|
|
|
MaybeLocal<Value> maybe_value =
|
|
|
|
|
err_obj->GetPrivate(env->context(), env->arrow_message_private_symbol());
|
|
|
|
|
|
|
|
|
|
Local<Value> arrow;
|
|
|
|
|
if (!(maybe_value.ToLocal(&arrow) && arrow->IsString())) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (stack.IsEmpty() || !stack->IsString()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Local<String> decorated_stack = String::Concat(
|
|
|
|
|
env->isolate(),
|
|
|
|
|
String::Concat(env->isolate(),
|
|
|
|
|
arrow.As<String>(),
|
|
|
|
|
FIXED_ONE_BYTE_STRING(env->isolate(), "\n")),
|
|
|
|
|
stack.As<String>());
|
|
|
|
|
err_obj->Set(env->context(), env->stack_string(), decorated_stack).FromJust();
|
|
|
|
|
err_obj->SetPrivate(
|
|
|
|
|
env->context(), env->decorated_private_symbol(), True(env->isolate()));
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-03 22:45:40 +08:00
|
|
|
void FatalException(Isolate* isolate,
|
|
|
|
|
Local<Value> error,
|
|
|
|
|
Local<Message> message) {
|
|
|
|
|
HandleScope scope(isolate);
|
|
|
|
|
|
|
|
|
|
Environment* env = Environment::GetCurrent(isolate);
|
|
|
|
|
CHECK_NOT_NULL(env); // TODO(addaleax): Handle nullptr here.
|
|
|
|
|
Local<Object> process_object = env->process_object();
|
|
|
|
|
Local<String> fatal_exception_string = env->fatal_exception_string();
|
|
|
|
|
Local<Value> fatal_exception_function =
|
2018-11-08 07:22:13 +01:00
|
|
|
process_object->Get(env->context(),
|
|
|
|
|
fatal_exception_string).ToLocalChecked();
|
2018-11-03 22:45:40 +08:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
} else {
|
2018-11-30 08:13:45 -06:00
|
|
|
errors::TryCatchScope fatal_try_catch(env);
|
2018-11-03 22:45:40 +08:00
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
MaybeLocal<Value> caught = fatal_exception_function.As<Function>()->Call(
|
|
|
|
|
env->context(), process_object, 1, &error);
|
|
|
|
|
|
|
|
|
|
if (fatal_try_catch.HasTerminated()) return;
|
|
|
|
|
|
|
|
|
|
if (fatal_try_catch.HasCaught()) {
|
|
|
|
|
// The fatal exception function threw, so we must exit
|
|
|
|
|
ReportException(env, fatal_try_catch);
|
|
|
|
|
exit(7);
|
2018-11-30 08:13:45 -06:00
|
|
|
|
2018-11-03 22:45:40 +08:00
|
|
|
} else if (caught.ToLocalChecked()->IsFalse()) {
|
|
|
|
|
ReportException(env, error, message);
|
|
|
|
|
|
|
|
|
|
// fatal_exception_function call before may have set a new exit code ->
|
|
|
|
|
// read it again, otherwise use default for uncaughtException 1
|
|
|
|
|
Local<String> exit_code = env->exit_code_string();
|
|
|
|
|
Local<Value> code;
|
|
|
|
|
if (!process_object->Get(env->context(), exit_code).ToLocal(&code) ||
|
|
|
|
|
!code->IsInt32()) {
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
exit(code.As<Int32>()->Value());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-30 08:13:45 -06:00
|
|
|
void FatalException(Isolate* isolate, const v8::TryCatch& try_catch) {
|
2018-11-03 22:45:40 +08:00
|
|
|
// If we try to print out a termination exception, we'd just get 'null',
|
|
|
|
|
// so just crashing here with that information seems like a better idea,
|
|
|
|
|
// and in particular it seems like we should handle terminations at the call
|
|
|
|
|
// site for this function rather than by printing them out somewhere.
|
|
|
|
|
CHECK(!try_catch.HasTerminated());
|
|
|
|
|
|
|
|
|
|
HandleScope scope(isolate);
|
|
|
|
|
if (!try_catch.IsVerbose()) {
|
|
|
|
|
FatalException(isolate, try_catch.Exception(), try_catch.Message());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FatalException(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
|
Isolate* isolate = args.GetIsolate();
|
|
|
|
|
Environment* env = Environment::GetCurrent(isolate);
|
|
|
|
|
if (env != nullptr && env->abort_on_uncaught_exception()) {
|
|
|
|
|
Abort();
|
|
|
|
|
}
|
|
|
|
|
Local<Value> exception = args[0];
|
|
|
|
|
Local<Message> message = Exception::CreateMessage(isolate, exception);
|
|
|
|
|
FatalException(isolate, exception, message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace node
|