GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_api.cc Lines: 530 574 92.3 %
Date: 2022-12-07 04:23:16 Branches: 257 410 62.7 %

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_process.h"
13
#include "node_url.h"
14
#include "threadpoolwork-inl.h"
15
#include "tracing/traced_value.h"
16
#include "util-inl.h"
17
18
#include <atomic>
19
#include <cstring>
20
#include <memory>
21
22
97
node_napi_env__::node_napi_env__(v8::Local<v8::Context> context,
23
97
                                 const std::string& module_filename)
24
97
    : napi_env__(context), filename(module_filename) {
25
97
  CHECK_NOT_NULL(node_env());
26
97
}
27
28
95
void node_napi_env__::DeleteMe() {
29
95
  destructing = true;
30
95
  napi_env__::DeleteMe();
31
95
}
32
33
119308
bool node_napi_env__::can_call_into_js() const {
34
119308
  return node_env()->can_call_into_js();
35
}
36
37
v8::Maybe<bool> node_napi_env__::mark_arraybuffer_as_untransferable(
38
    v8::Local<v8::ArrayBuffer> ab) const {
39
  return ab->SetPrivate(context(),
40
                        node_env()->untransferable_object_private_symbol(),
41
                        v8::True(isolate));
42
}
43
44
1579
void node_napi_env__::CallFinalizer(napi_finalize cb, void* data, void* hint) {
45
1579
  CallFinalizer<true>(cb, data, hint);
46
1579
}
47
48
template <bool enforceUncaughtExceptionPolicy>
49
1579
void node_napi_env__::CallFinalizer(napi_finalize cb, void* data, void* hint) {
50
1579
  if (destructing) {
51
    // we can not defer finalizers when the environment is being destructed.
52
1098
    v8::HandleScope handle_scope(isolate);
53
549
    v8::Context::Scope context_scope(context());
54
549
    CallbackIntoModule<enforceUncaughtExceptionPolicy>(
55
549
        [&](napi_env env) { cb(env, data, hint); });
56
549
    return;
57
  }
58
  // we need to keep the env live until the finalizer has been run
59
  // EnvRefHolder provides an exception safe wrapper to Ref and then
60
  // Unref once the lambda is freed
61
1030
  EnvRefHolder liveEnv(static_cast<napi_env>(this));
62
2060
  node_env()->SetImmediate(
63
1030
      [=, liveEnv = std::move(liveEnv)](node::Environment* node_env) {
64
1030
        node_napi_env__* env = static_cast<node_napi_env__*>(liveEnv.env());
65
2059
        v8::HandleScope handle_scope(env->isolate);
66
1030
        v8::Context::Scope context_scope(env->context());
67
1030
        env->CallbackIntoModule<enforceUncaughtExceptionPolicy>(
68
1030
            [&](napi_env env) { cb(env, data, hint); });
69
      });
70
}
71
72
8
void node_napi_env__::trigger_fatal_exception(v8::Local<v8::Value> local_err) {
73
  v8::Local<v8::Message> local_msg =
74
8
      v8::Exception::CreateMessage(isolate, local_err);
75
8
  node::errors::TriggerUncaughtException(isolate, local_err, local_msg);
76
6
}
77
78
// option enforceUncaughtExceptionPolicy is added for not breaking existing
79
// running n-api add-ons, and should be deprecated in the next major Node.js
80
// release.
81
template <bool enforceUncaughtExceptionPolicy, typename T>
82
233226
void node_napi_env__::CallbackIntoModule(T&& call) {
83
233235
  CallIntoModule(call, [](napi_env env_, v8::Local<v8::Value> local_err) {
84
9
    node_napi_env__* env = static_cast<node_napi_env__*>(env_);
85
9
    node::Environment* node_env = env->node_env();
86




9
    if (!node_env->options()->force_node_api_uncaught_exceptions_policy &&
87
        !enforceUncaughtExceptionPolicy) {
88
2
      ProcessEmitDeprecationWarning(
89
          node_env,
90
          "Uncaught N-API callback exception detected, please run node "
91
          "with option --force-node-api-uncaught-exceptions-policy=true"
92
          "to handle those exceptions properly.",
93
          "DEP0168");
94
2
      return;
95
    }
96
    // If there was an unhandled exception in the complete callback,
97
    // report it as a fatal exception. (There is no JavaScript on the
98
    // callstack that can possibly handle it.)
99
7
    env->trigger_fatal_exception(local_err);
100
  });
101
233222
}
102
103
namespace v8impl {
104
105
namespace {
106
107
class BufferFinalizer : private Finalizer {
108
 public:
109
  // node::Buffer::FreeCallback
110
14
  static void FinalizeBufferCallback(char* data, void* hint) {
111
    std::unique_ptr<BufferFinalizer, Deleter> finalizer{
112
14
        static_cast<BufferFinalizer*>(hint)};
113
14
    finalizer->_finalize_data = data;
114
115
14
    if (finalizer->_finalize_callback == nullptr) return;
116
26
    finalizer->_env->CallFinalizer(finalizer->_finalize_callback,
117
13
                                   finalizer->_finalize_data,
118
13
                                   finalizer->_finalize_hint);
119
  }
120
121
  struct Deleter {
122
14
    void operator()(BufferFinalizer* finalizer) {
123
14
      Finalizer::Delete(finalizer);
124
14
    }
125
  };
126
};
127
128
97
inline napi_env NewEnv(v8::Local<v8::Context> context,
129
                       const std::string& module_filename) {
130
  node_napi_env result;
131
132
97
  result = new node_napi_env__(context, module_filename);
133
  // TODO(addaleax): There was previously code that tried to delete the
134
  // napi_env when its v8::Context was garbage collected;
135
  // However, as long as N-API addons using this napi_env are in place,
136
  // the Context needs to be accessible and alive.
137
  // Ideally, we'd want an on-addon-unload hook that takes care of this
138
  // once all N-API addons using this napi_env are unloaded.
139
  // For now, a per-Environment cleanup hook is the best we can do.
140
97
  result->node_env()->AddCleanupHook(
141
95
      [](void* arg) { static_cast<napi_env>(arg)->Unref(); },
142
      static_cast<void*>(result));
143
144
97
  return result;
145
}
146
147
class ThreadSafeFunction : public node::AsyncResource {
148
 public:
149
20
  ThreadSafeFunction(v8::Local<v8::Function> func,
150
                     v8::Local<v8::Object> resource,
151
                     v8::Local<v8::String> name,
152
                     size_t thread_count_,
153
                     void* context_,
154
                     size_t max_queue_size_,
155
                     node_napi_env env_,
156
                     void* finalize_data_,
157
                     napi_finalize finalize_cb_,
158
                     napi_threadsafe_function_call_js call_js_cb_)
159
40
      : AsyncResource(env_->isolate,
160
                      resource,
161
40
                      *v8::String::Utf8Value(env_->isolate, name)),
162
        thread_count(thread_count_),
163
        is_closing(false),
164
        dispatch_state(kDispatchIdle),
165
        context(context_),
166
        max_queue_size(max_queue_size_),
167
        env(env_),
168
        finalize_data(finalize_data_),
169
        finalize_cb(finalize_cb_),
170
20
        call_js_cb(call_js_cb_ == nullptr ? CallJs : call_js_cb_),
171

60
        handles_closing(false) {
172
20
    ref.Reset(env->isolate, func);
173
20
    node::AddEnvironmentCleanupHook(env->isolate, Cleanup, this);
174
20
    env->Ref();
175
20
  }
176
177
120
  ~ThreadSafeFunction() override {
178
40
    node::RemoveEnvironmentCleanupHook(env->isolate, Cleanup, this);
179
40
    env->Unref();
180
80
  }
181
182
  // These methods can be called from any thread.
183
184
3770850
  napi_status Push(void* data, napi_threadsafe_function_call_mode mode) {
185
7541700
    node::Mutex::ScopedLock lock(this->mutex);
186
187

7483319
    while (queue.size() >= max_queue_size && max_queue_size > 0 &&
188
3684404
           !is_closing) {
189
3684401
      if (mode == napi_tsfn_nonblocking) {
190
3656336
        return napi_queue_full;
191
      }
192
28065
      cond->Wait(lock);
193
    }
194
195
114514
    if (is_closing) {
196
5
      if (thread_count == 0) {
197
        return napi_invalid_arg;
198
      } else {
199
5
        thread_count--;
200
5
        return napi_closing;
201
      }
202
    } else {
203
114509
      queue.push(data);
204
114509
      Send();
205
114509
      return napi_ok;
206
    }
207
  }
208
209
4
  napi_status Acquire() {
210
8
    node::Mutex::ScopedLock lock(this->mutex);
211
212
4
    if (is_closing) {
213
      return napi_closing;
214
    }
215
216
4
    thread_count++;
217
218
4
    return napi_ok;
219
  }
220
221
35
  napi_status Release(napi_threadsafe_function_release_mode mode) {
222
70
    node::Mutex::ScopedLock lock(this->mutex);
223
224
35
    if (thread_count == 0) {
225
      return napi_invalid_arg;
226
    }
227
228
35
    thread_count--;
229
230

35
    if (thread_count == 0 || mode == napi_tsfn_abort) {
231
18
      if (!is_closing) {
232
18
        is_closing = (mode == napi_tsfn_abort);
233

18
        if (is_closing && max_queue_size > 0) {
234
2
          cond->Signal(lock);
235
        }
236
18
        Send();
237
      }
238
    }
239
240
35
    return napi_ok;
241
  }
242
243
27
  void EmptyQueueAndDelete() {
244
27
    for (; !queue.empty(); queue.pop()) {
245
7
      call_js_cb(nullptr, nullptr, context, queue.front());
246
    }
247
20
    delete this;
248
20
  }
249
250
  // These methods must only be called from the loop thread.
251
252
20
  napi_status Init() {
253
20
    ThreadSafeFunction* ts_fn = this;
254
20
    uv_loop_t* loop = env->node_env()->event_loop();
255
256
20
    if (uv_async_init(loop, &async, AsyncCb) == 0) {
257
20
      if (max_queue_size > 0) {
258
12
        cond = std::make_unique<node::ConditionVariable>();
259
      }
260

20
      if (max_queue_size == 0 || cond) {
261
20
        return napi_ok;
262
      }
263
264
      env->node_env()->CloseHandle(
265
          reinterpret_cast<uv_handle_t*>(&async),
266
          [](uv_handle_t* handle) -> void {
267
            ThreadSafeFunction* ts_fn =
268
                node::ContainerOf(&ThreadSafeFunction::async,
269
                                  reinterpret_cast<uv_async_t*>(handle));
270
            delete ts_fn;
271
          });
272
273
      // Prevent the thread-safe function from being deleted here, because
274
      // the callback above will delete it.
275
      ts_fn = nullptr;
276
    }
277
278
    delete ts_fn;
279
280
    return napi_generic_failure;
281
  }
282
283
2
  napi_status Unref() {
284
2
    uv_unref(reinterpret_cast<uv_handle_t*>(&async));
285
286
2
    return napi_ok;
287
  }
288
289
  napi_status Ref() {
290
    uv_ref(reinterpret_cast<uv_handle_t*>(&async));
291
292
    return napi_ok;
293
  }
294
295
16
  inline void* Context() { return context; }
296
297
 protected:
298
380
  void Dispatch() {
299
380
    bool has_more = true;
300
301
    // Limit maximum synchronous iteration count to prevent event loop
302
    // starvation. See `src/node_messaging.cc` for an inspiration.
303
380
    unsigned int iterations_left = kMaxIterationCount;
304

114895
    while (has_more && --iterations_left != 0) {
305
114515
      dispatch_state = kDispatchRunning;
306
114515
      has_more = DispatchOne();
307
308
      // Send() was called while we were executing the JS function
309
114515
      if (dispatch_state.exchange(kDispatchIdle) != kDispatchRunning) {
310
74661
        has_more = true;
311
      }
312
    }
313
314
380
    if (has_more) {
315
84
      Send();
316
    }
317
380
  }
318
319
114515
  bool DispatchOne() {
320
114515
    void* data = nullptr;
321
114515
    bool popped_value = false;
322
114515
    bool has_more = false;
323
324
    {
325
229030
      node::Mutex::ScopedLock lock(this->mutex);
326
114515
      if (is_closing) {
327
3
        CloseHandlesAndMaybeDelete();
328
      } else {
329
114512
        size_t size = queue.size();
330
114512
        if (size > 0) {
331
114502
          data = queue.front();
332
114502
          queue.pop();
333
114502
          popped_value = true;
334

114502
          if (size == max_queue_size && max_queue_size > 0) {
335
83407
            cond->Signal(lock);
336
          }
337
114502
          size--;
338
        }
339
340
114512
        if (size == 0) {
341
4129
          if (thread_count == 0) {
342
15
            is_closing = true;
343
15
            if (max_queue_size > 0) {
344
9
              cond->Signal(lock);
345
            }
346
15
            CloseHandlesAndMaybeDelete();
347
          }
348
        } else {
349
110383
          has_more = true;
350
        }
351
      }
352
    }
353
354
114515
    if (popped_value) {
355
229004
      v8::HandleScope scope(env->isolate);
356
114502
      CallbackScope cb_scope(this);
357
114502
      napi_value js_callback = nullptr;
358
114502
      if (!ref.IsEmpty()) {
359
        v8::Local<v8::Function> js_cb =
360
209000
            v8::Local<v8::Function>::New(env->isolate, ref);
361
104500
        js_callback = v8impl::JsValueFromV8LocalValue(js_cb);
362
      }
363
114502
      env->CallbackIntoModule<false>(
364
114502
          [&](napi_env env) { call_js_cb(env, js_callback, context, data); });
365
    }
366
367
114515
    return has_more;
368
  }
369
370
20
  void Finalize() {
371
40
    v8::HandleScope scope(env->isolate);
372
20
    if (finalize_cb) {
373
20
      CallbackScope cb_scope(this);
374
      // Do not use CallFinalizer since it will defer the invocation, which
375
      // would lead to accessing a deleted ThreadSafeFunction.
376
20
      env->CallbackIntoModule<false>(
377
20
          [&](napi_env env) { finalize_cb(env, finalize_data, context); });
378
    }
379
20
    EmptyQueueAndDelete();
380
20
  }
381
382
20
  void CloseHandlesAndMaybeDelete(bool set_closing = false) {
383
20
    v8::HandleScope scope(env->isolate);
384
20
    if (set_closing) {
385
4
      node::Mutex::ScopedLock lock(this->mutex);
386
2
      is_closing = true;
387
2
      if (max_queue_size > 0) {
388
1
        cond->Signal(lock);
389
      }
390
    }
391
20
    if (handles_closing) {
392
      return;
393
    }
394
20
    handles_closing = true;
395
20
    env->node_env()->CloseHandle(
396
20
        reinterpret_cast<uv_handle_t*>(&async),
397
20
        [](uv_handle_t* handle) -> void {
398
          ThreadSafeFunction* ts_fn =
399
20
              node::ContainerOf(&ThreadSafeFunction::async,
400
20
                                reinterpret_cast<uv_async_t*>(handle));
401
20
          ts_fn->Finalize();
402
20
        });
403
  }
404
405
114611
  void Send() {
406
    // Ask currently running Dispatch() to make one more iteration
407
114611
    unsigned char current_state = dispatch_state.fetch_or(kDispatchPending);
408
114611
    if ((current_state & kDispatchRunning) == kDispatchRunning) {
409
105339
      return;
410
    }
411
412
9272
    CHECK_EQ(0, uv_async_send(&async));
413
  }
414
415
  // Default way of calling into JavaScript. Used when ThreadSafeFunction is
416
  //  without a call_js_cb_.
417
10002
  static void CallJs(napi_env env, napi_value cb, void* context, void* data) {
418

10002
    if (!(env == nullptr || cb == nullptr)) {
419
      napi_value recv;
420
      napi_status status;
421
422
10002
      status = napi_get_undefined(env, &recv);
423
10002
      if (status != napi_ok) {
424
        napi_throw_error(env,
425
                         "ERR_NAPI_TSFN_GET_UNDEFINED",
426
                         "Failed to retrieve undefined value");
427
        return;
428
      }
429
430
10002
      status = napi_call_function(env, recv, cb, 0, nullptr, nullptr);
431

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

485
      modobj->Get(context, node_env->filename_string()).ToLocal(&filename_js) &&
623
194
      filename_js->IsString()) {
624
92
    node::Utf8Value filename(node_env->isolate(), filename_js);
625
626
    // Turn the absolute path into a URL. Currently the absolute path is always
627
    // a file system path.
628
    // TODO(gabrielschulhof): Pass the `filename` through unchanged if/when we
629
    // receive it as a URL already.
630
92
    module_filename = node::url::URL::FromFilePath(filename.ToString()).href();
631
  }
632
633
  // Create a new napi_env for this specific module.
634
97
  napi_env env = v8impl::NewEnv(context, module_filename);
635
636
97
  napi_value _exports = nullptr;
637
97
  env->CallIntoModule([&](napi_env env) {
638
97
    _exports = init(env, v8impl::JsValueFromV8LocalValue(exports));
639
97
  });
640
641
  // If register function returned a non-null exports object different from
642
  // the exports object we passed it, set that as the "exports" property of
643
  // the module.
644
188
  if (_exports != nullptr &&
645

188
      _exports != v8impl::JsValueFromV8LocalValue(exports)) {
646
4
    napi_value _module = v8impl::JsValueFromV8LocalValue(module);
647
4
    napi_set_named_property(env, _module, "exports", _exports);
648
  }
649
}
650
651
namespace node {
652
92
node_module napi_module_to_node_module(const napi_module* mod) {
653
  return {
654
      -1,
655
92
      mod->nm_flags | NM_F_DELETEME,
656
      nullptr,
657
92
      mod->nm_filename,
658
      nullptr,
659
      napi_module_register_cb,
660
92
      mod->nm_modname,
661
      const_cast<napi_module*>(mod),  // priv
662
      nullptr,
663
92
  };
664
}
665
}  // namespace node
666
667
// Registers a NAPI module.
668
89
void NAPI_CDECL napi_module_register(napi_module* mod) {
669
  node::node_module* nm =
670
89
      new node::node_module(node::napi_module_to_node_module(mod));
671
89
  node::node_module_register(nm);
672
89
}
673
674
2
napi_status NAPI_CDECL napi_add_env_cleanup_hook(napi_env env,
675
                                                 napi_cleanup_hook fun,
676
                                                 void* arg) {
677
2
  CHECK_ENV(env);
678
2
  CHECK_ARG(env, fun);
679
680
2
  node::AddEnvironmentCleanupHook(env->isolate, fun, arg);
681
682
2
  return napi_ok;
683
}
684
685
1
napi_status NAPI_CDECL napi_remove_env_cleanup_hook(napi_env env,
686
                                                    napi_cleanup_hook fun,
687
                                                    void* arg) {
688
1
  CHECK_ENV(env);
689
1
  CHECK_ARG(env, fun);
690
691
1
  node::RemoveEnvironmentCleanupHook(env->isolate, fun, arg);
692
693
1
  return napi_ok;
694
}
695
696
struct napi_async_cleanup_hook_handle__ {
697
6
  napi_async_cleanup_hook_handle__(napi_env env,
698
                                   napi_async_cleanup_hook user_hook,
699
                                   void* user_data)
700
6
      : env_(env), user_hook_(user_hook), user_data_(user_data) {
701
6
    handle_ = node::AddEnvironmentCleanupHook(env->isolate, Hook, this);
702
6
    env->Ref();
703
6
  }
704
705
18
  ~napi_async_cleanup_hook_handle__() {
706
6
    node::RemoveEnvironmentCleanupHook(std::move(handle_));
707
6
    if (done_cb_ != nullptr) done_cb_(done_data_);
708
709
    // Release the `env` handle asynchronously since it would be surprising if
710
    // a call to a N-API function would destroy `env` synchronously.
711
12
    static_cast<node_napi_env>(env_)->node_env()->SetImmediate(
712
12
        [env = env_](node::Environment*) { env->Unref(); });
713
6
  }
714
715
4
  static void Hook(void* data, void (*done_cb)(void*), void* done_data) {
716
4
    napi_async_cleanup_hook_handle__* handle =
717
        static_cast<napi_async_cleanup_hook_handle__*>(data);
718
4
    handle->done_cb_ = done_cb;
719
4
    handle->done_data_ = done_data;
720
4
    handle->user_hook_(handle, handle->user_data_);
721
4
  }
722
723
  node::AsyncCleanupHookHandle handle_;
724
  napi_env env_ = nullptr;
725
  napi_async_cleanup_hook user_hook_ = nullptr;
726
  void* user_data_ = nullptr;
727
  void (*done_cb_)(void*) = nullptr;
728
  void* done_data_ = nullptr;
729
};
730
731
napi_status NAPI_CDECL
732
6
napi_add_async_cleanup_hook(napi_env env,
733
                            napi_async_cleanup_hook hook,
734
                            void* arg,
735
                            napi_async_cleanup_hook_handle* remove_handle) {
736
6
  CHECK_ENV(env);
737
6
  CHECK_ARG(env, hook);
738
739
  napi_async_cleanup_hook_handle__* handle =
740
6
      new napi_async_cleanup_hook_handle__(env, hook, arg);
741
742
6
  if (remove_handle != nullptr) *remove_handle = handle;
743
744
6
  return napi_clear_last_error(env);
745
}
746
747
napi_status NAPI_CDECL
748
6
napi_remove_async_cleanup_hook(napi_async_cleanup_hook_handle remove_handle) {
749
6
  if (remove_handle == nullptr) return napi_invalid_arg;
750
751
6
  delete remove_handle;
752
753
6
  return napi_ok;
754
}
755
756
1
napi_status NAPI_CDECL napi_fatal_exception(napi_env env, napi_value err) {
757


2
  NAPI_PREAMBLE(env);
758
1
  CHECK_ARG(env, err);
759
760
1
  v8::Local<v8::Value> local_err = v8impl::V8LocalValueFromJsValue(err);
761
1
  static_cast<node_napi_env>(env)->trigger_fatal_exception(local_err);
762
763
1
  return napi_clear_last_error(env);
764
}
765
766
NAPI_NO_RETURN void NAPI_CDECL napi_fatal_error(const char* location,
767
                                                size_t location_len,
768
                                                const char* message,
769
                                                size_t message_len) {
770
  std::string location_string;
771
  std::string message_string;
772
773
  if (location_len != NAPI_AUTO_LENGTH) {
774
    location_string.assign(const_cast<char*>(location), location_len);
775
  } else {
776
    location_string.assign(const_cast<char*>(location), strlen(location));
777
  }
778
779
  if (message_len != NAPI_AUTO_LENGTH) {
780
    message_string.assign(const_cast<char*>(message), message_len);
781
  } else {
782
    message_string.assign(const_cast<char*>(message), strlen(message));
783
  }
784
785
  node::FatalError(location_string.c_str(), message_string.c_str());
786
}
787
788
napi_status NAPI_CDECL
789
4
napi_open_callback_scope(napi_env env,
790
                         napi_value /** ignored */,
791
                         napi_async_context async_context_handle,
792
                         napi_callback_scope* result) {
793
  // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
794
  // JS exceptions.
795
4
  CHECK_ENV(env);
796
4
  CHECK_ARG(env, result);
797
798
4
  v8impl::AsyncContext* node_async_context =
799
      reinterpret_cast<v8impl::AsyncContext*>(async_context_handle);
800
801
4
  *result = node_async_context->OpenCallbackScope();
802
803
4
  return napi_clear_last_error(env);
804
}
805
806
4
napi_status NAPI_CDECL napi_close_callback_scope(napi_env env,
807
                                                 napi_callback_scope scope) {
808
  // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
809
  // JS exceptions.
810
4
  CHECK_ENV(env);
811
4
  CHECK_ARG(env, scope);
812
4
  if (env->open_callback_scopes == 0) {
813
    return napi_callback_scope_mismatch;
814
  }
815
816
4
  v8impl::AsyncContext::CloseCallbackScope(reinterpret_cast<node_napi_env>(env),
817
                                           scope);
818
819
4
  return napi_clear_last_error(env);
820
}
821
822
13
napi_status NAPI_CDECL napi_async_init(napi_env env,
823
                                       napi_value async_resource,
824
                                       napi_value async_resource_name,
825
                                       napi_async_context* result) {
826
13
  CHECK_ENV(env);
827
13
  CHECK_ARG(env, async_resource_name);
828
13
  CHECK_ARG(env, result);
829
830
13
  v8::Isolate* isolate = env->isolate;
831
13
  v8::Local<v8::Context> context = env->context();
832
833
  v8::Local<v8::Object> v8_resource;
834
  bool externally_managed_resource;
835
13
  if (async_resource != nullptr) {
836

22
    CHECK_TO_OBJECT(env, context, v8_resource, async_resource);
837
11
    externally_managed_resource = true;
838
  } else {
839
2
    v8_resource = v8::Object::New(isolate);
840
2
    externally_managed_resource = false;
841
  }
842
843
  v8::Local<v8::String> v8_resource_name;
844

26
  CHECK_TO_STRING(env, context, v8_resource_name, async_resource_name);
845
846
  v8impl::AsyncContext* async_context =
847
      new v8impl::AsyncContext(reinterpret_cast<node_napi_env>(env),
848
                               v8_resource,
849
                               v8_resource_name,
850
13
                               externally_managed_resource);
851
852
13
  *result = reinterpret_cast<napi_async_context>(async_context);
853
854
13
  return napi_clear_last_error(env);
855
}
856
857
13
napi_status NAPI_CDECL napi_async_destroy(napi_env env,
858
                                          napi_async_context async_context) {
859
13
  CHECK_ENV(env);
860
13
  CHECK_ARG(env, async_context);
861
862
13
  v8impl::AsyncContext* node_async_context =
863
      reinterpret_cast<v8impl::AsyncContext*>(async_context);
864
865
13
  delete node_async_context;
866
867
13
  return napi_clear_last_error(env);
868
}
869
870
25
napi_status NAPI_CDECL napi_make_callback(napi_env env,
871
                                          napi_async_context async_context,
872
                                          napi_value recv,
873
                                          napi_value func,
874
                                          size_t argc,
875
                                          const napi_value* argv,
876
                                          napi_value* result) {
877


50
  NAPI_PREAMBLE(env);
878
25
  CHECK_ARG(env, recv);
879
25
  if (argc > 0) {
880
4
    CHECK_ARG(env, argv);
881
  }
882
883
25
  v8::Local<v8::Context> context = env->context();
884
885
  v8::Local<v8::Object> v8recv;
886

75
  CHECK_TO_OBJECT(env, context, v8recv, recv);
887
888
  v8::Local<v8::Function> v8func;
889

75
  CHECK_TO_FUNCTION(env, v8func, func);
890
891
  v8::MaybeLocal<v8::Value> callback_result;
892
893
25
  if (async_context == nullptr) {
894
    callback_result = node::MakeCallback(
895
17
        env->isolate,
896
        v8recv,
897
        v8func,
898
        argc,
899
        reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)),
900
17
        {0, 0});
901
  } else {
902
8
    v8impl::AsyncContext* node_async_context =
903
        reinterpret_cast<v8impl::AsyncContext*>(async_context);
904
    callback_result = node_async_context->MakeCallback(
905
        v8recv,
906
        v8func,
907
        argc,
908
8
        reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)));
909
  }
