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: 430 476 90.3 %
Date: 2019-09-07 22:28:56 Branches: 212 368 57.6 %

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

51172670
    while (queue.size() >= max_queue_size &&
145

25586282
        max_queue_size > 0 &&
146
12793082
        !is_closing) {
147
12793079
      if (mode == napi_tsfn_nonblocking) {
148
12793040
        return napi_queue_full;
149
      }
150
39
      cond->Wait(lock);
151
    }
152
153
121
    if (is_closing) {
154
5
      if (thread_count == 0) {
155
        return napi_invalid_arg;
156
      } else {
157
5
        thread_count--;
158
5
        return napi_closing;
159
      }
160
    } else {
161
116
      if (uv_async_send(&async) != 0) {
162
        return napi_generic_failure;
163
      }
164
116
      queue.push(data);
165
116
      return napi_ok;
166
12793161
    }
167
  }
168
169
4
  napi_status Acquire() {
170
4
    node::Mutex::ScopedLock lock(this->mutex);
171
172
4
    if (is_closing) {
173
      return napi_closing;
174
    }
175
176
4
    thread_count++;
177
178
4
    return napi_ok;
179
  }
180
181
31
  napi_status Release(napi_threadsafe_function_release_mode mode) {
182
31
    node::Mutex::ScopedLock lock(this->mutex);
183
184
31
    if (thread_count == 0) {
185
      return napi_invalid_arg;
186
    }
187
188
31
    thread_count--;
189
190

31
    if (thread_count == 0 || mode == napi_tsfn_abort) {
191
15
      if (!is_closing) {
192
15
        is_closing = (mode == napi_tsfn_abort);
193

15
        if (is_closing && max_queue_size > 0) {
194
2
          cond->Signal(lock);
195
        }
196
15
        if (uv_async_send(&async) != 0) {
197
          return napi_generic_failure;
198
        }
199
      }
200
    }
201
202
31
    return napi_ok;
203
  }
204
205
17
  void EmptyQueueAndDelete() {
206
23
    for (; !queue.empty() ; queue.pop()) {
207
6
      call_js_cb(nullptr, nullptr, context, queue.front());
208
    }
209
17
    delete this;
210
17
  }
211
212
  // These methods must only be called from the loop thread.
213
214
17
  napi_status Init() {
215
17
    ThreadSafeFunction* ts_fn = this;
216
17
    uv_loop_t* loop = env->node_env()->event_loop();
217
218
17
    if (uv_async_init(loop, &async, AsyncCb) == 0) {
219
17
      if (max_queue_size > 0) {
220
11
        cond = std::make_unique<node::ConditionVariable>();
221
      }
222


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

110
          if (size == max_queue_size && max_queue_size > 0) {
276
71
            cond->Signal(lock);
277
          }
278
110
          size--;
279
        }
280
281
116
        if (size == 0) {
282
45
          if (thread_count == 0) {
283
12
            is_closing = true;
284
12
            if (max_queue_size > 0) {
285
8
              cond->Signal(lock);
286
            }
287
12
            CloseHandlesAndMaybeDelete();
288
          } else {
289
33
            if (uv_idle_stop(&idle) != 0) {
290
              idle_stop_failed = true;
291
            }
292
          }
293
        }
294
120
      }
295
    }
296
297

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

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

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

287
  if (_exports != nullptr &&
479
214
      _exports != v8impl::JsValueFromV8LocalValue(exports)) {
480
4
    napi_value _module = v8impl::JsValueFromV8LocalValue(module);
481
4
    napi_set_named_property(env, _module, "exports", _exports);
482
  }
