1// This file contains implementation of error APIs exposed in node.h 2 3#include "env-inl.h" 4#include "node.h" 5#include "node_errors.h" 6#include "util-inl.h" 7#include "uv.h" 8#include "v8.h" 9 10#include <cstring> 11 12namespace node { 13 14using v8::Context; 15using v8::Exception; 16using v8::Integer; 17using v8::Isolate; 18using v8::Local; 19using v8::Object; 20using v8::String; 21using v8::Value; 22 23Local<Value> ErrnoException(Isolate* isolate, 24 int errorno, 25 const char* syscall, 26 const char* msg, 27 const char* path) { 28 Environment* env = Environment::GetCurrent(isolate); 29 CHECK_NOT_NULL(env); 30 31 Local<Value> e; 32 Local<String> estring = OneByteString(isolate, errors::errno_string(errorno)); 33 if (msg == nullptr || msg[0] == '\0') { 34 msg = strerror(errorno); 35 } 36 Local<String> message = OneByteString(isolate, msg); 37 38 Local<String> cons = 39 String::Concat(isolate, estring, FIXED_ONE_BYTE_STRING(isolate, ", ")); 40 cons = String::Concat(isolate, cons, message); 41 42 Local<String> path_string; 43 if (path != nullptr) { 44 // FIXME(bnoordhuis) It's questionable to interpret the file path as UTF-8. 45 path_string = String::NewFromUtf8(isolate, path).ToLocalChecked(); 46 } 47 48 if (path_string.IsEmpty() == false) { 49 cons = String::Concat(isolate, cons, FIXED_ONE_BYTE_STRING(isolate, " '")); 50 cons = String::Concat(isolate, cons, path_string); 51 cons = String::Concat(isolate, cons, FIXED_ONE_BYTE_STRING(isolate, "'")); 52 } 53 e = Exception::Error(cons); 54 55 Local<Context> context = env->context(); 56 Local<Object> obj = e.As<Object>(); 57 obj->Set(context, 58 env->errno_string(), 59 Integer::New(isolate, errorno)).Check(); 60 obj->Set(context, env->code_string(), estring).Check(); 61 62 if (path_string.IsEmpty() == false) { 63 obj->Set(context, env->path_string(), path_string).Check(); 64 } 65 66 if (syscall != nullptr) { 67 obj->Set(context, 68 env->syscall_string(), 69 OneByteString(isolate, syscall)).Check(); 70 } 71 72 return e; 73} 74 75static Local<String> StringFromPath(Isolate* isolate, const char* path) { 76#ifdef _WIN32 77 if (strncmp(path, "\\\\?\\UNC\\", 8) == 0) { 78 return String::Concat( 79 isolate, 80 FIXED_ONE_BYTE_STRING(isolate, "\\\\"), 81 String::NewFromUtf8(isolate, path + 8).ToLocalChecked()); 82 } else if (strncmp(path, "\\\\?\\", 4) == 0) { 83 return String::NewFromUtf8(isolate, path + 4).ToLocalChecked(); 84 } 85#endif 86 87 return String::NewFromUtf8(isolate, path).ToLocalChecked(); 88} 89 90 91Local<Value> UVException(Isolate* isolate, 92 int errorno, 93 const char* syscall, 94 const char* msg, 95 const char* path, 96 const char* dest) { 97 Environment* env = Environment::GetCurrent(isolate); 98 CHECK_NOT_NULL(env); 99 100 if (!msg || !msg[0]) 101 msg = uv_strerror(errorno); 102 103 Local<String> js_code = OneByteString(isolate, uv_err_name(errorno)); 104 Local<String> js_syscall = OneByteString(isolate, syscall); 105 Local<String> js_path; 106 Local<String> js_dest; 107 108 Local<String> js_msg = js_code; 109 js_msg = 110 String::Concat(isolate, js_msg, FIXED_ONE_BYTE_STRING(isolate, ": ")); 111 js_msg = String::Concat(isolate, js_msg, OneByteString(isolate, msg)); 112 js_msg = 113 String::Concat(isolate, js_msg, FIXED_ONE_BYTE_STRING(isolate, ", ")); 114 js_msg = String::Concat(isolate, js_msg, js_syscall); 115 116 if (path != nullptr) { 117 js_path = StringFromPath(isolate, path); 118 119 js_msg = 120 String::Concat(isolate, js_msg, FIXED_ONE_BYTE_STRING(isolate, " '")); 121 js_msg = String::Concat(isolate, js_msg, js_path); 122 js_msg = 123 String::Concat(isolate, js_msg, FIXED_ONE_BYTE_STRING(isolate, "'")); 124 } 125 126 if (dest != nullptr) { 127 js_dest = StringFromPath(isolate, dest); 128 129 js_msg = String::Concat( 130 isolate, js_msg, FIXED_ONE_BYTE_STRING(isolate, " -> '")); 131 js_msg = String::Concat(isolate, js_msg, js_dest); 132 js_msg = 133 String::Concat(isolate, js_msg, FIXED_ONE_BYTE_STRING(isolate, "'")); 134 } 135 136 Local<Object> e = 137 Exception::Error(js_msg)->ToObject(isolate->GetCurrentContext()) 138 .ToLocalChecked(); 139 140 Local<Context> context = env->context(); 141 e->Set(context, 142 env->errno_string(), 143 Integer::New(isolate, errorno)).Check(); 144 e->Set(context, env->code_string(), js_code).Check(); 145 e->Set(context, env->syscall_string(), js_syscall).Check(); 146 if (!js_path.IsEmpty()) 147 e->Set(context, env->path_string(), js_path).Check(); 148 if (!js_dest.IsEmpty()) 149 e->Set(context, env->dest_string(), js_dest).Check(); 150 151 return e; 152} 153 154#ifdef _WIN32 155// Does about the same as strerror(), 156// but supports all windows error messages 157static const char* winapi_strerror(const int errorno, bool* must_free) { 158 char* errmsg = nullptr; 159 160 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | 161 FORMAT_MESSAGE_IGNORE_INSERTS, 162 nullptr, 163 errorno, 164 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 165 reinterpret_cast<LPTSTR>(&errmsg), 166 0, 167 nullptr); 168 169 if (errmsg) { 170 *must_free = true; 171 172 // Remove trailing newlines 173 for (int i = strlen(errmsg) - 1; 174 i >= 0 && (errmsg[i] == '\n' || errmsg[i] == '\r'); 175 i--) { 176 errmsg[i] = '\0'; 177 } 178 179 return errmsg; 180 } else { 181 // FormatMessage failed 182 *must_free = false; 183 return "Unknown error"; 184 } 185} 186 187Local<Value> WinapiErrnoException(Isolate* isolate, 188 int errorno, 189 const char* syscall, 190 const char* msg, 191 const char* path) { 192 Environment* env = Environment::GetCurrent(isolate); 193 CHECK_NOT_NULL(env); 194 Local<Value> e; 195 bool must_free = false; 196 if (!msg || !msg[0]) { 197 msg = winapi_strerror(errorno, &must_free); 198 } 199 Local<String> message = OneByteString(isolate, msg); 200 201 if (path) { 202 Local<String> cons1 = 203 String::Concat(isolate, message, FIXED_ONE_BYTE_STRING(isolate, " '")); 204 Local<String> cons2 = String::Concat( 205 isolate, 206 cons1, 207 String::NewFromUtf8(isolate, path).ToLocalChecked()); 208 Local<String> cons3 = 209 String::Concat(isolate, cons2, FIXED_ONE_BYTE_STRING(isolate, "'")); 210 e = Exception::Error(cons3); 211 } else { 212 e = Exception::Error(message); 213 } 214 215 Local<Context> context = env->context(); 216 Local<Object> obj = e.As<Object>(); 217 obj->Set(context, env->errno_string(), Integer::New(isolate, errorno)) 218 .Check(); 219 220 if (path != nullptr) { 221 obj->Set(context, 222 env->path_string(), 223 String::NewFromUtf8(isolate, path).ToLocalChecked()) 224 .Check(); 225 } 226 227 if (syscall != nullptr) { 228 obj->Set(context, 229 env->syscall_string(), 230 OneByteString(isolate, syscall)) 231 .Check(); 232 } 233 234 if (must_free) { 235 LocalFree(const_cast<char*>(msg)); 236 } 237 238 return e; 239} 240#endif // _WIN32 241 242// Implement the legacy name exposed in node.h. This has not been in fact 243// fatal any more, as the user can handle the exception in the 244// TryCatch by listening to `uncaughtException`. 245// TODO(joyeecheung): deprecate it in favor of a more accurate name. 246void FatalException(Isolate* isolate, const v8::TryCatch& try_catch) { 247 errors::TriggerUncaughtException(isolate, try_catch); 248} 249 250} // namespace node 251