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: 471 518 90.9 %
Date: 2020-08-17 22:13:26 Branches: 215 368 58.4 %

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

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

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

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

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

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

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

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

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

228
  if (_exports != nullptr &&
477
151
      _exports != v8impl::JsValueFromV8LocalValue(exports)) {
478
4
    napi_value _module = v8impl::JsValueFromV8LocalValue(module);
479
4
    napi_set_named_property(env, _module, "exports", _exports);
480
  }
481
}
482
483
// Registers a NAPI module.
484
74
void napi_module_register(napi_module* mod) {
485
  node::node_module* nm = new node::node_module {
486
    -1,
487
74
    mod->nm_flags | NM_F_DELETEME,
488
    nullptr,
489
74
    mod->nm_filename,
490
    nullptr,
491
    napi_module_register_cb,
492
74
    mod->nm_modname,
493
    mod,  // priv
494
    nullptr,
495
296
  };
496
74
  node::node_module_register(nm);
497
74
}
498
499
2
napi_status napi_add_env_cleanup_hook(napi_env env,
500
                                      void (*fun)(void* arg),
501
                                      void* arg) {
502
2
  CHECK_ENV(env);
503
2
  CHECK_ARG(env, fun);
504
505
2
  node::AddEnvironmentCleanupHook(env->isolate, fun, arg);
506
507
2
  return napi_ok;
508
}
509
510
1
napi_status napi_remove_env_cleanup_hook(napi_env env,
511
                                         void (*fun)(void* arg),
512
                                         void* arg) {
513
1
  CHECK_ENV(env);
514
1
  CHECK_ARG(env, fun);
515
516
1
  node::RemoveEnvironmentCleanupHook(env->isolate, fun, arg);
517
518
1
  return napi_ok;
519
}
520
521
4
struct napi_async_cleanup_hook_handle__ {
522
  node::AsyncCleanupHookHandle handle;
523
};
524
525
6
napi_status napi_add_async_cleanup_hook(
526
    napi_env env,
527
    void (*fun)(void* arg, void(* cb)(void*), void* cbarg),
528
    void* arg,
529
    napi_async_cleanup_hook_handle* remove_handle) {
530
6
  CHECK_ENV(env);
531
6
  CHECK_ARG(env, fun);
532
533
12
  auto handle = node::AddEnvironmentCleanupHook(env->isolate, fun, arg);
534
6
  if (remove_handle != nullptr) {
535
4
    *remove_handle = new napi_async_cleanup_hook_handle__ { std::move(handle) };
536
4
    env->Ref();
537
  }
538
539
6
  return napi_clear_last_error(env);
540
}
541
542
4
napi_status napi_remove_async_cleanup_hook(
543
    napi_env env,
544
    napi_async_cleanup_hook_handle remove_handle) {
545
4
  CHECK_ENV(env);
546
4
  CHECK_ARG(env, remove_handle);
547
548
4
  node::RemoveEnvironmentCleanupHook(std::move(remove_handle->handle));
549
4
  delete remove_handle;
550
551
  // Release the `env` handle asynchronously since it would be surprising if
552
  // a call to a N-API function would destroy `env` synchronously.
553
  static_cast<node_napi_env>(env)->node_env()
554
8
      ->SetImmediate([env](node::Environment*) { env->Unref(); });
555
556
4
  return napi_clear_last_error(env);
557
}
558
559
1
napi_status napi_fatal_exception(napi_env env, napi_value err) {
560


3
  NAPI_PREAMBLE(env);
561
1
  CHECK_ARG(env, err);
562
563
1
  v8::Local<v8::Value> local_err = v8impl::V8LocalValueFromJsValue(err);
564
1
  v8impl::trigger_fatal_exception(env, local_err);
565
566
1
  return napi_clear_last_error(env);
567
}
568
569
NAPI_NO_RETURN void napi_fatal_error(const char* location,
570
                                     size_t location_len,
571
                                     const char* message,
572
                                     size_t message_len) {
573
  std::string location_string;
574
  std::string message_string;
575
576
  if (location_len != NAPI_AUTO_LENGTH) {
577
    location_string.assign(
578
        const_cast<char*>(location), location_len);
579
  } else {
580
    location_string.assign(
581
        const_cast<char*>(location), strlen(location));
582
  }
583
584
  if (message_len != NAPI_AUTO_LENGTH) {
585
    message_string.assign(
586
        const_cast<char*>(message), message_len);
587
  } else {
588
    message_string.assign(
589
        const_cast<char*>(message), strlen(message));
590
  }
591
592
  node::FatalError(location_string.c_str(), message_string.c_str());
593
}
594
595
4
napi_status napi_open_callback_scope(napi_env env,
596
                                     napi_value resource_object,
597
                                     napi_async_context async_context_handle,
598
                                     napi_callback_scope* result) {
599
  // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
600
  // JS exceptions.
601
4
  CHECK_ENV(env);
602
4
  CHECK_ARG(env, result);
603
604
4
  v8::Local<v8::Context> context = env->context();
605
606
  node::async_context* node_async_context =
607
4
      reinterpret_cast<node::async_context*>(async_context_handle);
608
609
  v8::Local<v8::Object> resource;
610

16
  CHECK_TO_OBJECT(env, context, resource, resource_object);
611
612
8
  *result = v8impl::JsCallbackScopeFromV8CallbackScope(
613
4
      new node::CallbackScope(env->isolate,
614
                              resource,
615
8
                              *node_async_context));
616
617
4
  env->open_callback_scopes++;
618
4
  return napi_clear_last_error(env);
619
}
620
621
4
napi_status napi_close_callback_scope(napi_env env, napi_callback_scope scope) {
622
  // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
623
  // JS exceptions.
624
4
  CHECK_ENV(env);
625
4
  CHECK_ARG(env, scope);
626
4
  if (env->open_callback_scopes == 0) {
627
    return napi_callback_scope_mismatch;
628
  }
629
630
4
  env->open_callback_scopes--;
631
4
  delete v8impl::V8CallbackScopeFromJsCallbackScope(scope);
632
4
  return napi_clear_last_error(env);
633
}
634
635
11
napi_status napi_async_init(napi_env env,
636
                            napi_value async_resource,
637
                            napi_value async_resource_name,
638
                            napi_async_context* result) {
639
11
  CHECK_ENV(env);
640
11
  CHECK_ARG(env, async_resource_name);
641
11
  CHECK_ARG(env, result);
642
643
11
  v8::Isolate* isolate = env->isolate;
644
11
  v8::Local<v8::Context> context = env->context();
645
646
  v8::Local<v8::Object> v8_resource;
647
11
  if (async_resource != nullptr) {
648

40
    CHECK_TO_OBJECT(env, context, v8_resource, async_resource);
649
  } else {
650
1
    v8_resource = v8::Object::New(isolate);
651
  }
652
653
  v8::Local<v8::String> v8_resource_name;
654

44
  CHECK_TO_STRING(env, context, v8_resource_name, async_resource_name);
655
656
  // TODO(jasongin): Consider avoiding allocation here by using
657
  // a tagged pointer with 2×31 bit fields instead.
658
11
  node::async_context* async_context = new node::async_context();
659
660
11
  *async_context = node::EmitAsyncInit(isolate, v8_resource, v8_resource_name);
661
11
  *result = reinterpret_cast<napi_async_context>(async_context);
662
663
11
  return napi_clear_last_error(env);
664
}
665
666
11
napi_status napi_async_destroy(napi_env env,
667
                               napi_async_context async_context) {
668
11
  CHECK_ENV(env);
669
11
  CHECK_ARG(env, async_context);
670
671
  node::async_context* node_async_context =
672
11
      reinterpret_cast<node::async_context*>(async_context);
673
11
  node::EmitAsyncDestroy(
674
      reinterpret_cast<node_napi_env>(env)->node_env(),
675
11
      *node_async_context);
676
677
11
  delete node_async_context;
678
679
11
  return napi_clear_last_error(env);
680
}
681
682
23
napi_status napi_make_callback(napi_env env,
683
                               napi_async_context async_context,
684
                               napi_value recv,
685
                               napi_value func,
686
                               size_t argc,
687
                               const napi_value* argv,
688
                               napi_value* result) {
689


69
  NAPI_PREAMBLE(env);
690
23
  CHECK_ARG(env, recv);
691
23
  if (argc > 0) {
692
4
    CHECK_ARG(env, argv);
693
  }
694
695
23
  v8::Local<v8::Context> context = env->context();
696
697
  v8::Local<v8::Object> v8recv;
698

92
  CHECK_TO_OBJECT(env, context, v8recv, recv);
699
700
  v8::Local<v8::Function> v8func;
701

69
  CHECK_TO_FUNCTION(env, v8func, func);
702
703
  node::async_context* node_async_context =
704
23
    reinterpret_cast<node::async_context*>(async_context);
705
23
  if (node_async_context == nullptr) {
706
    static node::async_context empty_context = { 0, 0 };
707
17
    node_async_context = &empty_context;
708
  }
709
710
  v8::MaybeLocal<v8::Value> callback_result = node::MakeCallback(
711
23
      env->isolate, v8recv, v8func, argc,
712
      reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)),
