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: 421 467 90.1 %
Date: 2019-05-05 22:32:45 Branches: 211 368 57.3 %

Line Branch Exec Source
1
#include <node_buffer.h>
2
#include "env.h"
3
#define NAPI_EXPERIMENTAL
4
#include "js_native_api_v8.h"
5
#include "node_api.h"
6
#include "node_binding.h"
7
#include "node_errors.h"
8
#include "node_internals.h"
9
10
#include <memory>
11
12
132
struct node_napi_env__ : public napi_env__ {
13
67
  explicit node_napi_env__(v8::Local<v8::Context> context):
14
67
      napi_env__(context) {
15
67
    CHECK_NOT_NULL(node_env());
16
67
  }
17
18
5845
  inline node::Environment* node_env() const {
19
5845
    return node::Environment::GetCurrent(context());
20
  }
21
22
4640
  bool can_call_into_js() const override {
23
4640
    return node_env()->can_call_into_js();
24
  }
25
};
26
27
typedef node_napi_env__* node_napi_env;
28
29
namespace v8impl {
30
31
namespace {
32
33
class BufferFinalizer: private Finalizer {
34
 public:
35
  // node::Buffer::FreeCallback
36
3
  static void FinalizeBufferCallback(char* data, void* hint) {
37
3
    BufferFinalizer* finalizer = static_cast<BufferFinalizer*>(hint);
38
3
    if (finalizer->_finalize_callback != nullptr) {
39
3
      NapiCallIntoModuleThrow(finalizer->_env, [&]() {
40
        finalizer->_finalize_callback(
41
          finalizer->_env,
42
          data,
43
3
          finalizer->_finalize_hint);
44
6
      });
45
    }
46
47
3
    Delete(finalizer);
48
3
  }
49
};
50
51
69
static inline napi_env GetEnv(v8::Local<v8::Context> context) {
52
  node_napi_env result;
53
54
69
  auto isolate = context->GetIsolate();
55
69
  auto global = context->Global();
56
57
  // In the case of the string for which we grab the private and the value of
58
  // the private on the global object we can call .ToLocalChecked() directly
59
  // because we need to stop hard if either of them is empty.
60
  //
61
  // Re https://github.com/nodejs/node/pull/14217#discussion_r128775149
62
138
  auto value = global->GetPrivate(context, NAPI_PRIVATE_KEY(context, env))
63
138
      .ToLocalChecked();
64
65
69
  if (value->IsExternal()) {
66
4
    result = static_cast<node_napi_env>(value.As<v8::External>()->Value());
67
  } else {
68
67
    result = new node_napi_env__(context);
69
67
    auto external = v8::External::New(isolate, result);
70
71
    // We must also stop hard if the result of assigning the env to the global
72
    // is either nothing or false.
73
201
    CHECK(global->SetPrivate(context, NAPI_PRIVATE_KEY(context, env), external)
74
        .FromJust());
75
76
    // TODO(addaleax): There was previously code that tried to delete the
77
    // napi_env when its v8::Context was garbage collected;
78
    // However, as long as N-API addons using this napi_env are in place,
79
    // the Context needs to be accessible and alive.
80
    // Ideally, we'd want an on-addon-unload hook that takes care of this
81
    // once all N-API addons using this napi_env are unloaded.
82
    // For now, a per-Environment cleanup hook is the best we can do.
83
    result->node_env()->AddCleanupHook(
84
199
        [](void* arg) {
85
66
          static_cast<napi_env>(arg)->Unref();
86
199
        },
87
67
        static_cast<void*>(result));
88
  }
89
90
69
  return result;
91
}
92
93
static inline napi_callback_scope
94
4
JsCallbackScopeFromV8CallbackScope(node::CallbackScope* s) {
95
4
  return reinterpret_cast<napi_callback_scope>(s);
96
}
97
98
static inline node::CallbackScope*
99
4
V8CallbackScopeFromJsCallbackScope(napi_callback_scope s) {
100
4
  return reinterpret_cast<node::CallbackScope*>(s);
101
}
102
103
3
static inline void trigger_fatal_exception(
104
    napi_env env, v8::Local<v8::Value> local_err) {
105
  v8::Local<v8::Message> local_msg =
106
3
    v8::Exception::CreateMessage(env->isolate, local_err);
107
3
  node::FatalException(env->isolate, local_err, local_msg);
108
2
}
109
110
class ThreadSafeFunction : public node::AsyncResource {
111
 public:
112
14
  ThreadSafeFunction(v8::Local<v8::Function> func,
113
                     v8::Local<v8::Object> resource,
114
                     v8::Local<v8::String> name,
115
                     size_t thread_count_,
116
                     void* context_,
117
                     size_t max_queue_size_,
118
                     node_napi_env env_,
119
                     void* finalize_data_,
120
                     napi_finalize finalize_cb_,
121
                     napi_threadsafe_function_call_js call_js_cb_):
122
                     AsyncResource(env_->isolate,
123
                                   resource,
124
28
                                   *v8::String::Utf8Value(env_->isolate, name)),
125
      thread_count(thread_count_),
126
      is_closing(false),
127
      context(context_),
128
      max_queue_size(max_queue_size_),
129
      env(env_),
130
      finalize_data(finalize_data_),
131
      finalize_cb(finalize_cb_),
132
      call_js_cb(call_js_cb_ == nullptr ? CallJs : call_js_cb_),
133
42
      handles_closing(false) {
134
14
    ref.Reset(env->isolate, func);
135
14
    node::AddEnvironmentCleanupHook(env->isolate, Cleanup, this);
136
14
    env->Ref();
137
14
  }
138
139
56
  ~ThreadSafeFunction() override {
140
14
    node::RemoveEnvironmentCleanupHook(env->isolate, Cleanup, this);
141
14
    env->Unref();
142
28
  }
143
144
  // These methods can be called from any thread.
145
146
16895136
  napi_status Push(void* data, napi_threadsafe_function_call_mode mode) {
147
16895136
    node::Mutex::ScopedLock lock(this->mutex);
148
149

67580556
    while (queue.size() >= max_queue_size &&
150

33790228
        max_queue_size > 0 &&
151
16895061
        !is_closing) {
152
16895058
      if (mode == napi_tsfn_nonblocking) {
153
16895027
        return napi_queue_full;
154
      }
155
31
      cond->Wait(lock);
156
    }
157
158
109
    if (is_closing) {
159
5
      if (thread_count == 0) {
160
        return napi_invalid_arg;
161
      } else {
162
5
        thread_count--;
163
5
        return napi_closing;
164
      }
165
    } else {
166
104
      if (uv_async_send(&async) != 0) {
167
        return napi_generic_failure;
168
      }
169
104
      queue.push(data);
170
104
      return napi_ok;
171
16895136
    }
172
  }
173
174
4
  napi_status Acquire() {
175
4
    node::Mutex::ScopedLock lock(this->mutex);
176
177
4
    if (is_closing) {
178
      return napi_closing;
179
    }
180
181
4
    thread_count++;
182
183
4
    return napi_ok;
184
  }
185
186
27
  napi_status Release(napi_threadsafe_function_release_mode mode) {
187
27
    node::Mutex::ScopedLock lock(this->mutex);
188
189
27
    if (thread_count == 0) {
190
      return napi_invalid_arg;
191
    }
192
193
27
    thread_count--;
194
195

27
    if (thread_count == 0 || mode == napi_tsfn_abort) {
196
12
      if (!is_closing) {
197
12
        is_closing = (mode == napi_tsfn_abort);
198

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


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

98
          if (size == max_queue_size && max_queue_size > 0) {
281
60
            cond->Signal(lock);
282
          }
283
98
          size--;
284
        }
285
286
103
        if (size == 0) {
287
43
          if (thread_count == 0) {
288
9
            is_closing = true;
289
9
            if (max_queue_size > 0) {
290
7
              cond->Signal(lock);
291
            }
292
9
            CloseHandlesAndMaybeDelete();
293
          } else {
294
34
            if (uv_idle_stop(&idle) != 0) {
295
              idle_stop_failed = true;
296
            }
297
          }
298
        }
299
107
      }
300
    }
301
302

107
    if (popped_value || idle_stop_failed) {
303
98
      v8::HandleScope scope(env->isolate);
304
196
      CallbackScope cb_scope(this);
305
306
98
      if (idle_stop_failed) {
307
        CHECK(napi_throw_error(env,
308
                               "ERR_NAPI_TSFN_STOP_IDLE_LOOP",
309
                               "Failed to stop the idle loop") == napi_ok);
310
      } else {
311
        v8::Local<v8::Function> js_cb =
312
196
            v8::Local<v8::Function>::New(env->isolate, ref);
313
        call_js_cb(env,
314
                   v8impl::JsValueFromV8LocalValue(js_cb),
315
                   context,
316
196
                   data);
317
98
      }
318
    }
319
107
  }
320
321
90
  void MaybeStartIdle() {
322
90
    if (uv_idle_start(&idle, IdleCb) != 0) {
323
      v8::HandleScope scope(env->isolate);
324
      CallbackScope cb_scope(this);
325
      CHECK(napi_throw_error(env,
326
                             "ERR_NAPI_TSFN_START_IDLE_LOOP",
327
                             "Failed to start the idle loop") == napi_ok);
328
    }
329
90
  }
330
331
14
  void Finalize() {
332
14
    v8::HandleScope scope(env->isolate);
333
14
    if (finalize_cb) {
334
14
      CallbackScope cb_scope(this);
335
14
      finalize_cb(env, finalize_data, context);
336
    }
337
14
    EmptyQueueAndDelete();
338
14
  }
339
340
14
  inline void* Context() {
341
14
    return context;
342
  }
343
344
15
  void CloseHandlesAndMaybeDelete(bool set_closing = false) {
345
15
    v8::HandleScope scope(env->isolate);
346
15
    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
15
    if (handles_closing) {
354
16
      return;
355
    }
356
14
    handles_closing = true;
357
    env->node_env()->CloseHandle(
358
        reinterpret_cast<uv_handle_t*>(&async),
359
14
        [](uv_handle_t* handle) -> void {
360
          ThreadSafeFunction* ts_fn =
361
              node::ContainerOf(&ThreadSafeFunction::async,
362
14
                                reinterpret_cast<uv_async_t*>(handle));
363
14
          v8::HandleScope scope(ts_fn->env->isolate);
364
          ts_fn->env->node_env()->CloseHandle(
365
              reinterpret_cast<uv_handle_t*>(&ts_fn->idle),
366
14
              [](uv_handle_t* handle) -> void {
367
                ThreadSafeFunction* ts_fn =
368
                    node::ContainerOf(&ThreadSafeFunction::idle,
369
14
                                      reinterpret_cast<uv_idle_t*>(handle));
370
14
                ts_fn->Finalize();
371
28
              });
372
28
        });
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
107
  static void IdleCb(uv_idle_t* idle) {
399
    ThreadSafeFunction* ts_fn =
400
107
        node::ContainerOf(&ThreadSafeFunction::idle, idle);
401
107
    ts_fn->DispatchOne();
402
107
  }
403
404
90
  static void AsyncCb(uv_async_t* async) {
405
    ThreadSafeFunction* ts_fn =
406
90
        node::ContainerOf(&ThreadSafeFunction::async, async);
407
90
    ts_fn->MaybeStartIdle();
408
90
  }
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
68
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
68
      static_cast<napi_module*>(priv)->nm_register_func);
452
68
}
453
454
70
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
70
  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
71
    return;
464
  }