483
}
484
485
// Registers a NAPI module.
486
70
void napi_module_register(napi_module* mod) {
487
  node::node_module* nm = new node::node_module {
488
    -1,
489
70
    mod->nm_flags | NM_F_DELETEME,
490
    nullptr,
491
    mod->nm_filename,
492
    nullptr,
493
    napi_module_register_cb,
494
    mod->nm_modname,
495
    mod,  // priv
496
    nullptr,
497
140
  };
498
70
  node::node_module_register(nm);
499
70
}
500
501
2
napi_status napi_add_env_cleanup_hook(napi_env env,
502
                                      void (*fun)(void* arg),
503
                                      void* arg) {
504
2
  CHECK_ENV(env);
505
2
  CHECK_ARG(env, fun);
506
507
2
  node::AddEnvironmentCleanupHook(env->isolate, fun, arg);
508
509
2
  return napi_ok;
510
}
511
512
1
napi_status napi_remove_env_cleanup_hook(napi_env env,
513
                                         void (*fun)(void* arg),
514
                                         void* arg) {
515
1
  CHECK_ENV(env);
516
1
  CHECK_ARG(env, fun);
517
518
1
  node::RemoveEnvironmentCleanupHook(env->isolate, fun, arg);
519
520
1
  return napi_ok;
521
}
522
523
1
napi_status napi_fatal_exception(napi_env env, napi_value err) {
524


2
  NAPI_PREAMBLE(env);
525
1
  CHECK_ARG(env, err);
526
527
1
  v8::Local<v8::Value> local_err = v8impl::V8LocalValueFromJsValue(err);
528
1
  v8impl::trigger_fatal_exception(env, local_err);
529
530
1
  return napi_clear_last_error(env);
531
}
532
533
NAPI_NO_RETURN void napi_fatal_error(const char* location,
534
                                     size_t location_len,
535
                                     const char* message,
536
                                     size_t message_len) {
537
  std::string location_string;
538
  std::string message_string;
539
540
  if (location_len != NAPI_AUTO_LENGTH) {
541
    location_string.assign(
542
        const_cast<char*>(location), location_len);
543
  } else {
544
    location_string.assign(
545
        const_cast<char*>(location), strlen(location));
546
  }
547
548
  if (message_len != NAPI_AUTO_LENGTH) {
549
    message_string.assign(
550
        const_cast<char*>(message), message_len);
551
  } else {
552
    message_string.assign(
553
        const_cast<char*>(message), strlen(message));
554
  }
555
556
  node::FatalError(location_string.c_str(), message_string.c_str());
557
}
558
559
4
napi_status napi_open_callback_scope(napi_env env,
560
                                     napi_value resource_object,
561
                                     napi_async_context async_context_handle,
562
                                     napi_callback_scope* result) {
563
  // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
564
  // JS exceptions.
565
4
  CHECK_ENV(env);
566
4
  CHECK_ARG(env, result);
567
568
4
  v8::Local<v8::Context> context = env->context();
569
570
  node::async_context* node_async_context =
571
4
      reinterpret_cast<node::async_context*>(async_context_handle);
572
573
  v8::Local<v8::Object> resource;
574

16
  CHECK_TO_OBJECT(env, context, resource, resource_object);
575
576
  *result = v8impl::JsCallbackScopeFromV8CallbackScope(
577
      new node::CallbackScope(env->isolate,
578
                              resource,
579
4
                              *node_async_context));
580
581
4
  env->open_callback_scopes++;
582
4
  return napi_clear_last_error(env);
583
}
584
585
4
napi_status napi_close_callback_scope(napi_env env, napi_callback_scope scope) {
586
  // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
587
  // JS exceptions.
588
4
  CHECK_ENV(env);
589
4
  CHECK_ARG(env, scope);
590
4
  if (env->open_callback_scopes == 0) {
591
    return napi_callback_scope_mismatch;
592
  }
593
594
4
  env->open_callback_scopes--;
595
4
  delete v8impl::V8CallbackScopeFromJsCallbackScope(scope);
596
4
  return napi_clear_last_error(env);
597
}
598
599
8
napi_status napi_async_init(napi_env env,
600
                            napi_value async_resource,
601
                            napi_value async_resource_name,
602
                            napi_async_context* result) {
603
8
  CHECK_ENV(env);
604
8
  CHECK_ARG(env, async_resource_name);
605
8
  CHECK_ARG(env, result);
606
607
8
  v8::Isolate* isolate = env->isolate;
608
8
  v8::Local<v8::Context> context = env->context();
609
610
  v8::Local<v8::Object> v8_resource;
611
8
  if (async_resource != nullptr) {
612

28
    CHECK_TO_OBJECT(env, context, v8_resource, async_resource);
613
  } else {
614
1
    v8_resource = v8::Object::New(isolate);
615
  }
616
617
  v8::Local<v8::String> v8_resource_name;
618

32
  CHECK_TO_STRING(env, context, v8_resource_name, async_resource_name);
619
620
  // TODO(jasongin): Consider avoiding allocation here by using
621
  // a tagged pointer with 2×31 bit fields instead.
622
8
  node::async_context* async_context = new node::async_context();
623
624
8
  *async_context = node::EmitAsyncInit(isolate, v8_resource, v8_resource_name);
625
8
  *result = reinterpret_cast<napi_async_context>(async_context);
626
627
8
  return napi_clear_last_error(env);
628
}
629
630
7
napi_status napi_async_destroy(napi_env env,
631
                               napi_async_context async_context) {
632
7
  CHECK_ENV(env);
633
7
  CHECK_ARG(env, async_context);
634
635
  node::async_context* node_async_context =
636
7
      reinterpret_cast<node::async_context*>(async_context);
637
  node::EmitAsyncDestroy(
638
      reinterpret_cast<node_napi_env>(env)->node_env(),
639
7
      *node_async_context);
640
641
7
  delete node_async_context;
642
643
7
  return napi_clear_last_error(env);
644
}
645
646
23
napi_status napi_make_callback(napi_env env,
647
                               napi_async_context async_context,
648
                               napi_value recv,
649
                               napi_value func,
650
                               size_t argc,
651
                               const napi_value* argv,
652
                               napi_value* result) {
653


46
  NAPI_PREAMBLE(env);
654
23
  CHECK_ARG(env, recv);
655
23
  if (argc > 0) {
656
4
    CHECK_ARG(env, argv);
657
  }
658
659
23
  v8::Local<v8::Context> context = env->context();
660
661
  v8::Local<v8::Object> v8recv;
662

92
  CHECK_TO_OBJECT(env, context, v8recv, recv);
663
664
  v8::Local<v8::Function> v8func;
665

69
  CHECK_TO_FUNCTION(env, v8func, func);
666
667
  node::async_context* node_async_context =
668
23
    reinterpret_cast<node::async_context*>(async_context);
669
23
  if (node_async_context == nullptr) {
670
    static node::async_context empty_context = { 0, 0 };
671
17
    node_async_context = &empty_context;
672
  }
673
674
  v8::MaybeLocal<v8::Value> callback_result = node::MakeCallback(
675
      env->isolate, v8recv, v8func, argc,
676
      reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)),
