GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage/nodes/benchmark/out/../src/node_api.cc Lines: 411 457 89.9 %
Date: 2019-01-07 12:15:22 Branches: 212 364 58.2 %

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
struct node_napi_env__ : public napi_env__ {
11
58
  explicit node_napi_env__(v8::Local<v8::Context> context):
12
58
      napi_env__(context) {
13
58
    CHECK_NOT_NULL(node_env());
14
58
  }
15
1180
  inline node::Environment* node_env() const {
16
1180
    return node::Environment::GetCurrent(context());
17
  }
18
};
19
20
typedef node_napi_env__* node_napi_env;
21
22
namespace v8impl {
23
24
namespace {
25
26
class BufferFinalizer: private Finalizer {
27
 public:
28
  // node::Buffer::FreeCallback
29
3
  static void FinalizeBufferCallback(char* data, void* hint) {
30
3
    BufferFinalizer* finalizer = static_cast<BufferFinalizer*>(hint);
31
3
    if (finalizer->_finalize_callback != nullptr) {
32

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

67987914
    while (queue.size() >= max_queue_size &&
142

33993908
        max_queue_size > 0 &&
143
16996901
        !is_closing) {
144
16996898
      if (mode == napi_tsfn_nonblocking) {
145
16996865
        return napi_queue_full;
146
      }
147
33
      cond->Wait(lock);
148
    }
149
150
109
    if (is_closing) {
151
5
      if (thread_count == 0) {
152
        return napi_invalid_arg;
153
      } else {
154
5
        thread_count--;
155
5
        return napi_closing;
156
      }
157
    } else {
158
104
      if (uv_async_send(&async) != 0) {
159
        return napi_generic_failure;
160
      }
161
104
      queue.push(data);
162
104
      return napi_ok;
163
16996974
    }
164
  }
165
166
4
  napi_status Acquire() {
167
4
    node::Mutex::ScopedLock lock(this->mutex);
168
169
4
    if (is_closing) {
170
      return napi_closing;
171
    }
172
173
4
    thread_count++;
174
175
4
    return napi_ok;
176
  }
177
178
27
  napi_status Release(napi_threadsafe_function_release_mode mode) {
179
27
    node::Mutex::ScopedLock lock(this->mutex);
180
181
27
    if (thread_count == 0) {
182
      return napi_invalid_arg;
183
    }
184
185
27
    thread_count--;
186
187

27
    if (thread_count == 0 || mode == napi_tsfn_abort) {
188
12
      if (!is_closing) {
189
12
        is_closing = (mode == napi_tsfn_abort);
190

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


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

98
          if (size == max_queue_size && max_queue_size > 0) {
273
62
            cond->Signal(lock);
274
          }
275
98
          size--;
276
        }
277
278
103
        if (size == 0) {
279
41
          if (thread_count == 0) {
280
9
            is_closing = true;
281
9
            if (max_queue_size > 0) {
282
7
              cond->Signal(lock);
283
            }
284
9
            CloseHandlesAndMaybeDelete();
285
          } else {
286
32
            if (uv_idle_stop(&idle) != 0) {
287
              idle_stop_failed = true;
288
            }
289
          }
290
        }
291
107
      }
292
    }
293
294

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

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

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

181
  NAPI_CALL_INTO_MODULE_THROW(env,
464
      _exports = init(env, v8impl::JsValueFromV8LocalValue(exports)));
465
466
  // If register function returned a non-null exports object different from
467
  // the exports object we passed it, set that as the "exports" property of
468
  // the module.
469

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

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

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

24
    CHECK_TO_OBJECT(env, context, v8_resource, async_resource);
604
  } else {
605
1
    v8_resource = v8::Object::New(isolate);
606
  }
607
608
  v8::Local<v8::String> v8_resource_name;
609

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

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

92
  CHECK_TO_OBJECT(env, context, v8recv, recv);
653
654
  v8::Local<v8::Function> v8func;
655

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

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

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

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

511
  switch (code) {
805
  case 0:
806
510
    return napi_ok;
807
  case UV_EINVAL:
808
    return napi_invalid_arg;
809
  case UV_ECANCELED:
810
1
    return napi_cancelled;
811
  }
812
813
  return napi_generic_failure;
814
}
815
816
// Wrapper around uv_work_t which calls user-provided callbacks.
817
class Work : public node::AsyncResource, public node::ThreadPoolWork {
818
 private:
819
510
  explicit Work(node_napi_env env,
820
                v8::Local<v8::Object> async_resource,
821
                v8::Local<v8::String> async_resource_name,
822
                napi_async_execute_callback execute,
823
                napi_async_complete_callback complete = nullptr,
824
                void* data = nullptr)
825
    : AsyncResource(env->isolate,
826
                    async_resource,
827
1020
                    *v8::String::Utf8Value(env->isolate, async_resource_name)),
828
      ThreadPoolWork(env->node_env()),
829
      _env(env),
830
      _data(data),
831
      _execute(execute),
832
1020
      _complete(complete) {
833
510
  }
834
835
1016
  virtual ~Work() { }
836
837
 public:
838
510
  static Work* New(node_napi_env env,
839
                   v8::Local<v8::Object> async_resource,
840
                   v8::Local<v8::String> async_resource_name,
841
                   napi_async_execute_callback execute,
842
                   napi_async_complete_callback complete,
843
                   void* data) {
844
    return new Work(env, async_resource, async_resource_name,
845
510
                    execute, complete, data);
846
  }
847
848
508
  static void Delete(Work* work) {
849
508
    delete work;
850
508
  }
851
852
509
  void DoThreadPoolWork() override {
853
509
    _execute(_env, _data);
854
509
  }
855
856
510
  void AfterThreadPoolWork(int status) override {
857
510
    if (_complete == nullptr)
858
509
      return;
859
860
    // Establish a handle scope here so that every callback doesn't have to.
861
    // Also it is needed for the exception-handling below.
862
510
    v8::HandleScope scope(_env->isolate);
863
864
1019
    CallbackScope callback_scope(this);
865
866
    // We have to back up the env here because the `NAPI_CALL_INTO_MODULE` macro
867
    // makes use of it after the call into the module completes, but the module
868
    // may have deallocated **this**, and along with it the place where _env is
869
    // stored.
870
510
    napi_env env = _env;
871
872

1024
    NAPI_CALL_INTO_MODULE(env,
873
        _complete(_env, ConvertUVErrorCode(status), _data),
874
        [env] (v8::Local<v8::Value> local_err) {
875
          // If there was an unhandled exception in the complete callback,
876
          // report it as a fatal exception. (There is no JavaScript on the
877
          // callstack that can possibly handle it.)
878
          v8impl::trigger_fatal_exception(env, local_err);
879
509
        });
880
881
    // Note: Don't access `work` after this point because it was
882
    // likely deleted by the complete callback.
883
  }
884
885
 private:
886
  node_napi_env _env;
887
  void* _data;
888
  napi_async_execute_callback _execute;
889
  napi_async_complete_callback _complete;
890
};
891
892
}  // end of namespace uvimpl
893
}  // end of anonymous namespace
894
895
#define CALL_UV(env, condition)                                         \
896
  do {                                                                  \
897
    int result = (condition);                                           \
898
    napi_status status = uvimpl::ConvertUVErrorCode(result);            \
899
    if (status != napi_ok) {                                            \
900
      return napi_set_last_error(env, status, result);                  \
901
    }                                                                   \
902
  } while (0)
903
904
510
napi_status napi_create_async_work(napi_env env,
905
                                   napi_value async_resource,
906
                                   napi_value async_resource_name,
907
                                   napi_async_execute_callback execute,
908
                                   napi_async_complete_callback complete,
909
                                   void* data,
910
                                   napi_async_work* result) {
911
510
  CHECK_ENV(env);
912
510
  CHECK_ARG(env, execute);
913
510
  CHECK_ARG(env, result);
914
915
510
  v8::Local<v8::Context> context = env->context();
916
917
  v8::Local<v8::Object> resource;
918
510
  if (async_resource != nullptr) {
919

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

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

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

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