910
911
25
  if (try_catch.HasCaught()) {
912
4
    return napi_set_last_error(env, napi_pending_exception);
913
  } else {
914
21
    CHECK_MAYBE_EMPTY(env, callback_result, napi_generic_failure);
915
21
    if (result != nullptr) {
916
8
      *result =
917
8
          v8impl::JsValueFromV8LocalValue(callback_result.ToLocalChecked());
918
    }
919
  }
920
921
21
  return GET_RETURN_STATUS(env);
922
}
923
924
1
napi_status NAPI_CDECL napi_create_buffer(napi_env env,
925
                                          size_t length,
926
                                          void** data,
927
                                          napi_value* result) {
928


2
  NAPI_PREAMBLE(env);
929
1
  CHECK_ARG(env, result);
930
931
1
  v8::MaybeLocal<v8::Object> maybe = node::Buffer::New(env->isolate, length);
932
933
1
  CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
934
935
1
  v8::Local<v8::Object> buffer = maybe.ToLocalChecked();
936
937
1
  *result = v8impl::JsValueFromV8LocalValue(buffer);
938
939
1
  if (data != nullptr) {
940
1
    *data = node::Buffer::Data(buffer);
941
  }
942
943
1
  return GET_RETURN_STATUS(env);
944
}
945
946
14
napi_status NAPI_CDECL napi_create_external_buffer(napi_env env,
947
                                                   size_t length,
948
                                                   void* data,
949
                                                   napi_finalize finalize_cb,
950
                                                   void* finalize_hint,
951
                                                   napi_value* result) {
952


28
  NAPI_PREAMBLE(env);
953
14
  CHECK_ARG(env, result);
954
955
#if defined(V8_ENABLE_SANDBOX)
956
  return napi_set_last_error(env, napi_no_external_buffers_allowed);
957
#endif
958
959
14
  v8::Isolate* isolate = env->isolate;
960
961
  // The finalizer object will delete itself after invoking the callback.
962
  v8impl::Finalizer* finalizer =
963
14
      v8impl::Finalizer::New(env,
964
                             finalize_cb,
965
                             nullptr,
966
                             finalize_hint,
967
                             v8impl::Finalizer::kKeepEnvReference);
968
969
  v8::MaybeLocal<v8::Object> maybe =
970
      node::Buffer::New(isolate,
971
                        static_cast<char*>(data),
972
                        length,
973
                        v8impl::BufferFinalizer::FinalizeBufferCallback,
974
14
                        finalizer);
975
976
14
  CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
977
978
14
  *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked());
