xref: /third_party/node/src/fs_event_wrap.cc (revision 1cb0ef41)
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
29namespace node {
30
31using v8::Context;
32using v8::DontDelete;
33using v8::DontEnum;
34using v8::FunctionCallbackInfo;
35using v8::FunctionTemplate;
36using v8::HandleScope;
37using v8::Integer;
38using v8::Isolate;
39using v8::Local;
40using v8::MaybeLocal;
41using v8::Object;
42using v8::PropertyAttribute;
43using v8::ReadOnly;
44using v8::Signature;
45using v8::String;
46using v8::Value;
47
48namespace {
49
50class FSEventWrap: public HandleWrap {
51 public:
52  static void Initialize(Local<Object> target,
53                         Local<Value> unused,
54                         Local<Context> context,
55                         void* priv);
56  static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
57  static void New(const FunctionCallbackInfo<Value>& args);
58  static void Start(const FunctionCallbackInfo<Value>& args);
59  static void GetInitialized(const FunctionCallbackInfo<Value>& args);
60
61  SET_NO_MEMORY_INFO()
62  SET_MEMORY_INFO_NAME(FSEventWrap)
63  SET_SELF_SIZE(FSEventWrap)
64
65 private:
66  static const encoding kDefaultEncoding = UTF8;
67
68  FSEventWrap(Environment* env, Local<Object> object);
69  ~FSEventWrap() override = default;
70
71  static void OnEvent(uv_fs_event_t* handle, const char* filename, int events,
72    int status);
73
74  uv_fs_event_t handle_;
75  enum encoding encoding_ = kDefaultEncoding;
76};
77
78
79FSEventWrap::FSEventWrap(Environment* env, Local<Object> object)
80    : HandleWrap(env,
81                 object,
82                 reinterpret_cast<uv_handle_t*>(&handle_),
83                 AsyncWrap::PROVIDER_FSEVENTWRAP) {
84  MarkAsUninitialized();
85}
86
87
88void FSEventWrap::GetInitialized(const FunctionCallbackInfo<Value>& args) {
89  FSEventWrap* wrap = Unwrap<FSEventWrap>(args.This());
90  CHECK_NOT_NULL(wrap);
91  args.GetReturnValue().Set(!wrap->IsHandleClosing());
92}
93
94void FSEventWrap::Initialize(Local<Object> target,
95                             Local<Value> unused,
96                             Local<Context> context,
97                             void* priv) {
98  Environment* env = Environment::GetCurrent(context);
99  Isolate* isolate = env->isolate();
100
101  Local<FunctionTemplate> t = NewFunctionTemplate(isolate, New);
102  t->InstanceTemplate()->SetInternalFieldCount(
103      FSEventWrap::kInternalFieldCount);
104
105  t->Inherit(HandleWrap::GetConstructorTemplate(env));
106  SetProtoMethod(isolate, t, "start", Start);
107
108  Local<FunctionTemplate> get_initialized_templ =
109      FunctionTemplate::New(env->isolate(),
110                            GetInitialized,
111                            Local<Value>(),
112                            Signature::New(env->isolate(), t));
113
114  t->PrototypeTemplate()->SetAccessorProperty(
115      FIXED_ONE_BYTE_STRING(env->isolate(), "initialized"),
116      get_initialized_templ,
117      Local<FunctionTemplate>(),
118      static_cast<PropertyAttribute>(ReadOnly | DontDelete | DontEnum));
119
120  SetConstructorFunction(context, target, "FSEvent", t);
121}
122
123void FSEventWrap::RegisterExternalReferences(
124    ExternalReferenceRegistry* registry) {
125  registry->Register(New);
126  registry->Register(Start);
127  registry->Register(GetInitialized);
128}
129
130void FSEventWrap::New(const FunctionCallbackInfo<Value>& args) {
131  CHECK(args.IsConstructCall());
132  Environment* env = Environment::GetCurrent(args);
133  new FSEventWrap(env, args.This());
134}
135
136// wrap.start(filename, persistent, recursive, encoding)
137void FSEventWrap::Start(const FunctionCallbackInfo<Value>& args) {
138  Environment* env = Environment::GetCurrent(args);
139
140  FSEventWrap* wrap = Unwrap<FSEventWrap>(args.This());
141  CHECK_NOT_NULL(wrap);
142  CHECK(wrap->IsHandleClosing());  // Check that Start() has not been called.
143
144  const int argc = args.Length();
145  CHECK_GE(argc, 4);
146
147  BufferValue path(env->isolate(), args[0]);
148  CHECK_NOT_NULL(*path);
149
150  unsigned int flags = 0;
151  if (args[2]->IsTrue())
152    flags |= UV_FS_EVENT_RECURSIVE;
153
154  wrap->encoding_ = ParseEncoding(env->isolate(), args[3], kDefaultEncoding);
155
156  int err = uv_fs_event_init(wrap->env()->event_loop(), &wrap->handle_);
157  if (err != 0) {
158    return args.GetReturnValue().Set(err);
159  }
160
161  err = uv_fs_event_start(&wrap->handle_, OnEvent, *path, flags);
162  wrap->MarkAsInitialized();
163
164  if (err != 0) {
165    FSEventWrap::Close(args);
166    return args.GetReturnValue().Set(err);
167  }
168
169  // Check for persistent argument
170  if (!args[1]->IsTrue()) {
171    uv_unref(reinterpret_cast<uv_handle_t*>(&wrap->handle_));
172  }
173
174  args.GetReturnValue().Set(err);
175}
176
177
178void FSEventWrap::OnEvent(uv_fs_event_t* handle, const char* filename,
179    int events, int status) {
180  FSEventWrap* wrap = static_cast<FSEventWrap*>(handle->data);
181  Environment* env = wrap->env();
182
183  HandleScope handle_scope(env->isolate());
184  Context::Scope context_scope(env->context());
185
186  CHECK_EQ(wrap->persistent().IsEmpty(), false);
187
188  // We're in a bind here. libuv can set both UV_RENAME and UV_CHANGE but
189  // the Node API only lets us pass a single event to JS land.
190  //
191  // The obvious solution is to run the callback twice, once for each event.
192  // However, since the second event is not allowed to fire if the handle is
193  // closed after the first event, and since there is no good way to detect
194  // closed handles, that option is out.
195  //
196  // For now, ignore the UV_CHANGE event if UV_RENAME is also set. Make the
197  // assumption that a rename implicitly means an attribute change. Not too
198  // unreasonable, right? Still, we should revisit this before v1.0.
199  Local<String> event_string;
200  if (status) {
201    event_string = String::Empty(env->isolate());
202  } else if (events & UV_RENAME) {
203    event_string = env->rename_string();
204  } else if (events & UV_CHANGE) {
205    event_string = env->change_string();
206  } else {
207    UNREACHABLE("bad fs events flag");
208  }
209
210  Local<Value> argv[] = {
211    Integer::New(env->isolate(), status),
212    event_string,
213    Null(env->isolate())
214  };
215
216  if (filename != nullptr) {
217    Local<Value> error;
218    MaybeLocal<Value> fn = StringBytes::Encode(env->isolate(),
219                                               filename,
220                                               wrap->encoding_,
221                                               &error);
222    if (fn.IsEmpty()) {
223      argv[0] = Integer::New(env->isolate(), UV_EINVAL);
224      argv[2] = StringBytes::Encode(env->isolate(),
225                                    filename,
226                                    strlen(filename),
227                                    BUFFER,
228                                    &error).ToLocalChecked();
229    } else {
230      argv[2] = fn.ToLocalChecked();
231    }
232  }
233
234  wrap->MakeCallback(env->onchange_string(), arraysize(argv), argv);
235}
236
237}  // anonymous namespace
238}  // namespace node
239
240NODE_BINDING_CONTEXT_AWARE_INTERNAL(fs_event_wrap,
241                                    node::FSEventWrap::Initialize)
242NODE_BINDING_EXTERNAL_REFERENCE(fs_event_wrap,
243                                node::FSEventWrap::RegisterExternalReferences)
244