GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage-daily/nodes/benchmark/out/../src/node_api.cc Lines: 525 572 91.8 %
Date: 2020-11-21 04:10:54 Branches: 219 370 59.2 %

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_binding.h"
8
#include "node_buffer.h"
9
#include "node_errors.h"
10
#include "node_internals.h"
11
#include "threadpoolwork-inl.h"
12
#include "tracing/traced_value.h"
13
#include "util-inl.h"
14
15
#include <memory>
16
17
160
struct node_napi_env__ : public napi_env__ {
18
82
  explicit node_napi_env__(v8::Local<v8::Context> context):
19
82
      napi_env__(context) {
20
82
    CHECK_NOT_NULL(node_env());
21
82
  }
22
23
7215
  inline node::Environment* node_env() const {
24
7215
    return node::Environment::GetCurrent(context());
25
  }
26
27
4843
  bool can_call_into_js() const override {
28
4843
    return node_env()->can_call_into_js();
29
  }
30
31
  v8::Maybe<bool> mark_arraybuffer_as_untransferable(
32
      v8::Local<v8::ArrayBuffer> ab) const override {
33
    return ab->SetPrivate(
34
        context(),
35
        node_env()->untransferable_object_private_symbol(),
36
        v8::True(isolate));
37
  }
38
39
1016
  void CallFinalizer(napi_finalize cb, void* data, void* hint) override {
40
1016
    napi_env env = static_cast<napi_env>(this);
41
3048
    node_env()->SetImmediate([=](node::Environment* node_env) {
42
3048
      v8::HandleScope handle_scope(env->isolate);
43
1016
      v8::Context::Scope context_scope(env->context());
44
3048
      env->CallIntoModule([&](napi_env env) {
45
1016
        cb(env, data, hint);
46
2032
      });
47
2032
    });
48
1016
  }
49
};
50
51
typedef node_napi_env__* node_napi_env;
52
53
namespace v8impl {
54
55
namespace {
56
57
class BufferFinalizer : private Finalizer {
58
 public:
59
  // node::Buffer::FreeCallback
60
13
  static void FinalizeBufferCallback(char* data, void* hint) {
61
    std::unique_ptr<BufferFinalizer, Deleter> finalizer{
62
26
        static_cast<BufferFinalizer*>(hint)};
63
13
    finalizer->_finalize_data = data;
64
65
    node::Environment* node_env =
66
13
        static_cast<node_napi_env>(finalizer->_env)->node_env();
67
26
    node_env->SetImmediate(
68
65
        [finalizer = std::move(finalizer)](node::Environment* env) {
69
13
      if (finalizer->_finalize_callback == nullptr) return;
70
71
24
      v8::HandleScope handle_scope(finalizer->_env->isolate);
72
12
      v8::Context::Scope context_scope(finalizer->_env->context());
73
74
36
      finalizer->_env->CallIntoModule([&](napi_env env) {
75
36
        finalizer->_finalize_callback(
76
            env,
77
12
            finalizer->_finalize_data,
78
24
            finalizer->_finalize_hint);
79
24
      });
80
13
    });
81
13
  }
82
83
  struct Deleter {
84
13
    void operator()(BufferFinalizer* finalizer) {
85
13
      Finalizer::Delete(finalizer);
86
13
    }
87
  };
88
};
89
90
82
static inline napi_env NewEnv(v8::Local<v8::Context> context) {
91
  node_napi_env result;
92
93
82
  result = new node_napi_env__(context);
94
  // TODO(addaleax): There was previously code that tried to delete the
95
  // napi_env when its v8::Context was garbage collected;
96
  // However, as long as N-API addons using this napi_env are in place,
97
  // the Context needs to be accessible and alive.
98
  // Ideally, we'd want an on-addon-unload hook that takes care of this
99
  // once all N-API addons using this napi_env are unloaded.
100
  // For now, a per-Environment cleanup hook is the best we can do.
101
164
  result->node_env()->AddCleanupHook(
102
242
      [](void* arg) {
103
80
        static_cast<napi_env>(arg)->Unref();
104
242
      },
105
82
      static_cast<void*>(result));
106
107
82
  return result;
108
}
109
110
3
static inline void trigger_fatal_exception(
111
    napi_env env, v8::Local<v8::Value> local_err) {
112
  v8::Local<v8::Message> local_msg =
113
3
    v8::Exception::CreateMessage(env->isolate, local_err);
114
3
  node::errors::TriggerUncaughtException(env->isolate, local_err, local_msg);
115
2
}
116
117
class ThreadSafeFunction : public node::AsyncResource {
118
 public:
119
17
  ThreadSafeFunction(v8::Local<v8::Function> func,
120
                     v8::Local<v8::Object> resource,
121
                     v8::Local<v8::String> name,
122
                     size_t thread_count_,
123
                     void* context_,
124
                     size_t max_queue_size_,
125
                     node_napi_env env_,
126
                     void* finalize_data_,
127
                     napi_finalize finalize_cb_,
128
17
                     napi_threadsafe_function_call_js call_js_cb_):
129
17
                     AsyncResource(env_->isolate,
130
                                   resource,
131
34
                                   *v8::String::Utf8Value(env_->isolate, name)),
132
      thread_count(thread_count_),
133
      is_closing(false),
134
      context(context_),
135
      max_queue_size(max_queue_size_),
136
      env(env_),
137
      finalize_data(finalize_data_),
138
      finalize_cb(finalize_cb_),
139
17
      call_js_cb(call_js_cb_ == nullptr ? CallJs : call_js_cb_),
140
68
      handles_closing(false) {
141
17
    ref.Reset(env->isolate, func);
142
17
    node::AddEnvironmentCleanupHook(env->isolate, Cleanup, this);
143
17
    env->Ref();
144
17
  }
145
146
68
  ~ThreadSafeFunction() override {
147
17
    node::RemoveEnvironmentCleanupHook(env->isolate, Cleanup, this);
148
17
    env->Unref();
149
34
  }
150
151
  // These methods can be called from any thread.
152
153
9676198
  napi_status Push(void* data, napi_threadsafe_function_call_mode mode) {
154
19352396
    node::Mutex::ScopedLock lock(this->mutex);
155
156

29028671
    while (queue.size() >= max_queue_size &&
157

19352362
        max_queue_size > 0 &&
158
9676122
        !is_closing) {
159
9676119
      if (mode == napi_tsfn_nonblocking) {
160
9676077
        return napi_queue_full;
161
      }
162
42
      cond->Wait(lock);
163
    }
164
165
121
    if (is_closing) {
166
5
      if (thread_count == 0) {
167
        return napi_invalid_arg;
168
      } else {
169
5
        thread_count--;
170
5
        return napi_closing;
171
      }
172
    } else {
173
116
      if (uv_async_send(&async) != 0) {
174
        return napi_generic_failure;
175
      }
176
116
      queue.push(data);
177
116
      return napi_ok;
178
    }
179
  }
180
181
4
  napi_status Acquire() {
182
8
    node::Mutex::ScopedLock lock(this->mutex);
183
184
4
    if (is_closing) {
185
      return napi_closing;
186
    }
187
188
4
    thread_count++;
189
190
4
    return napi_ok;
191
  }
192
193
31
  napi_status Release(napi_threadsafe_function_release_mode mode) {
194
62
    node::Mutex::ScopedLock lock(this->mutex);
195
196
31
    if (thread_count == 0) {
197
      return napi_invalid_arg;
198
    }
199
200
31
    thread_count--;
201
202

31
    if (thread_count == 0 || mode == napi_tsfn_abort) {
203
15
      if (!is_closing) {
204
15
        is_closing = (mode == napi_tsfn_abort);
205

15
        if (is_closing && max_queue_size > 0) {
206
2
          cond->Signal(lock);
207
        }
208
15
        if (uv_async_send(&async) != 0) {
209
          return napi_generic_failure;
210
        }
211
      }
212
    }
213
214
31
    return napi_ok;
215
  }
216
217
23
  void EmptyQueueAndDelete() {
218
29
    for (; !queue.empty() ; queue.pop()) {
219
6
      call_js_cb(nullptr, nullptr, context, queue.front());
220
    }
221
17
    delete this;
222
17
  }
223
224
  // These methods must only be called from the loop thread.
225
226
17
  napi_status Init() {
227
17
    ThreadSafeFunction* ts_fn = this;
228
17
    uv_loop_t* loop = env->node_env()->event_loop();
229
230
17
    if (uv_async_init(loop, &async, AsyncCb) == 0) {
231
17
      if (max_queue_size > 0) {
232
11
        cond = std::make_unique<node::ConditionVariable>();
233
      }
234

17
      if (max_queue_size == 0 || cond) {
235
17
        CHECK_EQ(0, uv_idle_init(loop, &idle));
236
17
        return napi_ok;
237
      }
238
239
      env->node_env()->CloseHandle(
240
          reinterpret_cast<uv_handle_t*>(&async),
241
          [](uv_handle_t* handle) -> void {
242
            ThreadSafeFunction* ts_fn =
243
                node::ContainerOf(&ThreadSafeFunction::async,
244
                                  reinterpret_cast<uv_async_t*>(handle));
245
            delete ts_fn;
246
          });
247
248
      // Prevent the thread-safe function from being deleted here, because
249
      // the callback above will delete it.
250
      ts_fn = nullptr;
251
    }
252
253
    delete ts_fn;
254
255
    return napi_generic_failure;
256
  }
257
258
2
  napi_status Unref() {
259
2
    uv_unref(reinterpret_cast<uv_handle_t*>(&async));
260
2
    uv_unref(reinterpret_cast<uv_handle_t*>(&idle));
261
262
2
    return napi_ok;
263
  }
264
265
  napi_status Ref() {
266
    uv_ref(reinterpret_cast<uv_handle_t*>(&async));
267
    uv_ref(reinterpret_cast<uv_handle_t*>(&idle));
268
269
    return napi_ok;
270
  }
271
272
120
  void DispatchOne() {
273
120
    void* data = nullptr;
274
120
    bool popped_value = false;
275
276
    {
277
240
      node::Mutex::ScopedLock lock(this->mutex);
278
120
      if (is_closing) {
279
4
        CloseHandlesAndMaybeDelete();
280
      } else {
281
116
        size_t size = queue.size();
282
116
        if (size > 0) {
283
110
          data = queue.front();
284
110
          queue.pop();
285
110
          popped_value = true;
286

110
          if (size == max_queue_size && max_queue_size > 0) {
287
71
            cond->Signal(lock);
288
          }
289
110
          size--;
290
        }
291
292
116
        if (size == 0) {
293
45
          if (thread_count == 0) {
294
12
            is_closing = true;
295
12
            if (max_queue_size > 0) {
296
8
              cond->Signal(lock);
297
            }
298
12
            CloseHandlesAndMaybeDelete();
299
          } else {
300
33
            CHECK_EQ(0, uv_idle_stop(&idle));
301
          }
302
        }
303
      }
304
    }
305
306
120
    if (popped_value) {
307
220
      v8::HandleScope scope(env->isolate);
308
220
      CallbackScope cb_scope(this);
309
110
      napi_value js_callback = nullptr;
310
220
      if (!ref.IsEmpty()) {
311
        v8::Local<v8::Function> js_cb =
312
196
          v8::Local<v8::Function>::New(env->isolate, ref);
313
98
        js_callback = v8impl::JsValueFromV8LocalValue(js_cb);
314
      }
315
330
      env->CallIntoModule([&](napi_env env) {
316
110
        call_js_cb(env, js_callback, context, data);
317
220
      });
318
    }
319
120
  }
320
321
17
  void Finalize() {
322
34
    v8::HandleScope scope(env->isolate);
323
17
    if (finalize_cb) {
324
34
      CallbackScope cb_scope(this);
325
51
      env->CallIntoModule([&](napi_env env) {
326
17
        finalize_cb(env, finalize_data, context);
327
34
      });
328
    }
329
17
    EmptyQueueAndDelete();
330
17
  }
331
332
15
  inline void* Context() {
333
15
    return context;
334
  }
335
336
18
  void CloseHandlesAndMaybeDelete(bool set_closing = false) {
337
35
    v8::HandleScope scope(env->isolate);
338
18
    if (set_closing) {
339
4
      node::Mutex::ScopedLock lock(this->mutex);
340
2
      is_closing = true;
341
2
      if (max_queue_size > 0) {
342
1
        cond->Signal(lock);
343
      }
344
    }
345
18
    if (handles_closing) {
346
1
      return;
347
    }
348
17
    handles_closing = true;
349
34
    env->node_env()->CloseHandle(
350
17
        reinterpret_cast<uv_handle_t*>(&async),
351
17
        [](uv_handle_t* handle) -> void {
352
          ThreadSafeFunction* ts_fn =
353
34
              node::ContainerOf(&ThreadSafeFunction::async,
354
17
                                reinterpret_cast<uv_async_t*>(handle));
355
34
          v8::HandleScope scope(ts_fn->env->isolate);
356
34
          ts_fn->env->node_env()->CloseHandle(
357
17
              reinterpret_cast<uv_handle_t*>(&ts_fn->idle),
358
17
              [](uv_handle_t* handle) -> void {
359
                ThreadSafeFunction* ts_fn =
360
34
                    node::ContainerOf(&ThreadSafeFunction::idle,
361
17
                                      reinterpret_cast<uv_idle_t*>(handle));
362
17
                ts_fn->Finalize();
363
34
              });
364
34
        });
365
  }
366
367
  // Default way of calling into JavaScript. Used when ThreadSafeFunction is
368
  //  without a call_js_cb_.
369
10
  static void CallJs(napi_env env, napi_value cb, void* context, void* data) {
370

10
    if (!(env == nullptr || cb == nullptr)) {
371
      napi_value recv;
372
      napi_status status;
373
374
10
      status = napi_get_undefined(env, &recv);
375
10
      if (status != napi_ok) {
376
        napi_throw_error(env, "ERR_NAPI_TSFN_GET_UNDEFINED",
377
            "Failed to retrieve undefined value");
378
        return;
379
      }
380
381
10
      status = napi_call_function(env, recv, cb, 0, nullptr, nullptr);
382

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

241
  if (_exports != nullptr &&
575
159
      _exports != v8impl::JsValueFromV8LocalValue(exports)) {
576
4
    napi_value _module = v8impl::JsValueFromV8LocalValue(module);
577
4
    napi_set_named_property(env, _module, "exports", _exports);
578
  }
579
}
580
581
namespace node {
582
79
node_module napi_module_to_node_module(const napi_module* mod) {
583
  return {
584
    -1,
585
79
    mod->nm_flags | NM_F_DELETEME,
586
    nullptr,
587
79
    mod->nm_filename,
588
    nullptr,
589
    napi_module_register_cb,
590
79
    mod->nm_modname,
591
    const_cast<napi_module*>(mod),  // priv
592
    nullptr,
593
316
  };
594
}
595
}  // namespace node
596
597
// Registers a NAPI module.
598
77
void napi_module_register(napi_module* mod) {
599
  node::node_module* nm = new node::node_module(
600
77
      node::napi_module_to_node_module(mod));
601
77
  node::node_module_register(nm);
602
77
}
603
604
2
napi_status napi_add_env_cleanup_hook(napi_env env,
605
                                      void (*fun)(void* arg),
606
                                      void* arg) {
607
2
  CHECK_ENV(env);
608
2
  CHECK_ARG(env, fun);
609
610
2
  node::AddEnvironmentCleanupHook(env->isolate, fun, arg);
611
612
2
  return napi_ok;
613
}
614
615
1
napi_status napi_remove_env_cleanup_hook(napi_env env,
616
                                         void (*fun)(void* arg),
617
                                         void* arg) {
618
1
  CHECK_ENV(env);
619
1
  CHECK_ARG(env, fun);
620
621
1
  node::RemoveEnvironmentCleanupHook(env->isolate, fun, arg);
622
623
1
  return napi_ok;
624
}
625
626
struct napi_async_cleanup_hook_handle__ {
627
6
  napi_async_cleanup_hook_handle__(napi_env env,
628
                                   napi_async_cleanup_hook user_hook,
629
6
                                   void* user_data):
630
      env_(env),
631
      user_hook_(user_hook),
632
6
      user_data_(user_data) {
633
6
    handle_ = node::AddEnvironmentCleanupHook(env->isolate, Hook, this);
634
6
    env->Ref();
635
6
  }
636
637
12
  ~napi_async_cleanup_hook_handle__() {
638
6
    node::RemoveEnvironmentCleanupHook(std::move(handle_));
639
6
    if (done_cb_ != nullptr)
640
4
      done_cb_(done_data_);
641
642
    // Release the `env` handle asynchronously since it would be surprising if
643
    // a call to a N-API function would destroy `env` synchronously.
644
6
    static_cast<node_napi_env>(env_)->node_env()
645
18
        ->SetImmediate([env = env_](node::Environment*) { env->Unref(); });
646
6
  }
647
648
4
  static void Hook(void* data, void (*done_cb)(void*), void* done_data) {
649
4
    auto handle = static_cast<napi_async_cleanup_hook_handle__*>(data);
650
4
    handle->done_cb_ = done_cb;
651
4
    handle->done_data_ = done_data;
652
4
    handle->user_hook_(handle, handle->user_data_);
653
4
  }
654
655
  node::AsyncCleanupHookHandle handle_;
656
  napi_env env_ = nullptr;
657
  napi_async_cleanup_hook user_hook_ = nullptr;
658
  void* user_data_ = nullptr;
659
  void (*done_cb_)(void*) = nullptr;
660
  void* done_data_ = nullptr;
661
};
662
663
6
napi_status napi_add_async_cleanup_hook(
664
    napi_env env,
665
    napi_async_cleanup_hook hook,
666
    void* arg,
667
    napi_async_cleanup_hook_handle* remove_handle) {
668
6
  CHECK_ENV(env);
669
6
  CHECK_ARG(env, hook);
670
671
  napi_async_cleanup_hook_handle__* handle =
672
6
    new napi_async_cleanup_hook_handle__(env, hook, arg);
673
674
6
  if (remove_handle != nullptr)
675
4
    *remove_handle = handle;
676
677
6
  return napi_clear_last_error(env);
678
}
679
680
6
napi_status napi_remove_async_cleanup_hook(
681
    napi_async_cleanup_hook_handle remove_handle) {
682
683
6
  if (remove_handle == nullptr)
684
    return napi_invalid_arg;
685
686
6
  delete remove_handle;
687
688
6
  return napi_ok;
689
}
690
691
1
napi_status napi_fatal_exception(napi_env env, napi_value err) {
692


3
  NAPI_PREAMBLE(env);
693
1
  CHECK_ARG(env, err);
694
695
1
  v8::Local<v8::Value> local_err = v8impl::V8LocalValueFromJsValue(err);
696
1
  v8impl::trigger_fatal_exception(env, local_err);
697
698
1
  return napi_clear_last_error(env);
699
}
700
701
NAPI_NO_RETURN void napi_fatal_error(const char* location,
702
                                     size_t location_len,
703
                                     const char* message,
704
                                     size_t message_len) {
705
  std::string location_string;
706
  std::string message_string;
707
708
  if (location_len != NAPI_AUTO_LENGTH) {
709
    location_string.assign(
710
        const_cast<char*>(location), location_len);
711
  } else {
712
    location_string.assign(
713
        const_cast<char*>(location), strlen(location));
714
  }
715
716
  if (message_len != NAPI_AUTO_LENGTH) {
717
    message_string.assign(
718
        const_cast<char*>(message), message_len);
719
  } else {
720
    message_string.assign(
721
        const_cast<char*>(message), strlen(message));
722
  }
723
724
  node::FatalError(location_string.c_str(), message_string.c_str());
725
}
726
727
4
napi_status napi_open_callback_scope(napi_env env,
728
                                     napi_value /** ignored */,
729
                                     napi_async_context async_context_handle,
730
                                     napi_callback_scope* result) {
731
  // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
732
  // JS exceptions.
733
4
  CHECK_ENV(env);
734
4
  CHECK_ARG(env, result);
735
736
  v8impl::AsyncContext* node_async_context =
737
4
      reinterpret_cast<v8impl::AsyncContext*>(async_context_handle);
738
739
4
  *result = node_async_context->OpenCallbackScope();
740
741
4
  return napi_clear_last_error(env);
742
}
743
744
4
napi_status napi_close_callback_scope(napi_env env, napi_callback_scope scope) {
745
  // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
746
  // JS exceptions.
747
4
  CHECK_ENV(env);
748
4
  CHECK_ARG(env, scope);
749
4
  if (env->open_callback_scopes == 0) {
750
    return napi_callback_scope_mismatch;
751
  }
752
753
4
  v8impl::AsyncContext::CloseCallbackScope(reinterpret_cast<node_napi_env>(env),
754
8
                                           scope);
755
756
4
  return napi_clear_last_error(env);
757
}
758
759
13
napi_status napi_async_init(napi_env env,
760
                            napi_value async_resource,
761
                            napi_value async_resource_name,
762
                            napi_async_context* result) {
763
13
  CHECK_ENV(env);
764
13
  CHECK_ARG(env, async_resource_name);
765
13
  CHECK_ARG(env, result);
766
767
13
  v8::Isolate* isolate = env->isolate;
768
13
  v8::Local<v8::Context> context = env->context();
769
770
  v8::Local<v8::Object> v8_resource;
771
  bool externally_managed_resource;
772
13
  if (async_resource != nullptr) {
773

44
    CHECK_TO_OBJECT(env, context, v8_resource, async_resource);
774
11
    externally_managed_resource = true;
775
  } else {
776
2
    v8_resource = v8::Object::New(isolate);
777
2
    externally_managed_resource = false;
778
  }
779
780
  v8::Local<v8::String> v8_resource_name;
781

52
  CHECK_TO_STRING(env, context, v8_resource_name, async_resource_name);
782
783
  auto async_context =
784
      new v8impl::AsyncContext(reinterpret_cast<node_napi_env>(env),
785
                               v8_resource,
786
                               v8_resource_name,
787
13
                               externally_managed_resource);
788
789
13
  *result = reinterpret_cast<napi_async_context>(async_context);
790
791
13
  return napi_clear_last_error(env);
792
}
793
794
13
napi_status napi_async_destroy(napi_env env,
795
                               napi_async_context async_context) {
796
13
  CHECK_ENV(env);
797
13
  CHECK_ARG(env, async_context);
798
799
  v8impl::AsyncContext* node_async_context =
800
13
      reinterpret_cast<v8impl::AsyncContext*>(async_context);
801
802
13
  delete node_async_context;
803
804
13
  return napi_clear_last_error(env);
805
}
806
807
25
napi_status napi_make_callback(napi_env env,
808
                               napi_async_context async_context,
809
                               napi_value recv,
810
                               napi_value func,
811
                               size_t argc,
812
                               const napi_value* argv,
813
                               napi_value* result) {
814


75
  NAPI_PREAMBLE(env);
815
25
  CHECK_ARG(env, recv);
816
25
  if (argc > 0) {
817
4
    CHECK_ARG(env, argv);
818
  }
819
820
25
  v8::Local<v8::Context> context = env->context();
821
822
  v8::Local<v8::Object> v8recv;
823

100
  CHECK_TO_OBJECT(env, context, v8recv, recv);
824
825
  v8::Local<v8::Function> v8func;
826

75
  CHECK_TO_FUNCTION(env, v8func, func);
827
828
  v8::MaybeLocal<v8::Value> callback_result;
829
830
25
  if (async_context == nullptr) {
831
    callback_result = node::MakeCallback(
832
17
        env->isolate,
833
        v8recv,
834
        v8func,
835
        argc,
836
        reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)),
837
34
        {0, 0});
838
  } else {
839
    auto node_async_context =
840
8
        reinterpret_cast<v8impl::AsyncContext*>(async_context);
841
    callback_result = node_async_context->MakeCallback(
842
        v8recv,
843
        v8func,
844
        argc,
845
8
        reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)));
846
  }
847
848
25
  if (try_catch.HasCaught()) {
849
4
    return napi_set_last_error(env, napi_pending_exception);
850
  } else {
851
21
    CHECK_MAYBE_EMPTY(env, callback_result, napi_generic_failure);
852
21
    if (result != nullptr) {
853
8
      *result = v8impl::JsValueFromV8LocalValue(
854
          callback_result.ToLocalChecked());
855
    }
856
  }
857
858
21
  return GET_RETURN_STATUS(env);
859
}
860
861
1
napi_status napi_create_buffer(napi_env env,
862
                               size_t length,
863
                               void** data,
864
                               napi_value* result) {
865


3
  NAPI_PREAMBLE(env);
866
1
  CHECK_ARG(env, result);
867
868
1
  auto maybe = node::Buffer::New(env->isolate, length);
869
870
1
  CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
871
872
1
  v8::Local<v8::Object> buffer = maybe.ToLocalChecked();
873
874
2
  *result = v8impl::JsValueFromV8LocalValue(buffer);
875
876
1
  if (data != nullptr) {
877
1
    *data = node::Buffer::Data(buffer);
878
  }
879
880
1
  return GET_RETURN_STATUS(env);
881
}
882
883
13
napi_status napi_create_external_buffer(napi_env env,
884
                                        size_t length,
885
                                        void* data,
886
                                        napi_finalize finalize_cb,
887
                                        void* finalize_hint,
888
                                        napi_value* result) {
889


39
  NAPI_PREAMBLE(env);
890
13
  CHECK_ARG(env, result);
891
892
13
  v8::Isolate* isolate = env->isolate;
893
894
  // The finalizer object will delete itself after invoking the callback.
895
13
  v8impl::Finalizer* finalizer = v8impl::Finalizer::New(
896
      env, finalize_cb, nullptr, finalize_hint,
897
13
      v8impl::Finalizer::kKeepEnvReference);
898
899
  auto maybe = node::Buffer::New(isolate,
900
                                static_cast<char*>(data),
901
                                length,
902
                                v8impl::BufferFinalizer::FinalizeBufferCallback,
903
13
                                finalizer);
904
905
13
  CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
906
907
26
  *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked());
