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