677
23
      *node_async_context);
678
679
23
  if (try_catch.HasCaught()) {
680
4
    return napi_set_last_error(env, napi_pending_exception);
681
  } else {
682
19
    CHECK_MAYBE_EMPTY(env, callback_result, napi_generic_failure);
683
19
    if (result != nullptr) {
684
      *result = v8impl::JsValueFromV8LocalValue(
685
6
          callback_result.ToLocalChecked());
686
    }
687
  }
688
689
19
  return GET_RETURN_STATUS(env);
690
}
691
692
1
napi_status napi_create_buffer(napi_env env,
693
                               size_t length,
694
                               void** data,
695
                               napi_value* result) {
696


2
  NAPI_PREAMBLE(env);
697
1
  CHECK_ARG(env, result);
698
699
1
  auto maybe = node::Buffer::New(env->isolate, length);
700
701
1
  CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
702
703
1
  v8::Local<v8::Object> buffer = maybe.ToLocalChecked();
704
705
2
  *result = v8impl::JsValueFromV8LocalValue(buffer);
706
707
1
  if (data != nullptr) {
708
1
    *data = node::Buffer::Data(buffer);
709
  }
710
711
1
  return GET_RETURN_STATUS(env);
712
}
713
714
5
napi_status napi_create_external_buffer(napi_env env,
715
                                        size_t length,
716
                                        void* data,
717
                                        napi_finalize finalize_cb,
718
                                        void* finalize_hint,
719
                                        napi_value* result) {
720


10
  NAPI_PREAMBLE(env);
721
5
  CHECK_ARG(env, result);
722
723
5
  v8::Isolate* isolate = env->isolate;
724
725
  // The finalizer object will delete itself after invoking the callback.
726
  v8impl::Finalizer* finalizer = v8impl::Finalizer::New(
727
5
    env, finalize_cb, nullptr, finalize_hint);
728
729
  auto maybe = node::Buffer::New(isolate,
730
                                static_cast<char*>(data),
731
                                length,
732
                                v8impl::BufferFinalizer::FinalizeBufferCallback,
733
5
                                finalizer);
734
735
5
  CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
736
737
10
  *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked());
738
5
  return GET_RETURN_STATUS(env);
