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