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: 40 561 7.1 %
Date: 2021-02-19 04:08:54 Branches: 11 384 2.9 %

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
4
struct node_napi_env__ : public napi_env__ {
18
2
  explicit node_napi_env__(v8::Local<v8::Context> context,
19
2
                           const std::string& module_filename):
20
2
      napi_env__(context), filename(module_filename) {
21
2
    CHECK_NOT_NULL(node_env());
22
2
  }
23
24
7
  inline node::Environment* node_env() const {
25
7
    return node::Environment::GetCurrent(context());
26
  }
27
28
3
  bool can_call_into_js() const override {
29
3
    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
  void CallFinalizer(napi_finalize cb, void* data, void* hint) override {
41
    napi_env env = static_cast<napi_env>(this);
42
    node_env()->SetImmediate([=](node::Environment* node_env) {
43
      v8::HandleScope handle_scope(env->isolate);
44
      v8::Context::Scope context_scope(env->context());
45
      env->CallIntoModule([&](napi_env env) {
46
        cb(env, data, hint);
47
      });
48
    });
49
  }
50
51
  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
  static void FinalizeBufferCallback(char* data, void* hint) {
66
    std::unique_ptr<BufferFinalizer, Deleter> finalizer{
67
        static_cast<BufferFinalizer*>(hint)};
68
    finalizer->_finalize_data = data;
69
70
    node::Environment* node_env =
71
        static_cast<node_napi_env>(finalizer->_env)->node_env();
72
    node_env->SetImmediate(
73
        [finalizer = std::move(finalizer)](node::Environment* env) {
74
      if (finalizer->_finalize_callback == nullptr) return;
75
76
      v8::HandleScope handle_scope(finalizer->_env->isolate);
77
      v8::Context::Scope context_scope(finalizer->_env->context());
78
79
      finalizer->_env->CallIntoModule([&](napi_env env) {
80
        finalizer->_finalize_callback(
81
            env,
82
            finalizer->_finalize_data,
83
            finalizer->_finalize_hint);
84
      });
85
    });
86
  }
87
88
  struct Deleter {
89
    void operator()(BufferFinalizer* finalizer) {
90
      Finalizer::Delete(finalizer);
91
    }
92
  };
93
};
94
95
static inline napi_env
96
2
NewEnv(v8::Local<v8::Context> context, const std::string& module_filename) {
97
  node_napi_env result;
98
99
2
  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
4
  result->node_env()->AddCleanupHook(
108
6
      [](void* arg) {
109
2
        static_cast<napi_env>(arg)->Unref();
110
6
      },
111
2
      static_cast<void*>(result));
112
113
2
  return result;
114
}
115
116
static inline void trigger_fatal_exception(
117
    napi_env env, v8::Local<v8::Value> local_err) {
118
  v8::Local<v8::Message> local_msg =
119
    v8::Exception::CreateMessage(env->isolate, local_err);
120
  node::errors::TriggerUncaughtException(env->isolate, local_err, local_msg);
121
}
122
123
class ThreadSafeFunction : public node::AsyncResource {
124
 public:
125
  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
                     napi_threadsafe_function_call_js call_js_cb_):
135
                     AsyncResource(env_->isolate,
136
                                   resource,
137
                                   *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
      call_js_cb(call_js_cb_ == nullptr ? CallJs : call_js_cb_),
146
      handles_closing(false) {
147
    ref.Reset(env->isolate, func);
148
    node::AddEnvironmentCleanupHook(env->isolate, Cleanup, this);
149
    env->Ref();
150
  }
151
152
  ~ThreadSafeFunction() override {
153
    node::RemoveEnvironmentCleanupHook(env->isolate, Cleanup, this);
154
    env->Unref();
155
  }
156
157
  // These methods can be called from any thread.
158
159
  napi_status Push(void* data, napi_threadsafe_function_call_mode mode) {
160
    node::Mutex::ScopedLock lock(this->mutex);
161
162
    while (queue.size() >= max_queue_size &&
163
        max_queue_size > 0 &&
164
        !is_closing) {
165
      if (mode == napi_tsfn_nonblocking) {
166
        return napi_queue_full;
167
      }
168
      cond->Wait(lock);
169
    }
170
171
    if (is_closing) {
172
      if (thread_count == 0) {
173
        return napi_invalid_arg;
174
      } else {
175
        thread_count--;
176
        return napi_closing;
177
      }
178
    } else {
179
      if (uv_async_send(&async) != 0) {
180
        return napi_generic_failure;
181
      }
182
      queue.push(data);
183
      return napi_ok;
184
    }
185
  }
186
187
  napi_status Acquire() {
188
    node::Mutex::ScopedLock lock(this->mutex);
189
190
    if (is_closing) {
191
      return napi_closing;
192
    }
193
194
    thread_count++;
195
196
    return napi_ok;
197
  }
198
199
  napi_status Release(napi_threadsafe_function_release_mode mode) {
200
    node::Mutex::ScopedLock lock(this->mutex);
201
202
    if (thread_count == 0) {
203
      return napi_invalid_arg;
204
    }
205
206
    thread_count--;
207
208
    if (thread_count == 0 || mode == napi_tsfn_abort) {
209
      if (!is_closing) {
210
        is_closing = (mode == napi_tsfn_abort);
211
        if (is_closing && max_queue_size > 0) {
212
          cond->Signal(lock);
213
        }
214
        if (uv_async_send(&async) != 0) {
215
          return napi_generic_failure;
216
        }
217
      }
218
    }
219
220
    return napi_ok;
221
  }
222
223
  void EmptyQueueAndDelete() {
224
    for (; !queue.empty() ; queue.pop()) {
225
      call_js_cb(nullptr, nullptr, context, queue.front());
226
    }
227
    delete this;
228
  }
229
230
  // These methods must only be called from the loop thread.
231
232
  napi_status Init() {
233
    ThreadSafeFunction* ts_fn = this;
234
    uv_loop_t* loop = env->node_env()->event_loop();
235
236
    if (uv_async_init(loop, &async, AsyncCb) == 0) {
237
      if (max_queue_size > 0) {
238
        cond = std::make_unique<node::ConditionVariable>();
239
      }
240
      if (max_queue_size == 0 || cond) {
241
        CHECK_EQ(0, uv_idle_init(loop, &idle));
242
        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
  napi_status Unref() {
265
    uv_unref(reinterpret_cast<uv_handle_t*>(&async));
266
    uv_unref(reinterpret_cast<uv_handle_t*>(&idle));
267
268
    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
  void DispatchOne() {
279
    void* data = nullptr;
280
    bool popped_value = false;
281
282
    {
283
      node::Mutex::ScopedLock lock(this->mutex);
284
      if (is_closing) {
285
        CloseHandlesAndMaybeDelete();
286
      } else {
287
        size_t size = queue.size();
288
        if (size > 0) {
289
          data = queue.front();
290
          queue.pop();
291
          popped_value = true;
292
          if (size == max_queue_size && max_queue_size > 0) {
293
            cond->Signal(lock);
294
          }
295
          size--;
296
        }
297
298
        if (size == 0) {
299
          if (thread_count == 0) {
300
            is_closing = true;
301
            if (max_queue_size > 0) {
302
              cond->Signal(lock);
303
            }
304
            CloseHandlesAndMaybeDelete();
305
          } else {
306
            CHECK_EQ(0, uv_idle_stop(&idle));
307
          }
308
        }
309
      }
310
    }
311
312
    if (popped_value) {
313
      v8::HandleScope scope(env->isolate);
314
      CallbackScope cb_scope(this);
315
      napi_value js_callback = nullptr;
316
      if (!ref.IsEmpty()) {
317
        v8::Local<v8::Function> js_cb =
318
          v8::Local<v8::Function>::New(env->isolate, ref);
319
        js_callback = v8impl::JsValueFromV8LocalValue(js_cb);
320
      }
321
      env->CallIntoModule([&](napi_env env) {
322
        call_js_cb(env, js_callback, context, data);
323
      });
324
    }
325
  }
326
327
  void Finalize() {
328
    v8::HandleScope scope(env->isolate);
329
    if (finalize_cb) {
330
      CallbackScope cb_scope(this);
331
      env->CallIntoModule([&](napi_env env) {
332
        finalize_cb(env, finalize_data, context);
333
      });
334
    }
335
    EmptyQueueAndDelete();
336
  }
337
338
  inline void* Context() {
339
    return context;
340
  }
341
342
  void CloseHandlesAndMaybeDelete(bool set_closing = false) {
343
    v8::HandleScope scope(env->isolate);
344
    if (set_closing) {
345
      node::Mutex::ScopedLock lock(this->mutex);
346
      is_closing = true;
347
      if (max_queue_size > 0) {
348
        cond->Signal(lock);
349
      }
350
    }
351
    if (handles_closing) {
352
      return;
353
    }
354
    handles_closing = true;
355
    env->node_env()->CloseHandle(
356
        reinterpret_cast<uv_handle_t*>(&async),
357
        [](uv_handle_t* handle) -> void {
358
          ThreadSafeFunction* ts_fn =
359
              node::ContainerOf(&ThreadSafeFunction::async,
360
                                reinterpret_cast<uv_async_t*>(handle));
361
          v8::HandleScope scope(ts_fn->env->isolate);
362
          ts_fn->env->node_env()->CloseHandle(
363
              reinterpret_cast<uv_handle_t*>(&ts_fn->idle),
364
              [](uv_handle_t* handle) -> void {
365
                ThreadSafeFunction* ts_fn =
366
                    node::ContainerOf(&ThreadSafeFunction::idle,
367
                                      reinterpret_cast<uv_idle_t*>(handle));
368
                ts_fn->Finalize();
369
              });
370
        });
371
  }
372
373
  // Default way of calling into JavaScript. Used when ThreadSafeFunction is
374
  //  without a call_js_cb_.
375
  static void CallJs(napi_env env, napi_value cb, void* context, void* data) {
376
    if (!(env == nullptr || cb == nullptr)) {
377
      napi_value recv;
378
      napi_status status;
379
380
      status = napi_get_undefined(env, &recv);
381
      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
      status = napi_call_function(env, recv, cb, 0, nullptr, nullptr);
388
      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
  static void IdleCb(uv_idle_t* idle) {
397
    ThreadSafeFunction* ts_fn =
398
        node::ContainerOf(&ThreadSafeFunction::idle, idle);
399
    ts_fn->DispatchOne();
400
  }
401
402
  static void AsyncCb(uv_async_t* async) {
403
    ThreadSafeFunction* ts_fn =
404
        node::ContainerOf(&ThreadSafeFunction::async, async);
405
    CHECK_EQ(0, uv_idle_start(&ts_fn->idle, IdleCb));
406
  }
407
408
  static void Cleanup(void* data) {
409
    reinterpret_cast<ThreadSafeFunction*>(data)
410
        ->CloseHandlesAndMaybeDelete(true);
411
  }
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
  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
      : env_(env) {
450
    async_id_ = node_env()->new_async_id();
451
    trigger_async_id_ = node_env()->get_default_trigger_async_id();
452
    resource_.Reset(node_env()->isolate(), resource_object);
453
    lost_reference_ = false;
454
    if (externally_managed_resource) {
455
      resource_.SetWeak(
456
          this, AsyncContext::WeakCallback, v8::WeakCallbackType::kParameter);
457
    }
458
459
    node::AsyncWrap::EmitAsyncInit(node_env(),
460
                                   resource_object,
461
                                   resource_name,
462
                                   async_id_,
463
                                   trigger_async_id_);
464
  }
465
466
  ~AsyncContext() {
467
    resource_.Reset();
468
    lost_reference_ = true;
469
    node::AsyncWrap::EmitDestroy(node_env(), async_id_);
470
  }
471
472
  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
    EnsureReference();
478
    return node::InternalMakeCallback(node_env(),
479
                                      resource(),
480
                                      recv,
481
                                      callback,
482
                                      argc,
483
                                      argv,
484
                                      {async_id_, trigger_async_id_});
485
  }
486
487
  inline napi_callback_scope OpenCallbackScope() {
488
    EnsureReference();
489
    napi_callback_scope it =
490
        reinterpret_cast<napi_callback_scope>(new CallbackScope(this));
491
    env_->open_callback_scopes++;
492
    return it;
493
  }
494
495
  inline void EnsureReference() {
496
    if (lost_reference_) {
497
      const v8::HandleScope handle_scope(node_env()->isolate());
498
      resource_.Reset(node_env()->isolate(),
499
                      v8::Object::New(node_env()->isolate()));
500
      lost_reference_ = false;
501
    }
502
  }
503
504
  inline node::Environment* node_env() { return env_->node_env(); }
505
  inline v8::Local<v8::Object> resource() {
506
    return resource_.Get(node_env()->isolate());
507
  }
508
  inline node::async_context async_context() {
509
    return {async_id_, trigger_async_id_};
510
  }
511
512
  static inline void CloseCallbackScope(node_napi_env env,
513
                                        napi_callback_scope s) {
514
    CallbackScope* callback_scope = reinterpret_cast<CallbackScope*>(s);
515
    delete callback_scope;
516
    env->open_callback_scopes--;
517
  }
518
519
  static void WeakCallback(const v8::WeakCallbackInfo<AsyncContext>& data) {
520
    AsyncContext* async_context = data.GetParameter();
521
    async_context->resource_.Reset();
522
    async_context->lost_reference_ = true;
523
  }
524
525
 private:
526
  class CallbackScope : public node::CallbackScope {
527
   public:
528
    explicit CallbackScope(AsyncContext* async_context)
529
        : node::CallbackScope(async_context->node_env()->isolate(),
530
                              async_context->resource_.Get(
531
                                  async_context->node_env()->isolate()),
532
                              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
2
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
2
      static_cast<const napi_module*>(priv)->nm_register_func);
555
2
}
556
557
2
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
2
  node::Environment* node_env = node::Environment::GetCurrent(context);
562
4
  std::string module_filename = "";
563
2
  if (init == nullptr) {
564
    CHECK_NOT_NULL(node_env);
565
    node_env->ThrowError(
566
        "Module has no declared entry point.");
567
    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

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

10
      modobj->Get(context, node_env->filename_string()).ToLocal(&filename_js) &&
578
4
      filename_js->IsString()) {
579
    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
    module_filename = std::string("file://") + (*filename);
586
  }
587
588
  // Create a new napi_env for this specific module.
589
2
  napi_env env = v8impl::NewEnv(context, module_filename);
590
591
  napi_value _exports;
592
6
  env->CallIntoModule([&](napi_env env) {
593
4
    _exports = init(env, v8impl::JsValueFromV8LocalValue(exports));
594
4
  });
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

4
  if (_exports != nullptr &&
600
2
      _exports != v8impl::JsValueFromV8LocalValue(exports)) {
601
    napi_value _module = v8impl::JsValueFromV8LocalValue(module);
602
    napi_set_named_property(env, _module, "exports", _exports);
603
  }
604
}
605
606
namespace node {
607
2
node_module napi_module_to_node_module(const napi_module* mod) {
608
  return {
609
    -1,
610
2
    mod->nm_flags | NM_F_DELETEME,
611
    nullptr,
612
2
    mod->nm_filename,
613
    nullptr,
614
    napi_module_register_cb,
615
2
    mod->nm_modname,
616
    const_cast<napi_module*>(mod),  // priv
617
    nullptr,
618
8
  };
619
}
620
}  // namespace node
621
622
// Registers a NAPI module.
623
void napi_module_register(napi_module* mod) {
624
  node::node_module* nm = new node::node_module(
625
      node::napi_module_to_node_module(mod));
626
  node::node_module_register(nm);
627
}
628
629
napi_status napi_add_env_cleanup_hook(napi_env env,
630
                                      void (*fun)(void* arg),
631
                                      void* arg) {
632
  CHECK_ENV(env);
633
  CHECK_ARG(env, fun);
634
635
  node::AddEnvironmentCleanupHook(env->isolate, fun, arg);
636
637
  return napi_ok;
638
}
639
640
napi_status napi_remove_env_cleanup_hook(napi_env env,
641
                                         void (*fun)(void* arg),
642
                                         void* arg) {
643
  CHECK_ENV(env);
644
  CHECK_ARG(env, fun);
645
646
  node::RemoveEnvironmentCleanupHook(env->isolate, fun, arg);
647
648
  return napi_ok;
649
}
650
651
struct napi_async_cleanup_hook_handle__ {
652
  napi_async_cleanup_hook_handle__(napi_env env,
653
                                   napi_async_cleanup_hook user_hook,
654
                                   void* user_data):
655
      env_(env),
656
      user_hook_(user_hook),
657
      user_data_(user_data) {
658
    handle_ = node::AddEnvironmentCleanupHook(env->isolate, Hook, this);
659
    env->Ref();
660
  }
661
662
  ~napi_async_cleanup_hook_handle__() {
663
    node::RemoveEnvironmentCleanupHook(std::move(handle_));
664
    if (done_cb_ != nullptr)
665
      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
    static_cast<node_napi_env>(env_)->node_env()
670
        ->SetImmediate([env = env_](node::Environment*) { env->Unref(); });
671
  }
672
673
  static void Hook(void* data, void (*done_cb)(void*), void* done_data) {
674
    auto handle = static_cast<napi_async_cleanup_hook_handle__*>(data);
675
    handle->done_cb_ = done_cb;
676
    handle->done_data_ = done_data;
677
    handle->user_hook_(handle, handle->user_data_);
678
  }
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
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
  CHECK_ENV(env);
694
  CHECK_ARG(env, hook);
695
696
  napi_async_cleanup_hook_handle__* handle =
697
    new napi_async_cleanup_hook_handle__(env, hook, arg);
698
699
  if (remove_handle != nullptr)
700
    *remove_handle = handle;
701
702
  return napi_clear_last_error(env);
703
}
704
705
napi_status napi_remove_async_cleanup_hook(
706
    napi_async_cleanup_hook_handle remove_handle) {
707
708
  if (remove_handle == nullptr)
709
    return napi_invalid_arg;
710
711
  delete remove_handle;
712
713
  return napi_ok;
714
}
715
716
napi_status napi_fatal_exception(napi_env env, napi_value err) {
717
  NAPI_PREAMBLE(env);
718
  CHECK_ARG(env, err);
719
720
  v8::Local<v8::Value> local_err = v8impl::V8LocalValueFromJsValue(err);
721
  v8impl::trigger_fatal_exception(env, local_err);
722
723
  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
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
  CHECK_ENV(env);
759
  CHECK_ARG(env, result);
760
761
  v8impl::AsyncContext* node_async_context =
762
      reinterpret_cast<v8impl::AsyncContext*>(async_context_handle);
763
764
  *result = node_async_context->OpenCallbackScope();
765
766
  return napi_clear_last_error(env);
767
}
768
769
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
  CHECK_ENV(env);
773
  CHECK_ARG(env, scope);
774
  if (env->open_callback_scopes == 0) {
775
    return napi_callback_scope_mismatch;
776
  }
777
778
  v8impl::AsyncContext::CloseCallbackScope(reinterpret_cast<node_napi_env>(env),
779
                                           scope);
780
781
  return napi_clear_last_error(env);
782
}
783
784
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
  CHECK_ENV(env);
789
  CHECK_ARG(env, async_resource_name);
790
  CHECK_ARG(env, result);
791
792
  v8::Isolate* isolate = env->isolate;
793
  v8::Local<v8::Context> context = env->context();
794
795
  v8::Local<v8::Object> v8_resource;
796
  bool externally_managed_resource;
797
  if (async_resource != nullptr) {
798
    CHECK_TO_OBJECT(env, context, v8_resource, async_resource);
799
    externally_managed_resource = true;
800
  } else {
801
    v8_resource = v8::Object::New(isolate);
802
    externally_managed_resource = false;
803
  }
804
805
  v8::Local<v8::String> v8_resource_name;
806
  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
                               externally_managed_resource);
813
814
  *result = reinterpret_cast<napi_async_context>(async_context);
815
816
  return napi_clear_last_error(env);
817
}
818
819
napi_status napi_async_destroy(napi_env env,
820
                               napi_async_context async_context) {
821
  CHECK_ENV(env);
822
  CHECK_ARG(env, async_context);
823
824
  v8impl::AsyncContext* node_async_context =
825
      reinterpret_cast<v8impl::AsyncContext*>(async_context);
826
827
  delete node_async_context;
828
829
  return napi_clear_last_error(env);
830
}
831
832
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
  NAPI_PREAMBLE(env);
840
  CHECK_ARG(env, recv);
841
  if (argc > 0) {
842
    CHECK_ARG(env, argv);
843
  }
844
845
  v8::Local<v8::Context> context = env->context();
846
847
  v8::Local<v8::Object> v8recv;
848
  CHECK_TO_OBJECT(env, context, v8recv, recv);
849
850
  v8::Local<v8::Function> v8func;
851
  CHECK_TO_FUNCTION(env, v8func, func);
852
853
  v8::MaybeLocal<v8::Value> callback_result;
854
855
  if (async_context == nullptr) {
856
    callback_result = node::MakeCallback(
857
        env->isolate,
858
        v8recv,
859
        v8func,
860
        argc,
861
        reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)),
862
        {0, 0});
863
  } else {
864
    auto node_async_context =
865
        reinterpret_cast<v8impl::AsyncContext*>(async_context);
866
    callback_result = node_async_context->MakeCallback(
867
        v8recv,
868
        v8func,
869
        argc,
870
        reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)));
