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

66898078
    while (queue.size() >= max_queue_size &&
139

33448988
        max_queue_size > 0 &&
140
16724435
        !is_closing) {
141
16724432
      if (mode == napi_tsfn_nonblocking) {
142
16724389
        return napi_queue_full;
143
      }
144
43
      cond->Wait(lock);
145
    }
146
147
121
    if (is_closing) {
148
5
      if (thread_count == 0) {
149
        return napi_invalid_arg;
150
      } else {
151
5
        thread_count--;
152
5
        return napi_closing;
153
      }
154
    } else {
155
116
      if (uv_async_send(&async) != 0) {
156
        return napi_generic_failure;
157
      }
158
116
      queue.push(data);
159
116
      return napi_ok;
160
16724510
    }
161
  }
162
163
4
  napi_status Acquire() {
164
4
    node::Mutex::ScopedLock lock(this->mutex);
165
166
4
    if (is_closing) {
167
      return napi_closing;
168
    }
169
170
4
    thread_count++;
171
172
4
    return napi_ok;
173
  }
174
175
31
  napi_status Release(napi_threadsafe_function_release_mode mode) {
176
31
    node::Mutex::ScopedLock lock(this->mutex);
177
178
31
    if (thread_count == 0) {
179
      return napi_invalid_arg;
180
    }
181
182
31
    thread_count--;
183
184

31
    if (thread_count == 0 || mode == napi_tsfn_abort) {
185
15
      if (!is_closing) {
186
15
        is_closing = (mode == napi_tsfn_abort);
187

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


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

110
          if (size == max_queue_size && max_queue_size > 0) {
270
74
            cond->Signal(lock);
271
          }
272
110
          size--;
273
        }
274
275
117
        if (size == 0) {
276
43
          if (thread_count == 0) {
277
12
            is_closing = true;
278
12
            if (max_queue_size > 0) {
279
8
              cond->Signal(lock);
280
            }
281
12
            CloseHandlesAndMaybeDelete();
282
          } else {
283
31
            if (uv_idle_stop(&idle) != 0) {
284
              idle_stop_failed = true;
285
            }
286
          }
287
        }
288
121
      }
289
    }
290
291

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

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

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

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


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

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

28
    CHECK_TO_OBJECT(env, context, v8_resource, async_resource);
607
  } else {
608
1
    v8_resource = v8::Object::New(isolate);
609
  }
610
611
  v8::Local<v8::String> v8_resource_name;
612

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


46
  NAPI_PREAMBLE(env);
648
23
  CHECK_ARG(env, recv);
649
23
  if (argc > 0) {
650
4
    CHECK_ARG(env, argv);
651
  }
652
653
23
  v8::Local<v8::Context> context = env->context();
654
655
  v8::Local<v8::Object> v8recv;
656

92
  CHECK_TO_OBJECT(env, context, v8recv, recv);
657
658
  v8::Local<v8::Function> v8func;
659

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


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


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


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

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

16
    CHECK_TO_OBJECT(env, context, resource, async_resource);
918
  } else {
919
508
    resource = v8::Object::New(env->isolate);
920
  }
921
922
  v8::Local<v8::String> resource_name;
923

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

42
    CHECK_TO_FUNCTION(env, v8_func, func);
1005
  }
1006
1007
17
  v8::Local<v8::Context> v8_context = env->context();
1008
1009
  v8::Local<v8::Object> v8_resource;
1010
17
  if (async_resource == nullptr) {
1011
17
    v8_resource = v8::Object::New(env->isolate);
1012
  } else {
1013
    CHECK_TO_OBJECT(env, v8_context, v8_resource, async_resource);
1014
  }
1015
1016
  v8::Local<v8::String> v8_name;
1017

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