465
466
  // Create a new napi_env for this module or reference one if a pre-existing
467
  // one is found.
468
69
  napi_env env = v8impl::GetEnv(context);
469
470
  napi_value _exports;
471
69
  NapiCallIntoModuleThrow(env, [&]() {
472
138
    _exports = init(env, v8impl::JsValueFromV8LocalValue(exports));
473
138
  });
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

275
  if (_exports != nullptr &&
479
205
      _exports != v8impl::JsValueFromV8LocalValue(exports)) {
480
6
    napi_value _module = v8impl::JsValueFromV8LocalValue(module);
481
6
    napi_set_named_property(env, _module, "exports", _exports);
482
  }
483
}
484
485
// Registers a NAPI module.
486
67
void napi_module_register(napi_module* mod) {
487
  node::node_module* nm = new node::node_module {
488
    -1,
489
67
    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
134
  };
498
67
  node::node_module_register(nm);
499
67
}
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
3
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


6
  NAPI_PREAMBLE(env);
721
3
  CHECK_ARG(env, result);
722
723
3
  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
3
    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
3
                                finalizer);
734
735
3
  CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
736
737
6
  *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked());
738
3
  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
511
static napi_status ConvertUVErrorCode(int code) {
814

511
  switch (code) {
815
    case 0:
816
510
      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
510
  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
1020
                    *v8::String::Utf8Value(env->isolate, async_resource_name)),
838
      ThreadPoolWork(env->node_env()),
839
      _env(env),
840
      _data(data),
841
      _execute(execute),
842
1020
      _complete(complete) {
843
510
  }
844
845
1016
  ~Work() override = default;
846
847
 public:
848
510
  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
510
                    execute, complete, data);
