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