979
14
  return GET_RETURN_STATUS(env);
980
  // Tell coverity that 'finalizer' should not be freed when we return
981
  // as it will be deleted when the buffer to which it is associated
982
  // is finalized.
983
  // coverity[leaked_storage]
984
}
985
986
1
napi_status NAPI_CDECL napi_create_buffer_copy(napi_env env,
987
                                               size_t length,
988
                                               const void* data,
989
                                               void** result_data,
990
                                               napi_value* result) {
991


2
  NAPI_PREAMBLE(env);
992
1
  CHECK_ARG(env, result);
993
994
  v8::MaybeLocal<v8::Object> maybe =
995
1
      node::Buffer::Copy(env->isolate, static_cast<const char*>(data), length);
996
997
1
  CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
998
999
1
  v8::Local<v8::Object> buffer = maybe.ToLocalChecked();
1000
1
  *result = v8impl::JsValueFromV8LocalValue(buffer);
1001
1002
1
  if (result_data != nullptr) {
1003
    *result_data = node::Buffer::Data(buffer);
1004
  }
1005
1006
1
  return GET_RETURN_STATUS(env);
1007
}
1008
1009
1
napi_status NAPI_CDECL napi_is_buffer(napi_env env,
1010
                                      napi_value value,
1011
                                      bool* result) {
1012
1
  CHECK_ENV(env);
1013
1
  CHECK_ARG(env, value);
1014
1
  CHECK_ARG(env, result);
1015
1016
1
  *result = node::Buffer::HasInstance(v8impl::V8LocalValueFromJsValue(value));
1017
1
  return napi_clear_last_error(env);
1018
}
1019
1020
1
napi_status NAPI_CDECL napi_get_buffer_info(napi_env env,
1021
                                            napi_value value,
1022
                                            void** data,
1023
                                            size_t* length) {
1024
1
  CHECK_ENV(env);
1025
1
  CHECK_ARG(env, value);
1026
1027
1
  v8::Local<v8::Value> buffer = v8impl::V8LocalValueFromJsValue(value);
1028
1029
1
  if (data != nullptr) {
1030
1
    *data = node::Buffer::Data(buffer);
1031
  }
1032
1
  if (length != nullptr) {
1033
1
    *length = node::Buffer::Length(buffer);
1034
  }
1035
1036
1
  return napi_clear_last_error(env);
1037
}
1038
1039
1
napi_status NAPI_CDECL napi_get_node_version(napi_env env,
1040
                                             const napi_node_version** result) {
1041
1
  CHECK_ENV(env);
1042
1
  CHECK_ARG(env, result);
1043
  static const napi_node_version version = {
1044
      NODE_MAJOR_VERSION, NODE_MINOR_VERSION, NODE_PATCH_VERSION, NODE_RELEASE};
1045
1
  *result = &version;
1046
1
  return napi_clear_last_error(env);
1047
}
1048
1049
namespace {
1050
namespace uvimpl {
1051
1052
513
static napi_status ConvertUVErrorCode(int code) {
1053

513
  switch (code) {
1054
512
    case 0:
1055
512
      return napi_ok;
1056
    case UV_EINVAL:
1057
      return napi_invalid_arg;
1058
1
    case UV_ECANCELED:
1059
1
      return napi_cancelled;
1060
    default:
1061
      return napi_generic_failure;
1062
  }
1063
}
1064
1065
// Wrapper around uv_work_t which calls user-provided callbacks.
1066
class Work : public node::AsyncResource, public node::ThreadPoolWork {
1067
 private:
1068
512
  explicit Work(node_napi_env env,
1069
                v8::Local<v8::Object> async_resource,
1070
                v8::Local<v8::String> async_resource_name,
1071
                napi_async_execute_callback execute,
1072
                napi_async_complete_callback complete = nullptr,
1073
                void* data = nullptr)
1074
512
      : AsyncResource(
1075
512
            env->isolate,
1076
            async_resource,
1077
1024
            *v8::String::Utf8Value(env->isolate, async_resource_name)),
1078
        ThreadPoolWork(env->node_env(), "node_api"),
1079
        _env(env),
1080
        _data(data),
1081
        _execute(execute),
1082
1536
        _complete(complete) {}
1083
1084
2032
  ~Work() override = default;
1085
1086
 public:
1087
512
  static Work* New(node_napi_env env,
1088
                   v8::Local<v8::Object> async_resource,
1089
                   v8::Local<v8::String> async_resource_name,
1090
                   napi_async_execute_callback execute,
1091
                   napi_async_complete_callback complete,
1092
                   void* data) {
1093
    return new Work(
1094
512
        env, async_resource, async_resource_name, execute, complete, data);
1095
  }
1096
1097
508
  static void Delete(Work* work) { delete work; }
1098
1099
511
  void DoThreadPoolWork() override { _execute(_env, _data); }
1100
1101
512
  void AfterThreadPoolWork(int status) override {
1102
512
    if (_complete == nullptr) return;
1103
1104
    // Establish a handle scope here so that every callback doesn't have to.
1105
    // Also it is needed for the exception-handling below.
1106
1023
    v8::HandleScope scope(_env->isolate);
1107
1108
512
    CallbackScope callback_scope(this);
1109
1110
512
    _env->CallbackIntoModule<true>([&](napi_env env) {
1111
512
      _complete(env, ConvertUVErrorCode(status), _data);
1112
512
    });
1113
1114
    // Note: Don't access `work` after this point because it was
1115
    // likely deleted by the complete callback.
1116
  }
1117
1118
 private:
1119
  node_napi_env _env;
1120
  void* _data;
1121
  napi_async_execute_callback _execute;
1122
  napi_async_complete_callback _complete;
1123
};
1124
1125
}  // end of namespace uvimpl
1126
}  // end of anonymous namespace
1127
1128
#define CALL_UV(env, condition)                                                \
1129
  do {                                                                         \
1130
    int result = (condition);                                                  \
1131
    napi_status status = uvimpl::ConvertUVErrorCode(result);                   \
1132
    if (status != napi_ok) {                                                   \
1133
      return napi_set_last_error(env, status, result);                         \
1134
    }                                                                          \
1135
  } while (0)
