From ca9e24e8b43b89dc705227a4ed29172c2a95f57f Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Thu, 17 Jan 2019 03:00:55 +0800 Subject: src: move public C++ APIs into src/api/*.cc This patch moves most of the public C++ APIs into src/api/*.cc so that it's easier to tell that we need to be careful about the compatibility of these code. Some APIs, like `node::LoadEnvironmet()`, `node::Start()` and `node::Init()` still stay in `node.cc` because they are still very specific to our use cases and do not work quite well yet for embedders anyway - we could not even manage to write cctest for them at the moment. PR-URL: https://github.com/nodejs/node/pull/25541 Reviewed-By: Gus Caplan --- src/api/exceptions.cc | 245 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 src/api/exceptions.cc (limited to 'src/api/exceptions.cc') diff --git a/src/api/exceptions.cc b/src/api/exceptions.cc new file mode 100644 index 0000000000..4d1cca8b65 --- /dev/null +++ b/src/api/exceptions.cc @@ -0,0 +1,245 @@ +// This file contains implementation of error APIs exposed in node.h + +#include "env-inl.h" +#include "node.h" +#include "node_errors.h" +#include "util-inl.h" +#include "uv.h" +#include "v8.h" + +#include + +namespace node { + +using v8::Exception; +using v8::HandleScope; +using v8::Integer; +using v8::Isolate; +using v8::Local; +using v8::Message; +using v8::NewStringType; +using v8::Object; +using v8::String; +using v8::Value; + +Local ErrnoException(Isolate* isolate, + int errorno, + const char* syscall, + const char* msg, + const char* path) { + Environment* env = Environment::GetCurrent(isolate); + + Local e; + Local estring = OneByteString(isolate, errors::errno_string(errorno)); + if (msg == nullptr || msg[0] == '\0') { + msg = strerror(errorno); + } + Local message = OneByteString(isolate, msg); + + Local cons = + String::Concat(isolate, estring, FIXED_ONE_BYTE_STRING(isolate, ", ")); + cons = String::Concat(isolate, cons, message); + + Local path_string; + if (path != nullptr) { + // FIXME(bnoordhuis) It's questionable to interpret the file path as UTF-8. + path_string = String::NewFromUtf8(isolate, path, NewStringType::kNormal) + .ToLocalChecked(); + } + + if (path_string.IsEmpty() == false) { + cons = String::Concat(isolate, cons, FIXED_ONE_BYTE_STRING(isolate, " '")); + cons = String::Concat(isolate, cons, path_string); + cons = String::Concat(isolate, cons, FIXED_ONE_BYTE_STRING(isolate, "'")); + } + e = Exception::Error(cons); + + Local obj = e.As(); + obj->Set(env->context(), + env->errno_string(), + Integer::New(isolate, errorno)).FromJust(); + obj->Set(env->context(), env->code_string(), estring).FromJust(); + + if (path_string.IsEmpty() == false) { + obj->Set(env->context(), env->path_string(), path_string).FromJust(); + } + + if (syscall != nullptr) { + obj->Set(env->context(), + env->syscall_string(), + OneByteString(isolate, syscall)).FromJust(); + } + + return e; +} + +static Local StringFromPath(Isolate* isolate, const char* path) { +#ifdef _WIN32 + if (strncmp(path, "\\\\?\\UNC\\", 8) == 0) { + return String::Concat( + isolate, + FIXED_ONE_BYTE_STRING(isolate, "\\\\"), + String::NewFromUtf8(isolate, path + 8, NewStringType::kNormal) + .ToLocalChecked()); + } else if (strncmp(path, "\\\\?\\", 4) == 0) { + return String::NewFromUtf8(isolate, path + 4, NewStringType::kNormal) + .ToLocalChecked(); + } +#endif + + return String::NewFromUtf8(isolate, path, NewStringType::kNormal) + .ToLocalChecked(); +} + + +Local UVException(Isolate* isolate, + int errorno, + const char* syscall, + const char* msg, + const char* path, + const char* dest) { + Environment* env = Environment::GetCurrent(isolate); + + if (!msg || !msg[0]) + msg = uv_strerror(errorno); + + Local js_code = OneByteString(isolate, uv_err_name(errorno)); + Local js_syscall = OneByteString(isolate, syscall); + Local js_path; + Local js_dest; + + Local js_msg = js_code; + js_msg = + String::Concat(isolate, js_msg, FIXED_ONE_BYTE_STRING(isolate, ": ")); + js_msg = String::Concat(isolate, js_msg, OneByteString(isolate, msg)); + js_msg = + String::Concat(isolate, js_msg, FIXED_ONE_BYTE_STRING(isolate, ", ")); + js_msg = String::Concat(isolate, js_msg, js_syscall); + + if (path != nullptr) { + js_path = StringFromPath(isolate, path); + + js_msg = + String::Concat(isolate, js_msg, FIXED_ONE_BYTE_STRING(isolate, " '")); + js_msg = String::Concat(isolate, js_msg, js_path); + js_msg = + String::Concat(isolate, js_msg, FIXED_ONE_BYTE_STRING(isolate, "'")); + } + + if (dest != nullptr) { + js_dest = StringFromPath(isolate, dest); + + js_msg = String::Concat( + isolate, js_msg, FIXED_ONE_BYTE_STRING(isolate, " -> '")); + js_msg = String::Concat(isolate, js_msg, js_dest); + js_msg = + String::Concat(isolate, js_msg, FIXED_ONE_BYTE_STRING(isolate, "'")); + } + + Local e = + Exception::Error(js_msg)->ToObject(isolate->GetCurrentContext()) + .ToLocalChecked(); + + e->Set(env->context(), + env->errno_string(), + Integer::New(isolate, errorno)).FromJust(); + e->Set(env->context(), env->code_string(), js_code).FromJust(); + e->Set(env->context(), env->syscall_string(), js_syscall).FromJust(); + if (!js_path.IsEmpty()) + e->Set(env->context(), env->path_string(), js_path).FromJust(); + if (!js_dest.IsEmpty()) + e->Set(env->context(), env->dest_string(), js_dest).FromJust(); + + return e; +} + +#ifdef _WIN32 +// Does about the same as strerror(), +// but supports all windows error messages +static const char* winapi_strerror(const int errorno, bool* must_free) { + char* errmsg = nullptr; + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, errorno, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errmsg, 0, nullptr); + + if (errmsg) { + *must_free = true; + + // Remove trailing newlines + for (int i = strlen(errmsg) - 1; + i >= 0 && (errmsg[i] == '\n' || errmsg[i] == '\r'); i--) { + errmsg[i] = '\0'; + } + + return errmsg; + } else { + // FormatMessage failed + *must_free = false; + return "Unknown error"; + } +} + + +Local WinapiErrnoException(Isolate* isolate, + int errorno, + const char* syscall, + const char* msg, + const char* path) { + Environment* env = Environment::GetCurrent(isolate); + Local e; + bool must_free = false; + if (!msg || !msg[0]) { + msg = winapi_strerror(errorno, &must_free); + } + Local message = OneByteString(isolate, msg); + + if (path) { + Local cons1 = + String::Concat(isolate, message, FIXED_ONE_BYTE_STRING(isolate, " '")); + Local cons2 = String::Concat( + isolate, + cons1, + String::NewFromUtf8(isolate, path, NewStringType::kNormal) + .ToLocalChecked()); + Local cons3 = + String::Concat(isolate, cons2, FIXED_ONE_BYTE_STRING(isolate, "'")); + e = Exception::Error(cons3); + } else { + e = Exception::Error(message); + } + + Local obj = e.As(); + obj->Set(env->errno_string(), Integer::New(isolate, errorno)); + + if (path != nullptr) { + obj->Set(env->path_string(), + String::NewFromUtf8(isolate, path, NewStringType::kNormal) + .ToLocalChecked()); + } + + if (syscall != nullptr) { + obj->Set(env->syscall_string(), OneByteString(isolate, syscall)); + } + + if (must_free) + LocalFree((HLOCAL)msg); + + return e; +} +#endif + +void FatalException(Isolate* isolate, const v8::TryCatch& try_catch) { + // 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()); + } +} + +} // namespace node -- cgit v1.2.3