871
  }
872
873
  if (try_catch.HasCaught()) {
874
    return napi_set_last_error(env, napi_pending_exception);
875
  } else {
876
    CHECK_MAYBE_EMPTY(env, callback_result, napi_generic_failure);
877
    if (result != nullptr) {
878
      *result = v8impl::JsValueFromV8LocalValue(
879
          callback_result.ToLocalChecked());
880
    }
881
  }
882
883
  return GET_RETURN_STATUS(env);
884
}
885
886
napi_status napi_create_buffer(napi_env env,
887
                               size_t length,
888
                               void** data,
889
                               napi_value* result) {
890
  NAPI_PREAMBLE(env);
891
  CHECK_ARG(env, result);
892
893
  auto maybe = node::Buffer::New(env->isolate, length);
894
895
  CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
896
897
  v8::Local<v8::Object> buffer = maybe.ToLocalChecked();
898
899
  *result = v8impl::JsValueFromV8LocalValue(buffer);
900
901
  if (data != nullptr) {
902
    *data = node::Buffer::Data(buffer);
903
  }
904
905
  return GET_RETURN_STATUS(env);
906
}
907
908
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
  NAPI_PREAMBLE(env);
915
  CHECK_ARG(env, result);
916
917
  v8::Isolate* isolate = env->isolate;
