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