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: 538 585 92.0 %
Date: 2021-04-18 04:12:15 Branches: 229 384 59.6 %

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

28223047
    while (queue.size() >= max_queue_size &&
163

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

31
    if (thread_count == 0 || mode == napi_tsfn_abort) {
209
15
      if (!is_closing) {
210
15
        is_closing = (mode == napi_tsfn_abort);
211

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

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

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

10
    if (!(env == nullptr || cb == nullptr)) {
377
      napi_value recv;
378
      napi_status status;
379
380
10
      status = napi_get_undefined(env, &recv);
381
10
      if (status != napi_ok) {
382
        napi_throw_error(env, "ERR_NAPI_TSFN_GET_UNDEFINED",
383
            "Failed to retrieve undefined value");
384
        return;
385
      }
386
387
10
      status = napi_call_function(env, recv, cb, 0, nullptr, nullptr);
388

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

420
  if (module->ToObject(context).ToLocal(&modobj) &&
577

420
      modobj->Get(context, node_env->filename_string()).ToLocal(&filename_js) &&
578
168
      filename_js->IsString()) {
579
164
    node::Utf8Value filename(node_env->isolate(), filename_js);  // Cast
580
581
    // Turn the absolute path into a URL. Currently the absolute path is always
582
    // a file system path.
583
    // TODO(gabrielschulhof): Pass the `filename` through unchanged if/when we
584
    // receive it as a URL already.
585
82
    module_filename = std::string("file://") + (*filename);
586
  }
587
588
  // Create a new napi_env for this specific module.
589
84
  napi_env env = v8impl::NewEnv(context, module_filename);
590
591
  napi_value _exports;
592
252
  env->CallIntoModule([&](napi_env env) {
593
168
    _exports = init(env, v8impl::JsValueFromV8LocalValue(exports));
594
168
  });
595
596
  // If register function returned a non-null exports object different from
597
  // the exports object we passed it, set that as the "exports" property of
598
  // the module.
599

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


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

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

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


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

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

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


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


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


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

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

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

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

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

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

14310
}