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: 422 468 90.2 %
Date: 2019-03-02 22:23:06 Branches: 211 368 57.3 %

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

66443272
    while (queue.size() >= max_queue_size &&
148

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

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

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


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

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

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

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

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

271
  if (_exports != nullptr &&
477
202
      _exports != v8impl::JsValueFromV8LocalValue(exports)) {
478
6
    napi_value _module = v8impl::JsValueFromV8LocalValue(module);
479
6
    napi_set_named_property(env, _module, "exports", _exports);
480
  }
481
}
482
483
// Registers a NAPI module.
484
66
void napi_module_register(napi_module* mod) {
485
  node::node_module* nm = new node::node_module {
486
    -1,
487
66
    mod->nm_flags | NM_F_DELETEME,
488
    nullptr,
489
    mod->nm_filename,
490
    nullptr,
491
    napi_module_register_cb,
492
    mod->nm_modname,
493
    mod,  // priv
494
    nullptr,
495
132
  };
496
66
  node::node_module_register(nm);
497
66
}
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
1
napi_status napi_fatal_exception(napi_env env, napi_value err) {
522


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

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

24
    CHECK_TO_OBJECT(env, context, v8_resource, async_resource);
611
  } else {
612
1
    v8_resource = v8::Object::New(isolate);
613
  }
614
615
  v8::Local<v8::String> v8_resource_name;
616

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


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

92
  CHECK_TO_OBJECT(env, context, v8recv, recv);
660
661
  v8::Local<v8::Function> v8func;
662

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


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


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


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

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

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

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

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

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