GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_api.cc Lines: 545 589 92.5 %
Date: 2022-12-31 04:22:30 Branches: 266 422 63.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_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
  DrainFinalizerQueue();
31
95
  napi_env__::DeleteMe();
32
95
}
33
34
120069
bool node_napi_env__::can_call_into_js() const {
35
120069
  return node_env()->can_call_into_js();
36
}
37
38
v8::Maybe<bool> node_napi_env__::mark_arraybuffer_as_untransferable(
39
    v8::Local<v8::ArrayBuffer> ab) const {
40
  return ab->SetPrivate(context(),
41
                        node_env()->untransferable_object_private_symbol(),
42
                        v8::True(isolate));
43
}
44
45
1588
void node_napi_env__::CallFinalizer(napi_finalize cb, void* data, void* hint) {
46
1588
  CallFinalizer<true>(cb, data, hint);
47
1587
}
48
49
template <bool enforceUncaughtExceptionPolicy>
50
3216
void node_napi_env__::CallFinalizer(napi_finalize cb, void* data, void* hint) {
51
6430
  v8::HandleScope handle_scope(isolate);
52
3216
  v8::Context::Scope context_scope(context());
53
9648
  CallbackIntoModule<enforceUncaughtExceptionPolicy>(
54
1608
      [&](napi_env env) { cb(env, data, hint); });
55
3214
}
56
57
2024
void node_napi_env__::EnqueueFinalizer(v8impl::RefTracker* finalizer) {
58
2024
  napi_env__::EnqueueFinalizer(finalizer);
59
  // Schedule a second pass only when it has not been scheduled, and not
60
  // destructing the env.
61
  // When the env is being destructed, queued finalizers are drained in the
62
  // loop of `node_napi_env__::DrainFinalizerQueue`.
63

2024
  if (!finalization_scheduled && !destructing) {
64
18
    finalization_scheduled = true;
65
18
    Ref();
66
18
    node_env()->SetImmediate([this](node::Environment* node_env) {
67
18
      finalization_scheduled = false;
68
18
      Unref();
69
18
      DrainFinalizerQueue();
70
17
    });
71
  }
72
2024
}
73
74
1645
void node_napi_env__::DrainFinalizerQueue() {
75
  // As userland code can delete additional references in one finalizer,
76
  // the list of pending finalizers may be mutated as we execute them, so
77
  // we keep iterating it until it is empty.
78
1645
  while (!pending_finalizers.empty()) {
79
1533
    v8impl::RefTracker* ref_tracker = *pending_finalizers.begin();
80
1533
    pending_finalizers.erase(ref_tracker);
81
1533
    ref_tracker->Finalize();
82
  }
83
112
}
84
85
8
void node_napi_env__::trigger_fatal_exception(v8::Local<v8::Value> local_err) {
86
  v8::Local<v8::Message> local_msg =
87
8
      v8::Exception::CreateMessage(isolate, local_err);
88
8
  node::errors::TriggerUncaughtException(isolate, local_err, local_msg);
89
6
}
90
91
// option enforceUncaughtExceptionPolicy is added for not breaking existing
92
// running n-api add-ons, and should be deprecated in the next major Node.js
93
// release.
94
template <bool enforceUncaughtExceptionPolicy, typename T>
95
232272
void node_napi_env__::CallbackIntoModule(T&& call) {
96
232281
  CallIntoModule(call, [](napi_env env_, v8::Local<v8::Value> local_err) {
97
9
    node_napi_env__* env = static_cast<node_napi_env__*>(env_);
98


9
    if (env->terminatedOrTerminating()) {
99
      return;
100
    }
101
9
    node::Environment* node_env = env->node_env();
102




9
    if (!node_env->options()->force_node_api_uncaught_exceptions_policy &&
103
        !enforceUncaughtExceptionPolicy) {
104
2
      ProcessEmitDeprecationWarning(
105
          node_env,
106
          "Uncaught N-API callback exception detected, please run node "
107
          "with option --force-node-api-uncaught-exceptions-policy=true"
108
          "to handle those exceptions properly.",
109
          "DEP0168");
110
2
      return;
111
    }
112
    // If there was an unhandled exception in the complete callback,
113
    // report it as a fatal exception. (There is no JavaScript on the
114
    // callstack that can possibly handle it.)
115
7
    env->trigger_fatal_exception(local_err);
116
  });
117
232268
}
118
119
namespace v8impl {
120
121
namespace {
122
123
class BufferFinalizer : private Finalizer {
124
 public:
125
14
  static BufferFinalizer* New(napi_env env,
126
                              napi_finalize finalize_callback = nullptr,
127
                              void* finalize_data = nullptr,
128
                              void* finalize_hint = nullptr) {
129
    return new BufferFinalizer(
130
14
        env, finalize_callback, finalize_data, finalize_hint);
131
  }
132
  // node::Buffer::FreeCallback
133
14
  static void FinalizeBufferCallback(char* data, void* hint) {
134
    std::unique_ptr<BufferFinalizer, Deleter> finalizer{
135
14
        static_cast<BufferFinalizer*>(hint)};
136
14
    finalizer->finalize_data_ = data;
137
138
    // It is safe to call into JavaScript at this point.
139
14
    if (finalizer->finalize_callback_ == nullptr) return;
140
26
    finalizer->env_->CallFinalizer(finalizer->finalize_callback_,
141
13
                                   finalizer->finalize_data_,
142
13
                                   finalizer->finalize_hint_);
143
  }
144
145
  struct Deleter {
146
14
    void operator()(BufferFinalizer* finalizer) { delete finalizer; }
147
  };
148
149
 private:
150
14
  BufferFinalizer(napi_env env,
151
                  napi_finalize finalize_callback,
152
                  void* finalize_data,
153
                  void* finalize_hint)
154
14
      : Finalizer(env, finalize_callback, finalize_data, finalize_hint) {
155
14
    env_->Ref();
156
14
  }
157
158
56
  ~BufferFinalizer() { env_->Unref(); }
159
};
160
161
97
inline napi_env NewEnv(v8::Local<v8::Context> context,
162
                       const std::string& module_filename) {
163
  node_napi_env result;
164
165
97
  result = new node_napi_env__(context, module_filename);
166
  // TODO(addaleax): There was previously code that tried to delete the
167
  // napi_env when its v8::Context was garbage collected;
168
  // However, as long as N-API addons using this napi_env are in place,
169
  // the Context needs to be accessible and alive.
170
  // Ideally, we'd want an on-addon-unload hook that takes care of this
171
  // once all N-API addons using this napi_env are unloaded.
172
  // For now, a per-Environment cleanup hook is the best we can do.
173
97
  result->node_env()->AddCleanupHook(
174
95
      [](void* arg) { static_cast<napi_env>(arg)->Unref(); },
175
      static_cast<void*>(result));
176
177
97
  return result;
178
}
179
180
class ThreadSafeFunction : public node::AsyncResource {
181
 public:
182
20
  ThreadSafeFunction(v8::Local<v8::Function> func,
183
                     v8::Local<v8::Object> resource,
184
                     v8::Local<v8::String> name,
185
                     size_t thread_count_,
186
                     void* context_,
187
                     size_t max_queue_size_,
188
                     node_napi_env env_,
189
                     void* finalize_data_,
190
                     napi_finalize finalize_cb_,
191
                     napi_threadsafe_function_call_js call_js_cb_)
192
40
      : AsyncResource(env_->isolate,
193
                      resource,
194
40
                      *v8::String::Utf8Value(env_->isolate, name)),
195
        thread_count(thread_count_),
196
        is_closing(false),
197
        dispatch_state(kDispatchIdle),
198
        context(context_),
199
        max_queue_size(max_queue_size_),
200
        env(env_),
201
        finalize_data(finalize_data_),
202
        finalize_cb(finalize_cb_),
203
20
        call_js_cb(call_js_cb_ == nullptr ? CallJs : call_js_cb_),
204

60
        handles_closing(false) {
205
20
    ref.Reset(env->isolate, func);
206
20
    node::AddEnvironmentCleanupHook(env->isolate, Cleanup, this);
207
20
    env->Ref();
208
20
  }
209
210
120
  ~ThreadSafeFunction() override {
211
40
    node::RemoveEnvironmentCleanupHook(env->isolate, Cleanup, this);
212
40
    env->Unref();
213
80
  }
214
215
  // These methods can be called from any thread.
216
217
4558270
  napi_status Push(void* data, napi_threadsafe_function_call_mode mode) {
218
9116540
    node::Mutex::ScopedLock lock(this->mutex);
219
220

9068791
    while (queue.size() >= max_queue_size && max_queue_size > 0 &&
221
4477383
           !is_closing) {
222
4477380
      if (mode == napi_tsfn_nonblocking) {
223
4444242
        return napi_queue_full;
224
      }
225
33138
      cond->Wait(lock);
226
    }
227
228
114028
    if (is_closing) {
229
5
      if (thread_count == 0) {
230
        return napi_invalid_arg;
231
      } else {
232
5
        thread_count--;
233
5
        return napi_closing;
234
      }
235
    } else {
236
114023
      queue.push(data);
237
114023
      Send();
238
114023
      return napi_ok;
239
    }
240
  }
241
242
4
  napi_status Acquire() {
243
8
    node::Mutex::ScopedLock lock(this->mutex);
244
245
4
    if (is_closing) {
246
      return napi_closing;
247
    }
248
249
4
    thread_count++;
250
251
4
    return napi_ok;
252
  }
253
254
35
  napi_status Release(napi_threadsafe_function_release_mode mode) {
255
70
    node::Mutex::ScopedLock lock(this->mutex);
256
257
35
    if (thread_count == 0) {
258
      return napi_invalid_arg;
259
    }
260
261
35
    thread_count--;
262
263

35
    if (thread_count == 0 || mode == napi_tsfn_abort) {
264
18
      if (!is_closing) {
265
18
        is_closing = (mode == napi_tsfn_abort);
266

18
        if (is_closing && max_queue_size > 0) {
267
2
          cond->Signal(lock);
268
        }
269
18
        Send();
270
      }
271
    }
272
273
35
    return napi_ok;
274
  }
275
276
27
  void EmptyQueueAndDelete() {
277
27
    for (; !queue.empty(); queue.pop()) {
278
7
      call_js_cb(nullptr, nullptr, context, queue.front());
279
    }
280
20
    delete this;
281
20
  }
282
283
  // These methods must only be called from the loop thread.
284
285
20
  napi_status Init() {
286
20
    ThreadSafeFunction* ts_fn = this;
287
20
    uv_loop_t* loop = env->node_env()->event_loop();
288
289
20
    if (uv_async_init(loop, &async, AsyncCb) == 0) {
290
20
      if (max_queue_size > 0) {
291
12
        cond = std::make_unique<node::ConditionVariable>();
292
      }
293

20
      if (max_queue_size == 0 || cond) {
294
20
        return napi_ok;
295
      }
296
297
      env->node_env()->CloseHandle(
298
          reinterpret_cast<uv_handle_t*>(&async),
299
          [](uv_handle_t* handle) -> void {
300
            ThreadSafeFunction* ts_fn =
301
                node::ContainerOf(&ThreadSafeFunction::async,
302
                                  reinterpret_cast<uv_async_t*>(handle));
303
            delete ts_fn;
304
          });
305
306
      // Prevent the thread-safe function from being deleted here, because
307
      // the callback above will delete it.
308
      ts_fn = nullptr;
309
    }
310
311
    delete ts_fn;
312
313
    return napi_generic_failure;
314
  }
315
316
2
  napi_status Unref() {
317
2
    uv_unref(reinterpret_cast<uv_handle_t*>(&async));
318
319
2
    return napi_ok;
320
  }
321
322
  napi_status Ref() {
323
    uv_ref(reinterpret_cast<uv_handle_t*>(&async));
324
325
    return napi_ok;
326
  }
327
328
16
  inline void* Context() { return context; }
329
330
 protected:
331
510
  void Dispatch() {
332
510
    bool has_more = true;
333
334
    // Limit maximum synchronous iteration count to prevent event loop
335
    // starvation. See `src/node_messaging.cc` for an inspiration.
336
510
    unsigned int iterations_left = kMaxIterationCount;
337

114538
    while (has_more && --iterations_left != 0) {
338
114028
      dispatch_state = kDispatchRunning;
339
114028
      has_more = DispatchOne();
340
341
      // Send() was called while we were executing the JS function
342
114028
      if (dispatch_state.exchange(kDispatchIdle) != kDispatchRunning) {
343
71399
        has_more = true;
344
      }
345
    }
346
347
510
    if (has_more) {
348
90
      Send();
349
    }
350
510
  }
351
352
114028
  bool DispatchOne() {
353
114028
    void* data = nullptr;
354
114028
    bool popped_value = false;
355
114028
    bool has_more = false;
356
357
    {
358
228056
      node::Mutex::ScopedLock lock(this->mutex);
359
114028
      if (is_closing) {
360
4
        CloseHandlesAndMaybeDelete();
361
      } else {
362
114024
        size_t size = queue.size();
363
114024
        if (size > 0) {
364
114016
          data = queue.front();
365
114016
          queue.pop();
366
114016
          popped_value = true;
367

114016
          if (size == max_queue_size && max_queue_size > 0) {
368
82772
            cond->Signal(lock);
369
          }
370
114016
          size--;
371
        }
372
373
114024
        if (size == 0) {
374
4276
          if (thread_count == 0) {
375
15
            is_closing = true;
376
15
            if (max_queue_size > 0) {
377
9
              cond->Signal(lock);
378
            }
379
15
            CloseHandlesAndMaybeDelete();
380
          }
381
        } else {
382
109748
          has_more = true;
383
        }
384
      }
385
    }
386
387
114028
    if (popped_value) {
388
228032
      v8::HandleScope scope(env->isolate);
389
114016
      CallbackScope cb_scope(this);
390
114016
      napi_value js_callback = nullptr;
391
114016
      if (!ref.IsEmpty()) {
392
        v8::Local<v8::Function> js_cb =
393
208028
            v8::Local<v8::Function>::New(env->isolate, ref);
394
104014
        js_callback = v8impl::JsValueFromV8LocalValue(js_cb);
395
      }
396
114016
      env->CallbackIntoModule<false>(
397
114016
          [&](napi_env env) { call_js_cb(env, js_callback, context, data); });
398
    }
399
400
114028
    return has_more;
401
  }
402
403
20
  void Finalize() {
404
40
    v8::HandleScope scope(env->isolate);
405
20
    if (finalize_cb) {
406
40
      CallbackScope cb_scope(this);
407
20
      env->CallFinalizer<false>(finalize_cb, finalize_data, context);
408
    }
409
20
    EmptyQueueAndDelete();
410
20
  }
411
412
21
  void CloseHandlesAndMaybeDelete(bool set_closing = false) {
413
21
    v8::HandleScope scope(env->isolate);
414
21
    if (set_closing) {
415
4
      node::Mutex::ScopedLock lock(this->mutex);
416
2
      is_closing = true;
417
2
      if (max_queue_size > 0) {
418
1
        cond->Signal(lock);
419
      }
420
    }
421
21
    if (handles_closing) {
422
1
      return;
423
    }
424
20
    handles_closing = true;
425
20
    env->node_env()->CloseHandle(
426
20
        reinterpret_cast<uv_handle_t*>(&async),
427
20
        [](uv_handle_t* handle) -> void {
428
          ThreadSafeFunction* ts_fn =
429
20
              node::ContainerOf(&ThreadSafeFunction::async,
430
20
                                reinterpret_cast<uv_async_t*>(handle));
431
20
          ts_fn->Finalize();
432
20
        });
433
  }
434
435
114131
  void Send() {
436
    // Ask currently running Dispatch() to make one more iteration
437
114131
    unsigned char current_state = dispatch_state.fetch_or(kDispatchPending);
438
114131
    if ((current_state & kDispatchRunning) == kDispatchRunning) {
439
105041
      return;
440
    }
441
442
9090
    CHECK_EQ(0, uv_async_send(&async));
443
  }
444
445
  // Default way of calling into JavaScript. Used when ThreadSafeFunction is
446
  //  without a call_js_cb_.
447
10002
  static void CallJs(napi_env env, napi_value cb, void* context, void* data) {
448

10002
    if (!(env == nullptr || cb == nullptr)) {
449
      napi_value recv;
450
      napi_status status;
451
452
10002
      status = napi_get_undefined(env, &recv);
453
10002
      if (status != napi_ok) {
454
        napi_throw_error(env,
455
                         "ERR_NAPI_TSFN_GET_UNDEFINED",
456
                         "Failed to retrieve undefined value");
457
        return;
458
      }
459
460
10002
      status = napi_call_function(env, recv, cb, 0, nullptr, nullptr);
461

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

485
      modobj->Get(context, node_env->filename_string()).ToLocal(&filename_js) &&
653
194
      filename_js->IsString()) {
654
93
    node::Utf8Value filename(node_env->isolate(), filename_js);
655
656
    // Turn the absolute path into a URL. Currently the absolute path is always
657
    // a file system path.
658
    // TODO(gabrielschulhof): Pass the `filename` through unchanged if/when we
659
    // receive it as a URL already.
660
93
    module_filename = node::url::URL::FromFilePath(filename.ToString()).href();
661
  }
662
663
  // Create a new napi_env for this specific module.
664
97
  napi_env env = v8impl::NewEnv(context, module_filename);
665
666
97
  napi_value _exports = nullptr;
667
97
  env->CallIntoModule([&](napi_env env) {
668
97
    _exports = init(env, v8impl::JsValueFromV8LocalValue(exports));
669
97
  });
670
671
  // If register function returned a non-null exports object different from
672
  // the exports object we passed it, set that as the "exports" property of
673
  // the module.
674
188
  if (_exports != nullptr &&
675

188
      _exports != v8impl::JsValueFromV8LocalValue(exports)) {
676
4
    napi_value _module = v8impl::JsValueFromV8LocalValue(module);
677
4
    napi_set_named_property(env, _module, "exports", _exports);
678
  }
679
}
680
681
namespace node {
682
93
node_module napi_module_to_node_module(const napi_module* mod) {
683
  return {
684
      -1,
685
93
      mod->nm_flags | NM_F_DELETEME,
686
      nullptr,
687
93
      mod->nm_filename,
688
      nullptr,
689
      napi_module_register_cb,
690
93
      mod->nm_modname,
691
      const_cast<napi_module*>(mod),  // priv
692
      nullptr,
693
93
  };
694
}
695
}  // namespace node
696
697
// Registers a NAPI module.
698
90
void NAPI_CDECL napi_module_register(napi_module* mod) {
699
  node::node_module* nm =
700
90
      new node::node_module(node::napi_module_to_node_module(mod));
701
90
  node::node_module_register(nm);
702
90
}
703
704
2
napi_status NAPI_CDECL napi_add_env_cleanup_hook(napi_env env,
705
                                                 napi_cleanup_hook fun,
706
                                                 void* arg) {
707
2
  CHECK_ENV(env);
708
2
  CHECK_ARG(env, fun);
709
710
2
  node::AddEnvironmentCleanupHook(env->isolate, fun, arg);
711
712
2
  return napi_ok;
713
}
714
715
1
napi_status NAPI_CDECL napi_remove_env_cleanup_hook(napi_env env,
716
                                                    napi_cleanup_hook fun,
717
                                                    void* arg) {
718
1
  CHECK_ENV(env);
719
1
  CHECK_ARG(env, fun);
720
721
1
  node::RemoveEnvironmentCleanupHook(env->isolate, fun, arg);
722
723
1
  return napi_ok;
724
}
725
726
struct napi_async_cleanup_hook_handle__ {
727
6
  napi_async_cleanup_hook_handle__(napi_env env,
728
                                   napi_async_cleanup_hook user_hook,
729
                                   void* user_data)
730
6
      : env_(env), user_hook_(user_hook), user_data_(user_data) {
731
6
    handle_ = node::AddEnvironmentCleanupHook(env->isolate, Hook, this);
732
6
    env->Ref();
733
6
  }
734
735
18
  ~napi_async_cleanup_hook_handle__() {
736
6
    node::RemoveEnvironmentCleanupHook(std::move(handle_));
737
6
    if (done_cb_ != nullptr) done_cb_(done_data_);
738
739
    // Release the `env` handle asynchronously since it would be surprising if
740
    // a call to a N-API function would destroy `env` synchronously.
741
12
    static_cast<node_napi_env>(env_)->node_env()->SetImmediate(
742
12
        [env = env_](node::Environment*) { env->Unref(); });
743
6
  }
744
745
4
  static void Hook(void* data, void (*done_cb)(void*), void* done_data) {
746
4
    napi_async_cleanup_hook_handle__* handle =
747
        static_cast<napi_async_cleanup_hook_handle__*>(data);
748
4
    handle->done_cb_ = done_cb;
749
4
    handle->done_data_ = done_data;
750
4
    handle->user_hook_(handle, handle->user_data_);
751
4
  }
752
753
  node::AsyncCleanupHookHandle handle_;
754
  napi_env env_ = nullptr;
755
  napi_async_cleanup_hook user_hook_ = nullptr;
756
  void* user_data_ = nullptr;
757
  void (*done_cb_)(void*) = nullptr;
758
  void* done_data_ = nullptr;
759
};
760
761
napi_status NAPI_CDECL
762
6
napi_add_async_cleanup_hook(napi_env env,
763
                            napi_async_cleanup_hook hook,
764
                            void* arg,
765
                            napi_async_cleanup_hook_handle* remove_handle) {
766
6
  CHECK_ENV(env);
767
6
  CHECK_ARG(env, hook);
768
769
  napi_async_cleanup_hook_handle__* handle =
770
6
      new napi_async_cleanup_hook_handle__(env, hook, arg);
771
772
6
  if (remove_handle != nullptr) *remove_handle = handle;
773
774
6
  return napi_clear_last_error(env);
775
}
776
777
napi_status NAPI_CDECL
778
6
napi_remove_async_cleanup_hook(napi_async_cleanup_hook_handle remove_handle) {
779
6
  if (remove_handle == nullptr) return napi_invalid_arg;
780
781
6
  delete remove_handle;
782
783
6
  return napi_ok;
784
}
785
786
1
napi_status NAPI_CDECL napi_fatal_exception(napi_env env, napi_value err) {
787


2
  NAPI_PREAMBLE(env);
788
1
  CHECK_ARG(env, err);
789
790
1
  v8::Local<v8::Value> local_err = v8impl::V8LocalValueFromJsValue(err);
791
1
  static_cast<node_napi_env>(env)->trigger_fatal_exception(local_err);
792
793
1
  return napi_clear_last_error(env);
794
}
795
796
NAPI_NO_RETURN void NAPI_CDECL napi_fatal_error(const char* location,
797
                                                size_t location_len,
798
                                                const char* message,
799
                                                size_t message_len) {
800
  std::string location_string;
801
  std::string message_string;
802
803
  if (location_len != NAPI_AUTO_LENGTH) {
804
    location_string.assign(const_cast<char*>(location), location_len);
805
  } else {
806
    location_string.assign(const_cast<char*>(location), strlen(location));
807
  }
808
809
  if (message_len != NAPI_AUTO_LENGTH) {
810
    message_string.assign(const_cast<char*>(message), message_len);
811
  } else {
812
    message_string.assign(const_cast<char*>(message), strlen(message));
813
  }
814
815
  node::FatalError(location_string.c_str(), message_string.c_str());
816
}
817
818
napi_status NAPI_CDECL
819
4
napi_open_callback_scope(napi_env env,
820
                         napi_value /** ignored */,
821
                         napi_async_context async_context_handle,
822
                         napi_callback_scope* result) {
823
  // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
824
  // JS exceptions.
825
4
  CHECK_ENV(env);
826
4
  CHECK_ARG(env, result);
827
828
4
  v8impl::AsyncContext* node_async_context =
829
      reinterpret_cast<v8impl::AsyncContext*>(async_context_handle);
830
831
4
  *result = node_async_context->OpenCallbackScope();
832
833
4
  return napi_clear_last_error(env);
834
}
835
836
4
napi_status NAPI_CDECL napi_close_callback_scope(napi_env env,
837
                                                 napi_callback_scope scope) {
838
  // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
839
  // JS exceptions.
840
4
  CHECK_ENV(env);
841
4
  CHECK_ARG(env, scope);
842
4
  if (env->open_callback_scopes == 0) {
843
    return napi_callback_scope_mismatch;
844
  }
845
846
4
  v8impl::AsyncContext::CloseCallbackScope(reinterpret_cast<node_napi_env>(env),
847
                                           scope);
848
849
4
  return napi_clear_last_error(env);
850
}
851
852
13
napi_status NAPI_CDECL napi_async_init(napi_env env,
853
                                       napi_value async_resource,
854
                                       napi_value async_resource_name,
855
                                       napi_async_context* result) {
856
13
  CHECK_ENV(env);
857
13
  CHECK_ARG(env, async_resource_name);
858
13
  CHECK_ARG(env, result);
859
860
13
  v8::Isolate* isolate = env->isolate;
861
13
  v8::Local<v8::Context> context = env->context();
862
863
  v8::Local<v8::Object> v8_resource;
864
  bool externally_managed_resource;
865
13
  if (async_resource != nullptr) {
866

22
    CHECK_TO_OBJECT(env, context, v8_resource, async_resource);
867
11
    externally_managed_resource = true;
868
  } else {
869
2
    v8_resource = v8::Object::New(isolate);
870
2
    externally_managed_resource = false;
871
  }
872
873
  v8::Local<v8::String> v8_resource_name;
874

26
  CHECK_TO_STRING(env, context, v8_resource_name, async_resource_name);
875
876
  v8impl::AsyncContext* async_context =
877
      new v8impl::AsyncContext(reinterpret_cast<node_napi_env>(env),
878
                               v8_resource,
879
                               v8_resource_name,
880
13
                               externally_managed_resource);
881
882
13
  *result = reinterpret_cast<napi_async_context>(async_context);
883
884
13
  return napi_clear_last_error(env);
885
}
886
887
13
napi_status NAPI_CDECL napi_async_destroy(napi_env env,
888
                                          napi_async_context async_context) {
889
13
  CHECK_ENV(env);
890
13
  CHECK_ARG(env, async_context);
891
892
13
  v8impl::AsyncContext* node_async_context =
893
      reinterpret_cast<v8impl::AsyncContext*>(async_context);
894
895
13
  delete node_async_context;
896
897
13
  return napi_clear_last_error(env);
898
}
899
900
25
napi_status NAPI_CDECL napi_make_callback(napi_env env,
901
                                          napi_async_context async_context,
902
                                          napi_value recv,
903
                                          napi_value func,
904
                                          size_t argc,
905
                                          const napi_value* argv,
906
                                          napi_value* result) {
907


50
  NAPI_PREAMBLE(env);
908
25
  CHECK_ARG(env, recv);
909
25
  if (argc > 0) {
910
4
    CHECK_ARG(env, argv);
911
  }
912
913
25
  v8::Local<v8::Context> context = env->context();
914
915
  v8::Local<v8::Object> v8recv;
916

75
  CHECK_TO_OBJECT(env, context, v8recv, recv);
917
918
  v8::Local<v8::Function> v8func;
919

75
  CHECK_TO_FUNCTION(env, v8func, func);
920
921
  v8::MaybeLocal<v8::Value> callback_result;
922
923
25
  if (async_context == nullptr) {
924
    callback_result = node::MakeCallback(
925
17
        env->isolate,
926
        v8recv,
927
        v8func,
928
        argc,
929
        reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)),
930
17
        {0, 0});
931
  } else {
932
8
    v8impl::AsyncContext* node_async_context =
933
        reinterpret_cast<v8impl::AsyncContext*>(async_context);
934
    callback_result = node_async_context->MakeCallback(
935
        v8recv,
936
        v8func,
937
        argc,
938
8
        reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)));
939
  }
