• 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 "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