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