713
46
      *node_async_context);
714
715
23
  if (try_catch.HasCaught()) {
716
4
    return napi_set_last_error(env, napi_pending_exception);
717
  } else {
718
19
    CHECK_MAYBE_EMPTY(env, callback_result, napi_generic_failure);
719
19
    if (result != nullptr) {
720
6
      *result = v8impl::JsValueFromV8LocalValue(
721
          callback_result.ToLocalChecked());
722
    }
723
  }
724
725
19
  return GET_RETURN_STATUS(env);
726
}
727
728
1
napi_status napi_create_buffer(napi_env env,
729
                               size_t length,
730
                               void** data,
731
                               napi_value* result) {
732


3
  NAPI_PREAMBLE(env);
733
1
  CHECK_ARG(env, result);
734
735
1
  auto maybe = node::Buffer::New(env->isolate, length);
736
737
1
  CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
738
739
1
  v8::Local<v8::Object> buffer = maybe.ToLocalChecked();
740
741
2
  *result = v8impl::JsValueFromV8LocalValue(buffer);
742
743
1
  if (data != nullptr) {
744
1
    *data = node::Buffer::Data(buffer);
745
  }
746
747
1
  return GET_RETURN_STATUS(env);
748
}
749
750
12
napi_status napi_create_external_buffer(napi_env env,
751
                                        size_t length,
752
                                        void* data,
753
                                        napi_finalize finalize_cb,
754
                                        void* finalize_hint,
755
                                        napi_value* result) {
756


36
  NAPI_PREAMBLE(env);
757
12
  CHECK_ARG(env, result);
758
759
12
  v8::Isolate* isolate = env->isolate;
760
761
  // The finalizer object will delete itself after invoking the callback.
762
12
  v8impl::Finalizer* finalizer = v8impl::Finalizer::New(
763
      env, finalize_cb, nullptr, finalize_hint,
764
12
      v8impl::Finalizer::kKeepEnvReference);
765
766
  auto maybe = node::Buffer::New(isolate,
767
                                static_cast<char*>(data),
768
                                length,
769
                                v8impl::BufferFinalizer::FinalizeBufferCallback,
770
12
                                finalizer);
771
772
12
  CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
773
774
24
  *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked());
