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: 452 501 90.2 %
Date: 2020-02-27 22:14:15 Branches: 207 360 57.5 %

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

50422171
    while (queue.size() >= max_queue_size &&
153

33614696
        max_queue_size > 0 &&
154
16807289
        !is_closing) {
155
16807286
      if (mode == napi_tsfn_nonblocking) {
156
16807245
        return napi_queue_full;
157
      }
158
41
      cond->Wait(lock);
159
    }
160
161
121
    if (is_closing) {
162
5
      if (thread_count == 0) {
163
        return napi_invalid_arg;
164
      } else {
165
5
        thread_count--;
166
5
        return napi_closing;
167
      }
168
    } else {
169
116
      if (uv_async_send(&async) != 0) {
170
        return napi_generic_failure;
171
      }
172
116
      queue.push(data);
173
116
      return napi_ok;
174
    }
175
  }
176
177
4
  napi_status Acquire() {
178
8
    node::Mutex::ScopedLock lock(this->mutex);
179
180
4
    if (is_closing) {
181
      return napi_closing;
182
    }
183
184
4
    thread_count++;
185
186
4
    return napi_ok;
187
  }
188
189
31
  napi_status Release(napi_threadsafe_function_release_mode mode) {
190
62
    node::Mutex::ScopedLock lock(this->mutex);
191
192
31
    if (thread_count == 0) {
193
      return napi_invalid_arg;
194
    }
195
196
31
    thread_count--;
197
198

31
    if (thread_count == 0 || mode == napi_tsfn_abort) {
199
15
      if (!is_closing) {
200
15
        is_closing = (mode == napi_tsfn_abort);
201

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


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

110
          if (size == max_queue_size && max_queue_size > 0) {
284
74
            cond->Signal(lock);
285
          }
286
110
          size--;
287
        }
288
289
116
        if (size == 0) {
290
42
          if (thread_count == 0) {
291
12
            is_closing = true;
292
12
            if (max_queue_size > 0) {
293
8
              cond->Signal(lock);
294
            }
295
12
            CloseHandlesAndMaybeDelete();
296
          } else {
297
30
            if (uv_idle_stop(&idle) != 0) {
298
              idle_stop_failed = true;
299
            }
300
          }
301
        }
302
      }
303
    }
304
305

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

10
    if (!(env == nullptr || cb == nullptr)) {
387
      napi_value recv;
388
      napi_status status;
389
390
10
      status = napi_get_undefined(env, &recv);
391
10
      if (status != napi_ok) {
392
        napi_throw_error(env, "ERR_NAPI_TSFN_GET_UNDEFINED",
393
            "Failed to retrieve undefined value");
394
        return;
395
      }
396
397
10
      status = napi_call_function(env, recv, cb, 0, nullptr, nullptr);
398

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

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


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

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

28
    CHECK_TO_OBJECT(env, context, v8_resource, async_resource);
620
  } else {
621
1
    v8_resource = v8::Object::New(isolate);
622
  }
623
624
  v8::Local<v8::String> v8_resource_name;
625

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


69
  NAPI_PREAMBLE(env);
661
23
  CHECK_ARG(env, recv);
662
23
  if (argc > 0) {
663
4
    CHECK_ARG(env, argv);
664
  }
665
666
23
  v8::Local<v8::Context> context = env->context();
667
668
  v8::Local<v8::Object> v8recv;
669

92
  CHECK_TO_OBJECT(env, context, v8recv, recv);
670
671
  v8::Local<v8::Function> v8func;
672

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


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


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


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

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

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

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

42
    CHECK_TO_FUNCTION(env, v8_func, func);
1019
  }
1020
1021
17
  v8::Local<v8::Context> v8_context = env->context();
1022
1023
  v8::Local<v8::Object> v8_resource;
1024
17
  if (async_resource == nullptr) {
1025
17
    v8_resource = v8::Object::New(env->isolate);
1026
  } else {
1027
    CHECK_TO_OBJECT(env, v8_context, v8_resource, async_resource);
1028
  }
1029
1030
  v8::Local<v8::String> v8_name;
1031

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