diff options
author | Joyee Cheung <joyeec9h3@gmail.com> | 2019-01-17 03:00:55 +0800 |
---|---|---|
committer | Joyee Cheung <joyeec9h3@gmail.com> | 2019-02-01 07:43:27 +0800 |
commit | ca9e24e8b43b89dc705227a4ed29172c2a95f57f (patch) | |
tree | daba09ee200dab1bff3fb7bc02124b88756b133d /src/api/exceptions.cc | |
parent | 99c3243b229d4e552eadf1abf296440e20c604f8 (diff) | |
download | android-node-v8-ca9e24e8b43b89dc705227a4ed29172c2a95f57f.tar.gz android-node-v8-ca9e24e8b43b89dc705227a4ed29172c2a95f57f.tar.bz2 android-node-v8-ca9e24e8b43b89dc705227a4ed29172c2a95f57f.zip |
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 <me@gus.host>
Diffstat (limited to 'src/api/exceptions.cc')
-rw-r--r-- | src/api/exceptions.cc | 245 |
1 files changed, 245 insertions, 0 deletions
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 <string.h> + +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<Value> ErrnoException(Isolate* isolate, + int errorno, + const char* syscall, + const char* msg, + const char* path) { + Environment* env = Environment::GetCurrent(isolate); + + Local<Value> e; + Local<String> estring = OneByteString(isolate, errors::errno_string(errorno)); + if (msg == nullptr || msg[0] == '\0') { + msg = strerror(errorno); + } + Local<String> message = OneByteString(isolate, msg); + + Local<String> cons = + String::Concat(isolate, estring, FIXED_ONE_BYTE_STRING(isolate, ", ")); + cons = String::Concat(isolate, cons, message); + + Local<String> 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<Object> obj = e.As<Object>(); + 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<String> 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<Value> 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<String> js_code = OneByteString(isolate, uv_err_name(errorno)); + Local<String> js_syscall = OneByteString(isolate, syscall); + Local<String> js_path; + Local<String> js_dest; + + Local<String> 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<Object> 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<Value> WinapiErrnoException(Isolate* isolate, + int errorno, + const char* syscall, + const char* msg, + const char* path) { + Environment* env = Environment::GetCurrent(isolate); + Local<Value> e; + bool must_free = false; + if (!msg || !msg[0]) { + msg = winapi_strerror(errorno, &must_free); + } + Local<String> message = OneByteString(isolate, msg); + + if (path) { + Local<String> cons1 = + String::Concat(isolate, message, FIXED_ONE_BYTE_STRING(isolate, " '")); + Local<String> cons2 = String::Concat( + isolate, + cons1, + String::NewFromUtf8(isolate, path, NewStringType::kNormal) + .ToLocalChecked()); + Local<String> cons3 = + String::Concat(isolate, cons2, FIXED_ONE_BYTE_STRING(isolate, "'")); + e = Exception::Error(cons3); + } else { + e = Exception::Error(message); + } + + Local<Object> obj = e.As<Object>(); + 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 |