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::Context;
15 using v8::Exception;
16 using v8::Integer;
17 using v8::Isolate;
18 using v8::Local;
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).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
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).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
UVException(Isolate * isolate,int errorno,const char * syscall,const char * msg,const char * path,const char * dest)91 Local<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
winapi_strerror(const int errorno,bool * must_free)157 static 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
WinapiErrnoException(Isolate * isolate,int errorno,const char * syscall,const char * msg,const char * path)187 Local<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.
FatalException(Isolate * isolate,const v8::TryCatch & try_catch)246 void FatalException(Isolate* isolate, const v8::TryCatch& try_catch) {
247 errors::TriggerUncaughtException(isolate, try_catch);
248 }
249
250 } // namespace node
251