GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_api.cc Lines: 518 563 92.0 %
Date: 2022-05-23 04:15:47 Branches: 238 390 61.0 %

Line Branch Exec Source
1
#include "async_wrap-inl.h"
2
#include "env-inl.h"
3
#define NAPI_EXPERIMENTAL
4
#include "js_native_api_v8.h"
5
#include "memory_tracker-inl.h"
6
#include "node_api.h"
7
#include "node_api_internals.h"
8
#include "node_binding.h"
9
#include "node_buffer.h"
10
#include "node_errors.h"
11
#include "node_internals.h"
12
#include "node_url.h"
13
#include "threadpoolwork-inl.h"
14
#include "tracing/traced_value.h"
15
#include "util-inl.h"
16
17
#include <atomic>
18
#include <memory>
19
20
92
node_napi_env__::node_napi_env__(v8::Local<v8::Context> context,
21
92
                                 const std::string& module_filename)
22
92
    : napi_env__(context), filename(module_filename) {
23
92
  CHECK_NOT_NULL(node_env());
24
92
}
25
26
119779
bool node_napi_env__::can_call_into_js() const {
27
119779
  return node_env()->can_call_into_js();
28
}
29
30
v8::Maybe<bool> node_napi_env__::mark_arraybuffer_as_untransferable(
31
    v8::Local<v8::ArrayBuffer> ab) const {
32
  return ab->SetPrivate(context(),
33
                        node_env()->untransferable_object_private_symbol(),
34
                        v8::True(isolate));
35
}
36
37
1013
void node_napi_env__::CallFinalizer(napi_finalize cb, void* data, void* hint) {
38
  // we need to keep the env live until the finalizer has been run
39
  // EnvRefHolder provides an exception safe wrapper to Ref and then
40
  // Unref once the lambda is freed
41
1013
  EnvRefHolder liveEnv(static_cast<napi_env>(this));
42
2026
  node_env()->SetImmediate(
43
1013
      [=, liveEnv = std::move(liveEnv)](node::Environment* node_env) {
44
1013
        napi_env env = liveEnv.env();
45
2026
        v8::HandleScope handle_scope(env->isolate);
46
1013
        v8::Context::Scope context_scope(env->context());
47
2026
        env->CallIntoModule([&](napi_env env) { cb(env, data, hint); });
48
1013
      });
49
1013
}
50
51
namespace v8impl {
52
53
namespace {
54
55
class BufferFinalizer : private Finalizer {
56
 public:
57
  // node::Buffer::FreeCallback
58
13
  static void FinalizeBufferCallback(char* data, void* hint) {
59
    std::unique_ptr<BufferFinalizer, Deleter> finalizer{
60
13
        static_cast<BufferFinalizer*>(hint)};
61
13
    finalizer->_finalize_data = data;
62
63
    node::Environment* node_env =
64
13
        static_cast<node_napi_env>(finalizer->_env)->node_env();
65
13
    node_env->SetImmediate(
66
13
        [finalizer = std::move(finalizer)](node::Environment* env) {
67
13
          if (finalizer->_finalize_callback == nullptr) return;
68
69
24
          v8::HandleScope handle_scope(finalizer->_env->isolate);
70
12
          v8::Context::Scope context_scope(finalizer->_env->context());
71
72
12
          finalizer->_env->CallIntoModule([&](napi_env env) {
73
24
            finalizer->_finalize_callback(
74
12
                env, finalizer->_finalize_data, finalizer->_finalize_hint);
75
12
          });
76
        });
77
13
  }
78
79
  struct Deleter {
80
13
    void operator()(BufferFinalizer* finalizer) {
81
13
      Finalizer::Delete(finalizer);
82
13
    }
83
  };
84
};
85
86
92
static inline napi_env NewEnv(v8::Local<v8::Context> context,
87
                              const std::string& module_filename) {
88
  node_napi_env result;
89
90
92
  result = new node_napi_env__(context, module_filename);
91
  // TODO(addaleax): There was previously code that tried to delete the
92
  // napi_env when its v8::Context was garbage collected;
93
  // However, as long as N-API addons using this napi_env are in place,
94
  // the Context needs to be accessible and alive.
95
  // Ideally, we'd want an on-addon-unload hook that takes care of this
96
  // once all N-API addons using this napi_env are unloaded.
97
  // For now, a per-Environment cleanup hook is the best we can do.
98
92
  result->node_env()->AddCleanupHook(
99
90
      [](void* arg) { static_cast<napi_env>(arg)->Unref(); },
100
      static_cast<void*>(result));
101
102
92
  return result;
103
}
104
105
3
static inline void trigger_fatal_exception(napi_env env,
106
                                           v8::Local<v8::Value> local_err) {
107
  v8::Local<v8::Message> local_msg =
108
3
      v8::Exception::CreateMessage(env->isolate, local_err);
109
3
  node::errors::TriggerUncaughtException(env->isolate, local_err, local_msg);
110
2
}
111
112
class ThreadSafeFunction : public node::AsyncResource {
113
 public:
114
18
  ThreadSafeFunction(v8::Local<v8::Function> func,
115
                     v8::Local<v8::Object> resource,
116
                     v8::Local<v8::String> name,
117
                     size_t thread_count_,
118
                     void* context_,
119
                     size_t max_queue_size_,
120
                     node_napi_env env_,
121
                     void* finalize_data_,
122
                     napi_finalize finalize_cb_,
123
                     napi_threadsafe_function_call_js call_js_cb_)
124
36
      : AsyncResource(env_->isolate,
125
                      resource,
126
36
                      *v8::String::Utf8Value(env_->isolate, name)),
127
        thread_count(thread_count_),
128
        is_closing(false),
129
        dispatch_state(kDispatchIdle),
130
        context(context_),
131
        max_queue_size(max_queue_size_),
132
        env(env_),
133
        finalize_data(finalize_data_),
134
        finalize_cb(finalize_cb_),
135
18
        call_js_cb(call_js_cb_ == nullptr ? CallJs : call_js_cb_),
136

54
        handles_closing(false) {
137
18
    ref.Reset(env->isolate, func);
138
18
    node::AddEnvironmentCleanupHook(env->isolate, Cleanup, this);
139
18
    env->Ref();
140
18
  }
141
142
108
  ~ThreadSafeFunction() override {
143
36
    node::RemoveEnvironmentCleanupHook(env->isolate, Cleanup, this);
144
36
    env->Unref();
145
72
  }
146
147
  // These methods can be called from any thread.
148
149
2820459
  napi_status Push(void* data, napi_threadsafe_function_call_mode mode) {
150
5640918
    node::Mutex::ScopedLock lock(this->mutex);
151
152

5629848
    while (queue.size() >= max_queue_size && max_queue_size > 0 &&
153
2757419
           !is_closing) {
154
2757416
      if (mode == napi_tsfn_nonblocking) {
155
2705446
        return napi_queue_full;
156
      }
157
51970
      cond->Wait(lock);
158
    }
159
160
115013
    if (is_closing) {
161
5
      if (thread_count == 0) {
162
        return napi_invalid_arg;
163
      } else {
164
5
        thread_count--;
165
5
        return napi_closing;
166
      }
167
    } else {
168
115008
      queue.push(data);
169
115008
      Send();
170
115008
      return napi_ok;
171
    }
172
  }
173
174
4
  napi_status Acquire() {
175
8
    node::Mutex::ScopedLock lock(this->mutex);
176
177
4
    if (is_closing) {
178
      return napi_closing;
179
    }
180
181
4
    thread_count++;
182
183
4
    return napi_ok;
184
  }
185
186
33
  napi_status Release(napi_threadsafe_function_release_mode mode) {
187
66
    node::Mutex::ScopedLock lock(this->mutex);
188
189
33
    if (thread_count == 0) {
190
      return napi_invalid_arg;
191
    }
192
193
33
    thread_count--;
194
195

33
    if (thread_count == 0 || mode == napi_tsfn_abort) {
196
16
      if (!is_closing) {
197
16
        is_closing = (mode == napi_tsfn_abort);
198

16
        if (is_closing && max_queue_size > 0) {
199
2
          cond->Signal(lock);
200
        }
201
16
        Send();
202
      }
203
    }
204
205
33
    return napi_ok;
206
  }
207
208
25
  void EmptyQueueAndDelete() {
209
25
    for (; !queue.empty(); queue.pop()) {
210
7
      call_js_cb(nullptr, nullptr, context, queue.front());
211
    }
212
18
    delete this;
213
18
  }
214
215
  // These methods must only be called from the loop thread.
216
217
18
  napi_status Init() {
218
18
    ThreadSafeFunction* ts_fn = this;
219
18
    uv_loop_t* loop = env->node_env()->event_loop();
220
221
18
    if (uv_async_init(loop, &async, AsyncCb) == 0) {
222
18
      if (max_queue_size > 0) {
223
12
        cond = std::make_unique<node::ConditionVariable>();
224
      }
225

18
      if (max_queue_size == 0 || cond) {
226
18
        return napi_ok;
227
      }
228
229
      env->node_env()->CloseHandle(
230
          reinterpret_cast<uv_handle_t*>(&async),
231
          [](uv_handle_t* handle) -> void {
232
            ThreadSafeFunction* ts_fn =
233
                node::ContainerOf(&ThreadSafeFunction::async,
234
                                  reinterpret_cast<uv_async_t*>(handle));
235
            delete ts_fn;
236
          });
237
238
      // Prevent the thread-safe function from being deleted here, because
239
      // the callback above will delete it.
240
      ts_fn = nullptr;
241
    }
242
243
    delete ts_fn;
244
245
    return napi_generic_failure;
246
  }
247
248
2
  napi_status Unref() {
249
2
    uv_unref(reinterpret_cast<uv_handle_t*>(&async));
250
251
2
    return napi_ok;
252
  }
253
254
  napi_status Ref() {
255
    uv_ref(reinterpret_cast<uv_handle_t*>(&async));
256
257
    return napi_ok;
258
  }
259
260
16
  inline void* Context() { return context; }
261
262
 protected:
263
163
  void Dispatch() {
264
163
    bool has_more = true;
265
266
    // Limit maximum synchronous iteration count to prevent event loop
267
    // starvation. See `src/node_messaging.cc` for an inspiration.
268
163
    unsigned int iterations_left = kMaxIterationCount;
269

115176
    while (has_more && --iterations_left != 0) {
270
115013
      dispatch_state = kDispatchRunning;
271
115013
      has_more = DispatchOne();
272
273
      // Send() was called while we were executing the JS function
274
115013
      if (dispatch_state.exchange(kDispatchIdle) != kDispatchRunning) {
275
85864
        has_more = true;
276
      }
277
    }
278
279
163
    if (has_more) {
280
109
      Send();
281
    }
282
163
  }
283
284
115013
  bool DispatchOne() {
285
115013
    void* data = nullptr;
286
115013
    bool popped_value = false;
287
115013
    bool has_more = false;
288
289
    {
290
230026
      node::Mutex::ScopedLock lock(this->mutex);
291
115013
      if (is_closing) {
292
3
        CloseHandlesAndMaybeDelete();
293
      } else {
294
115010
        size_t size = queue.size();
295
115010
        if (size > 0) {
296
115001
          data = queue.front();
297
115001
          queue.pop();
298
115001
          popped_value = true;
299

115001
          if (size == max_queue_size && max_queue_size > 0) {
300
87962
            cond->Signal(lock);
301
          }
302
115001
          size--;
303
        }
304
305
115010
        if (size == 0) {
306
72
          if (thread_count == 0) {
307
13
            is_closing = true;
308
13
            if (max_queue_size > 0) {
309
9
              cond->Signal(lock);
310
            }
311
13
            CloseHandlesAndMaybeDelete();
312
          }
313
        } else {
314
114938
          has_more = true;
315
        }
316
      }
317
    }
318
319
115013
    if (popped_value) {
320
230002
      v8::HandleScope scope(env->isolate);
321
115001
      CallbackScope cb_scope(this);
322
115001
      napi_value js_callback = nullptr;
323
115001
      if (!ref.IsEmpty()) {
324
        v8::Local<v8::Function> js_cb =
325
209998
            v8::Local<v8::Function>::New(env->isolate, ref);
326
104999
        js_callback = v8impl::JsValueFromV8LocalValue(js_cb);
327
      }
328
115001
      env->CallIntoModule(
329
115001
          [&](napi_env env) { call_js_cb(env, js_callback, context, data); });
330
    }
331
332
115013
    return has_more;
333
  }
334
335
18
  void Finalize() {
336
36
    v8::HandleScope scope(env->isolate);
337
18
    if (finalize_cb) {
338
18
      CallbackScope cb_scope(this);
339
18
      env->CallIntoModule(
340
18
          [&](napi_env env) { finalize_cb(env, finalize_data, context); });
341
    }
342
18
    EmptyQueueAndDelete();
343
18
  }
344
345
18
  void CloseHandlesAndMaybeDelete(bool set_closing = false) {
346
18
    v8::HandleScope scope(env->isolate);
347
18
    if (set_closing) {
348
4
      node::Mutex::ScopedLock lock(this->mutex);
349
2
      is_closing = true;
350
2
      if (max_queue_size > 0) {
351
1
        cond->Signal(lock);
352
      }
353
    }
354
18
    if (handles_closing) {
355
      return;
356
    }
357
18
    handles_closing = true;
358
18
    env->node_env()->CloseHandle(
359
18
        reinterpret_cast<uv_handle_t*>(&async),
360
18
        [](uv_handle_t* handle) -> void {
361
          ThreadSafeFunction* ts_fn =
362
18
              node::ContainerOf(&ThreadSafeFunction::async,
363
18
                                reinterpret_cast<uv_async_t*>(handle));
364
18
          ts_fn->Finalize();
365
18
        });
366
  }
367
368
115133
  void Send() {
369
    // Ask currently running Dispatch() to make one more iteration
370
115133
    unsigned char current_state = dispatch_state.fetch_or(kDispatchPending);
371
115133
    if ((current_state & kDispatchRunning) == kDispatchRunning) {
372
104338
      return;
373
    }
374
375
10795
    CHECK_EQ(0, uv_async_send(&async));
376
  }
377
378
  // Default way of calling into JavaScript. Used when ThreadSafeFunction is
379
  //  without a call_js_cb_.
380
10000
  static void CallJs(napi_env env, napi_value cb, void* context, void* data) {
381

10000
    if (!(env == nullptr || cb == nullptr)) {
382
      napi_value recv;
383
      napi_status status;
384
385
10000
      status = napi_get_undefined(env, &recv);
386
10000
      if (status != napi_ok) {
387
        napi_throw_error(env,
388
                         "ERR_NAPI_TSFN_GET_UNDEFINED",
389
                         "Failed to retrieve undefined value");
390
        return;
391
      }
392
393
10000
      status = napi_call_function(env, recv, cb, 0, nullptr, nullptr);
394

10000
      if (status != napi_ok && status != napi_pending_exception) {
395
        napi_throw_error(
396
            env, "ERR_NAPI_TSFN_CALL_JS", "Failed to call JS callback");
397
        return;
398
      }
399
    }
400
  }
401
402
163
  static void AsyncCb(uv_async_t* async) {
403
    ThreadSafeFunction* ts_fn =
404
163
        node::ContainerOf(&ThreadSafeFunction::async, async);
405
163
    ts_fn->Dispatch();
406
163
  }
407
408
2
  static void Cleanup(void* data) {
409
2
    reinterpret_cast<ThreadSafeFunction*>(data)->CloseHandlesAndMaybeDelete(
410
        true);
411
2
  }
412
413
 private:
414
  static const unsigned char kDispatchIdle = 0;
415
  static const unsigned char kDispatchRunning = 1 << 0;
416
  static const unsigned char kDispatchPending = 1 << 1;
417
418
  static const unsigned int kMaxIterationCount = 1000;
419
420
  // These are variables protected by the mutex.
421
  node::Mutex mutex;
422
  std::unique_ptr<node::ConditionVariable> cond;
423
  std::queue<void*> queue;
424
  uv_async_t async;
425
  size_t thread_count;
426
  bool is_closing;
427
  std::atomic_uchar dispatch_state;
428
429
  // These are variables set once, upon creation, and then never again, which
430
  // means we don't need the mutex to read them.
431
  void* context;
432
  size_t max_queue_size;
433
434
  // These are variables accessed only from the loop thread.
435
  v8impl::Persistent<v8::Function> ref;
436
  node_napi_env env;
437
  void* finalize_data;
438
  napi_finalize finalize_cb;
439
  napi_threadsafe_function_call_js call_js_cb;
440
  bool handles_closing;
441
};
442
443
/**
444
 * Compared to node::AsyncResource, the resource object in AsyncContext is
445
 * gc-able. AsyncContext holds a weak reference to the resource object.
446
 * AsyncContext::MakeCallback doesn't implicitly set the receiver of the
447
 * callback to the resource object.
448
 */
449
class AsyncContext {
450
 public:
451
13
  AsyncContext(node_napi_env env,
452
               v8::Local<v8::Object> resource_object,
453
               const v8::Local<v8::String> resource_name,
454
               bool externally_managed_resource)
455
13
      : env_(env) {
456
13
    async_id_ = node_env()->new_async_id();
457
13
    trigger_async_id_ = node_env()->get_default_trigger_async_id();
458
13
    resource_.Reset(node_env()->isolate(), resource_object);
459
13
    lost_reference_ = false;
460
13
    if (externally_managed_resource) {
461
11
      resource_.SetWeak(
462
          this, AsyncContext::WeakCallback, v8::WeakCallbackType::kParameter);
463
    }
464
465
13
    node::AsyncWrap::EmitAsyncInit(node_env(),
466
                                   resource_object,
467
                                   resource_name,
468
                                   async_id_,
469
                                   trigger_async_id_);
470
13
  }
471
472
13
  ~AsyncContext() {
473
13
    resource_.Reset();
474
13
    lost_reference_ = true;
475
13
    node::AsyncWrap::EmitDestroy(node_env(), async_id_);
476
13
  }
477
478
8
  inline v8::MaybeLocal<v8::Value> MakeCallback(
479
      v8::Local<v8::Object> recv,
480
      const v8::Local<v8::Function> callback,
481
      int argc,
482
      v8::Local<v8::Value> argv[]) {
483
8
    EnsureReference();
484
    return node::InternalMakeCallback(node_env(),
485
                                      resource(),
486
                                      recv,
487
                                      callback,
488
                                      argc,
489
                                      argv,
490
8
                                      {async_id_, trigger_async_id_});
491
  }
492
493
4
  inline napi_callback_scope OpenCallbackScope() {
494
4
    EnsureReference();
495
    napi_callback_scope it =
496
4
        reinterpret_cast<napi_callback_scope>(new CallbackScope(this));
497
4
    env_->open_callback_scopes++;
498
4
    return it;
499
  }
500
501
12
  inline void EnsureReference() {
502
12
    if (lost_reference_) {
503
1
      const v8::HandleScope handle_scope(node_env()->isolate());
504
1
      resource_.Reset(node_env()->isolate(),
505
1
                      v8::Object::New(node_env()->isolate()));
506
1
      lost_reference_ = false;
507
    }
508
12
  }
509
510
92
  inline node::Environment* node_env() { return env_->node_env(); }
511
8
  inline v8::Local<v8::Object> resource() {
512
16
    return resource_.Get(node_env()->isolate());
513
  }
514
4
  inline node::async_context async_context() {
515
4
    return {async_id_, trigger_async_id_};
516
  }
517
518
4
  static inline void CloseCallbackScope(node_napi_env env,
519
                                        napi_callback_scope s) {
520
4
    CallbackScope* callback_scope = reinterpret_cast<CallbackScope*>(s);
521
4
    delete callback_scope;
522
4
    env->open_callback_scopes--;
523
4
  }
524
525
2
  static void WeakCallback(const v8::WeakCallbackInfo<AsyncContext>& data) {
526
2
    AsyncContext* async_context = data.GetParameter();
527
2
    async_context->resource_.Reset();
528
2
    async_context->lost_reference_ = true;
529
2
  }
530
531
 private:
532
  class CallbackScope : public node::CallbackScope {
533
   public:
534
4
    explicit CallbackScope(AsyncContext* async_context)
535
4
        : node::CallbackScope(async_context->node_env(),
536
                              async_context->resource_.Get(
537
                                  async_context->node_env()->isolate()),
538
8
                              async_context->async_context()) {}
539
  };
540
541
  node_napi_env env_;
542
  double async_id_;
543
  double trigger_async_id_;
544
  v8::Global<v8::Object> resource_;
545
  bool lost_reference_;
546
};
547
548
}  // end of anonymous namespace
549
550
}  // end of namespace v8impl
551
552
// Intercepts the Node-V8 module registration callback. Converts parameters
553
// to NAPI equivalents and then calls the registration callback specified
554
// by the NAPI module.
555
88
static void napi_module_register_cb(v8::Local<v8::Object> exports,
556
                                    v8::Local<v8::Value> module,
557
                                    v8::Local<v8::Context> context,
558
                                    void* priv) {
559
88
  napi_module_register_by_symbol(
560
      exports,
561
      module,
562
      context,
563
88
      static_cast<const napi_module*>(priv)->nm_register_func);
564
88
}
565
566
93
void napi_module_register_by_symbol(v8::Local<v8::Object> exports,
567
                                    v8::Local<v8::Value> module,
568
                                    v8::Local<v8::Context> context,
569
                                    napi_addon_register_func init) {
570
93
  node::Environment* node_env = node::Environment::GetCurrent(context);
571
93
  std::string module_filename = "";
572
93
  if (init == nullptr) {
573
1
    CHECK_NOT_NULL(node_env);
574
1
    node_env->ThrowError("Module has no declared entry point.");
575
1
    return;
576
  }
577
578
  // We set `env->filename` from `module.filename` here, but we could just as
579
  // easily add a private property to `exports` in `process.dlopen`, which
580
  // receives the file name from JS, and retrieve *that* here. Thus, we are not
581
  // endorsing commonjs here by making use of `module.filename`.
582
  v8::Local<v8::Value> filename_js;
583
  v8::Local<v8::Object> modobj;
584
92
  if (module->ToObject(context).ToLocal(&modobj) &&
585

460
      modobj->Get(context, node_env->filename_string()).ToLocal(&filename_js) &&
586
184
      filename_js->IsString()) {
587
87
    node::Utf8Value filename(node_env->isolate(), filename_js);
588
589
    // Turn the absolute path into a URL. Currently the absolute path is always
590
    // a file system path.
591
    // TODO(gabrielschulhof): Pass the `filename` through unchanged if/when we
592
    // receive it as a URL already.
593
87
    module_filename = node::url::URL::FromFilePath(filename.ToString()).href();
594
  }
595
596
  // Create a new napi_env for this specific module.
597
92
  napi_env env = v8impl::NewEnv(context, module_filename);
598
599
  napi_value _exports;
600
92
  env->CallIntoModule([&](napi_env env) {
601
92
    _exports = init(env, v8impl::JsValueFromV8LocalValue(exports));
602
92
  });
603
604
  // If register function returned a non-null exports object different from
605
  // the exports object we passed it, set that as the "exports" property of
606
  // the module.
607
178
  if (_exports != nullptr &&
608

178
      _exports != v8impl::JsValueFromV8LocalValue(exports)) {
609
4
    napi_value _module = v8impl::JsValueFromV8LocalValue(module);
610
4
    napi_set_named_property(env, _module, "exports", _exports);
611
  }
612
}
613
614
namespace node {
615
87
node_module napi_module_to_node_module(const napi_module* mod) {
616
  return {
617
      -1,
618
87
      mod->nm_flags | NM_F_DELETEME,
619
      nullptr,
620
87
      mod->nm_filename,
621
      nullptr,
622
      napi_module_register_cb,
623
87
      mod->nm_modname,
624
      const_cast<napi_module*>(mod),  // priv
625
      nullptr,
626
87
  };
627
}
628
}  // namespace node
629
630
// Registers a NAPI module.
631
84
void NAPI_CDECL napi_module_register(napi_module* mod) {
632
  node::node_module* nm =
633
84
      new node::node_module(node::napi_module_to_node_module(mod));
634
84
  node::node_module_register(nm);
635
84
}
636
637
2
napi_status NAPI_CDECL napi_add_env_cleanup_hook(
638
    napi_env env, void(NAPI_CDECL* fun)(void* arg), void* arg) {
639
2
  CHECK_ENV(env);
640
2
  CHECK_ARG(env, fun);
641
642
2
  node::AddEnvironmentCleanupHook(env->isolate, fun, arg);
643
644
2
  return napi_ok;
645
}
646
647
1
napi_status NAPI_CDECL napi_remove_env_cleanup_hook(
648
    napi_env env, void(NAPI_CDECL* fun)(void* arg), void* arg) {
649
1
  CHECK_ENV(env);
650
1
  CHECK_ARG(env, fun);
651
652
1
  node::RemoveEnvironmentCleanupHook(env->isolate, fun, arg);
653
654
1
  return napi_ok;
655
}
656
657
struct napi_async_cleanup_hook_handle__ {
658
6
  napi_async_cleanup_hook_handle__(napi_env env,
659
                                   napi_async_cleanup_hook user_hook,
660
                                   void* user_data)
661
6
      : env_(env), user_hook_(user_hook), user_data_(user_data) {
662
6
    handle_ = node::AddEnvironmentCleanupHook(env->isolate, Hook, this);
663
6
    env->Ref();
664
6
  }
665
666
18
  ~napi_async_cleanup_hook_handle__() {
667
6
    node::RemoveEnvironmentCleanupHook(std::move(handle_));
668
6
    if (done_cb_ != nullptr) done_cb_(done_data_);
669
670
    // Release the `env` handle asynchronously since it would be surprising if
671
    // a call to a N-API function would destroy `env` synchronously.
672
12
    static_cast<node_napi_env>(env_)->node_env()->SetImmediate(
673
12
        [env = env_](node::Environment*) { env->Unref(); });
674
6
  }
675
676
4
  static void Hook(void* data, void (*done_cb)(void*), void* done_data) {
677
4
    napi_async_cleanup_hook_handle__* handle =
678
        static_cast<napi_async_cleanup_hook_handle__*>(data);
679
4
    handle->done_cb_ = done_cb;
680
4
    handle->done_data_ = done_data;
681
4
    handle->user_hook_(handle, handle->user_data_);
682
4
  }
683
684
  node::AsyncCleanupHookHandle handle_;
685
  napi_env env_ = nullptr;
686
  napi_async_cleanup_hook user_hook_ = nullptr;
687
  void* user_data_ = nullptr;
688
  void (*done_cb_)(void*) = nullptr;
689
  void* done_data_ = nullptr;
690
};
691
692
napi_status NAPI_CDECL
693
6
napi_add_async_cleanup_hook(napi_env env,
694
                            napi_async_cleanup_hook hook,
695
                            void* arg,
696
                            napi_async_cleanup_hook_handle* remove_handle) {
697
6
  CHECK_ENV(env);
698
6
  CHECK_ARG(env, hook);
699
700
  napi_async_cleanup_hook_handle__* handle =
701
6
      new napi_async_cleanup_hook_handle__(env, hook, arg);
702
703
6
  if (remove_handle != nullptr) *remove_handle = handle;
704
705
6
  return napi_clear_last_error(env);
706
}
707
708
napi_status NAPI_CDECL
709
6
napi_remove_async_cleanup_hook(napi_async_cleanup_hook_handle remove_handle) {
710
6
  if (remove_handle == nullptr) return napi_invalid_arg;
711
712
6
  delete remove_handle;
713
714
6
  return napi_ok;
715
}
716
717
1
napi_status NAPI_CDECL napi_fatal_exception(napi_env env, napi_value err) {
718


2
  NAPI_PREAMBLE(env);
719
1
  CHECK_ARG(env, err);
720
721
1
  v8::Local<v8::Value> local_err = v8impl::V8LocalValueFromJsValue(err);
722
1
  v8impl::trigger_fatal_exception(env, local_err);
723
724
1
  return napi_clear_last_error(env);
725
}
726
727
NAPI_NO_RETURN void NAPI_CDECL napi_fatal_error(const char* location,
728
                                                size_t location_len,
729
                                                const char* message,
730
                                                size_t message_len) {
731
  std::string location_string;
732
  std::string message_string;
733
734
  if (location_len != NAPI_AUTO_LENGTH) {
735
    location_string.assign(const_cast<char*>(location), location_len);
736
  } else {
737
    location_string.assign(const_cast<char*>(location), strlen(location));
738
  }
739
740
  if (message_len != NAPI_AUTO_LENGTH) {
741
    message_string.assign(const_cast<char*>(message), message_len);
742
  } else {
743
    message_string.assign(const_cast<char*>(message), strlen(message));
744
  }
745
746
  node::FatalError(location_string.c_str(), message_string.c_str());
747
}
748
749
napi_status NAPI_CDECL
750
4
napi_open_callback_scope(napi_env env,
751
                         napi_value /** ignored */,
752
                         napi_async_context async_context_handle,
753
                         napi_callback_scope* result) {
754
  // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
755
  // JS exceptions.
756
4
  CHECK_ENV(env);
757
4
  CHECK_ARG(env, result);
758
759
4
  v8impl::AsyncContext* node_async_context =
760
      reinterpret_cast<v8impl::AsyncContext*>(async_context_handle);
761
762
4
  *result = node_async_context->OpenCallbackScope();
763
764
4
  return napi_clear_last_error(env);
765
}
766
767
4
napi_status NAPI_CDECL napi_close_callback_scope(napi_env env,
768
                                                 napi_callback_scope scope) {
769
  // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
770
  // JS exceptions.
771
4
  CHECK_ENV(env);
772
4
  CHECK_ARG(env, scope);
773
4
  if (env->open_callback_scopes == 0) {
774
    return napi_callback_scope_mismatch;
775
  }
776
777
4
  v8impl::AsyncContext::CloseCallbackScope(reinterpret_cast<node_napi_env>(env),
778
                                           scope);
779
780
4
  return napi_clear_last_error(env);
781
}
782
783
13
napi_status NAPI_CDECL napi_async_init(napi_env env,
784
                                       napi_value async_resource,
785
                                       napi_value async_resource_name,
786
                                       napi_async_context* result) {
787
13
  CHECK_ENV(env);
788
13
  CHECK_ARG(env, async_resource_name);
789
13
  CHECK_ARG(env, result);
790
791
13
  v8::Isolate* isolate = env->isolate;
792
13
  v8::Local<v8::Context> context = env->context();
793
794
  v8::Local<v8::Object> v8_resource;
795
  bool externally_managed_resource;
796
13
  if (async_resource != nullptr) {
797

22
    CHECK_TO_OBJECT(env, context, v8_resource, async_resource);
798
11
    externally_managed_resource = true;
799
  } else {
800
2
    v8_resource = v8::Object::New(isolate);
801
2
    externally_managed_resource = false;
802
  }
803
804
  v8::Local<v8::String> v8_resource_name;
805

26
  CHECK_TO_STRING(env, context, v8_resource_name, async_resource_name);
806
807
  v8impl::AsyncContext* async_context =
808
      new v8impl::AsyncContext(reinterpret_cast<node_napi_env>(env),
809
                               v8_resource,
810
                               v8_resource_name,
811
13
                               externally_managed_resource);
812
813
13
  *result = reinterpret_cast<napi_async_context>(async_context);
814
815
13
  return napi_clear_last_error(env);
816
}
817
818
13
napi_status NAPI_CDECL napi_async_destroy(napi_env env,
819
                                          napi_async_context async_context) {
820
13
  CHECK_ENV(env);
821
13
  CHECK_ARG(env, async_context);
822
823
13
  v8impl::AsyncContext* node_async_context =
824
      reinterpret_cast<v8impl::AsyncContext*>(async_context);
825
826
13
  delete node_async_context;
827
828
13
  return napi_clear_last_error(env);
829
}
830
831
25
napi_status NAPI_CDECL napi_make_callback(napi_env env,
832
                                          napi_async_context async_context,
833
                                          napi_value recv,
834
                                          napi_value func,
835
                                          size_t argc,
836
                                          const napi_value* argv,
837
                                          napi_value* result) {
838


50
  NAPI_PREAMBLE(env);
839
25
  CHECK_ARG(env, recv);
840
25
  if (argc > 0) {
841
4
    CHECK_ARG(env, argv);
842
  }
843
844
25
  v8::Local<v8::Context> context = env->context();
845
846
  v8::Local<v8::Object> v8recv;
847

75
  CHECK_TO_OBJECT(env, context, v8recv, recv);
848
849
  v8::Local<v8::Function> v8func;
850

75
  CHECK_TO_FUNCTION(env, v8func, func);
851
852
  v8::MaybeLocal<v8::Value> callback_result;
853
854
25
  if (async_context == nullptr) {
855
    callback_result = node::MakeCallback(
856
17
        env->isolate,
857
        v8recv,
858
        v8func,
859
        argc,
860
        reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)),
861
17
        {0, 0});
