GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage-daily/nodes/benchmark/out/../src/node_dir.cc Lines: 146 161 90.7 %
Date: 2019-10-08 22:34:21 Branches: 54 108 50.0 %

Line Branch Exec Source
1
#include "node_dir.h"
2
#include "node_process.h"
3
#include "util.h"
4
5
#include "tracing/trace_event.h"
6
7
#include "req_wrap-inl.h"
8
#include "string_bytes.h"
9
10
#include <fcntl.h>
11
#include <sys/types.h>
12
#include <sys/stat.h>
13
#include <cstring>
14
#include <cerrno>
15
#include <climits>
16
17
#include <memory>
18
19
namespace node {
20
21
namespace fs_dir {
22
23
using fs::FSReqAfterScope;
24
using fs::FSReqBase;
25
using fs::FSReqWrapSync;
26
using fs::GetReqWrap;
27
28
using v8::Array;
29
using v8::Context;
30
using v8::Function;
31
using v8::FunctionCallbackInfo;
32
using v8::FunctionTemplate;
33
using v8::HandleScope;
34
using v8::Integer;
35
using v8::Isolate;
36
using v8::Local;
37
using v8::MaybeLocal;
38
using v8::Null;
39
using v8::Object;
40
using v8::ObjectTemplate;
41
using v8::String;
42
using v8::Value;
43
44
#define TRACE_NAME(name) "fs_dir.sync." #name
45
#define GET_TRACE_ENABLED                                                      \
46
  (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED                                 \
47
  (TRACING_CATEGORY_NODE2(fs_dir, sync)) != 0)
48
#define FS_DIR_SYNC_TRACE_BEGIN(syscall, ...)                                  \
49
  if (GET_TRACE_ENABLED)                                                       \
50
  TRACE_EVENT_BEGIN(TRACING_CATEGORY_NODE2(fs_dir, sync), TRACE_NAME(syscall), \
51
  ##__VA_ARGS__);
52
#define FS_DIR_SYNC_TRACE_END(syscall, ...)                                    \
53
  if (GET_TRACE_ENABLED)                                                       \
54
  TRACE_EVENT_END(TRACING_CATEGORY_NODE2(fs_dir, sync), TRACE_NAME(syscall),   \
55
  ##__VA_ARGS__);
56
57
8
DirHandle::DirHandle(Environment* env, Local<Object> obj, uv_dir_t* dir)
58
    : AsyncWrap(env, obj, AsyncWrap::PROVIDER_DIRHANDLE),
59
8
      dir_(dir) {
60
8
  MakeWeak();
61
62
8
  dir_->nentries = 1;
63
8
  dir_->dirents = &dirent_;
64
8
}
65
66
8
DirHandle* DirHandle::New(Environment* env, uv_dir_t* dir) {
67
  Local<Object> obj;
68
16
  if (!env->dir_instance_template()
69
24
          ->NewInstance(env->context())
70
24
          .ToLocal(&obj)) {
71
    return nullptr;
72
  }
73
74
8
  return new DirHandle(env, obj, dir);
75
}
76
77
void DirHandle::New(const FunctionCallbackInfo<Value>& args) {
78
  CHECK(args.IsConstructCall());
79
}
80
81
24
DirHandle::~DirHandle() {
82
8
  CHECK(!closing_);  // We should not be deleting while explicitly closing!
83
8
  GCClose();         // Close synchronously and emit warning
84
8
  CHECK(closed_);    // We have to be closed at the point
85
16
}
86
87
// Close the directory handle if it hasn't already been closed. A process
88
// warning will be emitted using a SetImmediate to avoid calling back to
89
// JS during GC. If closing the fd fails at this point, a fatal exception
90
// will crash the process immediately.
91
8
inline void DirHandle::GCClose() {
92
15
  if (closed_) return;
93
  uv_fs_t req;
94
1
  int ret = uv_fs_closedir(nullptr, &req, dir_, nullptr);
95
1
  uv_fs_req_cleanup(&req);
96
1
  closing_ = false;
97
1
  closed_ = true;
98
99
  struct err_detail { int ret; };
100
101
1
  err_detail detail { ret };
102
103
1
  if (ret < 0) {
104
    // Do not unref this
105
    env()->SetImmediate([detail](Environment* env) {
106
      char msg[70];
107
      snprintf(msg, arraysize(msg),
108
              "Closing directory handle on garbage collection failed");
109
      // This exception will end up being fatal for the process because
110
      // it is being thrown from within the SetImmediate handler and
111
      // there is no JS stack to bubble it to. In other words, tearing
112
      // down the process is the only reasonable thing we can do here.
113
      HandleScope handle_scope(env->isolate());
114
      env->ThrowUVException(detail.ret, "close", msg);
115
    });
116
    return;
117
  }
118
119
  // If the close was successful, we still want to emit a process warning
120
  // to notify that the file descriptor was gc'd. We want to be noisy about
121
  // this because not explicitly closing the DirHandle is a bug.
122
123
1
  env()->SetUnrefImmediate([](Environment* env) {
124
    ProcessEmitWarning(env,
125
1
                       "Closing directory handle on garbage collection");
126
2
  });
127
}
128
129
6
void AfterClose(uv_fs_t* req) {
130
6
  FSReqBase* req_wrap = FSReqBase::from_req(req);
131
6
  FSReqAfterScope after(req_wrap, req);
132
133
6
  if (after.Proceed())
134
12
    req_wrap->Resolve(Undefined(req_wrap->env()->isolate()));
135
6
}
136
137
7
void DirHandle::Close(const FunctionCallbackInfo<Value>& args) {
138
7
  Environment* env = Environment::GetCurrent(args);
139
140
7
  const int argc = args.Length();
141
7
  CHECK_GE(argc, 1);
142
143
  DirHandle* dir;
144
14
  ASSIGN_OR_RETURN_UNWRAP(&dir, args.Holder());
145
146
7
  dir->closing_ = false;
147
7
  dir->closed_ = true;
148
149
7
  FSReqBase* req_wrap_async = GetReqWrap(env, args[0]);
150
7
  if (req_wrap_async != nullptr) {  // close(req)
151
    AsyncCall(env, req_wrap_async, args, "closedir", UTF8, AfterClose,
152
6
              uv_fs_closedir, dir->dir());
153
  } else {  // close(undefined, ctx)
154
1
    CHECK_EQ(argc, 2);
155
1
    FSReqWrapSync req_wrap_sync;
156

1
    FS_DIR_SYNC_TRACE_BEGIN(closedir);
157
    SyncCall(env, args[1], &req_wrap_sync, "closedir", uv_fs_closedir,
158
2
             dir->dir());
159

1
    FS_DIR_SYNC_TRACE_END(closedir);
160
  }
161
}
162
163
16
void AfterDirReadSingle(uv_fs_t* req) {
164
16
  FSReqBase* req_wrap = FSReqBase::from_req(req);
165
16
  FSReqAfterScope after(req_wrap, req);
166
167
16
  if (!after.Proceed()) {
168
    return;
169
  }
170
171
16
  Environment* env = req_wrap->env();
172
16
  Isolate* isolate = env->isolate();
173
  Local<Value> error;
174
175
16
  if (req->result == 0) {
176
    // Done
177
    Local<Value> done = Null(isolate);
178
2
    req_wrap->Resolve(done);
179
2
    return;
180
  }
181
182
14
  uv_dir_t* dir = static_cast<uv_dir_t*>(req->ptr);
183
14
  req->ptr = nullptr;
184
185
  // Single entries are returned without an array wrapper
186
14
  const uv_dirent_t& ent = dir->dirents[0];
187
188
  MaybeLocal<Value> filename =
189
    StringBytes::Encode(isolate,
190
                        ent.name,
191
                        req_wrap->encoding(),
192
14
                        &error);
193
14
  if (filename.IsEmpty())
194
    return req_wrap->Reject(error);
195
196
197
14
  Local<Array> result = Array::New(isolate, 2);
198
14
  result->Set(env->context(),
199
              0,
200
42
              filename.ToLocalChecked()).FromJust();
201
14
  result->Set(env->context(),
202
              1,
203
56
              Integer::New(isolate, ent.type)).FromJust();
204
28
  req_wrap->Resolve(result);
205
}
206
207
208
22
void DirHandle::Read(const FunctionCallbackInfo<Value>& args) {
209
22
  Environment* env = Environment::GetCurrent(args);
210
22
  Isolate* isolate = env->isolate();
211
212
22
  const int argc = args.Length();
213
22
  CHECK_GE(argc, 2);
214
215
22
  const enum encoding encoding = ParseEncoding(isolate, args[0], UTF8);
216
217
  DirHandle* dir;
218
23
  ASSIGN_OR_RETURN_UNWRAP(&dir, args.Holder());
219
220
22
  FSReqBase* req_wrap_async = static_cast<FSReqBase*>(GetReqWrap(env, args[1]));
221
22
  if (req_wrap_async != nullptr) {  // dir.read(encoding, req)
222
    AsyncCall(env, req_wrap_async, args, "readdir", encoding,
223
16
              AfterDirReadSingle, uv_fs_readdir, dir->dir());
224
  } else {  // dir.read(encoding, undefined, ctx)
225
6
    CHECK_EQ(argc, 3);
226
6
    FSReqWrapSync req_wrap_sync;
227

6
    FS_DIR_SYNC_TRACE_BEGIN(readdir);
228
    int err = SyncCall(env, args[2], &req_wrap_sync, "readdir", uv_fs_readdir,
229
12
                       dir->dir());
230

6
    FS_DIR_SYNC_TRACE_END(readdir);
231
6
    if (err < 0) {
232
      return;  // syscall failed, no need to continue, error info is in ctx
233
    }
234
235
6
    if (req_wrap_sync.req.result == 0) {
236
      // Done
237
      Local<Value> done = Null(isolate);
238
2
      args.GetReturnValue().Set(done);
239
1
      return;
240
    }
241
242
5
    CHECK_GE(req_wrap_sync.req.result, 0);
243
5
    const uv_dirent_t& ent = dir->dir()->dirents[0];
244
245
    Local<Value> error;
246
    MaybeLocal<Value> filename =
247
      StringBytes::Encode(isolate,
248
                          ent.name,
249
                          encoding,
250
5
                          &error);
251
5
    if (filename.IsEmpty()) {
252
      Local<Object> ctx = args[2].As<Object>();
253
      ctx->Set(env->context(), env->error_string(), error).FromJust();
254
      return;
255
    }
256
257
5
    Local<Array> result = Array::New(isolate, 2);
258
5
    result->Set(env->context(),
259
                0,
260
15
                filename.ToLocalChecked()).FromJust();
261
5
    result->Set(env->context(),
262
                1,
263
20
                Integer::New(isolate, ent.type)).FromJust();
264
10
    args.GetReturnValue().Set(result);
265
  }
266
}
267
268
7
void AfterOpenDir(uv_fs_t* req) {
269
7
  FSReqBase* req_wrap = FSReqBase::from_req(req);
270
7
  FSReqAfterScope after(req_wrap, req);
271
272
7
  if (!after.Proceed()) {
273
8
    return;
274
  }
275
276
6
  Environment* env = req_wrap->env();
277
  Local<Value> error;
278
279
6
  uv_dir_t* dir = static_cast<uv_dir_t*>(req->ptr);
280
6
  DirHandle* handle = DirHandle::New(env, dir);
281
282
12
  req_wrap->Resolve(handle->object().As<Value>());
283
}
284
285
10
static void OpenDir(const FunctionCallbackInfo<Value>& args) {
286
10
  Environment* env = Environment::GetCurrent(args);
287
10
  Isolate* isolate = env->isolate();
288
289
10
  const int argc = args.Length();
290
10
  CHECK_GE(argc, 3);
291
292
10
  BufferValue path(isolate, args[0]);
293
10
  CHECK_NOT_NULL(*path);
294
295
10
  const enum encoding encoding = ParseEncoding(isolate, args[1], UTF8);
296
297
10
  FSReqBase* req_wrap_async = static_cast<FSReqBase*>(GetReqWrap(env, args[2]));
298
10
  if (req_wrap_async != nullptr) {  // openDir(path, encoding, req)
299
    AsyncCall(env, req_wrap_async, args, "opendir", encoding, AfterOpenDir,
300
7
              uv_fs_opendir, *path);
301
  } else {  // openDir(path, encoding, undefined, ctx)
302
3
    CHECK_EQ(argc, 4);
303
3
    FSReqWrapSync req_wrap_sync;
304

3
    FS_DIR_SYNC_TRACE_BEGIN(opendir);
305
    int result = SyncCall(env, args[3], &req_wrap_sync, "opendir",
306
6
                          uv_fs_opendir, *path);
307

3
    FS_DIR_SYNC_TRACE_END(opendir);
308
3
    if (result < 0) {
309
11
      return;  // syscall failed, no need to continue, error info is in ctx
310
    }
311
312
2
    uv_fs_t* req = &req_wrap_sync.req;
313
2
    uv_dir_t* dir = static_cast<uv_dir_t*>(req->ptr);
314
2
    DirHandle* handle = DirHandle::New(env, dir);
315
316

6
    args.GetReturnValue().Set(handle->object().As<Value>());
317
9
  }
318
}
319
320
5128
void Initialize(Local<Object> target,
321
                Local<Value> unused,
322
                Local<Context> context,
323
                void* priv) {
324
5128
  Environment* env = Environment::GetCurrent(context);
325
5128
  Isolate* isolate = env->isolate();
326
327
5128
  env->SetMethod(target, "opendir", OpenDir);
328
329
  // Create FunctionTemplate for DirHandle
330
5128
  Local<FunctionTemplate> dir = env->NewFunctionTemplate(DirHandle::New);
331
10256
  dir->Inherit(AsyncWrap::GetConstructorTemplate(env));
332
5128
  env->SetProtoMethod(dir, "read", DirHandle::Read);
333
5128
  env->SetProtoMethod(dir, "close", DirHandle::Close);
334
5128
  Local<ObjectTemplate> dirt = dir->InstanceTemplate();
335
5128
  dirt->SetInternalFieldCount(DirHandle::kDirHandleFieldCount);
336
  Local<String> handleString =
337
5128
       FIXED_ONE_BYTE_STRING(isolate, "DirHandle");
338
5128
  dir->SetClassName(handleString);
339
  target
340
      ->Set(context, handleString,
341
20512
            dir->GetFunction(env->context()).ToLocalChecked())
342
10256
      .FromJust();
343
5128
  env->set_dir_instance_template(dirt);
344
5128
}
345
346
}  // namespace fs_dir
347
348
}  // end namespace node
349
350

19927
NODE_MODULE_CONTEXT_AWARE_INTERNAL(fs_dir, node::fs_dir::Initialize)