1 #include "async_wrap-inl.h"
2 #include "env-inl.h"
3 #define NAPI_EXPERIMENTAL
4 #include "js_native_api_v8.h"
5 #include "memory_tracker-inl.h"
6 #include "node_api.h"
7 #include "node_api_internals.h"
8 #include "node_binding.h"
9 #include "node_buffer.h"
10 #include "node_errors.h"
11 #include "node_internals.h"
12 #include "node_process.h"
13 #include "node_url.h"
14 #include "threadpoolwork-inl.h"
15 #include "tracing/traced_value.h"
16 #include "util-inl.h"
17
18 #include <atomic>
19 #include <cstring>
20 #include <memory>
21
node_napi_env__(v8::Local<v8::Context> context,const std::string & module_filename,int32_t module_api_version)22 node_napi_env__::node_napi_env__(v8::Local<v8::Context> context,
23 const std::string& module_filename,
24 int32_t module_api_version)
25 : napi_env__(context, module_api_version), filename(module_filename) {
26 CHECK_NOT_NULL(node_env());
27 }
28
DeleteMe()29 void node_napi_env__::DeleteMe() {
30 destructing = true;
31 DrainFinalizerQueue();
32 napi_env__::DeleteMe();
33 }
34
can_call_into_js() const35 bool node_napi_env__::can_call_into_js() const {
36 return node_env()->can_call_into_js();
37 }
38
CallFinalizer(napi_finalize cb,void * data,void * hint)39 void node_napi_env__::CallFinalizer(napi_finalize cb, void* data, void* hint) {
40 CallFinalizer<true>(cb, data, hint);
41 }
42
43 template <bool enforceUncaughtExceptionPolicy>
CallFinalizer(napi_finalize cb,void * data,void * hint)44 void node_napi_env__::CallFinalizer(napi_finalize cb, void* data, void* hint) {
45 v8::HandleScope handle_scope(isolate);
46 v8::Context::Scope context_scope(context());
47 CallbackIntoModule<enforceUncaughtExceptionPolicy>(
48 [&](napi_env env) { cb(env, data, hint); });
49 }
50
EnqueueFinalizer(v8impl::RefTracker * finalizer)51 void node_napi_env__::EnqueueFinalizer(v8impl::RefTracker* finalizer) {
52 napi_env__::EnqueueFinalizer(finalizer);
53 // Schedule a second pass only when it has not been scheduled, and not
54 // destructing the env.
55 // When the env is being destructed, queued finalizers are drained in the
56 // loop of `node_napi_env__::DrainFinalizerQueue`.
57 if (!finalization_scheduled && !destructing) {
58 finalization_scheduled = true;
59 Ref();
60 node_env()->SetImmediate([this](node::Environment* node_env) {
61 finalization_scheduled = false;
62 Unref();
63 DrainFinalizerQueue();
64 });
65 }
66 }
67
DrainFinalizerQueue()68 void node_napi_env__::DrainFinalizerQueue() {
69 // As userland code can delete additional references in one finalizer,
70 // the list of pending finalizers may be mutated as we execute them, so
71 // we keep iterating it until it is empty.
72 while (!pending_finalizers.empty()) {
73 v8impl::RefTracker* ref_tracker = *pending_finalizers.begin();
74 pending_finalizers.erase(ref_tracker);
75 ref_tracker->Finalize();
76 }
77 }
78
trigger_fatal_exception(v8::Local<v8::Value> local_err)79 void node_napi_env__::trigger_fatal_exception(v8::Local<v8::Value> local_err) {
80 v8::Local<v8::Message> local_msg =
81 v8::Exception::CreateMessage(isolate, local_err);
82 node::errors::TriggerUncaughtException(isolate, local_err, local_msg);
83 }
84
85 // option enforceUncaughtExceptionPolicy is added for not breaking existing
86 // running n-api add-ons, and should be deprecated in the next major Node.js
87 // release.
88 template <bool enforceUncaughtExceptionPolicy, typename T>
CallbackIntoModule(T && call)89 void node_napi_env__::CallbackIntoModule(T&& call) {
90 CallIntoModule(call, [](napi_env env_, v8::Local<v8::Value> local_err) {
91 node_napi_env__* env = static_cast<node_napi_env__*>(env_);
92 if (env->terminatedOrTerminating()) {
93 return;
94 }
95 node::Environment* node_env = env->node_env();
96 if (!node_env->options()->force_node_api_uncaught_exceptions_policy &&
97 !enforceUncaughtExceptionPolicy) {
98 ProcessEmitDeprecationWarning(
99 node_env,
100 "Uncaught N-API callback exception detected, please run node "
101 "with option --force-node-api-uncaught-exceptions-policy=true"
102 "to handle those exceptions properly.",
103 "DEP0168");
104 return;
105 }
106 // If there was an unhandled exception in the complete callback,
107 // report it as a fatal exception. (There is no JavaScript on the
108 // callstack that can possibly handle it.)
109 env->trigger_fatal_exception(local_err);
110 });
111 }
112
113 namespace v8impl {
114
115 namespace {
116
117 class BufferFinalizer : private Finalizer {
118 public:
New(napi_env env,napi_finalize finalize_callback=nullptr,void * finalize_data=nullptr,void * finalize_hint=nullptr)119 static BufferFinalizer* New(napi_env env,
120 napi_finalize finalize_callback = nullptr,
121 void* finalize_data = nullptr,
122 void* finalize_hint = nullptr) {
123 return new BufferFinalizer(
124 env, finalize_callback, finalize_data, finalize_hint);
125 }
126 // node::Buffer::FreeCallback
FinalizeBufferCallback(char * data,void * hint)127 static void FinalizeBufferCallback(char* data, void* hint) {
128 std::unique_ptr<BufferFinalizer, Deleter> finalizer{
129 static_cast<BufferFinalizer*>(hint)};
130 finalizer->finalize_data_ = data;
131
132 // It is safe to call into JavaScript at this point.
133 if (finalizer->finalize_callback_ == nullptr) return;
134 finalizer->env_->CallFinalizer(finalizer->finalize_callback_,
135 finalizer->finalize_data_,
136 finalizer->finalize_hint_);
137 }
138
139 struct Deleter {
operator ()v8impl::__anon2b7257620411::BufferFinalizer::Deleter140 void operator()(BufferFinalizer* finalizer) { delete finalizer; }
141 };
142
143 private:
BufferFinalizer(napi_env env,napi_finalize finalize_callback,void * finalize_data,void * finalize_hint)144 BufferFinalizer(napi_env env,
145 napi_finalize finalize_callback,
146 void* finalize_data,
147 void* finalize_hint)
148 : Finalizer(env, finalize_callback, finalize_data, finalize_hint) {
149 env_->Ref();
150 }
151
~BufferFinalizer()152 ~BufferFinalizer() { env_->Unref(); }
153 };
154
ThrowNodeApiVersionError(node::Environment * node_env,const char * module_name,int32_t module_api_version)155 void ThrowNodeApiVersionError(node::Environment* node_env,
156 const char* module_name,
157 int32_t module_api_version) {
158 std::string error_message;
159 error_message += module_name;
160 error_message += " requires Node-API version ";
161 error_message += std::to_string(module_api_version);
162 error_message += ", but this version of Node.js only supports version ";
163 error_message += NODE_STRINGIFY(NAPI_VERSION) " add-ons.";
164 node_env->ThrowError(error_message.c_str());
165 }
166
NewEnv(v8::Local<v8::Context> context,const std::string & module_filename,int32_t module_api_version)167 inline napi_env NewEnv(v8::Local<v8::Context> context,
168 const std::string& module_filename,
169 int32_t module_api_version) {
170 node_napi_env result;
171
172 // Validate module_api_version.
173 if (module_api_version < NODE_API_DEFAULT_MODULE_API_VERSION) {
174 module_api_version = NODE_API_DEFAULT_MODULE_API_VERSION;
175 } else if (module_api_version > NAPI_VERSION &&
176 module_api_version != NAPI_VERSION_EXPERIMENTAL) {
177 node::Environment* node_env = node::Environment::GetCurrent(context);
178 CHECK_NOT_NULL(node_env);
179 ThrowNodeApiVersionError(
180 node_env, module_filename.c_str(), module_api_version);
181 return nullptr;
182 }
183
184 result = new node_napi_env__(context, module_filename, module_api_version);
185 // TODO(addaleax): There was previously code that tried to delete the
186 // napi_env when its v8::Context was garbage collected;
187 // However, as long as N-API addons using this napi_env are in place,
188 // the Context needs to be accessible and alive.
189 // Ideally, we'd want an on-addon-unload hook that takes care of this
190 // once all N-API addons using this napi_env are unloaded.
191 // For now, a per-Environment cleanup hook is the best we can do.
192 result->node_env()->AddCleanupHook(
193 [](void* arg) { static_cast<napi_env>(arg)->Unref(); },
194 static_cast<void*>(result));
195
196 return result;
197 }
198
199 class ThreadSafeFunction : public node::AsyncResource {
200 public:
ThreadSafeFunction(v8::Local<v8::Function> func,v8::Local<v8::Object> resource,v8::Local<v8::String> name,size_t thread_count_,void * context_,size_t max_queue_size_,node_napi_env env_,void * finalize_data_,napi_finalize finalize_cb_,napi_threadsafe_function_call_js call_js_cb_)201 ThreadSafeFunction(v8::Local<v8::Function> func,
202 v8::Local<v8::Object> resource,
203 v8::Local<v8::String> name,
204 size_t thread_count_,
205 void* context_,
206 size_t max_queue_size_,
207 node_napi_env env_,
208 void* finalize_data_,
209 napi_finalize finalize_cb_,
210 napi_threadsafe_function_call_js call_js_cb_)
211 : AsyncResource(env_->isolate,
212 resource,
213 *v8::String::Utf8Value(env_->isolate, name)),
214 thread_count(thread_count_),
215 is_closing(false),
216 dispatch_state(kDispatchIdle),
217 context(context_),
218 max_queue_size(max_queue_size_),
219 env(env_),
220 finalize_data(finalize_data_),
221 finalize_cb(finalize_cb_),
222 call_js_cb(call_js_cb_ == nullptr ? CallJs : call_js_cb_),
223 handles_closing(false) {
224 ref.Reset(env->isolate, func);
225 node::AddEnvironmentCleanupHook(env->isolate, Cleanup, this);
226 env->Ref();
227 }
228
~ThreadSafeFunction()229 ~ThreadSafeFunction() override {
230 node::RemoveEnvironmentCleanupHook(env->isolate, Cleanup, this);
231 env->Unref();
232 }
233
234 // These methods can be called from any thread.
235
Push(void * data,napi_threadsafe_function_call_mode mode)236 napi_status Push(void* data, napi_threadsafe_function_call_mode mode) {
237 node::Mutex::ScopedLock lock(this->mutex);
238
239 while (queue.size() >= max_queue_size && max_queue_size > 0 &&
240 !is_closing) {
241 if (mode == napi_tsfn_nonblocking) {
242 return napi_queue_full;
243 }
244 cond->Wait(lock);
245 }
246
247 if (is_closing) {
248 if (thread_count == 0) {
249 return napi_invalid_arg;
250 } else {
251 thread_count--;
252 return napi_closing;
253 }
254 } else {
255 queue.push(data);
256 Send();
257 return napi_ok;
258 }
259 }
260
Acquire()261 napi_status Acquire() {
262 node::Mutex::ScopedLock lock(this->mutex);
263
264 if (is_closing) {
265 return napi_closing;
266 }
267
268 thread_count++;
269
270 return napi_ok;
271 }
272
Release(napi_threadsafe_function_release_mode mode)273 napi_status Release(napi_threadsafe_function_release_mode mode) {
274 node::Mutex::ScopedLock lock(this->mutex);
275
276 if (thread_count == 0) {
277 return napi_invalid_arg;
278 }
279
280 thread_count--;
281
282 if (thread_count == 0 || mode == napi_tsfn_abort) {
283 if (!is_closing) {
284 is_closing = (mode == napi_tsfn_abort);
285 if (is_closing && max_queue_size > 0) {
286 cond->Signal(lock);
287 }
288 Send();
289 }
290 }
291
292 return napi_ok;
293 }
294
EmptyQueueAndDelete()295 void EmptyQueueAndDelete() {
296 for (; !queue.empty(); queue.pop()) {
297 call_js_cb(nullptr, nullptr, context, queue.front());
298 }
299 delete this;
300 }
301
302 // These methods must only be called from the loop thread.
303
Init()304 napi_status Init() {
305 ThreadSafeFunction* ts_fn = this;
306 uv_loop_t* loop = env->node_env()->event_loop();
307
308 if (uv_async_init(loop, &async, AsyncCb) == 0) {
309 if (max_queue_size > 0) {
310 cond = std::make_unique<node::ConditionVariable>();
311 }
312 if (max_queue_size == 0 || cond) {
313 return napi_ok;
314 }
315
316 env->node_env()->CloseHandle(
317 reinterpret_cast<uv_handle_t*>(&async),
318 [](uv_handle_t* handle) -> void {
319 ThreadSafeFunction* ts_fn =
320 node::ContainerOf(&ThreadSafeFunction::async,
321 reinterpret_cast<uv_async_t*>(handle));
322 delete ts_fn;
323 });
324
325 // Prevent the thread-safe function from being deleted here, because
326 // the callback above will delete it.
327 ts_fn = nullptr;
328 }
329
330 delete ts_fn;
331
332 return napi_generic_failure;
333 }
334
Unref()335 napi_status Unref() {
336 uv_unref(reinterpret_cast<uv_handle_t*>(&async));
337
338 return napi_ok;
339 }
340
Ref()341 napi_status Ref() {
342 uv_ref(reinterpret_cast<uv_handle_t*>(&async));
343
344 return napi_ok;
345 }
346
Context()347 inline void* Context() { return context; }
348
349 protected:
Dispatch()350 void Dispatch() {
351 bool has_more = true;
352
353 // Limit maximum synchronous iteration count to prevent event loop
354 // starvation. See `src/node_messaging.cc` for an inspiration.
355 unsigned int iterations_left = kMaxIterationCount;
356 while (has_more && --iterations_left != 0) {
357 dispatch_state = kDispatchRunning;
358 has_more = DispatchOne();
359
360 // Send() was called while we were executing the JS function
361 if (dispatch_state.exchange(kDispatchIdle) != kDispatchRunning) {
362 has_more = true;
363 }
364 }
365
366 if (has_more) {
367 Send();
368 }
369 }
370
DispatchOne()371 bool DispatchOne() {
372 void* data = nullptr;
373 bool popped_value = false;
374 bool has_more = false;
375
376 {
377 node::Mutex::ScopedLock lock(this->mutex);
378 if (is_closing) {
379 CloseHandlesAndMaybeDelete();
380 } else {
381 size_t size = queue.size();
382 if (size > 0) {
383 data = queue.front();
384 queue.pop();
385 popped_value = true;
386 if (size == max_queue_size && max_queue_size > 0) {
387 cond->Signal(lock);
388 }
389 size--;
390 }
391
392 if (size == 0) {
393 if (thread_count == 0) {
394 is_closing = true;
395 if (max_queue_size > 0) {
396 cond->Signal(lock);
397 }
398 CloseHandlesAndMaybeDelete();
399 }
400 } else {
401 has_more = true;
402 }
403 }
404 }
405
406 if (popped_value) {
407 v8::HandleScope scope(env->isolate);
408 CallbackScope cb_scope(this);
409 napi_value js_callback = nullptr;
410 if (!ref.IsEmpty()) {
411 v8::Local<v8::Function> js_cb =
412 v8::Local<v8::Function>::New(env->isolate, ref);
413 js_callback = v8impl::JsValueFromV8LocalValue(js_cb);
414 }
415 env->CallbackIntoModule<false>(
416 [&](napi_env env) { call_js_cb(env, js_callback, context, data); });
417 }
418
419 return has_more;
420 }
421
Finalize()422 void Finalize() {
423 v8::HandleScope scope(env->isolate);
424 if (finalize_cb) {
425 CallbackScope cb_scope(this);
426 env->CallFinalizer<false>(finalize_cb, finalize_data, context);
427 }
428 EmptyQueueAndDelete();
429 }
430
CloseHandlesAndMaybeDelete(bool set_closing=false)431 void CloseHandlesAndMaybeDelete(bool set_closing = false) {
432 v8::HandleScope scope(env->isolate);
433 if (set_closing) {
434 node::Mutex::ScopedLock lock(this->mutex);
435 is_closing = true;
436 if (max_queue_size > 0) {
437 cond->Signal(lock);
438 }
439 }
440 if (handles_closing) {
441 return;
442 }
443 handles_closing = true;
444 env->node_env()->CloseHandle(
445 reinterpret_cast<uv_handle_t*>(&async),
446 [](uv_handle_t* handle) -> void {
447 ThreadSafeFunction* ts_fn =
448 node::ContainerOf(&ThreadSafeFunction::async,
449 reinterpret_cast<uv_async_t*>(handle));
450 ts_fn->Finalize();
451 });
452 }
453
Send()454 void Send() {
455 // Ask currently running Dispatch() to make one more iteration
456 unsigned char current_state = dispatch_state.fetch_or(kDispatchPending);
457 if ((current_state & kDispatchRunning) == kDispatchRunning) {
458 return;
459 }
460
461 CHECK_EQ(0, uv_async_send(&async));
462 }
463
464 // Default way of calling into JavaScript. Used when ThreadSafeFunction is
465 // without a call_js_cb_.
CallJs(napi_env env,napi_value cb,void * context,void * data)466 static void CallJs(napi_env env, napi_value cb, void* context, void* data) {
467 if (!(env == nullptr || cb == nullptr)) {
468 napi_value recv;
469 napi_status status;
470
471 status = napi_get_undefined(env, &recv);
472 if (status != napi_ok) {
473 napi_throw_error(env,
474 "ERR_NAPI_TSFN_GET_UNDEFINED",
475 "Failed to retrieve undefined value");
476 return;
477 }
478
479 status = napi_call_function(env, recv, cb, 0, nullptr, nullptr);
480 if (status != napi_ok && status != napi_pending_exception) {
481 napi_throw_error(
482 env, "ERR_NAPI_TSFN_CALL_JS", "Failed to call JS callback");
483 return;
484 }
485 }
486 }
487
AsyncCb(uv_async_t * async)488 static void AsyncCb(uv_async_t* async) {
489 ThreadSafeFunction* ts_fn =
490 node::ContainerOf(&ThreadSafeFunction::async, async);
491 ts_fn->Dispatch();
492 }
493
Cleanup(void * data)494 static void Cleanup(void* data) {
495 reinterpret_cast<ThreadSafeFunction*>(data)->CloseHandlesAndMaybeDelete(
496 true);
497 }
498
499 private:
500 static const unsigned char kDispatchIdle = 0;
501 static const unsigned char kDispatchRunning = 1 << 0;
502 static const unsigned char kDispatchPending = 1 << 1;
503
504 static const unsigned int kMaxIterationCount = 1000;
505
506 // These are variables protected by the mutex.
507 node::Mutex mutex;
508 std::unique_ptr<node::ConditionVariable> cond;
509 std::queue<void*> queue;
510 uv_async_t async;
511 size_t thread_count;
512 bool is_closing;
513 std::atomic_uchar dispatch_state;
514
515 // These are variables set once, upon creation, and then never again, which
516 // means we don't need the mutex to read them.
517 void* context;
518 size_t max_queue_size;
519
520 // These are variables accessed only from the loop thread.
521 v8impl::Persistent<v8::Function> ref;
522 node_napi_env env;
523 void* finalize_data;
524 napi_finalize finalize_cb;
525 napi_threadsafe_function_call_js call_js_cb;
526 bool handles_closing;
527 };
528
529 /**
530 * Compared to node::AsyncResource, the resource object in AsyncContext is
531 * gc-able. AsyncContext holds a weak reference to the resource object.
532 * AsyncContext::MakeCallback doesn't implicitly set the receiver of the
533 * callback to the resource object.
534 */
535 class AsyncContext {
536 public:
AsyncContext(node_napi_env env,v8::Local<v8::Object> resource_object,const v8::Local<v8::String> resource_name,bool externally_managed_resource)537 AsyncContext(node_napi_env env,
538 v8::Local<v8::Object> resource_object,
539 const v8::Local<v8::String> resource_name,
540 bool externally_managed_resource)
541 : env_(env) {
542 async_id_ = node_env()->new_async_id();
543 trigger_async_id_ = node_env()->get_default_trigger_async_id();
544 resource_.Reset(node_env()->isolate(), resource_object);
545 lost_reference_ = false;
546 if (externally_managed_resource) {
547 resource_.SetWeak(
548 this, AsyncContext::WeakCallback, v8::WeakCallbackType::kParameter);
549 }
550
551 node::AsyncWrap::EmitAsyncInit(node_env(),
552 resource_object,
553 resource_name,
554 async_id_,
555 trigger_async_id_);
556 }
557
~AsyncContext()558 ~AsyncContext() {
559 resource_.Reset();
560 lost_reference_ = true;
561 node::AsyncWrap::EmitDestroy(node_env(), async_id_);
562 }
563
MakeCallback(v8::Local<v8::Object> recv,const v8::Local<v8::Function> callback,int argc,v8::Local<v8::Value> argv[])564 inline v8::MaybeLocal<v8::Value> MakeCallback(
565 v8::Local<v8::Object> recv,
566 const v8::Local<v8::Function> callback,
567 int argc,
568 v8::Local<v8::Value> argv[]) {
569 EnsureReference();
570 return node::InternalMakeCallback(node_env(),
571 resource(),
572 recv,
573 callback,
574 argc,
575 argv,
576 {async_id_, trigger_async_id_});
577 }
578
OpenCallbackScope()579 inline napi_callback_scope OpenCallbackScope() {
580 EnsureReference();
581 napi_callback_scope it =
582 reinterpret_cast<napi_callback_scope>(new CallbackScope(this));
583 env_->open_callback_scopes++;
584 return it;
585 }
586
EnsureReference()587 inline void EnsureReference() {
588 if (lost_reference_) {
589 const v8::HandleScope handle_scope(node_env()->isolate());
590 resource_.Reset(node_env()->isolate(),
591 v8::Object::New(node_env()->isolate()));
592 lost_reference_ = false;
593 }
594 }
595
node_env()596 inline node::Environment* node_env() { return env_->node_env(); }
resource()597 inline v8::Local<v8::Object> resource() {
598 return resource_.Get(node_env()->isolate());
599 }
async_context()600 inline node::async_context async_context() {
601 return {async_id_, trigger_async_id_};
602 }
603
CloseCallbackScope(node_napi_env env,napi_callback_scope s)604 static inline void CloseCallbackScope(node_napi_env env,
605 napi_callback_scope s) {
606 CallbackScope* callback_scope = reinterpret_cast<CallbackScope*>(s);
607 delete callback_scope;
608 env->open_callback_scopes--;
609 }
610
WeakCallback(const v8::WeakCallbackInfo<AsyncContext> & data)611 static void WeakCallback(const v8::WeakCallbackInfo<AsyncContext>& data) {
612 AsyncContext* async_context = data.GetParameter();
613 async_context->resource_.Reset();
614 async_context->lost_reference_ = true;
615 }
616
617 private:
618 class CallbackScope : public node::CallbackScope {
619 public:
CallbackScope(AsyncContext * async_context)620 explicit CallbackScope(AsyncContext* async_context)
621 : node::CallbackScope(async_context->node_env(),
622 async_context->resource_.Get(
623 async_context->node_env()->isolate()),
624 async_context->async_context()) {}
625 };
626
627 node_napi_env env_;
628 double async_id_;
629 double trigger_async_id_;
630 v8::Global<v8::Object> resource_;
631 bool lost_reference_;
632 };
633
634 } // end of anonymous namespace
635
636 } // end of namespace v8impl
637
638 // Intercepts the Node-V8 module registration callback. Converts parameters
639 // to NAPI equivalents and then calls the registration callback specified
640 // by the NAPI module.
napi_module_register_cb(v8::Local<v8::Object> exports,v8::Local<v8::Value> module,v8::Local<v8::Context> context,void * priv)641 static void napi_module_register_cb(v8::Local<v8::Object> exports,
642 v8::Local<v8::Value> module,
643 v8::Local<v8::Context> context,
644 void* priv) {
645 napi_module_register_by_symbol(
646 exports,
647 module,
648 context,
649 static_cast<const napi_module*>(priv)->nm_register_func);
650 }
651
652 template <int32_t module_api_version>
node_api_context_register_func(v8::Local<v8::Object> exports,v8::Local<v8::Value> module,v8::Local<v8::Context> context,void * priv)653 static void node_api_context_register_func(v8::Local<v8::Object> exports,
654 v8::Local<v8::Value> module,
655 v8::Local<v8::Context> context,
656 void* priv) {
657 napi_module_register_by_symbol(
658 exports,
659 module,
660 context,
661 reinterpret_cast<napi_addon_register_func>(priv),
662 module_api_version);
663 }
664
665 // This function must be augmented for each new Node API version.
666 // The key role of this function is to encode module_api_version in the function
667 // pointer. We are not going to have many Node API versions and having one
668 // function per version is relatively cheap. It avoids dynamic memory
669 // allocations or implementing more expensive changes to module registration.
670 // Currently AddLinkedBinding is the only user of this function.
get_node_api_context_register_func(node::Environment * node_env,const char * module_name,int32_t module_api_version)671 node::addon_context_register_func get_node_api_context_register_func(
672 node::Environment* node_env,
673 const char* module_name,
674 int32_t module_api_version) {
675 static_assert(
676 NAPI_VERSION == 9,
677 "New version of Node-API requires adding another else-if statement below "
678 "for the new version and updating this assert condition.");
679 if (module_api_version <= NODE_API_DEFAULT_MODULE_API_VERSION) {
680 return node_api_context_register_func<NODE_API_DEFAULT_MODULE_API_VERSION>;
681 } else if (module_api_version == 9) {
682 return node_api_context_register_func<9>;
683 } else if (module_api_version == NAPI_VERSION_EXPERIMENTAL) {
684 return node_api_context_register_func<NAPI_VERSION_EXPERIMENTAL>;
685 } else {
686 v8impl::ThrowNodeApiVersionError(node_env, module_name, module_api_version);
687 return nullptr;
688 }
689 }
690
napi_module_register_by_symbol(v8::Local<v8::Object> exports,v8::Local<v8::Value> module,v8::Local<v8::Context> context,napi_addon_register_func init,int32_t module_api_version)691 void napi_module_register_by_symbol(v8::Local<v8::Object> exports,
692 v8::Local<v8::Value> module,
693 v8::Local<v8::Context> context,
694 napi_addon_register_func init,
695 int32_t module_api_version) {
696 node::Environment* node_env = node::Environment::GetCurrent(context);
697 std::string module_filename = "";
698 if (init == nullptr) {
699 CHECK_NOT_NULL(node_env);
700 node_env->ThrowError("Module has no declared entry point.");
701 return;
702 }
703
704 // We set `env->filename` from `module.filename` here, but we could just as
705 // easily add a private property to `exports` in `process.dlopen`, which
706 // receives the file name from JS, and retrieve *that* here. Thus, we are not
707 // endorsing commonjs here by making use of `module.filename`.
708 v8::Local<v8::Value> filename_js;
709 v8::Local<v8::Object> modobj;
710 if (module->ToObject(context).ToLocal(&modobj) &&
711 modobj->Get(context, node_env->filename_string()).ToLocal(&filename_js) &&
712 filename_js->IsString()) {
713 node::Utf8Value filename(node_env->isolate(), filename_js);
714
715 // Turn the absolute path into a URL. Currently the absolute path is always
716 // a file system path.
717 // TODO(gabrielschulhof): Pass the `filename` through unchanged if/when we
718 // receive it as a URL already.
719 module_filename = node::url::FromFilePath(filename.ToStringView());
720 }
721
722 // Create a new napi_env for this specific module.
723 napi_env env = v8impl::NewEnv(context, module_filename, module_api_version);
724
725 napi_value _exports = nullptr;
726 env->CallIntoModule([&](napi_env env) {
727 _exports = init(env, v8impl::JsValueFromV8LocalValue(exports));
728 });
729
730 // If register function returned a non-null exports object different from
731 // the exports object we passed it, set that as the "exports" property of
732 // the module.
733 if (_exports != nullptr &&
734 _exports != v8impl::JsValueFromV8LocalValue(exports)) {
735 napi_value _module = v8impl::JsValueFromV8LocalValue(module);
736 napi_set_named_property(env, _module, "exports", _exports);
737 }
738 }
739
740 namespace node {
napi_module_to_node_module(const napi_module * mod)741 node_module napi_module_to_node_module(const napi_module* mod) {
742 return {
743 -1,
744 mod->nm_flags | NM_F_DELETEME,
745 nullptr,
746 mod->nm_filename,
747 nullptr,
748 napi_module_register_cb,
749 mod->nm_modname,
750 const_cast<napi_module*>(mod), // priv
751 nullptr,
752 };
753 }
754 } // namespace node
755
756 // Registers a NAPI module.
napi_module_register(napi_module * mod)757 void NAPI_CDECL napi_module_register(napi_module* mod) {
758 node::node_module* nm =
759 new node::node_module(node::napi_module_to_node_module(mod));
760 node::node_module_register(nm);
761 }
762
napi_add_env_cleanup_hook(napi_env env,napi_cleanup_hook fun,void * arg)763 napi_status NAPI_CDECL napi_add_env_cleanup_hook(napi_env env,
764 napi_cleanup_hook fun,
765 void* arg) {
766 CHECK_ENV(env);
767 CHECK_ARG(env, fun);
768
769 node::AddEnvironmentCleanupHook(env->isolate, fun, arg);
770
771 return napi_ok;
772 }
773
napi_remove_env_cleanup_hook(napi_env env,napi_cleanup_hook fun,void * arg)774 napi_status NAPI_CDECL napi_remove_env_cleanup_hook(napi_env env,
775 napi_cleanup_hook fun,
776 void* arg) {
777 CHECK_ENV(env);
778 CHECK_ARG(env, fun);
779
780 node::RemoveEnvironmentCleanupHook(env->isolate, fun, arg);
781
782 return napi_ok;
783 }
784
785 struct napi_async_cleanup_hook_handle__ {
napi_async_cleanup_hook_handle__napi_async_cleanup_hook_handle__786 napi_async_cleanup_hook_handle__(napi_env env,
787 napi_async_cleanup_hook user_hook,
788 void* user_data)
789 : env_(env), user_hook_(user_hook), user_data_(user_data) {
790 handle_ = node::AddEnvironmentCleanupHook(env->isolate, Hook, this);
791 env->Ref();
792 }
793
~napi_async_cleanup_hook_handle__napi_async_cleanup_hook_handle__794 ~napi_async_cleanup_hook_handle__() {
795 node::RemoveEnvironmentCleanupHook(std::move(handle_));
796 if (done_cb_ != nullptr) done_cb_(done_data_);
797
798 // Release the `env` handle asynchronously since it would be surprising if
799 // a call to a N-API function would destroy `env` synchronously.
800 static_cast<node_napi_env>(env_)->node_env()->SetImmediate(
801 [env = env_](node::Environment*) { env->Unref(); });
802 }
803
Hooknapi_async_cleanup_hook_handle__804 static void Hook(void* data, void (*done_cb)(void*), void* done_data) {
805 napi_async_cleanup_hook_handle__* handle =
806 static_cast<napi_async_cleanup_hook_handle__*>(data);
807 handle->done_cb_ = done_cb;
808 handle->done_data_ = done_data;
809 handle->user_hook_(handle, handle->user_data_);
810 }
811
812 node::AsyncCleanupHookHandle handle_;
813 napi_env env_ = nullptr;
814 napi_async_cleanup_hook user_hook_ = nullptr;
815 void* user_data_ = nullptr;
816 void (*done_cb_)(void*) = nullptr;
817 void* done_data_ = nullptr;
818 };
819
820 napi_status NAPI_CDECL
napi_add_async_cleanup_hook(napi_env env,napi_async_cleanup_hook hook,void * arg,napi_async_cleanup_hook_handle * remove_handle)821 napi_add_async_cleanup_hook(napi_env env,
822 napi_async_cleanup_hook hook,
823 void* arg,
824 napi_async_cleanup_hook_handle* remove_handle) {
825 CHECK_ENV(env);
826 CHECK_ARG(env, hook);
827
828 napi_async_cleanup_hook_handle__* handle =
829 new napi_async_cleanup_hook_handle__(env, hook, arg);
830
831 if (remove_handle != nullptr) *remove_handle = handle;
832
833 return napi_clear_last_error(env);
834 }
835
836 napi_status NAPI_CDECL
napi_remove_async_cleanup_hook(napi_async_cleanup_hook_handle remove_handle)837 napi_remove_async_cleanup_hook(napi_async_cleanup_hook_handle remove_handle) {
838 if (remove_handle == nullptr) return napi_invalid_arg;
839
840 delete remove_handle;
841
842 return napi_ok;
843 }
844
napi_fatal_exception(napi_env env,napi_value err)845 napi_status NAPI_CDECL napi_fatal_exception(napi_env env, napi_value err) {
846 NAPI_PREAMBLE(env);
847 CHECK_ARG(env, err);
848
849 v8::Local<v8::Value> local_err = v8impl::V8LocalValueFromJsValue(err);
850 static_cast<node_napi_env>(env)->trigger_fatal_exception(local_err);
851
852 return napi_clear_last_error(env);
853 }
854
napi_fatal_error(const char * location,size_t location_len,const char * message,size_t message_len)855 NAPI_NO_RETURN void NAPI_CDECL napi_fatal_error(const char* location,
856 size_t location_len,
857 const char* message,
858 size_t message_len) {
859 std::string location_string;
860 std::string message_string;
861
862 if (location_len != NAPI_AUTO_LENGTH) {
863 location_string.assign(const_cast<char*>(location), location_len);
864 } else {
865 location_string.assign(const_cast<char*>(location), strlen(location));
866 }
867
868 if (message_len != NAPI_AUTO_LENGTH) {
869 message_string.assign(const_cast<char*>(message), message_len);
870 } else {
871 message_string.assign(const_cast<char*>(message), strlen(message));
872 }
873
874 node::OnFatalError(location_string.c_str(), message_string.c_str());
875 }
876
877 napi_status NAPI_CDECL
napi_open_callback_scope(napi_env env,napi_value,napi_async_context async_context_handle,napi_callback_scope * result)878 napi_open_callback_scope(napi_env env,
879 napi_value /** ignored */,
880 napi_async_context async_context_handle,
881 napi_callback_scope* result) {
882 // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
883 // JS exceptions.
884 CHECK_ENV(env);
885 CHECK_ARG(env, result);
886
887 v8impl::AsyncContext* node_async_context =
888 reinterpret_cast<v8impl::AsyncContext*>(async_context_handle);
889
890 *result = node_async_context->OpenCallbackScope();
891
892 return napi_clear_last_error(env);
893 }
894
napi_close_callback_scope(napi_env env,napi_callback_scope scope)895 napi_status NAPI_CDECL napi_close_callback_scope(napi_env env,
896 napi_callback_scope scope) {
897 // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
898 // JS exceptions.
899 CHECK_ENV(env);
900 CHECK_ARG(env, scope);
901 if (env->open_callback_scopes == 0) {
902 return napi_callback_scope_mismatch;
903 }
904
905 v8impl::AsyncContext::CloseCallbackScope(reinterpret_cast<node_napi_env>(env),
906 scope);
907
908 return napi_clear_last_error(env);
909 }
910
napi_async_init(napi_env env,napi_value async_resource,napi_value async_resource_name,napi_async_context * result)911 napi_status NAPI_CDECL napi_async_init(napi_env env,
912 napi_value async_resource,
913 napi_value async_resource_name,
914 napi_async_context* result) {
915 CHECK_ENV(env);
916 CHECK_ARG(env, async_resource_name);
917 CHECK_ARG(env, result);
918
919 v8::Isolate* isolate = env->isolate;
920 v8::Local<v8::Context> context = env->context();
921
922 v8::Local<v8::Object> v8_resource;
923 bool externally_managed_resource;
924 if (async_resource != nullptr) {
925 CHECK_TO_OBJECT(env, context, v8_resource, async_resource);
926 externally_managed_resource = true;
927 } else {
928 v8_resource = v8::Object::New(isolate);
929 externally_managed_resource = false;
930 }
931
932 v8::Local<v8::String> v8_resource_name;
933 CHECK_TO_STRING(env, context, v8_resource_name, async_resource_name);
934
935 v8impl::AsyncContext* async_context =
936 new v8impl::AsyncContext(reinterpret_cast<node_napi_env>(env),
937 v8_resource,
938 v8_resource_name,
939 externally_managed_resource);
940
941 *result = reinterpret_cast<napi_async_context>(async_context);
942
943 return napi_clear_last_error(env);
944 }
945
napi_async_destroy(napi_env env,napi_async_context async_context)946 napi_status NAPI_CDECL napi_async_destroy(napi_env env,
947 napi_async_context async_context) {
948 CHECK_ENV(env);
949 CHECK_ARG(env, async_context);
950
951 v8impl::AsyncContext* node_async_context =
952 reinterpret_cast<v8impl::AsyncContext*>(async_context);
953
954 delete node_async_context;
955
956 return napi_clear_last_error(env);
957 }
958
napi_make_callback(napi_env env,napi_async_context async_context,napi_value recv,napi_value func,size_t argc,const napi_value * argv,napi_value * result)959 napi_status NAPI_CDECL napi_make_callback(napi_env env,
960 napi_async_context async_context,
961 napi_value recv,
962 napi_value func,
963 size_t argc,
964 const napi_value* argv,
965 napi_value* result) {
966 NAPI_PREAMBLE(env);
967 CHECK_ARG(env, recv);
968 if (argc > 0) {
969 CHECK_ARG(env, argv);
970 }
971
972 v8::Local<v8::Context> context = env->context();
973
974 v8::Local<v8::Object> v8recv;
975 CHECK_TO_OBJECT(env, context, v8recv, recv);
976
977 v8::Local<v8::Function> v8func;
978 CHECK_TO_FUNCTION(env, v8func, func);
979
980 v8::MaybeLocal<v8::Value> callback_result;
981
982 if (async_context == nullptr) {
983 callback_result = node::MakeCallback(
984 env->isolate,
985 v8recv,
986 v8func,
987 argc,
988 reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)),
989 {0, 0});
990 } else {
991 v8impl::AsyncContext* node_async_context =
992 reinterpret_cast<v8impl::AsyncContext*>(async_context);
993 callback_result = node_async_context->MakeCallback(
994 v8recv,
995 v8func,
996 argc,
997 reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)));
998 }
999
1000 if (try_catch.HasCaught()) {
1001 return napi_set_last_error(env, napi_pending_exception);
1002 } else {
1003 CHECK_MAYBE_EMPTY(env, callback_result, napi_generic_failure);
1004 if (result != nullptr) {
1005 *result =
1006 v8impl::JsValueFromV8LocalValue(callback_result.ToLocalChecked());
1007 }
1008 }
1009
1010 return GET_RETURN_STATUS(env);
1011 }
1012
napi_create_buffer(napi_env env,size_t length,void ** data,napi_value * result)1013 napi_status NAPI_CDECL napi_create_buffer(napi_env env,
1014 size_t length,
1015 void** data,
1016 napi_value* result) {
1017 NAPI_PREAMBLE(env);
1018 CHECK_ARG(env, result);
1019
1020 v8::MaybeLocal<v8::Object> maybe = node::Buffer::New(env->isolate, length);
1021
1022 CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
1023
1024 v8::Local<v8::Object> buffer = maybe.ToLocalChecked();
1025
1026 *result = v8impl::JsValueFromV8LocalValue(buffer);
1027
1028 if (data != nullptr) {
1029 *data = node::Buffer::Data(buffer);
1030 }
1031
1032 return GET_RETURN_STATUS(env);
1033 }
1034
napi_create_external_buffer(napi_env env,size_t length,void * data,napi_finalize finalize_cb,void * finalize_hint,napi_value * result)1035 napi_status NAPI_CDECL napi_create_external_buffer(napi_env env,
1036 size_t length,
1037 void* data,
1038 napi_finalize finalize_cb,
1039 void* finalize_hint,
1040 napi_value* result) {
1041 NAPI_PREAMBLE(env);
1042 CHECK_ARG(env, result);
1043
1044 #if defined(V8_ENABLE_SANDBOX)
1045 return napi_set_last_error(env, napi_no_external_buffers_allowed);
1046 #endif
1047
1048 v8::Isolate* isolate = env->isolate;
1049
1050 // The finalizer object will delete itself after invoking the callback.
1051 v8impl::BufferFinalizer* finalizer =
1052 v8impl::BufferFinalizer::New(env, finalize_cb, nullptr, finalize_hint);
1053
1054 v8::MaybeLocal<v8::Object> maybe =
1055 node::Buffer::New(isolate,
1056 static_cast<char*>(data),
1057 length,
1058 v8impl::BufferFinalizer::FinalizeBufferCallback,
1059 finalizer);
1060
1061 CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
1062
1063 *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked());
1064 return GET_RETURN_STATUS(env);
1065 // Tell coverity that 'finalizer' should not be freed when we return
1066 // as it will be deleted when the buffer to which it is associated
1067 // is finalized.
1068 // coverity[leaked_storage]
1069 }
1070
napi_create_buffer_copy(napi_env env,size_t length,const void * data,void ** result_data,napi_value * result)1071 napi_status NAPI_CDECL napi_create_buffer_copy(napi_env env,
1072 size_t length,
1073 const void* data,
1074 void** result_data,
1075 napi_value* result) {
1076 NAPI_PREAMBLE(env);
1077 CHECK_ARG(env, result);
1078
1079 v8::MaybeLocal<v8::Object> maybe =
1080 node::Buffer::Copy(env->isolate, static_cast<const char*>(data), length);
1081
1082 CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
1083
1084 v8::Local<v8::Object> buffer = maybe.ToLocalChecked();
1085 *result = v8impl::JsValueFromV8LocalValue(buffer);
1086
1087 if (result_data != nullptr) {
1088 *result_data = node::Buffer::Data(buffer);
1089 }
1090
1091 return GET_RETURN_STATUS(env);
1092 }
1093
napi_is_buffer(napi_env env,napi_value value,bool * result)1094 napi_status NAPI_CDECL napi_is_buffer(napi_env env,
1095 napi_value value,
1096 bool* result) {
1097 CHECK_ENV(env);
1098 CHECK_ARG(env, value);
1099 CHECK_ARG(env, result);
1100
1101 *result = node::Buffer::HasInstance(v8impl::V8LocalValueFromJsValue(value));
1102 return napi_clear_last_error(env);
1103 }
1104
napi_get_buffer_info(napi_env env,napi_value value,void ** data,size_t * length)1105 napi_status NAPI_CDECL napi_get_buffer_info(napi_env env,
1106 napi_value value,
1107 void** data,
1108 size_t* length) {
1109 CHECK_ENV(env);
1110 CHECK_ARG(env, value);
1111
1112 v8::Local<v8::Value> buffer = v8impl::V8LocalValueFromJsValue(value);
1113
1114 if (data != nullptr) {
1115 *data = node::Buffer::Data(buffer);
1116 }
1117 if (length != nullptr) {
1118 *length = node::Buffer::Length(buffer);
1119 }
1120
1121 return napi_clear_last_error(env);
1122 }
1123
napi_get_node_version(napi_env env,const napi_node_version ** result)1124 napi_status NAPI_CDECL napi_get_node_version(napi_env env,
1125 const napi_node_version** result) {
1126 CHECK_ENV(env);
1127 CHECK_ARG(env, result);
1128 static const napi_node_version version = {
1129 NODE_MAJOR_VERSION, NODE_MINOR_VERSION, NODE_PATCH_VERSION, NODE_RELEASE};
1130 *result = &version;
1131 return napi_clear_last_error(env);
1132 }
1133
1134 namespace {
1135 namespace uvimpl {
1136
ConvertUVErrorCode(int code)1137 static napi_status ConvertUVErrorCode(int code) {
1138 switch (code) {
1139 case 0:
1140 return napi_ok;
1141 case UV_EINVAL:
1142 return napi_invalid_arg;
1143 case UV_ECANCELED:
1144 return napi_cancelled;
1145 default:
1146 return napi_generic_failure;
1147 }
1148 }
1149
1150 // Wrapper around uv_work_t which calls user-provided callbacks.
1151 class Work : public node::AsyncResource, public node::ThreadPoolWork {
1152 private:
Work(node_napi_env env,v8::Local<v8::Object> async_resource,v8::Local<v8::String> async_resource_name,napi_async_execute_callback execute,napi_async_complete_callback complete=nullptr,void * data=nullptr)1153 explicit Work(node_napi_env env,
1154 v8::Local<v8::Object> async_resource,
1155 v8::Local<v8::String> async_resource_name,
1156 napi_async_execute_callback execute,
1157 napi_async_complete_callback complete = nullptr,
1158 void* data = nullptr)
1159 : AsyncResource(
1160 env->isolate,
1161 async_resource,
1162 *v8::String::Utf8Value(env->isolate, async_resource_name)),
1163 ThreadPoolWork(env->node_env(), "node_api"),
1164 _env(env),
1165 _data(data),
1166 _execute(execute),
1167 _complete(complete) {}
1168
1169 ~Work() override = default;
1170
1171 public:
New(node_napi_env env,v8::Local<v8::Object> async_resource,v8::Local<v8::String> async_resource_name,napi_async_execute_callback execute,napi_async_complete_callback complete,void * data)1172 static Work* New(node_napi_env env,
1173 v8::Local<v8::Object> async_resource,
1174 v8::Local<v8::String> async_resource_name,
1175 napi_async_execute_callback execute,
1176 napi_async_complete_callback complete,
1177 void* data) {
1178 return new Work(
1179 env, async_resource, async_resource_name, execute, complete, data);
1180 }
1181
Delete(Work * work)1182 static void Delete(Work* work) { delete work; }
1183
DoThreadPoolWork()1184 void DoThreadPoolWork() override { _execute(_env, _data); }
1185
AfterThreadPoolWork(int status)1186 void AfterThreadPoolWork(int status) override {
1187 if (_complete == nullptr) return;
1188
1189 // Establish a handle scope here so that every callback doesn't have to.
1190 // Also it is needed for the exception-handling below.
1191 v8::HandleScope scope(_env->isolate);
1192
1193 CallbackScope callback_scope(this);
1194
1195 _env->CallbackIntoModule<true>([&](napi_env env) {
1196 _complete(env, ConvertUVErrorCode(status), _data);
1197 });
1198
1199 // Note: Don't access `work` after this point because it was
1200 // likely deleted by the complete callback.
1201 }
1202
1203 private:
1204 node_napi_env _env;
1205 void* _data;
1206 napi_async_execute_callback _execute;
1207 napi_async_complete_callback _complete;
1208 };
1209
1210 } // end of namespace uvimpl
1211 } // end of anonymous namespace
1212
1213 #define CALL_UV(env, condition) \
1214 do { \
1215 int result = (condition); \
1216 napi_status status = uvimpl::ConvertUVErrorCode(result); \
1217 if (status != napi_ok) { \
1218 return napi_set_last_error(env, status, result); \
1219 } \
1220 } while (0)
1221
1222 napi_status NAPI_CDECL
napi_create_async_work(napi_env env,napi_value async_resource,napi_value async_resource_name,napi_async_execute_callback execute,napi_async_complete_callback complete,void * data,napi_async_work * result)1223 napi_create_async_work(napi_env env,
1224 napi_value async_resource,
1225 napi_value async_resource_name,
1226 napi_async_execute_callback execute,
1227 napi_async_complete_callback complete,
1228 void* data,
1229 napi_async_work* result) {
1230 CHECK_ENV(env);
1231 CHECK_ARG(env, execute);
1232 CHECK_ARG(env, result);
1233
1234 v8::Local<v8::Context> context = env->context();
1235
1236 v8::Local<v8::Object> resource;
1237 if (async_resource != nullptr) {
1238 CHECK_TO_OBJECT(env, context, resource, async_resource);
1239 } else {
1240 resource = v8::Object::New(env->isolate);
1241 }
1242
1243 v8::Local<v8::String> resource_name;
1244 CHECK_TO_STRING(env, context, resource_name, async_resource_name);
1245
1246 uvimpl::Work* work = uvimpl::Work::New(reinterpret_cast<node_napi_env>(env),
1247 resource,
1248 resource_name,
1249 execute,
1250 complete,
1251 data);
1252
1253 *result = reinterpret_cast<napi_async_work>(work);
1254
1255 return napi_clear_last_error(env);
1256 }
1257
napi_delete_async_work(napi_env env,napi_async_work work)1258 napi_status NAPI_CDECL napi_delete_async_work(napi_env env,
1259 napi_async_work work) {
1260 CHECK_ENV(env);
1261 CHECK_ARG(env, work);
1262
1263 uvimpl::Work::Delete(reinterpret_cast<uvimpl::Work*>(work));
1264
1265 return napi_clear_last_error(env);
1266 }
1267
napi_get_uv_event_loop(napi_env env,uv_loop_t ** loop)1268 napi_status NAPI_CDECL napi_get_uv_event_loop(napi_env env, uv_loop_t** loop) {
1269 CHECK_ENV(env);
1270 CHECK_ARG(env, loop);
1271 *loop = reinterpret_cast<node_napi_env>(env)->node_env()->event_loop();
1272 return napi_clear_last_error(env);
1273 }
1274
napi_queue_async_work(napi_env env,napi_async_work work)1275 napi_status NAPI_CDECL napi_queue_async_work(napi_env env,
1276 napi_async_work work) {
1277 CHECK_ENV(env);
1278 CHECK_ARG(env, work);
1279
1280 uv_loop_t* event_loop = nullptr;
1281 STATUS_CALL(napi_get_uv_event_loop(env, &event_loop));
1282
1283 uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work);
1284
1285 w->ScheduleWork();
1286
1287 return napi_clear_last_error(env);
1288 }
1289
napi_cancel_async_work(napi_env env,napi_async_work work)1290 napi_status NAPI_CDECL napi_cancel_async_work(napi_env env,
1291 napi_async_work work) {
1292 CHECK_ENV(env);
1293 CHECK_ARG(env, work);
1294
1295 uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work);
1296
1297 CALL_UV(env, w->CancelWork());
1298
1299 return napi_clear_last_error(env);
1300 }
1301
1302 napi_status NAPI_CDECL
napi_create_threadsafe_function(napi_env env,napi_value func,napi_value async_resource,napi_value async_resource_name,size_t max_queue_size,size_t initial_thread_count,void * thread_finalize_data,napi_finalize thread_finalize_cb,void * context,napi_threadsafe_function_call_js call_js_cb,napi_threadsafe_function * result)1303 napi_create_threadsafe_function(napi_env env,
1304 napi_value func,
1305 napi_value async_resource,
1306 napi_value async_resource_name,
1307 size_t max_queue_size,
1308 size_t initial_thread_count,
1309 void* thread_finalize_data,
1310 napi_finalize thread_finalize_cb,
1311 void* context,
1312 napi_threadsafe_function_call_js call_js_cb,
1313 napi_threadsafe_function* result) {
1314 CHECK_ENV(env);
1315 CHECK_ARG(env, async_resource_name);
1316 RETURN_STATUS_IF_FALSE(env, initial_thread_count > 0, napi_invalid_arg);
1317 CHECK_ARG(env, result);
1318
1319 napi_status status = napi_ok;
1320
1321 v8::Local<v8::Function> v8_func;
1322 if (func == nullptr) {
1323 CHECK_ARG(env, call_js_cb);
1324 } else {
1325 CHECK_TO_FUNCTION(env, v8_func, func);
1326 }
1327
1328 v8::Local<v8::Context> v8_context = env->context();
1329
1330 v8::Local<v8::Object> v8_resource;
1331 if (async_resource == nullptr) {
1332 v8_resource = v8::Object::New(env->isolate);
1333 } else {
1334 CHECK_TO_OBJECT(env, v8_context, v8_resource, async_resource);
1335 }
1336
1337 v8::Local<v8::String> v8_name;
1338 CHECK_TO_STRING(env, v8_context, v8_name, async_resource_name);
1339
1340 v8impl::ThreadSafeFunction* ts_fn =
1341 new v8impl::ThreadSafeFunction(v8_func,
1342 v8_resource,
1343 v8_name,
1344 initial_thread_count,
1345 context,
1346 max_queue_size,
1347 reinterpret_cast<node_napi_env>(env),
1348 thread_finalize_data,
1349 thread_finalize_cb,
1350 call_js_cb);
1351
1352 if (ts_fn == nullptr) {
1353 status = napi_generic_failure;
1354 } else {
1355 // Init deletes ts_fn upon failure.
1356 status = ts_fn->Init();
1357 if (status == napi_ok) {
1358 *result = reinterpret_cast<napi_threadsafe_function>(ts_fn);
1359 }
1360 }
1361
1362 return napi_set_last_error(env, status);
1363 }
1364
napi_get_threadsafe_function_context(napi_threadsafe_function func,void ** result)1365 napi_status NAPI_CDECL napi_get_threadsafe_function_context(
1366 napi_threadsafe_function func, void** result) {
1367 CHECK_NOT_NULL(func);
1368 CHECK_NOT_NULL(result);
1369
1370 *result = reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Context();
1371 return napi_ok;
1372 }
1373
1374 napi_status NAPI_CDECL
napi_call_threadsafe_function(napi_threadsafe_function func,void * data,napi_threadsafe_function_call_mode is_blocking)1375 napi_call_threadsafe_function(napi_threadsafe_function func,
1376 void* data,
1377 napi_threadsafe_function_call_mode is_blocking) {
1378 CHECK_NOT_NULL(func);
1379 return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Push(data,
1380 is_blocking);
1381 }
1382
1383 napi_status NAPI_CDECL
napi_acquire_threadsafe_function(napi_threadsafe_function func)1384 napi_acquire_threadsafe_function(napi_threadsafe_function func) {
1385 CHECK_NOT_NULL(func);
1386 return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Acquire();
1387 }
1388
napi_release_threadsafe_function(napi_threadsafe_function func,napi_threadsafe_function_release_mode mode)1389 napi_status NAPI_CDECL napi_release_threadsafe_function(
1390 napi_threadsafe_function func, napi_threadsafe_function_release_mode mode) {
1391 CHECK_NOT_NULL(func);
1392 return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Release(mode);
1393 }
1394
1395 napi_status NAPI_CDECL
napi_unref_threadsafe_function(napi_env env,napi_threadsafe_function func)1396 napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
1397 CHECK_NOT_NULL(func);
1398 return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Unref();
1399 }
1400
1401 napi_status NAPI_CDECL
napi_ref_threadsafe_function(napi_env env,napi_threadsafe_function func)1402 napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
1403 CHECK_NOT_NULL(func);
1404 return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Ref();
1405 }
1406
node_api_get_module_file_name(napi_env env,const char ** result)1407 napi_status NAPI_CDECL node_api_get_module_file_name(napi_env env,
1408 const char** result) {
1409 CHECK_ENV(env);
1410 CHECK_ARG(env, result);
1411
1412 *result = static_cast<node_napi_env>(env)->GetFilename();
1413 return napi_clear_last_error(env);
1414 }
1415