918
919
  // The finalizer object will delete itself after invoking the callback.
920
  v8impl::Finalizer* finalizer = v8impl::Finalizer::New(
921
      env, finalize_cb, nullptr, finalize_hint,
922
      v8impl::Finalizer::kKeepEnvReference);
923
924
  auto maybe = node::Buffer::New(isolate,
925
                                static_cast<char*>(data),
926
                                length,
927
                                v8impl::BufferFinalizer::FinalizeBufferCallback,
928
                                finalizer);
929
930
  CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
931
932
  *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked());
933
  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
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
  NAPI_PREAMBLE(env);
946
  CHECK_ARG(env, result);
947
948
  auto maybe = node::Buffer::Copy(env->isolate,
949
    static_cast<const char*>(data), length);
950
951
  CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
952
953
  v8::Local<v8::Object> buffer = maybe.ToLocalChecked();
954
  *result = v8impl::JsValueFromV8LocalValue(buffer);
955
956
  if (result_data != nullptr) {
957
    *result_data = node::Buffer::Data(buffer);
958
  }
959
960
  return GET_RETURN_STATUS(env);
961
}
962
963
napi_status napi_is_buffer(napi_env env, napi_value value, bool* result) {
964
  CHECK_ENV(env);
965
  CHECK_ARG(env, value);
966
  CHECK_ARG(env, result);
967
968
  *result = node::Buffer::HasInstance(v8impl::V8LocalValueFromJsValue(value));
969
  return napi_clear_last_error(env);
970
}
971
972
napi_status napi_get_buffer_info(napi_env env,
973
                                 napi_value value,
974
                                 void** data,
975
                                 size_t* length) {
976
  CHECK_ENV(env);
977
  CHECK_ARG(env, value);
978
979
  v8::Local<v8::Value> buffer = v8impl::V8LocalValueFromJsValue(value);
980
981
  if (data != nullptr) {
982
    *data = node::Buffer::Data(buffer);
983
  }
984
  if (length != nullptr) {
985
    *length = node::Buffer::Length(buffer);
986
  }
987
988
  return napi_clear_last_error(env);
989
}
990
991
napi_status napi_get_node_version(napi_env env,
992
                                  const napi_node_version** result) {
993
  CHECK_ENV(env);
994
  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
  *result = &version;
1002
  return napi_clear_last_error(env);
1003
}
1004
1005
namespace {
1006
namespace uvimpl {
1007
1008
static napi_status ConvertUVErrorCode(int code) {
1009
  switch (code) {
1010
    case 0:
1011
      return napi_ok;
1012
    case UV_EINVAL:
1013
      return napi_invalid_arg;
1014
    case UV_ECANCELED:
1015
      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
  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
    : AsyncResource(env->isolate,
1031
                    async_resource,
1032
                    *v8::String::Utf8Value(env->isolate, async_resource_name)),
1033
      ThreadPoolWork(env->node_env()),
1034
      _env(env),
1035
      _data(data),
1036
      _execute(execute),
1037
      _complete(complete) {
1038
  }
1039
1040
  ~Work() override = default;
1041
1042
 public:
1043
  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
                    execute, complete, data);
1051
  }
1052
1053
  static void Delete(Work* work) {
1054
    delete work;
1055
  }
1056
1057
  void DoThreadPoolWork() override {
1058
    _execute(_env, _data);
1059
  }
1060
1061
  void AfterThreadPoolWork(int status) override {
1062
    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
    v8::HandleScope scope(_env->isolate);
1068
1069
    CallbackScope callback_scope(this);
1070
1071
    _env->CallIntoModule([&](napi_env env) {
1072
      _complete(env, ConvertUVErrorCode(status), _data);
1073
    }, [](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
      v8impl::trigger_fatal_exception(env, local_err);
1078
    });
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
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
  CHECK_ENV(env);
1111
  CHECK_ARG(env, execute);
1112
  CHECK_ARG(env, result);
1113
1114
  v8::Local<v8::Context> context = env->context();
1115
1116
  v8::Local<v8::Object> resource;
1117
  if (async_resource != nullptr) {
1118
    CHECK_TO_OBJECT(env, context, resource, async_resource);
1119
  } else {
1120
    resource = v8::Object::New(env->isolate);
1121
  }
1122
1123
  v8::Local<v8::String> resource_name;
1124
  CHECK_TO_STRING(env, context, resource_name, async_resource_name);
1125
1126
  uvimpl::Work* work = uvimpl::Work::New(reinterpret_cast<node_napi_env>(env),
1127
                                         resource,
1128
                                         resource_name,
1129
                                         execute,
1130
                                         complete,
1131
                                         data);
1132
1133
  *result = reinterpret_cast<napi_async_work>(work);
1134
1135
  return napi_clear_last_error(env);
1136
}
1137
1138
napi_status napi_delete_async_work(napi_env env, napi_async_work work) {
1139
  CHECK_ENV(env);
1140
  CHECK_ARG(env, work);
1141
1142
  uvimpl::Work::Delete(reinterpret_cast<uvimpl::Work*>(work));
1143
1144
  return napi_clear_last_error(env);
1145
}
1146
1147
napi_status napi_get_uv_event_loop(napi_env env, uv_loop_t** loop) {
1148
  CHECK_ENV(env);
1149
  CHECK_ARG(env, loop);
1150
  *loop = reinterpret_cast<node_napi_env>(env)->node_env()->event_loop();
1151
  return napi_clear_last_error(env);
1152
}
1153
1154
napi_status napi_queue_async_work(napi_env env, napi_async_work work) {
1155
  CHECK_ENV(env);
1156
  CHECK_ARG(env, work);
1157
1158
  uv_loop_t* event_loop = nullptr;
1159
  STATUS_CALL(napi_get_uv_event_loop(env, &event_loop));
1160
1161
  uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work);
1162
1163
  w->ScheduleWork();
1164
1165
  return napi_clear_last_error(env);
1166
}
1167
1168
napi_status napi_cancel_async_work(napi_env env, napi_async_work work) {
1169
  CHECK_ENV(env);
1170
  CHECK_ARG(env, work);
1171
1172
  uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work);
1173
1174
  CALL_UV(env, w->CancelWork());
1175
1176
  return napi_clear_last_error(env);
1177
}
1178
1179
napi_status
1180
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
  CHECK_ENV(env);