775
12
  return GET_RETURN_STATUS(env);
776
  // Tell coverity that 'finalizer' should not be freed when we return
777
  // as it will be deleted when the buffer to which it is associated
778
  // is finalized.
779
  // coverity[leaked_storage]
780
}
781
782
1
napi_status napi_create_buffer_copy(napi_env env,
783
                                    size_t length,
784
                                    const void* data,
785
                                    void** result_data,
786
                                    napi_value* result) {
787


3
  NAPI_PREAMBLE(env);
788
1
  CHECK_ARG(env, result);
789
790
1
  auto maybe = node::Buffer::Copy(env->isolate,
791
2
    static_cast<const char*>(data), length);
792
793
1
  CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
794
795
1
  v8::Local<v8::Object> buffer = maybe.ToLocalChecked();
796
2
  *result = v8impl::JsValueFromV8LocalValue(buffer);
797
798
1
  if (result_data != nullptr) {
799
    *result_data = node::Buffer::Data(buffer);
800
  }
801
802
1
  return GET_RETURN_STATUS(env);
803
}
804
805
1
napi_status napi_is_buffer(napi_env env, napi_value value, bool* result) {
806
1
  CHECK_ENV(env);
807
1
  CHECK_ARG(env, value);
808
1
  CHECK_ARG(env, result);
809
810
1
  *result = node::Buffer::HasInstance(v8impl::V8LocalValueFromJsValue(value));
811
1
  return napi_clear_last_error(env);
812
}
813
814
1
napi_status napi_get_buffer_info(napi_env env,
815
                                 napi_value value,
816
                                 void** data,
817
                                 size_t* length) {
818
1
  CHECK_ENV(env);
819
1
  CHECK_ARG(env, value);
820
821
1
  v8::Local<v8::Value> buffer = v8impl::V8LocalValueFromJsValue(value);
822
823
1
  if (data != nullptr) {
824
1
    *data = node::Buffer::Data(buffer);
825
  }
826
1
  if (length != nullptr) {
827
1
    *length = node::Buffer::Length(buffer);
828
  }
829
830
1
  return napi_clear_last_error(env);
831
}
832
833
1
napi_status napi_get_node_version(napi_env env,
834
                                  const napi_node_version** result) {
835
1
  CHECK_ENV(env);
836
1
  CHECK_ARG(env, result);
837
  static const napi_node_version version = {
838
    NODE_MAJOR_VERSION,
839
    NODE_MINOR_VERSION,
840
    NODE_PATCH_VERSION,
841
    NODE_RELEASE
842
  };
843
1
  *result = &version;
844
1
  return napi_clear_last_error(env);
845
}
846
847
namespace {
848
namespace uvimpl {
849
850
513
static napi_status ConvertUVErrorCode(int code) {
851

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

16
    CHECK_TO_OBJECT(env, context, resource, async_resource);
961
  } else {
962
508
    resource = v8::Object::New(env->isolate);
963
  }
964
965
  v8::Local<v8::String> resource_name;
966

2048
  CHECK_TO_STRING(env, context, resource_name, async_resource_name);
967
968
512
  uvimpl::Work* work = uvimpl::Work::New(reinterpret_cast<node_napi_env>(env),
969
                                         resource,
970
                                         resource_name,
971
                                         execute,
972
                                         complete,
973
512
                                         data);
974
975
512
  *result = reinterpret_cast<napi_async_work>(work);
976
977
512
  return napi_clear_last_error(env);
978
}
979
980
508
napi_status napi_delete_async_work(napi_env env, napi_async_work work) {
981
508
  CHECK_ENV(env);
982
508
  CHECK_ARG(env, work);
983
984
508
  uvimpl::Work::Delete(reinterpret_cast<uvimpl::Work*>(work));
985
986
508
  return napi_clear_last_error(env);
987
}
988
989
518
napi_status napi_get_uv_event_loop(napi_env env, uv_loop_t** loop) {
990
518
  CHECK_ENV(env);
991
518
  CHECK_ARG(env, loop);
992
518
  *loop = reinterpret_cast<node_napi_env>(env)->node_env()->event_loop();
993
518
  return napi_clear_last_error(env);
994
}
995
996
512
napi_status napi_queue_async_work(napi_env env, napi_async_work work) {
997
512
  CHECK_ENV(env);
998
512
  CHECK_ARG(env, work);
999
1000
  napi_status status;
1001
512
  uv_loop_t* event_loop = nullptr;
1002
512
  status = napi_get_uv_event_loop(env, &event_loop);
1003
512
  if (status != napi_ok)
1004
    return napi_set_last_error(env, status);
1005
1006
512
  uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work);
1007
1008
512
  w->ScheduleWork();
1009
1010
512
  return napi_clear_last_error(env);
1011
}
1012
1013
1
napi_status napi_cancel_async_work(napi_env env, napi_async_work work) {
1014
1
  CHECK_ENV(env);
1015
1
  CHECK_ARG(env, work);
1016
1017
1
  uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work);
