GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: api/exceptions.cc Lines: 66 66 100.0 %
Date: 2021-09-17 04:12:37 Branches: 21 28 75.0 %

Line Branch Exec Source
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
23
8
Local<Value> ErrnoException(Isolate* isolate,
24
                            int errorno,
25
                            const char* syscall,
26
                            const char* msg,
27
                            const char* path) {
28
8
  Environment* env = Environment::GetCurrent(isolate);
29
8
  CHECK_NOT_NULL(env);
30
31
  Local<Value> e;
32
8
  Local<String> estring = OneByteString(isolate, errors::errno_string(errorno));
33

8
  if (msg == nullptr || msg[0] == '\0') {
34
7
    msg = strerror(errorno);
35
  }
36
8
  Local<String> message = OneByteString(isolate, msg);
37
38
  Local<String> cons =
39
8
      String::Concat(isolate, estring, FIXED_ONE_BYTE_STRING(isolate, ", "));
40
8
  cons = String::Concat(isolate, cons, message);
41
42
  Local<String> path_string;
43
8
  if (path != nullptr) {
44
    // FIXME(bnoordhuis) It's questionable to interpret the file path as UTF-8.
45
2
    path_string = String::NewFromUtf8(isolate, path).ToLocalChecked();
46
  }
47
48
8
  if (path_string.IsEmpty() == false) {
49
1
    cons = String::Concat(isolate, cons, FIXED_ONE_BYTE_STRING(isolate, " '"));
50
1
    cons = String::Concat(isolate, cons, path_string);
51
1
    cons = String::Concat(isolate, cons, FIXED_ONE_BYTE_STRING(isolate, "'"));
52
  }
53
8
  e = Exception::Error(cons);
54
55
8
  Local<Context> context = env->context();
56
8
  Local<Object> obj = e.As<Object>();
57
8
  obj->Set(context,
58
           env->errno_string(),
59
32
           Integer::New(isolate, errorno)).Check();
60
24
  obj->Set(context, env->code_string(), estring).Check();
61
62
8
  if (path_string.IsEmpty() == false) {
63
3
    obj->Set(context, env->path_string(), path_string).Check();
64
  }
65
66
8
  if (syscall != nullptr) {
67
8
    obj->Set(context,
68
             env->syscall_string(),
69
32
             OneByteString(isolate, syscall)).Check();
70
  }
71
72
8
  return e;
73
}
74
75
4676
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
9352
  return String::NewFromUtf8(isolate, path).ToLocalChecked();
88
}
89
90
91
4695
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
4695
  Environment* env = Environment::GetCurrent(isolate);
98
4695
  CHECK_NOT_NULL(env);
99
100

4695
  if (!msg || !msg[0])
101
4695
    msg = uv_strerror(errorno);
102
103
4695
  Local<String> js_code = OneByteString(isolate, uv_err_name(errorno));
104
4695
  Local<String> js_syscall = OneByteString(isolate, syscall);
105
  Local<String> js_path;
106
  Local<String> js_dest;
107
108
4695
  Local<String> js_msg = js_code;
109
  js_msg =
110
4695
      String::Concat(isolate, js_msg, FIXED_ONE_BYTE_STRING(isolate, ": "));
111
4695
  js_msg = String::Concat(isolate, js_msg, OneByteString(isolate, msg));
112
  js_msg =
113
4695
      String::Concat(isolate, js_msg, FIXED_ONE_BYTE_STRING(isolate, ", "));
114
4695
  js_msg = String::Concat(isolate, js_msg, js_syscall);
115
116
4695
  if (path != nullptr) {
117
4664
    js_path = StringFromPath(isolate, path);
118
119
    js_msg =
120
4664
        String::Concat(isolate, js_msg, FIXED_ONE_BYTE_STRING(isolate, " '"));
121
4664
    js_msg = String::Concat(isolate, js_msg, js_path);
122
    js_msg =
123
4664
        String::Concat(isolate, js_msg, FIXED_ONE_BYTE_STRING(isolate, "'"));
124
  }
125
126
4695
  if (dest != nullptr) {
127
12
    js_dest = StringFromPath(isolate, dest);
128
129
    js_msg = String::Concat(
130
12
        isolate, js_msg, FIXED_ONE_BYTE_STRING(isolate, " -> '"));
131
12
    js_msg = String::Concat(isolate, js_msg, js_dest);
132
    js_msg =
133
12
        String::Concat(isolate, js_msg, FIXED_ONE_BYTE_STRING(isolate, "'"));
134
  }
135
136
  Local<Object> e =
137
9390
    Exception::Error(js_msg)->ToObject(isolate->GetCurrentContext())
138
4695
      .ToLocalChecked();
139
140
4695
  Local<Context> context = env->context();
141
4695
  e->Set(context,
142
         env->errno_string(),
143
18780
         Integer::New(isolate, errorno)).Check();
144
14085
  e->Set(context, env->code_string(), js_code).Check();
145
14085
  e->Set(context, env->syscall_string(), js_syscall).Check();
146
4695
  if (!js_path.IsEmpty())
147
13992
    e->Set(context, env->path_string(), js_path).Check();
148
4695
  if (!js_dest.IsEmpty())
149
36
    e->Set(context, env->dest_string(), js_dest).Check();
150
151
4695
  return e;
152
}
153
154
#ifdef _WIN32
155
// Does about the same as strerror(),
156
// but supports all windows error messages
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
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
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.
246
1
void FatalException(Isolate* isolate, const v8::TryCatch& try_catch) {
247
1
  errors::TriggerUncaughtException(isolate, try_catch);
248
1
}
249
250
}  // namespace node