xref: /third_party/node/src/api/exceptions.cc (revision 1cb0ef41)
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
12namespace node {
13
14using v8::Context;
15using v8::Exception;
16using v8::Integer;
17using v8::Isolate;
18using v8::Local;
19using v8::Object;
20using v8::String;
21using v8::Value;
22
23Local<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
75static 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
91Local<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
157static 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
187Local<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.
246void FatalException(Isolate* isolate, const v8::TryCatch& try_catch) {
247  errors::TriggerUncaughtException(isolate, try_catch);
248}
249
250}  // namespace node
251