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: 485 533 91.0 %
Date: 2020-09-03 22:13:26 Branches: 216 368 58.7 %

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

51276918
    while (queue.size() >= max_queue_size &&
164

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

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

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

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

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

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

10
      if (status != napi_ok && status != napi_pending_exception) {
390
        napi_throw_error(env, "ERR_NAPI_TSFN_CALL_JS",
391
            "Failed to call JS callback");
392
        return;
393
      }
394
    }
395
  }
396
397
120
  static void IdleCb(uv_idle_t* idle) {
398
    ThreadSafeFunction* ts_fn =
399
120
        node::ContainerOf(&ThreadSafeFunction::idle, idle);
400
120
    ts_fn->DispatchOne();
401
120
  }
402
403
103
  static void AsyncCb(uv_async_t* async) {
404
    ThreadSafeFunction* ts_fn =
405
103
        node::ContainerOf(&ThreadSafeFunction::async, async);
406
103
    CHECK_EQ(0, uv_idle_start(&ts_fn->idle, IdleCb));
407
103
  }
408
409
2
  static void Cleanup(void* data) {
410
    reinterpret_cast<ThreadSafeFunction*>(data)
411
2
        ->CloseHandlesAndMaybeDelete(true);
412
2
  }
413
414
 private:
415
  // These are variables protected by the mutex.
416
  node::Mutex mutex;
417
  std::unique_ptr<node::ConditionVariable> cond;
418
  std::queue<void*> queue;
419
  uv_async_t async;
420
  uv_idle_t idle;
421
  size_t thread_count;
422
  bool is_closing;
423
424
  // These are variables set once, upon creation, and then never again, which
425
  // means we don't need the mutex to read them.
426
  void* context;
427
  size_t max_queue_size;
428
429
  // These are variables accessed only from the loop thread.
430
  v8impl::Persistent<v8::Function> ref;
431
  node_napi_env env;
432
  void* finalize_data;
433
  napi_finalize finalize_cb;
434
  napi_threadsafe_function_call_js call_js_cb;
435
  bool handles_closing;