862
  } else {
863
8
    v8impl::AsyncContext* node_async_context =
864
        reinterpret_cast<v8impl::AsyncContext*>(async_context);
865
    callback_result = node_async_context->MakeCallback(
866
        v8recv,
867
        v8func,
868
        argc,
869
8
        reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)));
870
  }
871
872
25
  if (try_catch.HasCaught()) {
873
4
    return napi_set_last_error(env, napi_pending_exception);
874
  } else {
875
21
    CHECK_MAYBE_EMPTY(env, callback_result, napi_generic_failure);
876
21
    if (result != nullptr) {
877
8
      *result =
878
8
          v8impl::JsValueFromV8LocalValue(callback_result.ToLocalChecked());
879
    }
880
  }
881
882
21
  return GET_RETURN_STATUS(env);
883
}
884
885
1
napi_status NAPI_CDECL napi_create_buffer(napi_env env,
886
                                          size_t length,
887
                                          void** data,
888
                                          napi_value* result) {
889


2
  NAPI_PREAMBLE(env);
890
1
  CHECK_ARG(env, result);
891
892
1
  v8::MaybeLocal<v8::Object> maybe = node::Buffer::New(env->isolate, length);
893
894
1
  CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
895
896
1
  v8::Local<v8::Object> buffer = maybe.ToLocalChecked();
897
898
1
  *result = v8impl::JsValueFromV8LocalValue(buffer);
899
900
1
  if (data != nullptr) {
901
1
    *data = node::Buffer::Data(buffer);
902
  }
903
904
1
  return GET_RETURN_STATUS(env);
905
}
906
907
13
napi_status NAPI_CDECL napi_create_external_buffer(napi_env env,
908
                                                   size_t length,
909
                                                   void* data,
910
                                                   napi_finalize finalize_cb,
911
                                                   void* finalize_hint,
912
                                                   napi_value* result) {
913


26
  NAPI_PREAMBLE(env);
914
13
  CHECK_ARG(env, result);
915
916
13
  v8::Isolate* isolate = env->isolate;
917
918
  // The finalizer object will delete itself after invoking the callback.
919
  v8impl::Finalizer* finalizer =
920
13
      v8impl::Finalizer::New(env,
921
                             finalize_cb,
922
                             nullptr,
923
                             finalize_hint,
924
                             v8impl::Finalizer::kKeepEnvReference);
925
926
  v8::MaybeLocal<v8::Object> maybe =
927
      node::Buffer::New(isolate,
928
                        static_cast<char*>(data),
929
                        length,
930
                        v8impl::BufferFinalizer::FinalizeBufferCallback,
931
13
                        finalizer);
932
933
13
  CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
934
935
13
  *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked());