908
13
  return GET_RETURN_STATUS(env);
909
  // Tell coverity that 'finalizer' should not be freed when we return
910
  // as it will be deleted when the buffer to which it is associated
911
  // is finalized.
912
  // coverity[leaked_storage]
913
}
914
915
1
napi_status napi_create_buffer_copy(napi_env env,
916
                                    size_t length,
917
                                    const void* data,
918
                                    void** result_data,
919
                                    napi_value* result) {
920


3
  NAPI_PREAMBLE(env);
921
1
  CHECK_ARG(env, result);
922
923
1
  auto maybe = node::Buffer::Copy(env->isolate,
924
2
    static_cast<const char*>(data), length);
925
926
1
  CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
927
928
1
  v8::Local<v8::Object> buffer = maybe.ToLocalChecked();
929
2
  *result = v8impl::JsValueFromV8LocalValue(buffer);
930
931
1
  if (result_data != nullptr) {
932
    *result_data = node::Buffer::Data(buffer);
933
  }
934
935
1
  return GET_RETURN_STATUS(env);
936
}
937
938
1
napi_status napi_is_buffer(napi_env env, napi_value value, bool* result) {
939
1
  CHECK_ENV(env);
940
1
  CHECK_ARG(env, value);
941
1
  CHECK_ARG(env, result);
942
943
1
  *result = node::Buffer::HasInstance(v8impl::V8LocalValueFromJsValue(value));
944
1
  return napi_clear_last_error(env);
945
}
946
947
1
napi_status napi_get_buffer_info(napi_env env,
948
                                 napi_value value,
949
                                 void** data,
950
                                 size_t* length) {
951
1
  CHECK_ENV(env);
952
1
  CHECK_ARG(env, value);
953
954
1
  v8::Local<v8::Value> buffer = v8impl::V8LocalValueFromJsValue(value);
955
956
1
  if (data != nullptr) {
957
1
    *data = node::Buffer::Data(buffer);
958
  }
959
1
  if (length != nullptr) {
960
1
    *length = node::Buffer::Length(buffer);
961
  }
962
963
1
  return napi_clear_last_error(env);
964
}
965
966
1
napi_status napi_get_node_version(napi_env env,
967
                                  const napi_node_version** result) {
968
1
  CHECK_ENV(env);
969
1
  CHECK_ARG(env, result);
970
  static const napi_node_version version = {
971
    NODE_MAJOR_VERSION,
972
    NODE_MINOR_VERSION,
973
    NODE_PATCH_VERSION,
974
    NODE_RELEASE
975
  };
976
1
  *result = &version;
977
1
  return napi_clear_last_error(env);
978
}
979
980
namespace {
981
namespace uvimpl {
982
983
513
static napi_status ConvertUVErrorCode(int code) {
984

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

16
    CHECK_TO_OBJECT(env, context, resource, async_resource);
1094
  } else {
1095
508
    resource = v8::Object::New(env->isolate);
1096
  }
1097
1098
  v8::Local<v8::String> resource_name;
1099

2048
  CHECK_TO_STRING(env, context, resource_name, async_resource_name);
1100
1101
512
  uvimpl::Work* work = uvimpl::Work::New(reinterpret_cast<node_napi_env>(env),
1102
                                         resource,
1103
                                         resource_name,
1104
                                         execute,
1105
                                         complete,
1106
512
                                         data);
1107
1108
512
  *result = reinterpret_cast<napi_async_work>(work);
1109
1110
512
  return napi_clear_last_error(env);
1111
}
1112
1113
508
napi_status napi_delete_async_work(napi_env env, napi_async_work work) {
1114
508
  CHECK_ENV(env);
1115
508
  CHECK_ARG(env, work);
1116
1117
508
  uvimpl::Work::Delete(reinterpret_cast<uvimpl::Work*>(work));
1118
1119
508
  return napi_clear_last_error(env);
1120
}
1121
1122
518
napi_status napi_get_uv_event_loop(napi_env env, uv_loop_t** loop) {
1123
518
  CHECK_ENV(env);
1124
518
  CHECK_ARG(env, loop);
1125
518
  *loop = reinterpret_cast<node_napi_env>(env)->node_env()->event_loop();
1126
518
  return napi_clear_last_error(env);
1127
}
1128
1129
512
napi_status napi_queue_async_work(napi_env env, napi_async_work work) {
1130
512
  CHECK_ENV(env);
1131
512
  CHECK_ARG(env, work);
1132
1133
512
  uv_loop_t* event_loop = nullptr;
1134
512
  STATUS_CALL(napi_get_uv_event_loop(env, &event_loop));
1135
1136
512
  uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work);
1137
1138
512
  w->ScheduleWork();
1139
1140
512
  return napi_clear_last_error(env);
1141
}
1142
1143
1
napi_status napi_cancel_async_work(napi_env env, napi_async_work work) {
1144
1
  CHECK_ENV(env);
1145
1
  CHECK_ARG(env, work);
1146
1147
1
  uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work);