436
};
437
438
}  // end of anonymous namespace
439
440
}  // end of namespace v8impl
441
442
// Intercepts the Node-V8 module registration callback. Converts parameters
443
// to NAPI equivalents and then calls the registration callback specified
444
// by the NAPI module.
445
76
static void napi_module_register_cb(v8::Local<v8::Object> exports,
446
                                    v8::Local<v8::Value> module,
447
                                    v8::Local<v8::Context> context,
448
                                    void* priv) {
449
76
  napi_module_register_by_symbol(exports, module, context,
450
76
      static_cast<napi_module*>(priv)->nm_register_func);
451
76
}
452
453
79
void napi_module_register_by_symbol(v8::Local<v8::Object> exports,
454
                                    v8::Local<v8::Value> module,
455
                                    v8::Local<v8::Context> context,
456
                                    napi_addon_register_func init) {
457
79
  if (init == nullptr) {
458
1
    node::Environment* node_env = node::Environment::GetCurrent(context);
459
1
    CHECK_NOT_NULL(node_env);
460
1
    node_env->ThrowError(
461
1
        "Module has no declared entry point.");
462
1
    return;
463
  }
464
465
  // Create a new napi_env for this specific module.
466
78
  napi_env env = v8impl::NewEnv(context);
467
468
  napi_value _exports;
469
234
  env->CallIntoModule([&](napi_env env) {
470
156
    _exports = init(env, v8impl::JsValueFromV8LocalValue(exports));
471
156
  });
472
473
  // If register function returned a non-null exports object different from
474
  // the exports object we passed it, set that as the "exports" property of
475
  // the module.
476

231
  if (_exports != nullptr &&
477
153
      _exports != v8impl::JsValueFromV8LocalValue(exports)) {
478
4
    napi_value _module = v8impl::JsValueFromV8LocalValue(module);
479
4
    napi_set_named_property(env, _module, "exports", _exports);
480
  }
481
}
482
483
// Registers a NAPI module.
484
75
void napi_module_register(napi_module* mod) {
485
  node::node_module* nm = new node::node_module {
486
    -1,
487
75
    mod->nm_flags | NM_F_DELETEME,
488
    nullptr,
489
75
    mod->nm_filename,
490
    nullptr,
491
    napi_module_register_cb,
492
75
    mod->nm_modname,
493
    mod,  // priv
494
    nullptr,
495
300
  };
496
75
  node::node_module_register(nm);
497
75
}
498
499
2
napi_status napi_add_env_cleanup_hook(napi_env env,
500
                                      void (*fun)(void* arg),
501
                                      void* arg) {
502
2
  CHECK_ENV(env);
503
2
  CHECK_ARG(env, fun);
504
505
2
  node::AddEnvironmentCleanupHook(env->isolate, fun, arg);
506
507
2
  return napi_ok;
508
}
509
510
1
napi_status napi_remove_env_cleanup_hook(napi_env env,
511
                                         void (*fun)(void* arg),
512
                                         void* arg) {
513
1
  CHECK_ENV(env);
514
1
  CHECK_ARG(env, fun);
515
516
1
  node::RemoveEnvironmentCleanupHook(env->isolate, fun, arg);
517
518
1
  return napi_ok;
519
}
520
521
struct napi_async_cleanup_hook_handle__ {
522
6
  napi_async_cleanup_hook_handle__(napi_env env,
523
                                   napi_async_cleanup_hook user_hook,
524
6
                                   void* user_data):
525
      env_(env),
526
      user_hook_(user_hook),
527
6
      user_data_(user_data) {
528
6
    handle_ = node::AddEnvironmentCleanupHook(env->isolate, Hook, this);
529
6
    env->Ref();
530
6
  }
531
532
12
  ~napi_async_cleanup_hook_handle__() {
533
6
    node::RemoveEnvironmentCleanupHook(std::move(handle_));
534
6
    if (done_cb_ != nullptr)
535
4
      done_cb_(done_data_);
536
537
    // Release the `env` handle asynchronously since it would be surprising if
538
    // a call to a N-API function would destroy `env` synchronously.
539
6
    static_cast<node_napi_env>(env_)->node_env()
540
18
        ->SetImmediate([env = env_](node::Environment*) { env->Unref(); });
541
6
  }
542
543
4
  static void Hook(void* data, void (*done_cb)(void*), void* done_data) {
544
4
    auto handle = static_cast<napi_async_cleanup_hook_handle__*>(data);
545
4
    handle->done_cb_ = done_cb;
546
4
    handle->done_data_ = done_data;
547
4
    handle->user_hook_(handle, handle->user_data_);
548
4
  }
549
550
  node::AsyncCleanupHookHandle handle_;
551
  napi_env env_ = nullptr;
552
  napi_async_cleanup_hook user_hook_ = nullptr;
553
  void* user_data_ = nullptr;
554
  void (*done_cb_)(void*) = nullptr;
555
  void* done_data_ = nullptr;
556
};
557
558
6
napi_status napi_add_async_cleanup_hook(
559
    napi_env env,
560
    napi_async_cleanup_hook hook,
561
    void* arg,
562
    napi_async_cleanup_hook_handle* remove_handle) {
563
6
  CHECK_ENV(env);
564
6
  CHECK_ARG(env, hook);
565
566
  napi_async_cleanup_hook_handle__* handle =
567
6
    new napi_async_cleanup_hook_handle__(env, hook, arg);
568
569
6
  if (remove_handle != nullptr)
570
4
    *remove_handle = handle;
571
572
6
  return napi_clear_last_error(env);
573
}
574
575
6
napi_status napi_remove_async_cleanup_hook(
576
    napi_async_cleanup_hook_handle remove_handle) {
577
578
6
  if (remove_handle == nullptr)
579
    return napi_invalid_arg;
580
581
6
  delete remove_handle;
582
583
6
  return napi_ok;
584
}
585
586
1
napi_status napi_fatal_exception(napi_env env, napi_value err) {
587


3
  NAPI_PREAMBLE(env);
588
1
  CHECK_ARG(env, err);
589
590
1
  v8::Local<v8::Value> local_err = v8impl::V8LocalValueFromJsValue(err);
591
1
  v8impl::trigger_fatal_exception(env, local_err);
592
593
1
  return napi_clear_last_error(env);
594
}
595
596
NAPI_NO_RETURN void napi_fatal_error(const char* location,
597
                                     size_t location_len,
598
                                     const char* message,
599
                                     size_t message_len) {
600
  std::string location_string;
601
  std::string message_string;
602
603
  if (location_len != NAPI_AUTO_LENGTH) {
604
    location_string.assign(
605
        const_cast<char*>(location), location_len);
606
  } else {
607
    location_string.assign(
608
        const_cast<char*>(location), strlen(location));
609
  }
610
611
  if (message_len != NAPI_AUTO_LENGTH) {
612
    message_string.assign(
613
        const_cast<char*>(message), message_len);
614
  } else {
615
    message_string.assign(
616
        const_cast<char*>(message), strlen(message));
617
  }
618
619
  node::FatalError(location_string.c_str(), message_string.c_str());
620
}
621
622
4
napi_status napi_open_callback_scope(napi_env env,
623
                                     napi_value resource_object,
624
                                     napi_async_context async_context_handle,
625
                                     napi_callback_scope* result) {
626
  // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
627
  // JS exceptions.
628
4
  CHECK_ENV(env);
629
4
  CHECK_ARG(env, result);
630
631
4
  v8::Local<v8::Context> context = env->context();
632
633
  node::async_context* node_async_context =
634
4
      reinterpret_cast<node::async_context*>(async_context_handle);
635
636
  v8::Local<v8::Object> resource;
637

16
  CHECK_TO_OBJECT(env, context, resource, resource_object);
638
639
8
  *result = v8impl::JsCallbackScopeFromV8CallbackScope(
640
4
      new node::CallbackScope(env->isolate,
641
                              resource,
642
8
                              *node_async_context));
643
644
4
  env->open_callback_scopes++;
645
4
  return napi_clear_last_error(env);
646
}
647
648
4
napi_status napi_close_callback_scope(napi_env env, napi_callback_scope scope) {
649
  // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
650
  // JS exceptions.
651
4
  CHECK_ENV(env);
652
4
  CHECK_ARG(env, scope);
653
4
  if (env->open_callback_scopes == 0) {
654
    return napi_callback_scope_mismatch;
655
  }
656
657
4
  env->open_callback_scopes--;
658
4
  delete v8impl::V8CallbackScopeFromJsCallbackScope(scope);
659
4
  return napi_clear_last_error(env);
660
}
661
662
11
napi_status napi_async_init(napi_env env,
663
                            napi_value async_resource,
664
                            napi_value async_resource_name,
665
                            napi_async_context* result) {
666
11
  CHECK_ENV(env);
667
11
  CHECK_ARG(env, async_resource_name);
668
11
  CHECK_ARG(env, result);
669
670
11
  v8::Isolate* isolate = env->isolate;
671
11
  v8::Local<v8::Context> context = env->context();
672
673
  v8::Local<v8::Object> v8_resource;
674
11
  if (async_resource != nullptr) {
675

40
    CHECK_TO_OBJECT(env, context, v8_resource, async_resource);
676
  } else {
677
1
    v8_resource = v8::Object::New(isolate);
678
  }
679
680
  v8::Local<v8::String> v8_resource_name;
681

44
  CHECK_TO_STRING(env, context, v8_resource_name, async_resource_name);
682
683
  // TODO(jasongin): Consider avoiding allocation here by using
684
  // a tagged pointer with 2×31 bit fields instead.
685
11
  node::async_context* async_context = new node::async_context();
686
687
11
  *async_context = node::EmitAsyncInit(isolate, v8_resource, v8_resource_name);
688
11
  *result = reinterpret_cast<napi_async_context>(async_context);
689
690
11
  return napi_clear_last_error(env);
691
}
692
693
11
napi_status napi_async_destroy(napi_env env,
694
                               napi_async_context async_context) {
695
11
  CHECK_ENV(env);
696
11
  CHECK_ARG(env, async_context);
697
698
  node::async_context* node_async_context =
699
11
      reinterpret_cast<node::async_context*>(async_context);
700
11
  node::EmitAsyncDestroy(
701
      reinterpret_cast<node_napi_env>(env)->node_env(),
702
11
      *node_async_context);
703
704
11
  delete node_async_context;
705
706
11
  return napi_clear_last_error(env);
707
}
708
709
23
napi_status napi_make_callback(napi_env env,
710
                               napi_async_context async_context,
711
                               napi_value recv,
712
                               napi_value func,
713
                               size_t argc,
714
                               const napi_value* argv,
715
                               napi_value* result) {
716


69
  NAPI_PREAMBLE(env);
717
23
  CHECK_ARG(env, recv);
718
23
  if (argc > 0) {
719
4
    CHECK_ARG(env, argv);
720
  }
721
722
23
  v8::Local<v8::Context> context = env->context();
723
724
  v8::Local<v8::Object> v8recv;
725

92
  CHECK_TO_OBJECT(env, context, v8recv, recv);
726
727
  v8::Local<v8::Function> v8func;
728

69
  CHECK_TO_FUNCTION(env, v8func, func);
729
730
  node::async_context* node_async_context =
731
23
    reinterpret_cast<node::async_context*>(async_context);
732
23
  if (node_async_context == nullptr) {
733
    static node::async_context empty_context = { 0, 0 };
734
17
    node_async_context = &empty_context;
735
  }
736
737
  v8::MaybeLocal<v8::Value> callback_result = node::MakeCallback(
738
23
      env->isolate, v8recv, v8func, argc,
739
      reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)),