940
941
25
  if (try_catch.HasCaught()) {
942
4
    return napi_set_last_error(env, napi_pending_exception);
943
  } else {
944
21
    CHECK_MAYBE_EMPTY(env, callback_result, napi_generic_failure);
945
21
    if (result != nullptr) {
946
8
      *result =
947
8
          v8impl::JsValueFromV8LocalValue(callback_result.ToLocalChecked());
948
    }
949
  }
950
951
21
  return GET_RETURN_STATUS(env);
952
}
953
954
1
napi_status NAPI_CDECL napi_create_buffer(napi_env env,
955
                                          size_t length,
956
                                          void** data,
957
                                          napi_value* result) {
958


2
  NAPI_PREAMBLE(env);
959
1
  CHECK_ARG(env, result);
960
961
1
  v8::MaybeLocal<v8::Object> maybe = node::Buffer::New(env->isolate, length);
962
963
1
  CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
964
965
1
  v8::Local<v8::Object> buffer = maybe.ToLocalChecked();
966
967
1
  *result = v8impl::JsValueFromV8LocalValue(buffer);
968
969
1
  if (data != nullptr) {
970
1
    *data = node::Buffer::Data(buffer);
971
  }
972
973
1
  return GET_RETURN_STATUS(env);
974
}
975
976
14
napi_status NAPI_CDECL napi_create_external_buffer(napi_env env,
977
                                                   size_t length,
978
                                                   void* data,
979
                                                   napi_finalize finalize_cb,
980
                                                   void* finalize_hint,
981
                                                   napi_value* result) {
982


28
  NAPI_PREAMBLE(env);
983
14
  CHECK_ARG(env, result);
984
985
#if defined(V8_ENABLE_SANDBOX)
986
  return napi_set_last_error(env, napi_no_external_buffers_allowed);
987
#endif
988
989
14
  v8::Isolate* isolate = env->isolate;
990
991
  // The finalizer object will delete itself after invoking the callback.
992
  v8impl::BufferFinalizer* finalizer =
993
14
      v8impl::BufferFinalizer::New(env, finalize_cb, nullptr, finalize_hint);
994
995
  v8::MaybeLocal<v8::Object> maybe =
996
      node::Buffer::New(isolate,
997
                        static_cast<char*>(data),
998
                        length,
999
                        v8impl::BufferFinalizer::FinalizeBufferCallback,
1000
14
                        finalizer);
1001
1002
14
  CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
1003
1004
14
  *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked());