739
  // Tell coverity that 'finalizer' should not be freed when we return
740
  // as it will be deleted when the buffer to which it is associated
741
  // is finalized.
742
  // coverity[leaked_storage]
743
}
744
745
1
napi_status napi_create_buffer_copy(napi_env env,
746
                                    size_t length,
747
                                    const void* data,
748
                                    void** result_data,
749
                                    napi_value* result) {
750


2
  NAPI_PREAMBLE(env);
751
1
  CHECK_ARG(env, result);
752
753
  auto maybe = node::Buffer::Copy(env->isolate,
754
1
    static_cast<const char*>(data), length);
755
756
1
  CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
757
758
1
  v8::Local<v8::Object> buffer = maybe.ToLocalChecked();
759
2
  *result = v8impl::JsValueFromV8LocalValue(buffer);
760
761
1
  if (result_data != nullptr) {
762
    *result_data = node::Buffer::Data(buffer);
763
  }
764
765
1
  return GET_RETURN_STATUS(env);
766
}
767
768
1
napi_status napi_is_buffer(napi_env env, napi_value value, bool* result) {
769
1
  CHECK_ENV(env);
770
1
  CHECK_ARG(env, value);
771
1
  CHECK_ARG(env, result);
772
773
1
  *result = node::Buffer::HasInstance(v8impl::V8LocalValueFromJsValue(value));
774
1
  return napi_clear_last_error(env);
775
}
776
777
1
napi_status napi_get_buffer_info(napi_env env,
778
                                 napi_value value,
779
                                 void** data,
780
                                 size_t* length) {
781
1
  CHECK_ENV(env);
782
1
  CHECK_ARG(env, value);
783
784
1
  v8::Local<v8::Value> buffer = v8impl::V8LocalValueFromJsValue(value);
785
786
1
  if (data != nullptr) {
787
1
    *data = node::Buffer::Data(buffer);
788
  }
789
1
  if (length != nullptr) {
790
1
    *length = node::Buffer::Length(buffer);
791
  }
792
793
1
  return napi_clear_last_error(env);
794
}
795
796
1
napi_status napi_get_node_version(napi_env env,
797
                                  const napi_node_version** result) {
798
1
  CHECK_ENV(env);
799
1
  CHECK_ARG(env, result);
800
  static const napi_node_version version = {
801
    NODE_MAJOR_VERSION,
802
    NODE_MINOR_VERSION,
803
    NODE_PATCH_VERSION,
804
    NODE_RELEASE
805
  };
806
1
  *result = &version;
807
1
  return napi_clear_last_error(env);
808
}
809
810
namespace {
811
namespace uvimpl {
812
813
513
static napi_status ConvertUVErrorCode(int code) {
814

513
  switch (code) {
815
    case 0:
816
512
      return napi_ok;
817
    case UV_EINVAL:
818
      return napi_invalid_arg;
819
    case UV_ECANCELED:
820
1
      return napi_cancelled;
821
    default:
822
      return napi_generic_failure;
823
  }
824
}
825
826
// Wrapper around uv_work_t which calls user-provided callbacks.
827
class Work : public node::AsyncResource, public node::ThreadPoolWork {
828
 private:
829
512
  explicit Work(node_napi_env env,
830
                v8::Local<v8::Object> async_resource,
831
                v8::Local<v8::String> async_resource_name,
832
                napi_async_execute_callback execute,
833
                napi_async_complete_callback complete = nullptr,
834
                void* data = nullptr)
835
    : AsyncResource(env->isolate,
836
                    async_resource,
837
1024
                    *v8::String::Utf8Value(env->isolate, async_resource_name)),
838
      ThreadPoolWork(env->node_env()),
839
      _env(env),
840
      _data(data),
841
      _execute(execute),
842
1024
      _complete(complete) {
843
512
  }
844
845
1016
  ~Work() override = default;
846
847
 public:
848
512
  static Work* New(node_napi_env env,
849
                   v8::Local<v8::Object> async_resource,
850
                   v8::Local<v8::String> async_resource_name,
851
                   napi_async_execute_callback execute,
852
                   napi_async_complete_callback complete,
853
                   void* data) {
854
    return new Work(env, async_resource, async_resource_name,
855
512
                    execute, complete, data);
856
  }
857
858
508
  static void Delete(Work* work) {
859
508
    delete work;
860
508
  }
861
862
511
  void DoThreadPoolWork() override {
863
511
    _execute(_env, _data);
864
511
  }
865
866
512
  void AfterThreadPoolWork(int status) override {
867
512
    if (_complete == nullptr)
868
511
      return;
869
870
    // Establish a handle scope here so that every callback doesn't have to.
871
    // Also it is needed for the exception-handling below.
872
512
    v8::HandleScope scope(_env->isolate);
873
874
1023
    CallbackScope callback_scope(this);
875
876
512
    _env->CallIntoModule([&](napi_env env) {
877
512
      _complete(env, ConvertUVErrorCode(status), _data);
878
514
    }, [](napi_env env, v8::Local<v8::Value> local_err) {
879
      // If there was an unhandled exception in the complete callback,
880
      // report it as a fatal exception. (There is no JavaScript on the
881
      // callstack that can possibly handle it.)
882
2
      v8impl::trigger_fatal_exception(env, local_err);
883
1024
    });
884
885
    // Note: Don't access `work` after this point because it was
886
    // likely deleted by the complete callback.
887
  }
888
889
 private:
890
  node_napi_env _env;
891
  void* _data;
892
  napi_async_execute_callback _execute;
893
  napi_async_complete_callback _complete;
894
};
895
896
}  // end of namespace uvimpl
897
}  // end of anonymous namespace
898
899
#define CALL_UV(env, condition)                                         \
900
  do {                                                                  \
901
    int result = (condition);                                           \
902
    napi_status status = uvimpl::ConvertUVErrorCode(result);            \
903
    if (status != napi_ok) {                                            \
904
      return napi_set_last_error(env, status, result);                  \
905
    }                                                                   \
906
  } while (0)
