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