1018
1019
1
  CALL_UV(env, w->CancelWork());
1020
1021
1
  return napi_clear_last_error(env);
1022
}
1023
1024
napi_status
1025
17
napi_create_threadsafe_function(napi_env env,
1026
                                napi_value func,
1027
                                napi_value async_resource,
1028
                                napi_value async_resource_name,
1029
                                size_t max_queue_size,
1030
                                size_t initial_thread_count,
1031
                                void* thread_finalize_data,
1032
                                napi_finalize thread_finalize_cb,
1033
                                void* context,
1034
                                napi_threadsafe_function_call_js call_js_cb,
1035
                                napi_threadsafe_function* result) {
1036
17
  CHECK_ENV(env);
1037
17
  CHECK_ARG(env, async_resource_name);
1038
17
  RETURN_STATUS_IF_FALSE(env, initial_thread_count > 0, napi_invalid_arg);
1039
17
  CHECK_ARG(env, result);
1040
1041
17
  napi_status status = napi_ok;
1042
1043
  v8::Local<v8::Function> v8_func;
1044
17
  if (func == nullptr) {
1045
3
    CHECK_ARG(env, call_js_cb);
1046
  } else {
1047

42
    CHECK_TO_FUNCTION(env, v8_func, func);
1048
  }
1049
1050
17
  v8::Local<v8::Context> v8_context = env->context();
1051
1052
  v8::Local<v8::Object> v8_resource;
1053
17
  if (async_resource == nullptr) {
1054
17
    v8_resource = v8::Object::New(env->isolate);
1055
  } else {
1056
    CHECK_TO_OBJECT(env, v8_context, v8_resource, async_resource);
1057
  }
1058
1059
  v8::Local<v8::String> v8_name;
1060

68
  CHECK_TO_STRING(env, v8_context, v8_name, async_resource_name);
1061
1062
  v8impl::ThreadSafeFunction* ts_fn =
1063
      new v8impl::ThreadSafeFunction(v8_func,
1064
                                     v8_resource,
1065
                                     v8_name,
1066
                                     initial_thread_count,
1067
                                     context,
1068
                                     max_queue_size,
1069
                                     reinterpret_cast<node_napi_env>(env),
1070
                                     thread_finalize_data,
1071
                                     thread_finalize_cb,
1072
17
                                     call_js_cb);
1073
1074
17
  if (ts_fn == nullptr) {
1075
    status = napi_generic_failure;
1076
  } else {
1077
    // Init deletes ts_fn upon failure.
1078
17
    status = ts_fn->Init();
1079
17
    if (status == napi_ok) {
1080
17
      *result = reinterpret_cast<napi_threadsafe_function>(ts_fn);
1081
    }
1082
  }
1083
1084
17
  return napi_set_last_error(env, status);
1085
}
1086
1087
napi_status
1088
15
napi_get_threadsafe_function_context(napi_threadsafe_function func,
1089
                                     void** result) {
1090
15
  CHECK_NOT_NULL(func);
1091
15
  CHECK_NOT_NULL(result);
1092
1093
15
  *result = reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Context();
1094
15
  return napi_ok;
1095
}
1096
1097
napi_status
1098
16457943
napi_call_threadsafe_function(napi_threadsafe_function func,
1099
                              void* data,
1100
                              napi_threadsafe_function_call_mode is_blocking) {
1101
16457943
  CHECK_NOT_NULL(func);
1102
16457943
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Push(data,
1103
16457943
                                                                   is_blocking);
1104
}
1105
1106
napi_status
1107
4
napi_acquire_threadsafe_function(napi_threadsafe_function func) {
1108
4
  CHECK_NOT_NULL(func);
1109
4
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Acquire();
1110
}
1111
1112
napi_status
1113
31
napi_release_threadsafe_function(napi_threadsafe_function func,
1114
                                 napi_threadsafe_function_release_mode mode) {
1115
31
  CHECK_NOT_NULL(func);
1116
31
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Release(mode);
1117
}
1118
1119
napi_status
1120
2
napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
1121
2
  CHECK_NOT_NULL(func);
1122
2
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Unref();
1123
}
1124
1125
napi_status
1126
napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
1127
  CHECK_NOT_NULL(func);
1128
  return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Ref();
1129

13380
}