740
46
      *node_async_context);
741
742
23
  if (try_catch.HasCaught()) {
743
4
    return napi_set_last_error(env, napi_pending_exception);
744
  } else {
745
19
    CHECK_MAYBE_EMPTY(env, callback_result, napi_generic_failure);
746
19
    if (result != nullptr) {
747
6
      *result = v8impl::JsValueFromV8LocalValue(
748
          callback_result.ToLocalChecked());
749
    }
750
  }
751
752
19
  return GET_RETURN_STATUS(env);
753
}
754
755
1
napi_status napi_create_buffer(napi_env env,
756
                               size_t length,
757
                               void** data,
758
                               napi_value* result) {
759


3
  NAPI_PREAMBLE(env);
760
1
  CHECK_ARG(env, result);
761
762
1
  auto maybe = node::Buffer::New(env->isolate, length);
763
764
1
  CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
765
766
1
  v8::Local<v8::Object> buffer = maybe.ToLocalChecked();
767
768
2
  *result = v8impl::JsValueFromV8LocalValue(buffer);
769
770
1
  if (data != nullptr) {
771
1
    *data = node::Buffer::Data(buffer);
772
  }
773
774
1
  return GET_RETURN_STATUS(env);
775
}
776
777
13
napi_status napi_create_external_buffer(napi_env env,
778
                                        size_t length,
779
                                        void* data,
780
                                        napi_finalize finalize_cb,
781
                                        void* finalize_hint,
782
                                        napi_value* result) {
783


39
  NAPI_PREAMBLE(env);
784
13
  CHECK_ARG(env, result);
785
786
13
  v8::Isolate* isolate = env->isolate;
787
788
  // The finalizer object will delete itself after invoking the callback.
789
13
  v8impl::Finalizer* finalizer = v8impl::Finalizer::New(
790
      env, finalize_cb, nullptr, finalize_hint,
791
13
      v8impl::Finalizer::kKeepEnvReference);
792
793
  auto maybe = node::Buffer::New(isolate,
794
                                static_cast<char*>(data),
795
                                length,
796
                                v8impl::BufferFinalizer::FinalizeBufferCallback,
797
13
                                finalizer);
798
799
13
  CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
800
801
26
  *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked());