1005
14
  return GET_RETURN_STATUS(env);
1006
  // Tell coverity that 'finalizer' should not be freed when we return
1007
  // as it will be deleted when the buffer to which it is associated
1008
  // is finalized.
1009
  // coverity[leaked_storage]
1010
}
1011
1012
1
napi_status NAPI_CDECL napi_create_buffer_copy(napi_env env,
1013
                                               size_t length,
1014
                                               const void* data,
1015
                                               void** result_data,
1016
                                               napi_value* result) {
1017


2
  NAPI_PREAMBLE(env);
1018
1
  CHECK_ARG(env, result);
1019
1020
  v8::MaybeLocal<v8::Object> maybe =
1021
1
      node::Buffer::Copy(env->isolate, static_cast<const char*>(data), length);
1022
1023
1
  CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
1024
1025
1
  v8::Local<v8::Object> buffer = maybe.ToLocalChecked();
1026
1
  *result = v8impl::JsValueFromV8LocalValue(buffer);
1027
1028
1
  if (result_data != nullptr) {
1029
    *result_data = node::Buffer::Data(buffer);
1030
  }
1031
1032
1
  return GET_RETURN_STATUS(env);
1033
}
1034
1035
1
napi_status NAPI_CDECL napi_is_buffer(napi_env env,
1036
                                      napi_value value,
1037
                                      bool* result) {
1038
1
  CHECK_ENV(env);
1039
1
  CHECK_ARG(env, value);
1040
1
  CHECK_ARG(env, result);
1041
1042
1
  *result = node::Buffer::HasInstance(v8impl::V8LocalValueFromJsValue(value));
1043
1
  return napi_clear_last_error(env);
1044
}
1045
1046
1
napi_status NAPI_CDECL napi_get_buffer_info(napi_env env,
1047
                                            napi_value value,
1048
                                            void** data,
1049
                                            size_t* length) {
1050
1
  CHECK_ENV(env);
1051
1
  CHECK_ARG(env, value);
1052
1053
1
  v8::Local<v8::Value> buffer = v8impl::V8LocalValueFromJsValue(value);
1054
1055
1
  if (data != nullptr) {
1056
1
    *data = node::Buffer::Data(buffer);
1057
  }
1058
1
  if (length != nullptr) {
1059
1
    *length = node::Buffer::Length(buffer);
1060
  }
1061
1062
1
  return napi_clear_last_error(env);
1063
}
1064
1065
1
napi_status NAPI_CDECL napi_get_node_version(napi_env env,
1066
                                             const napi_node_version** result) {
1067
1
  CHECK_ENV(env);
1068
1
  CHECK_ARG(env, result);
1069
  static const napi_node_version version = {
1070
      NODE_MAJOR_VERSION, NODE_MINOR_VERSION, NODE_PATCH_VERSION, NODE_RELEASE};
1071
1
  *result = &version;
1072
1
  return napi_clear_last_error(env);
1073
}
1074
1075
namespace {
1076
namespace uvimpl {
1077
1078
513
static napi_status ConvertUVErrorCode(int code) {
1079

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

12
    CHECK_TO_OBJECT(env, context, resource, async_resource);
1180
  } else {
1181
508
    resource = v8::Object::New(env->isolate);
1182
  }
1183
1184
  v8::Local<v8::String> resource_name;
1185

1024
  CHECK_TO_STRING(env, context, resource_name, async_resource_name);
1186
1187
512
  uvimpl::Work* work = uvimpl::Work::New(reinterpret_cast<node_napi_env>(env),
1188
                                         resource,
1189
                                         resource_name,
1190
                                         execute,
1191
                                         complete,
1192
                                         data);
1193
1194
512
  *result = reinterpret_cast<napi_async_work>(work);
1195
1196
512
  return napi_clear_last_error(env);
1197
}
1198
1199
508
napi_status NAPI_CDECL napi_delete_async_work(napi_env env,
1200
                                              napi_async_work work) {
1201
508
  CHECK_ENV(env);
1202
508
  CHECK_ARG(env, work);
1203
1204
508
  uvimpl::Work::Delete(reinterpret_cast<uvimpl::Work*>(work));
1205
1206
508
  return napi_clear_last_error(env);
1207
}
1208
1209
518
napi_status NAPI_CDECL napi_get_uv_event_loop(napi_env env, uv_loop_t** loop) {
1210
518
  CHECK_ENV(env);
1211
518
  CHECK_ARG(env, loop);
1212
518
  *loop = reinterpret_cast<node_napi_env>(env)->node_env()->event_loop();
1213
518
  return napi_clear_last_error(env);
1214
}
1215
1216
512
napi_status NAPI_CDECL napi_queue_async_work(napi_env env,
1217
                                             napi_async_work work) {
1218
512
  CHECK_ENV(env);
1219
512
  CHECK_ARG(env, work);
1220
1221
512
  uv_loop_t* event_loop = nullptr;
1222
512
  STATUS_CALL(napi_get_uv_event_loop(env, &event_loop));
1223
1224
512
  uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work);
1225
1226
512
  w->ScheduleWork();
1227
1228
512
  return napi_clear_last_error(env);
1229
}
1230
1231
1
napi_status NAPI_CDECL napi_cancel_async_work(napi_env env,
1232
                                              napi_async_work work) {
1233
1
  CHECK_ENV(env);
1234
1
  CHECK_ARG(env, work);
1235
1236
1
  uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work);
