• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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