802
13
  return GET_RETURN_STATUS(env);
803
  // Tell coverity that 'finalizer' should not be freed when we return
804
  // as it will be deleted when the buffer to which it is associated
805
  // is finalized.
806
  // coverity[leaked_storage]
807
}
808
809
1
napi_status napi_create_buffer_copy(napi_env env,
810
                                    size_t length,
811
                                    const void* data,
812
                                    void** result_data,
813
                                    napi_value* result) {
814


3
  NAPI_PREAMBLE(env);
815
1
  CHECK_ARG(env, result);
816
817
1
  auto maybe = node::Buffer::Copy(env->isolate,
818
2
    static_cast<const char*>(data), length);
819
820
1
  CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
821
822
1
  v8::Local<v8::Object> buffer = maybe.ToLocalChecked();
823
2
  *result = v8impl::JsValueFromV8LocalValue(buffer);
824
825
1
  if (result_data != nullptr) {
826
    *result_data = node::Buffer::Data(buffer);
827
  }
828
829
1
  return GET_RETURN_STATUS(env);
830
}
831
832
1
napi_status napi_is_buffer(napi_env env, napi_value value, bool* result) {
833
1
  CHECK_ENV(env);
834
1
  CHECK_ARG(env, value);
835
1
  CHECK_ARG(env, result);
836
837
1
  *result = node::Buffer::HasInstance(v8impl::V8LocalValueFromJsValue(value));
838
1
  return napi_clear_last_error(env);
839
}
840
841
1
napi_status napi_get_buffer_info(napi_env env,
842
                                 napi_value value,
843
                                 void** data,
844
                                 size_t* length) {
845
1
  CHECK_ENV(env);
846
1
  CHECK_ARG(env, value);
847
848
1
  v8::Local<v8::Value> buffer = v8impl::V8LocalValueFromJsValue(value);
849
850
1
  if (data != nullptr) {
851
1
    *data = node::Buffer::Data(buffer);
852
  }
853
1
  if (length != nullptr) {
854
1
    *length = node::Buffer::Length(buffer);
855
  }
856
857
1
  return napi_clear_last_error(env);
858
}
859
860
1
napi_status napi_get_node_version(napi_env env,
861
                                  const napi_node_version** result) {
862
1
  CHECK_ENV(env);
863
1
  CHECK_ARG(env, result);
864
  static const napi_node_version version = {
865
    NODE_MAJOR_VERSION,
866
    NODE_MINOR_VERSION,
867
    NODE_PATCH_VERSION,
868
    NODE_RELEASE
869
  };
870
1
  *result = &version;
871
1
  return napi_clear_last_error(env);
872
}
873
874
namespace {
875
namespace uvimpl {
876
877
513
static napi_status ConvertUVErrorCode(int code) {
878

513
  switch (code) {
879
    case 0:
880
512
      return napi_ok;
881
    case UV_EINVAL:
882
      return napi_invalid_arg;
883
    case UV_ECANCELED:
884
1
      return napi_cancelled;
885
    default:
886
      return napi_generic_failure;
887
  }
888
}
889
890
// Wrapper around uv_work_t which calls user-provided callbacks.
891
class Work : public node::AsyncResource, public node::ThreadPoolWork {
892
 private:
893
512
  explicit Work(node_napi_env env,
894
                v8::Local<v8::Object> async_resource,
895
                v8::Local<v8::String> async_resource_name,
896
                napi_async_execute_callback execute,
897
                napi_async_complete_callback complete = nullptr,
898
                void* data = nullptr)
899
1024
    : AsyncResource(env->isolate,
900
                    async_resource,
901
1024
                    *v8::String::Utf8Value(env->isolate, async_resource_name)),
902
      ThreadPoolWork(env->node_env()),
903
      _env(env),
904
      _data(data),
905
      _execute(execute),
906
1024
      _complete(complete) {
907
512
  }
908
909
1016
  ~Work() override = default;
910
911
 public:
912
512
  static Work* New(node_napi_env env,
913
                   v8::Local<v8::Object> async_resource,
914
                   v8::Local<v8::String> async_resource_name,
915
                   napi_async_execute_callback execute,
916
                   napi_async_complete_callback complete,
917
                   void* data) {
918
    return new Work(env, async_resource, async_resource_name,
919
512
                    execute, complete, data);
920
  }
921
922
508
  static void Delete(Work* work) {
923
508
    delete work;
924
508
  }
925
926
511
  void DoThreadPoolWork() override {
927
511
    _execute(_env, _data);
928
511
  }
929
930
512
  void AfterThreadPoolWork(int status) override {
931
512
    if (_complete == nullptr)
932
      return;
933
934
    // Establish a handle scope here so that every callback doesn't have to.
935
    // Also it is needed for the exception-handling below.
936
1023
    v8::HandleScope scope(_env->isolate);
937
938
1023
    CallbackScope callback_scope(this);
939
940
1535
    _env->CallIntoModule([&](napi_env env) {
941
512
      _complete(env, ConvertUVErrorCode(status), _data);
942
514
    }, [](napi_env env, v8::Local<v8::Value> local_err) {
943
      // If there was an unhandled exception in the complete callback,
944
      // report it as a fatal exception. (There is no JavaScript on the
945
      // callstack that can possibly handle it.)
946
2
      v8impl::trigger_fatal_exception(env, local_err);
947
513
    });
948
949
    // Note: Don't access `work` after this point because it was
950
    // likely deleted by the complete callback.
951
  }
952
953
 private:
954
  node_napi_env _env;
955
  void* _data;
956
  napi_async_execute_callback _execute;
957
  napi_async_complete_callback _complete;
958
};
959
960
}  // end of namespace uvimpl
961
}  // end of anonymous namespace
962
963
#define CALL_UV(env, condition)                                         \
964
  do {                                                                  \
965
    int result = (condition);                                           \
966
    napi_status status = uvimpl::ConvertUVErrorCode(result);            \
967
    if (status != napi_ok) {                                            \
968
      return napi_set_last_error(env, status, result);                  \
969
    }                                                                   \
970
  } while (0)
