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: 445 492 90.4 %
Date: 2020-07-19 22:14:24 Branches: 208 356 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
148
struct node_napi_env__ : public napi_env__ {
15
75
  explicit node_napi_env__(v8::Local<v8::Context> context):
16
75
      napi_env__(context) {
17
75
    CHECK_NOT_NULL(node_env());
18
75
  }
19
20
6056
  inline node::Environment* node_env() const {
21
6056
    return node::Environment::GetCurrent(context());
22
  }
23
24
4805
  bool can_call_into_js() const override {
25
4805
    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
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
13
  static void FinalizeBufferCallback(char* data, void* hint) {
47
    std::unique_ptr<BufferFinalizer, Deleter> finalizer{
48
26
        static_cast<BufferFinalizer*>(hint)};
49
13
    finalizer->_finalize_data = data;
50
51
    node::Environment* node_env =
52
13
        static_cast<node_napi_env>(finalizer->_env)->node_env();
53
26
    node_env->SetImmediate(
54
65
        [finalizer = std::move(finalizer)](node::Environment* env) {
55
13
      if (finalizer->_finalize_callback == nullptr) return;
56
57
24
      v8::HandleScope handle_scope(finalizer->_env->isolate);
58
12
      v8::Context::Scope context_scope(finalizer->_env->context());
59
60
36
      finalizer->_env->CallIntoModule([&](napi_env env) {
61
36
        finalizer->_finalize_callback(
62
            env,
63
12
            finalizer->_finalize_data,
64
24
            finalizer->_finalize_hint);
65
24
      });
66
13
    });
67
13
  }
68
69
  struct Deleter {
70
13
    void operator()(BufferFinalizer* finalizer) {
71
13
      Finalizer::Delete(finalizer);
72
13
    }
73
  };
74
};
75
76
75
static inline napi_env NewEnv(v8::Local<v8::Context> context) {
77
  node_napi_env result;
78
79
75
  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
150
  result->node_env()->AddCleanupHook(
88
223
      [](void* arg) {
89
74
        static_cast<napi_env>(arg)->Unref();
90
223
      },
91
75
      static_cast<void*>(result));
92
93
75
  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
17205561
  napi_status Push(void* data, napi_threadsafe_function_call_mode mode) {
150
34411122
    node::Mutex::ScopedLock lock(this->mutex);
151
152

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

34411088
        max_queue_size > 0 &&
154
17205485
        !is_closing) {
155
17205482
      if (mode == napi_tsfn_nonblocking) {
156
17205440
        return napi_queue_full;
157
      }
158
42
      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

17
      if (max_queue_size == 0 || cond) {
231
17
        CHECK_EQ(0, uv_idle_init(loop, &idle));
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
272
    {
273
240
      node::Mutex::ScopedLock lock(this->mutex);
274
120
      if (is_closing) {
275
4
        CloseHandlesAndMaybeDelete();
276
      } else {
277
116
        size_t size = queue.size();
278
116
        if (size > 0) {
279
110
          data = queue.front();
280
110
          queue.pop();
281
110
          popped_value = true;
282

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

10
    if (!(env == nullptr || cb == nullptr)) {
367
      napi_value recv;
368
      napi_status status;
369
370
10
      status = napi_get_undefined(env, &recv);
371
10
      if (status != napi_ok) {
372
        napi_throw_error(env, "ERR_NAPI_TSFN_GET_UNDEFINED",
373
            "Failed to retrieve undefined value");
374
        return;
375
      }
376
377
10
      status = napi_call_function(env, recv, cb, 0, nullptr, nullptr);
378

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

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


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

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

40
    CHECK_TO_OBJECT(env, context, v8_resource, async_resource);
600
  } else {
601
1
    v8_resource = v8::Object::New(isolate);
602
  }
603
604
  v8::Local<v8::String> v8_resource_name;
605

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


69
  NAPI_PREAMBLE(env);
641
23
  CHECK_ARG(env, recv);
642
23
  if (argc > 0) {
643
4
    CHECK_ARG(env, argv);
644
  }
645
646
23
  v8::Local<v8::Context> context = env->context();
647
648
  v8::Local<v8::Object> v8recv;
649

92
  CHECK_TO_OBJECT(env, context, v8recv, recv);
650
651
  v8::Local<v8::Function> v8func;
652

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


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


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


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

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

16
    CHECK_TO_OBJECT(env, context, resource, async_resource);
912
  } else {
913
508
    resource = v8::Object::New(env->isolate);
914
  }
915
916
  v8::Local<v8::String> resource_name;
917

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

42
    CHECK_TO_FUNCTION(env, v8_func, func);
999
  }
1000
1001
17
  v8::Local<v8::Context> v8_context = env->context();
1002
1003
  v8::Local<v8::Object> v8_resource;
1004
17
  if (async_resource == nullptr) {
1005
17
    v8_resource = v8::Object::New(env->isolate);
1006
  } else {
1007
    CHECK_TO_OBJECT(env, v8_context, v8_resource, async_resource);
1008
  }
1009
1010
  v8::Local<v8::String> v8_name;
1011

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

14970
}