936
13
  return GET_RETURN_STATUS(env);
937
  // Tell coverity that 'finalizer' should not be freed when we return
938
  // as it will be deleted when the buffer to which it is associated
939
  // is finalized.
940
  // coverity[leaked_storage]
941
}
942
943
1
napi_status NAPI_CDECL napi_create_buffer_copy(napi_env env,
944
                                               size_t length,
945
                                               const void* data,
946
                                               void** result_data,
947
                                               napi_value* result) {
948


2
  NAPI_PREAMBLE(env);
949
1
  CHECK_ARG(env, result);
950
951
  v8::MaybeLocal<v8::Object> maybe =
952
1
      node::Buffer::Copy(env->isolate, static_cast<const char*>(data), length);
953
954
1
  CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
955
956
1
  v8::Local<v8::Object> buffer = maybe.ToLocalChecked();
957
1
  *result = v8impl::JsValueFromV8LocalValue(buffer);
958
959
1
  if (result_data != nullptr) {
960
    *result_data = node::Buffer::Data(buffer);
961
  }
962
963
1
  return GET_RETURN_STATUS(env);
964
}
965
966
1
napi_status NAPI_CDECL napi_is_buffer(napi_env env,
967
                                      napi_value value,
968
                                      bool* result) {
969
1
  CHECK_ENV(env);
970
1
  CHECK_ARG(env, value);
971
1
  CHECK_ARG(env, result);
972
973
1
  *result = node::Buffer::HasInstance(v8impl::V8LocalValueFromJsValue(value));
974
1
  return napi_clear_last_error(env);
975
}
976
977
1
napi_status NAPI_CDECL napi_get_buffer_info(napi_env env,
978
                                            napi_value value,
979
                                            void** data,
980
                                            size_t* length) {
981
1
  CHECK_ENV(env);
982
1
  CHECK_ARG(env, value);
983
984
1
  v8::Local<v8::Value> buffer = v8impl::V8LocalValueFromJsValue(value);
985
986
1
  if (data != nullptr) {
987
1
    *data = node::Buffer::Data(buffer);
988
  }
989
1
  if (length != nullptr) {
990
1
    *length = node::Buffer::Length(buffer);
991
  }
992
993
1
  return napi_clear_last_error(env);
994
}
995
996
1
napi_status NAPI_CDECL napi_get_node_version(napi_env env,
997
                                             const napi_node_version** result) {
998
1
  CHECK_ENV(env);
999
1
  CHECK_ARG(env, result);
1000
  static const napi_node_version version = {
1001
      NODE_MAJOR_VERSION, NODE_MINOR_VERSION, NODE_PATCH_VERSION, NODE_RELEASE};
1002
1
  *result = &version;
1003
1
  return napi_clear_last_error(env);
1004
}
1005
1006
namespace {
1007
namespace uvimpl {
1008
1009
513
static napi_status ConvertUVErrorCode(int code) {
1010

513
  switch (code) {
1011
512
    case 0:
1012
512
      return napi_ok;
1013
    case UV_EINVAL:
1014
      return napi_invalid_arg;
1015
1
    case UV_ECANCELED:
1016
1
      return napi_cancelled;
1017
    default:
1018
      return napi_generic_failure;
1019
  }
1020
}
1021
1022
// Wrapper around uv_work_t which calls user-provided callbacks.
1023
class Work : public node::AsyncResource, public node::ThreadPoolWork {
1024
 private:
1025
512
  explicit Work(node_napi_env env,
1026
                v8::Local<v8::Object> async_resource,
1027
                v8::Local<v8::String> async_resource_name,
1028
                napi_async_execute_callback execute,
1029
                napi_async_complete_callback complete = nullptr,
1030
                void* data = nullptr)
1031
512
      : AsyncResource(
1032
512
            env->isolate,
1033
            async_resource,
1034
1024
            *v8::String::Utf8Value(env->isolate, async_resource_name)),
1035
        ThreadPoolWork(env->node_env()),
1036
        _env(env),
1037
        _data(data),
1038
        _execute(execute),
1039
1536
        _complete(complete) {}
1040
1041
2032
  ~Work() override = default;
1042
1043
 public:
1044
512
  static Work* New(node_napi_env env,
1045
                   v8::Local<v8::Object> async_resource,
1046
                   v8::Local<v8::String> async_resource_name,
1047
                   napi_async_execute_callback execute,
1048
                   napi_async_complete_callback complete,
1049
                   void* data) {
1050
    return new Work(
1051
512
        env, async_resource, async_resource_name, execute, complete, data);
1052
  }
1053
1054
508
  static void Delete(Work* work) { delete work; }
1055
1056
511
  void DoThreadPoolWork() override { _execute(_env, _data); }
1057
1058
512
  void AfterThreadPoolWork(int status) override {
1059
512
    if (_complete == nullptr) return;
1060
1061
    // Establish a handle scope here so that every callback doesn't have to.
1062
    // Also it is needed for the exception-handling below.
1063
1023
    v8::HandleScope scope(_env->isolate);
1064
1065
512
    CallbackScope callback_scope(this);
1066
1067
512
    _env->CallIntoModule(
1068
512
        [&](napi_env env) {
1069
512
          _complete(env, ConvertUVErrorCode(status), _data);
1070
512
        },
1071
2
        [](napi_env env, v8::Local<v8::Value> local_err) {
1072
          // If there was an unhandled exception in the complete callback,
1073
          // report it as a fatal exception. (There is no JavaScript on the
1074
          // callstack that can possibly handle it.)
1075
2
          v8impl::trigger_fatal_exception(env, local_err);
1076
1
        });
1077
1078
    // Note: Don't access `work` after this point because it was
1079
    // likely deleted by the complete callback.
1080
  }
1081
1082
 private:
1083
  node_napi_env _env;
1084
  void* _data;
1085
  napi_async_execute_callback _execute;
1086
  napi_async_complete_callback _complete;
1087
};
1088
1089
}  // end of namespace uvimpl
1090
}  // end of anonymous namespace
1091
1092
#define CALL_UV(env, condition)                                                \
1093
  do {                                                                         \
1094
    int result = (condition);                                                  \
1095
    napi_status status = uvimpl::ConvertUVErrorCode(result);                   \
1096
    if (status != napi_ok) {                                                   \
1097
      return napi_set_last_error(env, status, result);                         \
1098
    }                                                                          \
1099
  } while (0)
