GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: fs_event_wrap.cc Lines: 76 83 91.6 %
Date: 2022-06-06 04:15:48 Branches: 22 36 61.1 %

Line Branch Exec Source
1
// Copyright Joyent, Inc. and other Node contributors.
2
//
3
// Permission is hereby granted, free of charge, to any person obtaining a
4
// copy of this software and associated documentation files (the
5
// "Software"), to deal in the Software without restriction, including
6
// without limitation the rights to use, copy, modify, merge, publish,
7
// distribute, sublicense, and/or sell copies of the Software, and to permit
8
// persons to whom the Software is furnished to do so, subject to the
9
// following conditions:
10
//
11
// The above copyright notice and this permission notice shall be included
12
// in all copies or substantial portions of the Software.
13
//
14
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20
// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22
#include "async_wrap-inl.h"
23
#include "env-inl.h"
24
#include "handle_wrap.h"
25
#include "node.h"
26
#include "node_external_reference.h"
27
#include "string_bytes.h"
28
29
namespace node {
30
31
using v8::Context;
32
using v8::DontDelete;
33
using v8::DontEnum;
34
using v8::FunctionCallbackInfo;
35
using v8::FunctionTemplate;
36
using v8::HandleScope;
37
using v8::Integer;
38
using v8::Local;
39
using v8::MaybeLocal;
40
using v8::Object;
41
using v8::PropertyAttribute;
42
using v8::ReadOnly;
43
using v8::Signature;
44
using v8::String;
45
using v8::Value;
46
47
namespace {
48
49
class FSEventWrap: public HandleWrap {
50
 public:
51
  static void Initialize(Local<Object> target,
52
                         Local<Value> unused,
53
                         Local<Context> context,
54
                         void* priv);
55
  static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
56
  static void New(const FunctionCallbackInfo<Value>& args);
57
  static void Start(const FunctionCallbackInfo<Value>& args);
58
  static void GetInitialized(const FunctionCallbackInfo<Value>& args);
59
60
1
  SET_NO_MEMORY_INFO()
61
1
  SET_MEMORY_INFO_NAME(FSEventWrap)
62
1
  SET_SELF_SIZE(FSEventWrap)
63
64
 private:
65
  static const encoding kDefaultEncoding = UTF8;
66
67
  FSEventWrap(Environment* env, Local<Object> object);
68
136
  ~FSEventWrap() override = default;
69
70
  static void OnEvent(uv_fs_event_t* handle, const char* filename, int events,
71
    int status);
72
73
  uv_fs_event_t handle_;
74
  enum encoding encoding_ = kDefaultEncoding;
75
};
76
77
78
34
FSEventWrap::FSEventWrap(Environment* env, Local<Object> object)
79
    : HandleWrap(env,
80
                 object,
81
34
                 reinterpret_cast<uv_handle_t*>(&handle_),
82
34
                 AsyncWrap::PROVIDER_FSEVENTWRAP) {
83
34
  MarkAsUninitialized();
84
34
}
85
86
87
44
void FSEventWrap::GetInitialized(const FunctionCallbackInfo<Value>& args) {
88
44
  FSEventWrap* wrap = Unwrap<FSEventWrap>(args.This());
89
44
  CHECK_NOT_NULL(wrap);
90
44
  args.GetReturnValue().Set(!wrap->IsHandleClosing());
91
44
}
92
93
854
void FSEventWrap::Initialize(Local<Object> target,
94
                             Local<Value> unused,
95
                             Local<Context> context,
96
                             void* priv) {
97
854
  Environment* env = Environment::GetCurrent(context);
98
99
854
  Local<FunctionTemplate> t = env->NewFunctionTemplate(New);
100
1708
  t->InstanceTemplate()->SetInternalFieldCount(
101
      FSEventWrap::kInternalFieldCount);
102
103
854
  t->Inherit(HandleWrap::GetConstructorTemplate(env));
104
854
  env->SetProtoMethod(t, "start", Start);
105
106
  Local<FunctionTemplate> get_initialized_templ =
107
      FunctionTemplate::New(env->isolate(),
108
                            GetInitialized,
109
                            Local<Value>(),
110
854
                            Signature::New(env->isolate(), t));
111
112
3416
  t->PrototypeTemplate()->SetAccessorProperty(
113
      FIXED_ONE_BYTE_STRING(env->isolate(), "initialized"),
114
      get_initialized_templ,
115
      Local<FunctionTemplate>(),
116
      static_cast<PropertyAttribute>(ReadOnly | DontDelete | DontEnum));
117
118
854
  env->SetConstructorFunction(target, "FSEvent", t);
119
854
}
120
121
5206
void FSEventWrap::RegisterExternalReferences(
122
    ExternalReferenceRegistry* registry) {
123
5206
  registry->Register(New);
124
5206
  registry->Register(Start);
125
5206
  registry->Register(GetInitialized);
126
5206
}
127
128
34
void FSEventWrap::New(const FunctionCallbackInfo<Value>& args) {
129
34
  CHECK(args.IsConstructCall());
130
34
  Environment* env = Environment::GetCurrent(args);
131
34
  new FSEventWrap(env, args.This());
132
34
}
133
134
// wrap.start(filename, persistent, recursive, encoding)
135
24
void FSEventWrap::Start(const FunctionCallbackInfo<Value>& args) {
136
24
  Environment* env = Environment::GetCurrent(args);
137
138
24
  FSEventWrap* wrap = Unwrap<FSEventWrap>(args.This());
139
24
  CHECK_NOT_NULL(wrap);
140
24
  CHECK(wrap->IsHandleClosing());  // Check that Start() has not been called.
141
142
24
  const int argc = args.Length();
143
24
  CHECK_GE(argc, 4);
144
145
24
  BufferValue path(env->isolate(), args[0]);
146
24
  CHECK_NOT_NULL(*path);
147
148
24
  unsigned int flags = 0;
149
24
  if (args[2]->IsTrue())
150
    flags |= UV_FS_EVENT_RECURSIVE;
151
152
24
  wrap->encoding_ = ParseEncoding(env->isolate(), args[3], kDefaultEncoding);
153
154
24
  int err = uv_fs_event_init(wrap->env()->event_loop(), &wrap->handle_);
155
24
  if (err != 0) {
156
    return args.GetReturnValue().Set(err);
157
  }
158
159
24
  err = uv_fs_event_start(&wrap->handle_, OnEvent, *path, flags);
160
24
  wrap->MarkAsInitialized();
161
162
24
  if (err != 0) {
163
1
    FSEventWrap::Close(args);
164
2
    return args.GetReturnValue().Set(err);
165
  }
166
167
  // Check for persistent argument
168
23
  if (!args[1]->IsTrue()) {
169
2
    uv_unref(reinterpret_cast<uv_handle_t*>(&wrap->handle_));
170
  }
171
172
46
  args.GetReturnValue().Set(err);
173
}
174
175
176
13
void FSEventWrap::OnEvent(uv_fs_event_t* handle, const char* filename,
177
    int events, int status) {
178
13
  FSEventWrap* wrap = static_cast<FSEventWrap*>(handle->data);
179
13
  Environment* env = wrap->env();
180
181
26
  HandleScope handle_scope(env->isolate());
182
13
  Context::Scope context_scope(env->context());
183
184
13
  CHECK_EQ(wrap->persistent().IsEmpty(), false);
185
186
  // We're in a bind here. libuv can set both UV_RENAME and UV_CHANGE but
187
  // the Node API only lets us pass a single event to JS land.
188
  //
189
  // The obvious solution is to run the callback twice, once for each event.
190
  // However, since the second event is not allowed to fire if the handle is
191
  // closed after the first event, and since there is no good way to detect
192
  // closed handles, that option is out.
193
  //
194
  // For now, ignore the UV_CHANGE event if UV_RENAME is also set. Make the
195
  // assumption that a rename implicitly means an attribute change. Not too
196
  // unreasonable, right? Still, we should revisit this before v1.0.
197
  Local<String> event_string;
198
13
  if (status) {
199
    event_string = String::Empty(env->isolate());
200
13
  } else if (events & UV_RENAME) {
201
7
    event_string = env->rename_string();
202
6
  } else if (events & UV_CHANGE) {
203
6
    event_string = env->change_string();
204
  } else {
205
    CHECK(0 && "bad fs events flag");
206
  }
207
208
  Local<Value> argv[] = {
209
    Integer::New(env->isolate(), status),
210
    event_string,
211
    Null(env->isolate())
212
26
  };
213
214
13
  if (filename != nullptr) {
215
    Local<Value> error;
216
    MaybeLocal<Value> fn = StringBytes::Encode(env->isolate(),
217
                                               filename,
218
                                               wrap->encoding_,
219
13
                                               &error);
220
13
    if (fn.IsEmpty()) {
221
      argv[0] = Integer::New(env->isolate(), UV_EINVAL);
222
      argv[2] = StringBytes::Encode(env->isolate(),
223
                                    filename,
224
                                    strlen(filename),
225
                                    BUFFER,
226
                                    &error).ToLocalChecked();
227
    } else {
228
13
      argv[2] = fn.ToLocalChecked();
229
    }
230
  }
231
232
13
  wrap->MakeCallback(env->onchange_string(), arraysize(argv), argv);
233
13
}
234
235
}  // anonymous namespace
236
}  // namespace node
237
238
5274
NODE_MODULE_CONTEXT_AWARE_INTERNAL(fs_event_wrap, node::FSEventWrap::Initialize)
239
5206
NODE_MODULE_EXTERNAL_REFERENCE(fs_event_wrap,
240
                               node::FSEventWrap::RegisterExternalReferences)