1192
  CHECK_ARG(env, async_resource_name);
1193
  RETURN_STATUS_IF_FALSE(env, initial_thread_count > 0, napi_invalid_arg);
1194
  CHECK_ARG(env, result);
1195
1196
  napi_status status = napi_ok;
1197
1198
  v8::Local<v8::Function> v8_func;
1199
  if (func == nullptr) {
1200
    CHECK_ARG(env, call_js_cb);
1201
  } else {
1202
    CHECK_TO_FUNCTION(env, v8_func, func);
1203
  }
1204
1205
  v8::Local<v8::Context> v8_context = env->context();
1206
1207
  v8::Local<v8::Object> v8_resource;
1208
  if (async_resource == nullptr) {
1209
    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
  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
                                     call_js_cb);
1228
1229
  if (ts_fn == nullptr) {
1230
    status = napi_generic_failure;
1231
  } else {
1232
    // Init deletes ts_fn upon failure.
1233
    status = ts_fn->Init();
1234
    if (status == napi_ok) {
1235
      *result = reinterpret_cast<napi_threadsafe_function>(ts_fn);
1236
    }
1237
  }
1238
1239
  return napi_set_last_error(env, status);
1240
}
1241
1242
napi_status
1243
napi_get_threadsafe_function_context(napi_threadsafe_function func,
1244
                                     void** result) {
1245
  CHECK_NOT_NULL(func);
1246
  CHECK_NOT_NULL(result);
1247
1248
  *result = reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Context();
1249
  return napi_ok;
1250
}
1251
1252
napi_status
1253
napi_call_threadsafe_function(napi_threadsafe_function func,
1254
                              void* data,
1255
                              napi_threadsafe_function_call_mode is_blocking) {
1256
  CHECK_NOT_NULL(func);
1257
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Push(data,
1258
                                                                   is_blocking);
1259
}
1260
1261
napi_status
1262
napi_acquire_threadsafe_function(napi_threadsafe_function func) {
1263
  CHECK_NOT_NULL(func);
1264
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Acquire();
1265
}
1266
1267
napi_status
1268
napi_release_threadsafe_function(napi_threadsafe_function func,
1269
                                 napi_threadsafe_function_release_mode mode) {
1270
  CHECK_NOT_NULL(func);
1271
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Release(mode);
1272
}
1273
1274
napi_status
1275
napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
1276
  CHECK_NOT_NULL(func);
1277
  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
napi_status node_api_get_module_file_name(napi_env env, const char** result) {
1287
  CHECK_ENV(env);
1288
  CHECK_ARG(env, result);
1289
1290
  *result = static_cast<node_napi_env>(env)->GetFilename();
1291
  return napi_clear_last_error(env);
1292

366
}