1136
1137
napi_status NAPI_CDECL
1138
512
napi_create_async_work(napi_env env,
1139
                       napi_value async_resource,
1140
                       napi_value async_resource_name,
1141
                       napi_async_execute_callback execute,
1142
                       napi_async_complete_callback complete,
1143
                       void* data,
1144
                       napi_async_work* result) {
1145
512
  CHECK_ENV(env);
1146
512
  CHECK_ARG(env, execute);
1147
512
  CHECK_ARG(env, result);
1148
1149
512
  v8::Local<v8::Context> context = env->context();
1150
1151
  v8::Local<v8::Object> resource;
1152
512
  if (async_resource != nullptr) {
1153

12
    CHECK_TO_OBJECT(env, context, resource, async_resource);
1154
  } else {
1155
508
    resource = v8::Object::New(env->isolate);
1156
  }
1157
1158
  v8::Local<v8::String> resource_name;
1159

1024
  CHECK_TO_STRING(env, context, resource_name, async_resource_name);
1160
1161
512
  uvimpl::Work* work = uvimpl::Work::New(reinterpret_cast<node_napi_env>(env),
1162
                                         resource,
1163
                                         resource_name,
1164
                                         execute,
1165
                                         complete,
1166
                                         data);
1167
1168
512
  *result = reinterpret_cast<napi_async_work>(work);
1169
1170
512
  return napi_clear_last_error(env);
1171
}
1172
1173
508
napi_status NAPI_CDECL napi_delete_async_work(napi_env env,
1174
                                              napi_async_work work) {
1175
508
  CHECK_ENV(env);
1176
508
  CHECK_ARG(env, work);
1177
1178
508
  uvimpl::Work::Delete(reinterpret_cast<uvimpl::Work*>(work));
1179
1180
508
  return napi_clear_last_error(env);
1181
}
1182
1183
518
napi_status NAPI_CDECL napi_get_uv_event_loop(napi_env env, uv_loop_t** loop) {
1184
518
  CHECK_ENV(env);
1185
518
  CHECK_ARG(env, loop);
1186
518
  *loop = reinterpret_cast<node_napi_env>(env)->node_env()->event_loop();
1187
518
  return napi_clear_last_error(env);
1188
}
1189
1190
512
napi_status NAPI_CDECL napi_queue_async_work(napi_env env,
1191
                                             napi_async_work work) {
1192
512
  CHECK_ENV(env);
1193
512
  CHECK_ARG(env, work);
1194
1195
512
  uv_loop_t* event_loop = nullptr;
1196
512
  STATUS_CALL(napi_get_uv_event_loop(env, &event_loop));
1197
1198
512
  uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work);