1237
1238
1
  CALL_UV(env, w->CancelWork());
1239
1240
1
  return napi_clear_last_error(env);
1241
}
1242
1243
napi_status NAPI_CDECL
1244
20
napi_create_threadsafe_function(napi_env env,
1245
                                napi_value func,
1246
                                napi_value async_resource,
1247
                                napi_value async_resource_name,
1248
                                size_t max_queue_size,
1249
                                size_t initial_thread_count,
1250
                                void* thread_finalize_data,
1251
                                napi_finalize thread_finalize_cb,
1252
                                void* context,
1253
                                napi_threadsafe_function_call_js call_js_cb,
1254
                                napi_threadsafe_function* result) {
1255
20
  CHECK_ENV(env);
1256
20
  CHECK_ARG(env, async_resource_name);
1257
20
  RETURN_STATUS_IF_FALSE(env, initial_thread_count > 0, napi_invalid_arg);
1258
20
  CHECK_ARG(env, result);
1259
1260
20
  napi_status status = napi_ok;
1261
1262
  v8::Local<v8::Function> v8_func;
1263
20
  if (func == nullptr) {
1264
3
    CHECK_ARG(env, call_js_cb);
1265
  } else {
1266

51
    CHECK_TO_FUNCTION(env, v8_func, func);
1267
  }
1268
1269
20
  v8::Local<v8::Context> v8_context = env->context();
1270
1271
  v8::Local<v8::Object> v8_resource;
1272
20
  if (async_resource == nullptr) {
1273
18
    v8_resource = v8::Object::New(env->isolate);
1274
  } else {
1275

6
    CHECK_TO_OBJECT(env, v8_context, v8_resource, async_resource);
1276
  }
1277
1278
  v8::Local<v8::String> v8_name;
1279

40
  CHECK_TO_STRING(env, v8_context, v8_name, async_resource_name);
1280
1281
  v8impl::ThreadSafeFunction* ts_fn =
1282
      new v8impl::ThreadSafeFunction(v8_func,
1283
                                     v8_resource,
1284
                                     v8_name,
1285
                                     initial_thread_count,
1286
                                     context,
1287
                                     max_queue_size,
1288
                                     reinterpret_cast<node_napi_env>(env),
1289
                                     thread_finalize_data,
1290
                                     thread_finalize_cb,
1291
20
                                     call_js_cb);
1292
1293
20
  if (ts_fn == nullptr) {
1294
    status = napi_generic_failure;
1295
  } else {
1296
    // Init deletes ts_fn upon failure.
1297
20
    status = ts_fn->Init();
1298
20
    if (status == napi_ok) {
1299
20
      *result = reinterpret_cast<napi_threadsafe_function>(ts_fn);
1300
    }
1301
  }
1302
1303
20
  return napi_set_last_error(env, status);
1304
}
1305
1306
16
napi_status NAPI_CDECL napi_get_threadsafe_function_context(
1307
    napi_threadsafe_function func, void** result) {
1308
16
  CHECK_NOT_NULL(func);
1309
16
  CHECK_NOT_NULL(result);
1310
1311
16
  *result = reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Context();
1312
16
  return napi_ok;
1313
}
1314
1315
napi_status NAPI_CDECL
1316
4558270
napi_call_threadsafe_function(napi_threadsafe_function func,
1317
                              void* data,
1318
                              napi_threadsafe_function_call_mode is_blocking) {
1319
4558270
  CHECK_NOT_NULL(func);
1320
4558270
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Push(data,
1321
4558270
                                                                   is_blocking);
1322
}
1323
1324
napi_status NAPI_CDECL
1325
4
napi_acquire_threadsafe_function(napi_threadsafe_function func) {
1326
4
  CHECK_NOT_NULL(func);
1327
4
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Acquire();
1328
}
1329
1330
35
napi_status NAPI_CDECL napi_release_threadsafe_function(
1331
    napi_threadsafe_function func, napi_threadsafe_function_release_mode mode) {
1332
35
  CHECK_NOT_NULL(func);
1333
35
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Release(mode);
1334
}
1335
1336
napi_status NAPI_CDECL
1337
2
napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
1338
2
  CHECK_NOT_NULL(func);
1339
2
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Unref();
1340
}
1341
1342
napi_status NAPI_CDECL
1343
napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
1344
  CHECK_NOT_NULL(func);
1345
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Ref();
1346
}
1347
1348
2
napi_status NAPI_CDECL node_api_get_module_file_name(napi_env env,
1349
                                                     const char** result) {
1350
2
  CHECK_ENV(env);
1351
2
  CHECK_ARG(env, result);
1352
1353
2
  *result = static_cast<node_napi_env>(env)->GetFilename();
1354
2
  return napi_clear_last_error(env);
1355
}