1100
1101
napi_status NAPI_CDECL
1102
512
napi_create_async_work(napi_env env,
1103
                       napi_value async_resource,
1104
                       napi_value async_resource_name,
1105
                       napi_async_execute_callback execute,
1106
                       napi_async_complete_callback complete,
1107
                       void* data,
1108
                       napi_async_work* result) {
1109
512
  CHECK_ENV(env);
1110
512
  CHECK_ARG(env, execute);
1111
512
  CHECK_ARG(env, result);
1112
1113
512
  v8::Local<v8::Context> context = env->context();
1114
1115
  v8::Local<v8::Object> resource;
1116
512
  if (async_resource != nullptr) {
1117

12
    CHECK_TO_OBJECT(env, context, resource, async_resource);
1118
  } else {
1119
508
    resource = v8::Object::New(env->isolate);
1120
  }
1121
1122
  v8::Local<v8::String> resource_name;
1123

1024
  CHECK_TO_STRING(env, context, resource_name, async_resource_name);
1124
1125
512
  uvimpl::Work* work = uvimpl::Work::New(reinterpret_cast<node_napi_env>(env),
1126
                                         resource,
1127
                                         resource_name,
1128
                                         execute,
1129
                                         complete,
1130
                                         data);
1131
1132
512
  *result = reinterpret_cast<napi_async_work>(work);
1133
1134
512
  return napi_clear_last_error(env);
1135
}
1136
1137
508
napi_status NAPI_CDECL napi_delete_async_work(napi_env env,
1138
                                              napi_async_work work) {
1139
508
  CHECK_ENV(env);
1140
508
  CHECK_ARG(env, work);
1141
1142
508
  uvimpl::Work::Delete(reinterpret_cast<uvimpl::Work*>(work));
1143
1144
508
  return napi_clear_last_error(env);
1145
}
1146
1147
518
napi_status NAPI_CDECL napi_get_uv_event_loop(napi_env env, uv_loop_t** loop) {
1148
518
  CHECK_ENV(env);
1149
518
  CHECK_ARG(env, loop);
1150
518
  *loop = reinterpret_cast<node_napi_env>(env)->node_env()->event_loop();
1151
518
  return napi_clear_last_error(env);
1152
}
1153
1154
512
napi_status NAPI_CDECL napi_queue_async_work(napi_env env,
1155
                                             napi_async_work work) {
1156
512
  CHECK_ENV(env);
1157
512
  CHECK_ARG(env, work);
1158
1159
512
  uv_loop_t* event_loop = nullptr;
1160
512
  STATUS_CALL(napi_get_uv_event_loop(env, &event_loop));
1161
1162
512
  uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work);