1199
1200
512
  w->ScheduleWork();
1201
1202
512
  return napi_clear_last_error(env);
1203
}
1204
1205
1
napi_status NAPI_CDECL napi_cancel_async_work(napi_env env,
1206
                                              napi_async_work work) {
1207
1
  CHECK_ENV(env);
1208
1
  CHECK_ARG(env, work);
1209
1210
1
  uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work);
1211
1212
1
  CALL_UV(env, w->CancelWork());
1213
1214
1
  return napi_clear_last_error(env);
1215
}
1216
1217
napi_status NAPI_CDECL
1218
20
napi_create_threadsafe_function(napi_env env,
1219
                                napi_value func,
1220
                                napi_value async_resource,
1221
                                napi_value async_resource_name,
1222
                                size_t max_queue_size,
1223
                                size_t initial_thread_count,
1224
                                void* thread_finalize_data,
1225
                                napi_finalize thread_finalize_cb,
1226
                                void* context,
1227
                                napi_threadsafe_function_call_js call_js_cb,
1228
                                napi_threadsafe_function* result) {
1229
20
  CHECK_ENV(env);
1230
20
  CHECK_ARG(env, async_resource_name);
1231
20
  RETURN_STATUS_IF_FALSE(env, initial_thread_count > 0, napi_invalid_arg);
1232
20
  CHECK_ARG(env, result);
1233
1234
20
  napi_status status = napi_ok;
1235
1236
  v8::Local<v8::Function> v8_func;
1237
20
  if (func == nullptr) {
1238
3
    CHECK_ARG(env, call_js_cb);
1239
  } else {
1240

51
    CHECK_TO_FUNCTION(env, v8_func, func);
1241
  }
1242
1243
20
  v8::Local<v8::Context> v8_context = env->context();
1244
1245
  v8::Local<v8::Object> v8_resource;
1246
20
  if (async_resource == nullptr) {
1247
18
    v8_resource = v8::Object::New(env->isolate);
1248
  } else {
1249

6
    CHECK_TO_OBJECT(env, v8_context, v8_resource, async_resource);
1250
  }
1251
1252
  v8::Local<v8::String> v8_name;
1253

40
  CHECK_TO_STRING(env, v8_context, v8_name, async_resource_name);
1254
1255
  v8impl::ThreadSafeFunction* ts_fn =
1256
      new v8impl::ThreadSafeFunction(v8_func,
1257
                                     v8_resource,
1258
                                     v8_name,
1259
                                     initial_thread_count,
1260
                                     context,
1261
                                     max_queue_size,
1262
                                     reinterpret_cast<node_napi_env>(env),
1263
                                     thread_finalize_data,
1264
                                     thread_finalize_cb,
1265
20
                                     call_js_cb);
1266
1267
20
  if (ts_fn == nullptr) {
1268
    status = napi_generic_failure;
1269
  } else {
1270
    // Init deletes ts_fn upon failure.
1271
20
    status = ts_fn->Init();
1272
20
    if (status == napi_ok) {
1273
20
      *result = reinterpret_cast<napi_threadsafe_function>(ts_fn);
1274
    }
1275
  }
1276
1277
20
  return napi_set_last_error(env, status);
1278
}
1279
1280
16
napi_status NAPI_CDECL napi_get_threadsafe_function_context(
1281
    napi_threadsafe_function func, void** result) {
1282
16
  CHECK_NOT_NULL(func);
1283
16
  CHECK_NOT_NULL(result);
1284
1285
16
  *result = reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Context();
1286
16
  return napi_ok;
1287
}
1288
1289
napi_status NAPI_CDECL
1290
3770850
napi_call_threadsafe_function(napi_threadsafe_function func,
1291
                              void* data,
1292
                              napi_threadsafe_function_call_mode is_blocking) {
1293
3770850
  CHECK_NOT_NULL(func);
1294
3770850
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Push(data,
1295
3770850
                                                                   is_blocking);
1296
}
1297
1298
napi_status NAPI_CDECL
1299
4
napi_acquire_threadsafe_function(napi_threadsafe_function func) {
1300
4
  CHECK_NOT_NULL(func);
1301
4
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Acquire();
1302
}
1303
1304
35
napi_status NAPI_CDECL napi_release_threadsafe_function(
1305
    napi_threadsafe_function func, napi_threadsafe_function_release_mode mode) {
1306
35
  CHECK_NOT_NULL(func);
1307
35
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Release(mode);
1308
}
1309
1310
napi_status NAPI_CDECL
1311
2
napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
1312
2
  CHECK_NOT_NULL(func);
1313
2
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Unref();
1314
}
1315
1316
napi_status NAPI_CDECL
1317
napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
1318
  CHECK_NOT_NULL(func);
1319
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Ref();
1320
}
1321
1322
2
napi_status NAPI_CDECL node_api_get_module_file_name(napi_env env,
1323
                                                     const char** result) {
1324
2
  CHECK_ENV(env);
1325
2
  CHECK_ARG(env, result);
1326
1327
2
  *result = static_cast<node_napi_env>(env)->GetFilename();
1328
2
  return napi_clear_last_error(env);
1329
}