• 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::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