907
908
512
napi_status napi_create_async_work(napi_env env,
909
                                   napi_value async_resource,
910
                                   napi_value async_resource_name,
911
                                   napi_async_execute_callback execute,
912
                                   napi_async_complete_callback complete,
913
                                   void* data,
914
                                   napi_async_work* result) {
915
512
  CHECK_ENV(env);
916
512
  CHECK_ARG(env, execute);
917
512
  CHECK_ARG(env, result);
918
919
512
  v8::Local<v8::Context> context = env->context();
920
921
  v8::Local<v8::Object> resource;
922
512
  if (async_resource != nullptr) {
923

16
    CHECK_TO_OBJECT(env, context, resource, async_resource);
924
  } else {
925
508
    resource = v8::Object::New(env->isolate);
926
  }
927
928
  v8::Local<v8::String> resource_name;
929

2048
  CHECK_TO_STRING(env, context, resource_name, async_resource_name);
930
931
  uvimpl::Work* work = uvimpl::Work::New(reinterpret_cast<node_napi_env>(env),
932
                                         resource,
933
                                         resource_name,
934
                                         execute,
935
                                         complete,
936
512
                                         data);
937
938
512
  *result = reinterpret_cast<napi_async_work>(work);
939
940
512
  return napi_clear_last_error(env);
941
}
942
943
508
napi_status napi_delete_async_work(napi_env env, napi_async_work work) {
944
508
  CHECK_ENV(env);
945
508
  CHECK_ARG(env, work);
946
947
508
  uvimpl::Work::Delete(reinterpret_cast<uvimpl::Work*>(work));
948
949
508
  return napi_clear_last_error(env);
950
}
951
952
514
napi_status napi_get_uv_event_loop(napi_env env, uv_loop_t** loop) {
953
514
  CHECK_ENV(env);
954
514
  CHECK_ARG(env, loop);
955
514
  *loop = reinterpret_cast<node_napi_env>(env)->node_env()->event_loop();
956
514
  return napi_clear_last_error(env);
957
}
958
959
512
napi_status napi_queue_async_work(napi_env env, napi_async_work work) {
960
512
  CHECK_ENV(env);
961
512
  CHECK_ARG(env, work);
962
963
  napi_status status;
964
512
  uv_loop_t* event_loop = nullptr;
965
512
  status = napi_get_uv_event_loop(env, &event_loop);
966
512
  if (status != napi_ok)
967
    return napi_set_last_error(env, status);
968
969
512
  uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work);
970
971
512
  w->ScheduleWork();
972
973
512
  return napi_clear_last_error(env);