1163
1164
512
  w->ScheduleWork();
1165
1166
512
  return napi_clear_last_error(env);
1167
}
1168
1169
1
napi_status NAPI_CDECL napi_cancel_async_work(napi_env env,
1170
                                              napi_async_work work) {
1171
1
  CHECK_ENV(env);
1172
1
  CHECK_ARG(env, work);
1173
1174
1
  uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work);
1175
1176
1
  CALL_UV(env, w->CancelWork());
1177
1178
1
  return napi_clear_last_error(env);
1179
}
1180
1181
napi_status NAPI_CDECL
1182
18
napi_create_threadsafe_function(napi_env env,
1183
                                napi_value func,
1184
                                napi_value async_resource,
1185
                                napi_value async_resource_name,
1186
                                size_t max_queue_size,
1187
                                size_t initial_thread_count,
1188
                                void* thread_finalize_data,
1189
                                napi_finalize thread_finalize_cb,
1190
                                void* context,
1191
                                napi_threadsafe_function_call_js call_js_cb,
1192
                                napi_threadsafe_function* result) {
1193
18
  CHECK_ENV(env);
1194
18
  CHECK_ARG(env, async_resource_name);
1195
18
  RETURN_STATUS_IF_FALSE(env, initial_thread_count > 0, napi_invalid_arg);
1196
18
  CHECK_ARG(env, result);
1197
1198
18
  napi_status status = napi_ok;
1199
1200
  v8::Local<v8::Function> v8_func;
1201
18
  if (func == nullptr) {
1202
3
    CHECK_ARG(env, call_js_cb);
1203
  } else {
1204

45
    CHECK_TO_FUNCTION(env, v8_func, func);
1205
  }
1206
1207
18
  v8::Local<v8::Context> v8_context = env->context();
1208
1209
  v8::Local<v8::Object> v8_resource;
1210
18
  if (async_resource == nullptr) {
1211
18
    v8_resource = v8::Object::New(env->isolate);
1212
  } else {
1213
    CHECK_TO_OBJECT(env, v8_context, v8_resource, async_resource);
1214
  }
1215
1216
  v8::Local<v8::String> v8_name;
1217

36
  CHECK_TO_STRING(env, v8_context, v8_name, async_resource_name);
1218
1219
  v8impl::ThreadSafeFunction* ts_fn =
1220
      new v8impl::ThreadSafeFunction(v8_func,
1221
                                     v8_resource,
1222
                                     v8_name,
1223
                                     initial_thread_count,
1224
                                     context,
1225
                                     max_queue_size,
1226
                                     reinterpret_cast<node_napi_env>(env),
1227
                                     thread_finalize_data,
1228
                                     thread_finalize_cb,
1229
18
                                     call_js_cb);
1230
1231
18
  if (ts_fn == nullptr) {
1232
    status = napi_generic_failure;
1233
  } else {
1234
    // Init deletes ts_fn upon failure.
1235
18
    status = ts_fn->Init();
1236
18
    if (status == napi_ok) {
1237
18
      *result = reinterpret_cast<napi_threadsafe_function>(ts_fn);
1238
    }
1239
  }
1240
1241
18
  return napi_set_last_error(env, status);
1242
}
1243
1244
16
napi_status NAPI_CDECL napi_get_threadsafe_function_context(
1245
    napi_threadsafe_function func, void** result) {
1246
16
  CHECK_NOT_NULL(func);
1247
16
  CHECK_NOT_NULL(result);
1248
1249
16
  *result = reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Context();
1250
16
  return napi_ok;
1251
}
1252
1253
napi_status NAPI_CDECL
1254
2820459
napi_call_threadsafe_function(napi_threadsafe_function func,
1255
                              void* data,
1256
                              napi_threadsafe_function_call_mode is_blocking) {
1257
2820459
  CHECK_NOT_NULL(func);
1258
2820459
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Push(data,
1259
2820459
                                                                   is_blocking);
1260
}
1261
1262
napi_status NAPI_CDECL
1263
4
napi_acquire_threadsafe_function(napi_threadsafe_function func) {
1264
4
  CHECK_NOT_NULL(func);
1265
4
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Acquire();
1266
}
1267
1268
33
napi_status NAPI_CDECL napi_release_threadsafe_function(
1269
    napi_threadsafe_function func, napi_threadsafe_function_release_mode mode) {
1270
33
  CHECK_NOT_NULL(func);
1271
33
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Release(mode);
1272
}
1273
1274
napi_status NAPI_CDECL
1275
2
napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
1276
2
  CHECK_NOT_NULL(func);
1277
2
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Unref();
1278
}
1279
1280
napi_status NAPI_CDECL
1281
napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
1282
  CHECK_NOT_NULL(func);
1283
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Ref();
1284
}
1285
1286
2
napi_status NAPI_CDECL node_api_get_module_file_name(napi_env env,
1287
                                                     const char** result) {
1288
2
  CHECK_ENV(env);
1289
2
  CHECK_ARG(env, result);
1290
1291
2
  *result = static_cast<node_napi_env>(env)->GetFilename();
1292
2
  return napi_clear_last_error(env);
1293
}