1148
1149
1
  CALL_UV(env, w->CancelWork());
1150
1151
1
  return napi_clear_last_error(env);
1152
}
1153
1154
napi_status
1155
17
napi_create_threadsafe_function(napi_env env,
1156
                                napi_value func,
1157
                                napi_value async_resource,
1158
                                napi_value async_resource_name,
1159
                                size_t max_queue_size,
1160
                                size_t initial_thread_count,
1161
                                void* thread_finalize_data,
1162
                                napi_finalize thread_finalize_cb,
1163
                                void* context,
1164
                                napi_threadsafe_function_call_js call_js_cb,
1165
                                napi_threadsafe_function* result) {
1166
17
  CHECK_ENV(env);
1167
17
  CHECK_ARG(env, async_resource_name);
1168
17
  RETURN_STATUS_IF_FALSE(env, initial_thread_count > 0, napi_invalid_arg);
1169
17
  CHECK_ARG(env, result);
1170
1171
17
  napi_status status = napi_ok;
1172
1173
  v8::Local<v8::Function> v8_func;
1174
17
  if (func == nullptr) {
1175
3
    CHECK_ARG(env, call_js_cb);
1176
  } else {
1177

42
    CHECK_TO_FUNCTION(env, v8_func, func);
1178
  }
1179
1180
17
  v8::Local<v8::Context> v8_context = env->context();
1181
1182
  v8::Local<v8::Object> v8_resource;
1183
17
  if (async_resource == nullptr) {
1184
17
    v8_resource = v8::Object::New(env->isolate);
1185
  } else {
1186
    CHECK_TO_OBJECT(env, v8_context, v8_resource, async_resource);
1187
  }
1188
1189
  v8::Local<v8::String> v8_name;
1190

68
  CHECK_TO_STRING(env, v8_context, v8_name, async_resource_name);
1191
1192
  v8impl::ThreadSafeFunction* ts_fn =
1193
      new v8impl::ThreadSafeFunction(v8_func,
1194
                                     v8_resource,
1195
                                     v8_name,
1196
                                     initial_thread_count,
1197
                                     context,
1198
                                     max_queue_size,
1199
                                     reinterpret_cast<node_napi_env>(env),
1200
                                     thread_finalize_data,
1201
                                     thread_finalize_cb,
1202
17
                                     call_js_cb);
1203
1204
17
  if (ts_fn == nullptr) {
1205
    status = napi_generic_failure;
1206
  } else {
1207
    // Init deletes ts_fn upon failure.
1208
17
    status = ts_fn->Init();
1209
17
    if (status == napi_ok) {
1210
17
      *result = reinterpret_cast<napi_threadsafe_function>(ts_fn);
1211
    }
1212
  }
1213
1214
17
  return napi_set_last_error(env, status);
1215
}
1216
1217
napi_status
1218
15
napi_get_threadsafe_function_context(napi_threadsafe_function func,
1219
                                     void** result) {
1220
15
  CHECK_NOT_NULL(func);
1221
15
  CHECK_NOT_NULL(result);
1222
1223
15
  *result = reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Context();
1224
15
  return napi_ok;
1225
}
1226
1227
napi_status
1228
9676198
napi_call_threadsafe_function(napi_threadsafe_function func,
1229
                              void* data,
1230
                              napi_threadsafe_function_call_mode is_blocking) {
1231
9676198
  CHECK_NOT_NULL(func);
1232
9676198
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Push(data,
1233
9676198
                                                                   is_blocking);
1234
}
1235
1236
napi_status
1237
4
napi_acquire_threadsafe_function(napi_threadsafe_function func) {
1238
4
  CHECK_NOT_NULL(func);
1239
4
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Acquire();
1240
}
1241
1242
napi_status
1243
31
napi_release_threadsafe_function(napi_threadsafe_function func,
1244
                                 napi_threadsafe_function_release_mode mode) {
1245
31
  CHECK_NOT_NULL(func);
1246
31
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Release(mode);
1247
}
1248
1249
napi_status
1250
2
napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
1251
2
  CHECK_NOT_NULL(func);
1252
2
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Unref();
1253
}
1254
1255
napi_status
1256
napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
1257
  CHECK_NOT_NULL(func);
1258
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Ref();
1259

14034
}