856
  }
857
858
508
  static void Delete(Work* work) {
859
508
    delete work;
860
508
  }
861
862
509
  void DoThreadPoolWork() override {
863
509
    _execute(_env, _data);
864
509
  }
865
866
510
  void AfterThreadPoolWork(int status) override {
867
510
    if (_complete == nullptr)
868
509
      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
510
    v8::HandleScope scope(_env->isolate);
873
874
1019
    CallbackScope callback_scope(this);
875
876
    // We have to back up the env here because the `NAPI_CALL_INTO_MODULE` macro
877
    // makes use of it after the call into the module completes, but the module
878
    // may have deallocated **this**, and along with it the place where _env is
879
    // stored.
880
510
    napi_env env = _env;
881
882
510
    NapiCallIntoModule(env, [&]() {
883
510
      _complete(_env, ConvertUVErrorCode(status), _data);
884
512
    }, [env](v8::Local<v8::Value> local_err) {
885
      // If there was an unhandled exception in the complete callback,
886
      // report it as a fatal exception. (There is no JavaScript on the
887
      // callstack that can possibly handle it.)
888
2
      v8impl::trigger_fatal_exception(env, local_err);
889
1020
    });
890
891
    // Note: Don't access `work` after this point because it was
892
    // likely deleted by the complete callback.
893
  }
