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