974
}
975
976
1
napi_status napi_cancel_async_work(napi_env env, napi_async_work work) {
977
1
  CHECK_ENV(env);
978
1
  CHECK_ARG(env, work);
979
980
1
  uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work);
981
982
1
  CALL_UV(env, w->CancelWork());
983
984
1
  return napi_clear_last_error(env);
985
}
986
987
napi_status
988
17
napi_create_threadsafe_function(napi_env env,
989
                                napi_value func,
990
                                napi_value async_resource,
991
                                napi_value async_resource_name,
992
                                size_t max_queue_size,
993
                                size_t initial_thread_count,
994
                                void* thread_finalize_data,
995
                                napi_finalize thread_finalize_cb,
996
                                void* context,
997
                                napi_threadsafe_function_call_js call_js_cb,
998
                                napi_threadsafe_function* result) {
999
17
  CHECK_ENV(env);
1000
17
  CHECK_ARG(env, async_resource_name);
1001
17
  RETURN_STATUS_IF_FALSE(env, initial_thread_count > 0, napi_invalid_arg);
1002
17
  CHECK_ARG(env, result);
1003
1004
17
  napi_status status = napi_ok;
1005
1006
  v8::Local<v8::Function> v8_func;
1007
17
  if (func == nullptr) {
1008
3
    CHECK_ARG(env, call_js_cb);
1009
  } else {
1010

42
    CHECK_TO_FUNCTION(env, v8_func, func);
1011
  }
1012
1013
17
  v8::Local<v8::Context> v8_context = env->context();
1014
1015
  v8::Local<v8::Object> v8_resource;
1016
17
  if (async_resource == nullptr) {
1017
17
    v8_resource = v8::Object::New(env->isolate);
1018
  } else {
1019
    CHECK_TO_OBJECT(env, v8_context, v8_resource, async_resource);
1020
  }
1021
1022
  v8::Local<v8::String> v8_name;
1023

68
  CHECK_TO_STRING(env, v8_context, v8_name, async_resource_name);
1024
1025
  v8impl::ThreadSafeFunction* ts_fn =
1026
      new v8impl::ThreadSafeFunction(v8_func,
1027
                                     v8_resource,
1028
                                     v8_name,
1029
                                     initial_thread_count,
1030
                                     context,
1031
                                     max_queue_size,
1032
                                     reinterpret_cast<node_napi_env>(env),
1033
                                     thread_finalize_data,
1034
                                     thread_finalize_cb,
1035
17
                                     call_js_cb);
1036
1037
17
  if (ts_fn == nullptr) {
1038
    status = napi_generic_failure;
1039
  } else {
1040
    // Init deletes ts_fn upon failure.
1041
17
    status = ts_fn->Init();
1042
17
    if (status == napi_ok) {
1043
17
      *result = reinterpret_cast<napi_threadsafe_function>(ts_fn);
1044
    }
1045
  }
1046
1047
17
  return napi_set_last_error(env, status);
1048
}
1049
1050
napi_status
1051
15
napi_get_threadsafe_function_context(napi_threadsafe_function func,
1052
                                     void** result) {
1053
15
  CHECK_NOT_NULL(func);
1054
15
  CHECK_NOT_NULL(result);
1055
1056
15
  *result = reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Context();
1057
15
  return napi_ok;
1058
}
1059
1060
napi_status
1061
12793161
napi_call_threadsafe_function(napi_threadsafe_function func,
1062
                              void* data,
1063
                              napi_threadsafe_function_call_mode is_blocking) {
1064
12793161
  CHECK_NOT_NULL(func);
1065
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Push(data,
1066
12793161
                                                                   is_blocking);
1067
}
1068
1069
napi_status
1070
4
napi_acquire_threadsafe_function(napi_threadsafe_function func) {
1071
4
  CHECK_NOT_NULL(func);
1072
4
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Acquire();
1073
}
1074
1075
napi_status
1076
31
napi_release_threadsafe_function(napi_threadsafe_function func,
1077
                                 napi_threadsafe_function_release_mode mode) {
1078
31
  CHECK_NOT_NULL(func);
1079
31
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Release(mode);
1080
}
1081
1082
napi_status
1083
2
napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
1084
2
  CHECK_NOT_NULL(func);
1085
2
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Unref();
1086
}
1087
1088
napi_status
1089
napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
1090
  CHECK_NOT_NULL(func);
1091
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Ref();
1092
}