894
895
 private:
896
  node_napi_env _env;
897
  void* _data;
898
  napi_async_execute_callback _execute;
899
  napi_async_complete_callback _complete;
900
};
901
902
}  // end of namespace uvimpl
903
}  // end of anonymous namespace
904
905
#define CALL_UV(env, condition)                                         \
906
  do {                                                                  \
907
    int result = (condition);                                           \
908
    napi_status status = uvimpl::ConvertUVErrorCode(result);            \
909
    if (status != napi_ok) {                                            \
910
      return napi_set_last_error(env, status, result);                  \
911
    }                                                                   \
912
  } while (0)
913
914
510
napi_status napi_create_async_work(napi_env env,
915
                                   napi_value async_resource,
916
                                   napi_value async_resource_name,
917
                                   napi_async_execute_callback execute,
918
                                   napi_async_complete_callback complete,
919
                                   void* data,
920
                                   napi_async_work* result) {
921
510
  CHECK_ENV(env);
922
510
  CHECK_ARG(env, execute);
923
510
  CHECK_ARG(env, result);
924
925
510
  v8::Local<v8::Context> context = env->context();
926
927
  v8::Local<v8::Object> resource;
928
510
  if (async_resource != nullptr) {
929

16
    CHECK_TO_OBJECT(env, context, resource, async_resource);
930
  } else {
931
506
    resource = v8::Object::New(env->isolate);
932
  }
933
934
  v8::Local<v8::String> resource_name;
935

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

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

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