971
972
512
napi_status napi_create_async_work(napi_env env,
973
                                   napi_value async_resource,
974
                                   napi_value async_resource_name,
975
                                   napi_async_execute_callback execute,
976
                                   napi_async_complete_callback complete,
977
                                   void* data,
978
                                   napi_async_work* result) {
979
512
  CHECK_ENV(env);
980
512
  CHECK_ARG(env, execute);
981
512
  CHECK_ARG(env, result);
982
983
512
  v8::Local<v8::Context> context = env->context();
984
985
  v8::Local<v8::Object> resource;
986
512
  if (async_resource != nullptr) {
987

16
    CHECK_TO_OBJECT(env, context, resource, async_resource);
988
  } else {
989
508
    resource = v8::Object::New(env->isolate);
990
  }
991
992
  v8::Local<v8::String> resource_name;
993

2048
  CHECK_TO_STRING(env, context, resource_name, async_resource_name);
994
995
512
  uvimpl::Work* work = uvimpl::Work::New(reinterpret_cast<node_napi_env>(env),
996
                                         resource,
997
                                         resource_name,
998
                                         execute,
999
                                         complete,
1000
512
                                         data);
1001
1002
512
  *result = reinterpret_cast<napi_async_work>(work);
1003
1004
512
  return napi_clear_last_error(env);
1005
}
1006
1007
508
napi_status napi_delete_async_work(napi_env env, napi_async_work work) {
1008
508
  CHECK_ENV(env);
1009
508
  CHECK_ARG(env, work);
1010
1011
508
  uvimpl::Work::Delete(reinterpret_cast<uvimpl::Work*>(work));
1012
1013
508
  return napi_clear_last_error(env);
1014
}
1015
1016
518
napi_status napi_get_uv_event_loop(napi_env env, uv_loop_t** loop) {
1017
518
  CHECK_ENV(env);
1018
518
  CHECK_ARG(env, loop);
1019
518
  *loop = reinterpret_cast<node_napi_env>(env)->node_env()->event_loop();
1020
518
  return napi_clear_last_error(env);
1021
}
1022
1023
512
napi_status napi_queue_async_work(napi_env env, napi_async_work work) {
1024
512
  CHECK_ENV(env);
1025
512
  CHECK_ARG(env, work);
1026
1027
  napi_status status;
1028
512
  uv_loop_t* event_loop = nullptr;
1029
512
  status = napi_get_uv_event_loop(env, &event_loop);
1030
512
  if (status != napi_ok)
1031
    return napi_set_last_error(env, status);
1032
1033
512
  uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work);
1034
1035
512
  w->ScheduleWork();
1036
1037
512
  return napi_clear_last_error(env);
1038
}
1039
1040
1
napi_status napi_cancel_async_work(napi_env env, napi_async_work work) {
1041
1
  CHECK_ENV(env);
1042
1
  CHECK_ARG(env, work);
1043
1044
1
  uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work);
1045
1046
1
  CALL_UV(env, w->CancelWork());
1047
1048
1
  return napi_clear_last_error(env);
1049
}
1050
1051
napi_status
1052
17
napi_create_threadsafe_function(napi_env env,
1053
                                napi_value func,
1054
                                napi_value async_resource,
1055
                                napi_value async_resource_name,
1056
                                size_t max_queue_size,
1057
                                size_t initial_thread_count,
1058
                                void* thread_finalize_data,
1059
                                napi_finalize thread_finalize_cb,
1060
                                void* context,
1061
                                napi_threadsafe_function_call_js call_js_cb,
1062
                                napi_threadsafe_function* result) {
1063
17
  CHECK_ENV(env);
1064
17
  CHECK_ARG(env, async_resource_name);
1065
17
  RETURN_STATUS_IF_FALSE(env, initial_thread_count > 0, napi_invalid_arg);
1066
17
  CHECK_ARG(env, result);
1067
1068
17
  napi_status status = napi_ok;
1069
1070
  v8::Local<v8::Function> v8_func;
1071
17
  if (func == nullptr) {
1072
3
    CHECK_ARG(env, call_js_cb);
1073
  } else {
1074

42
    CHECK_TO_FUNCTION(env, v8_func, func);
1075
  }
1076
1077
17
  v8::Local<v8::Context> v8_context = env->context();
1078
1079
  v8::Local<v8::Object> v8_resource;
1080
17
  if (async_resource == nullptr) {
1081
17
    v8_resource = v8::Object::New(env->isolate);
1082
  } else {
1083
    CHECK_TO_OBJECT(env, v8_context, v8_resource, async_resource);
1084
  }
1085
1086
  v8::Local<v8::String> v8_name;
1087

68
  CHECK_TO_STRING(env, v8_context, v8_name, async_resource_name);
1088
1089
  v8impl::ThreadSafeFunction* ts_fn =
1090
      new v8impl::ThreadSafeFunction(v8_func,
1091
                                     v8_resource,
1092
                                     v8_name,
1093
                                     initial_thread_count,
1094
                                     context,
1095
                                     max_queue_size,
1096
                                     reinterpret_cast<node_napi_env>(env),
1097
                                     thread_finalize_data,
1098
                                     thread_finalize_cb,
1099
17
                                     call_js_cb);
1100
1101
17
  if (ts_fn == nullptr) {
1102
    status = napi_generic_failure;
1103
  } else {
1104
    // Init deletes ts_fn upon failure.
1105
17
    status = ts_fn->Init();
1106
17
    if (status == napi_ok) {
1107
17
      *result = reinterpret_cast<napi_threadsafe_function>(ts_fn);
1108
    }
1109
  }
1110
1111
17
  return napi_set_last_error(env, status);
1112
}
1113
1114
napi_status
1115
15
napi_get_threadsafe_function_context(napi_threadsafe_function func,
1116
                                     void** result) {
1117
15
  CHECK_NOT_NULL(func);
1118
15
  CHECK_NOT_NULL(result);
1119
1120
15
  *result = reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Context();
1121
15
  return napi_ok;
1122
}
1123
1124
napi_status
1125
17092283
napi_call_threadsafe_function(napi_threadsafe_function func,
1126
                              void* data,
1127
                              napi_threadsafe_function_call_mode is_blocking) {
1128
17092283
  CHECK_NOT_NULL(func);
1129
17092283
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Push(data,
1130
17092283
                                                                   is_blocking);
1131
}
1132
1133
napi_status
1134
4
napi_acquire_threadsafe_function(napi_threadsafe_function func) {
1135
4
  CHECK_NOT_NULL(func);
1136
4
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Acquire();
1137
}
1138
1139
napi_status
1140
31
napi_release_threadsafe_function(napi_threadsafe_function func,
1141
                                 napi_threadsafe_function_release_mode mode) {
1142
31
  CHECK_NOT_NULL(func);
1143
31
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Release(mode);
1144
}
1145
1146
napi_status
1147
2
napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
1148
2
  CHECK_NOT_NULL(func);
1149
2
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Unref();
1150
}
1151
1152
napi_status
1153
napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
1154
  CHECK_NOT_NULL(func);
1155
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Ref();
1156

13416
}