• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "napi/native_node_api.h"
17 #include "native_api_internal.h"
18 #include "native_engine/native_engine.h"
19 #include "utils/log.h"
20 
21 static constexpr int32_t MAX_THEAD_SAFE_COUNT = 128;
22 
napi_module_register(napi_module * mod)23 NAPI_EXTERN void napi_module_register(napi_module* mod)
24 {
25     if (mod == nullptr) {
26         HILOG_ERROR("mod is nullptr");
27         return;
28     }
29 
30     NativeModuleManager* moduleManager = NativeModuleManager::GetInstance();
31     NativeModule module;
32 
33     module.version = mod->nm_version;
34     module.fileName = mod->nm_filename;
35     module.name = mod->nm_modname;
36     module.registerCallback = (RegisterCallback)mod->nm_register_func;
37 
38     moduleManager->Register(&module);
39 }
40 
napi_fatal_error(const char * location,size_t location_len,const char * message,size_t message_len)41 NAPI_EXTERN NAPI_NO_RETURN void napi_fatal_error(const char* location,
42                                                  size_t location_len,
43                                                  const char* message,
44                                                  size_t message_len)
45 {
46     (void)location_len;
47     (void)message_len;
48     HILOG_FATAL("FATAL ERROR: %{public}s %{public}s\n", location, message);
49     abort();
50 }
51 
napi_fatal_exception(napi_env env,napi_value err)52 NAPI_INNER_EXTERN napi_status napi_fatal_exception(napi_env env, napi_value err)
53 {
54     HILOG_INFO("%{public}s, start.", __func__);
55     CHECK_ENV(env);
56     CHECK_ARG(env, err);
57 
58     auto engine = reinterpret_cast<NativeEngine*>(env);
59     auto jsError = reinterpret_cast<NativeValue*>(err);
60     if (engine->TriggerFatalException(jsError)) {
61         HILOG_INFO("%{public}s, end.", __func__);
62         return napi_status::napi_ok;
63     } else {
64         HILOG_INFO("%{public}s, end.", __func__);
65         exit(1);
66         return napi_status::napi_ok;
67     }
68 }
69 
70 // Methods to manage simple async operations
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)71 NAPI_EXTERN napi_status napi_create_async_work(napi_env env,
72                                                napi_value async_resource,
73                                                napi_value async_resource_name,
74                                                napi_async_execute_callback execute,
75                                                napi_async_complete_callback complete,
76                                                void* data,
77                                                napi_async_work* result)
78 {
79     CHECK_ENV(env);
80     CHECK_ARG(env, async_resource_name);
81     CHECK_ARG(env, execute);
82     CHECK_ARG(env, complete);
83     CHECK_ARG(env, result);
84 
85     auto engine = reinterpret_cast<NativeEngine*>(env);
86     auto asyncResource = reinterpret_cast<NativeValue*>(async_resource);
87     auto asyncResourceName = reinterpret_cast<NativeValue*>(async_resource_name);
88     auto asyncExecute = reinterpret_cast<NativeAsyncExecuteCallback>(execute);
89     auto asyncComplete = reinterpret_cast<NativeAsyncCompleteCallback>(complete);
90 
91     auto asyncWork = engine->CreateAsyncWork(asyncResource, asyncResourceName, asyncExecute, asyncComplete, data);
92 
93     *result = reinterpret_cast<napi_async_work>(asyncWork);
94     return napi_status::napi_ok;
95 }
96 
napi_delete_async_work(napi_env env,napi_async_work work)97 NAPI_EXTERN napi_status napi_delete_async_work(napi_env env, napi_async_work work)
98 {
99     CHECK_ENV(env);
100     CHECK_ARG(env, work);
101 
102     auto asyncWork = reinterpret_cast<NativeAsyncWork*>(work);
103 
104     delete asyncWork;
105     asyncWork = nullptr;
106 
107     return napi_status::napi_ok;
108 }
109 
napi_queue_async_work(napi_env env,napi_async_work work)110 NAPI_EXTERN napi_status napi_queue_async_work(napi_env env, napi_async_work work)
111 {
112     CHECK_ENV(env);
113     CHECK_ARG(env, work);
114 
115     auto asyncWork = reinterpret_cast<NativeAsyncWork*>(work);
116 
117     asyncWork->Queue();
118     return napi_status::napi_ok;
119 }
120 
napi_cancel_async_work(napi_env env,napi_async_work work)121 NAPI_EXTERN napi_status napi_cancel_async_work(napi_env env, napi_async_work work)
122 {
123     CHECK_ENV(env);
124     CHECK_ARG(env, work);
125 
126     auto asyncWork = reinterpret_cast<NativeAsyncWork*>(work);
127 
128     asyncWork->Cancel();
129     return napi_status::napi_ok;
130 }
131 
132 // Version management
napi_get_node_version(napi_env env,const napi_node_version ** version)133 NAPI_EXTERN napi_status napi_get_node_version(napi_env env, const napi_node_version** version)
134 {
135     (void)version;
136     return napi_status::napi_ok;
137 }
138 
139 // Return the current libuv event loop for a given environment
napi_get_uv_event_loop(napi_env env,struct uv_loop_s ** loop)140 NAPI_EXTERN napi_status napi_get_uv_event_loop(napi_env env, struct uv_loop_s** loop)
141 {
142     CHECK_ENV(env);
143     CHECK_ARG(env, loop);
144 
145     auto engine = reinterpret_cast<NativeEngine*>(env);
146     *loop = engine->GetUVLoop();
147 
148     return napi_status::napi_ok;
149 }
150 
napi_add_env_cleanup_hook(napi_env env,void (* fun)(void * arg),void * arg)151 NAPI_INNER_EXTERN napi_status napi_add_env_cleanup_hook(napi_env env, void (*fun)(void* arg), void* arg)
152 {
153     CHECK_ENV(env);
154     CHECK_ARG(env, fun);
155 
156     auto engine = reinterpret_cast<NativeEngine*>(env);
157     engine->AddCleanupHook(fun, arg);
158 
159     return napi_clear_last_error(env);
160 }
161 
napi_remove_env_cleanup_hook(napi_env env,void (* fun)(void * arg),void * arg)162 NAPI_INNER_EXTERN napi_status napi_remove_env_cleanup_hook(napi_env env, void (*fun)(void* arg), void* arg)
163 {
164     CHECK_ENV(env);
165     CHECK_ARG(env, fun);
166 
167     auto engine = reinterpret_cast<NativeEngine*>(env);
168     engine->RemoveCleanupHook(fun, arg);
169 
170     return napi_clear_last_error(env);
171 }
172 
173 using CleanupHook = void (*)(void* arg);
174 using AsyncCleanupHook = void (*)(void* arg, void (*)(void*), void*);
175 
176 struct AsyncCleanupHookInfo final {
177     napi_env env;
178     AsyncCleanupHook fun;
179     void* arg;
180     bool started = false;
181     // Use a self-reference to make sure the storage is kept alive while the
182     // cleanup hook is registered but not yet finished.
183     std::shared_ptr<AsyncCleanupHookInfo> self;
184 };
185 
186 // Opaque type that is basically an alias for `shared_ptr<AsyncCleanupHookInfo>`
187 // (but not publicly so for easier ABI/API changes). In particular,
188 // std::shared_ptr does not generally maintain a consistent ABI even on a
189 // specific platform.
190 struct ACHHandle final {
191     std::shared_ptr<AsyncCleanupHookInfo> info;
192 };
193 
194 struct DeleteACHHandle {
operator ()DeleteACHHandle195     void operator()(ACHHandle* handle) const
196     {
197         delete handle;
198     };
199 };
200 using AsyncCleanupHookHandle = std::unique_ptr<ACHHandle, DeleteACHHandle>;
201 
FinishAsyncCleanupHook(void * arg)202 static void FinishAsyncCleanupHook(void* arg)
203 {
204     HILOG_INFO("%{public}s, start.", __func__);
205     AsyncCleanupHookInfo* info = static_cast<AsyncCleanupHookInfo*>(arg);
206     std::shared_ptr<AsyncCleanupHookInfo> keep_alive = info->self;
207     auto engine = reinterpret_cast<NativeEngine*>(info->env);
208     engine->DecreaseWaitingRequestCounter();
209     info->self.reset();
210     HILOG_INFO("%{public}s, end.", __func__);
211 }
212 
RunAsyncCleanupHook(void * arg)213 static void RunAsyncCleanupHook(void* arg)
214 {
215     HILOG_INFO("%{public}s, start.", __func__);
216     AsyncCleanupHookInfo* info = static_cast<AsyncCleanupHookInfo*>(arg);
217     auto engine = reinterpret_cast<NativeEngine*>(info->env);
218     engine->IncreaseWaitingRequestCounter();
219     info->started = true;
220     info->fun(info->arg, FinishAsyncCleanupHook, info);
221     HILOG_INFO("%{public}s, end.", __func__);
222 }
223 
AddEnvironmentCleanupHook(napi_env env,AsyncCleanupHook fun,void * arg)224 static AsyncCleanupHookHandle AddEnvironmentCleanupHook(napi_env env, AsyncCleanupHook fun, void* arg)
225 {
226     HILOG_INFO("%{public}s, start.", __func__);
227     auto info = std::make_shared<AsyncCleanupHookInfo>();
228     info->env = env;
229     info->fun = fun;
230     info->arg = arg;
231     info->self = info;
232     auto engine = reinterpret_cast<NativeEngine*>(env);
233     engine->AddCleanupHook(RunAsyncCleanupHook, info.get());
234     HILOG_INFO("%{public}s, end.", __func__);
235     return AsyncCleanupHookHandle(new ACHHandle { info });
236 }
237 
RemoveEnvironmentCleanupHook(AsyncCleanupHookHandle handle)238 static void RemoveEnvironmentCleanupHook(AsyncCleanupHookHandle handle)
239 {
240     HILOG_INFO("%{public}s, start.", __func__);
241     if (handle->info->started)
242         return;
243     handle->info->self.reset();
244     auto engine = reinterpret_cast<NativeEngine*>(handle->info->env);
245     engine->RemoveCleanupHook(RunAsyncCleanupHook, handle->info.get());
246     HILOG_INFO("%{public}s, end.", __func__);
247 }
248 
249 struct napi_async_cleanup_hook_handle__ {
napi_async_cleanup_hook_handle__napi_async_cleanup_hook_handle__250     napi_async_cleanup_hook_handle__(napi_env env, napi_async_cleanup_hook user_hook, void* user_data)
251         : env_(env), user_hook_(user_hook), user_data_(user_data)
252     {
253         HILOG_INFO("%{public}s, start.", __func__);
254         handle_ = AddEnvironmentCleanupHook(env, Hook, this);
255         HILOG_INFO("%{public}s, end.", __func__);
256     }
257 
~napi_async_cleanup_hook_handle__napi_async_cleanup_hook_handle__258     ~napi_async_cleanup_hook_handle__()
259     {
260         HILOG_INFO("%{public}s, start.", __func__);
261         RemoveEnvironmentCleanupHook(std::move(handle_));
262         if (done_cb_ != nullptr) {
263             done_cb_(done_data_);
264         }
265         HILOG_INFO("%{public}s, end.", __func__);
266     }
267 
Hooknapi_async_cleanup_hook_handle__268     static void Hook(void* data, void (*done_cb)(void*), void* done_data)
269     {
270         HILOG_INFO("%{public}s, start.", __func__);
271         auto handle = static_cast<napi_async_cleanup_hook_handle__*>(data);
272         handle->done_cb_ = done_cb;
273         handle->done_data_ = done_data;
274         handle->user_hook_(handle, handle->user_data_);
275         HILOG_INFO("%{public}s, end.", __func__);
276     }
277 
278     AsyncCleanupHookHandle handle_;
279     napi_env env_ = nullptr;
280     napi_async_cleanup_hook user_hook_ = nullptr;
281     void* user_data_ = nullptr;
282     void (*done_cb_)(void*) = nullptr;
283     void* done_data_ = nullptr;
284 };
285 
napi_add_async_cleanup_hook(napi_env env,napi_async_cleanup_hook hook,void * arg,napi_async_cleanup_hook_handle * remove_handle)286 NAPI_INNER_EXTERN napi_status napi_add_async_cleanup_hook(
287     napi_env env, napi_async_cleanup_hook hook, void* arg, napi_async_cleanup_hook_handle* remove_handle)
288 {
289     CHECK_ENV(env);
290     CHECK_ARG(env, hook);
291 
292     napi_async_cleanup_hook_handle__* handle = new napi_async_cleanup_hook_handle__(env, hook, arg);
293 
294     if (remove_handle != nullptr)
295         *remove_handle = handle;
296 
297     return napi_clear_last_error(env);
298 }
299 
napi_remove_async_cleanup_hook(napi_async_cleanup_hook_handle remove_handle)300 NAPI_INNER_EXTERN napi_status napi_remove_async_cleanup_hook(napi_async_cleanup_hook_handle remove_handle)
301 {
302     if (remove_handle == nullptr) {
303         return napi_invalid_arg;
304     }
305 
306     delete remove_handle;
307     return napi_ok;
308 }
309 
310 // Methods to manager threadsafe
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)311 NAPI_INNER_EXTERN napi_status napi_create_threadsafe_function(napi_env env, napi_value func, napi_value async_resource,
312     napi_value async_resource_name, size_t max_queue_size, size_t initial_thread_count, void* thread_finalize_data,
313     napi_finalize thread_finalize_cb, void* context, napi_threadsafe_function_call_js call_js_cb,
314     napi_threadsafe_function* result)
315 {
316     CHECK_ENV(env);
317     CHECK_ARG(env, async_resource_name);
318     RETURN_STATUS_IF_FALSE(env, max_queue_size >= 0, napi_invalid_arg);
319     RETURN_STATUS_IF_FALSE(
320         env, initial_thread_count > 0 && initial_thread_count <= MAX_THEAD_SAFE_COUNT, napi_invalid_arg);
321     CHECK_ARG(env, result);
322     if (func == nullptr) {
323         CHECK_ARG(env, call_js_cb);
324     }
325 
326     auto engine = reinterpret_cast<NativeEngine*>(env);
327     auto jsFunc = reinterpret_cast<NativeValue*>(func);
328     auto asyncResource = reinterpret_cast<NativeValue*>(async_resource);
329     auto asyncResourceName = reinterpret_cast<NativeValue*>(async_resource_name);
330     auto finalizeCallback = reinterpret_cast<NativeFinalize>(thread_finalize_cb);
331     auto callJsCallback = reinterpret_cast<NativeThreadSafeFunctionCallJs>(call_js_cb);
332     auto safeAsyncWork = engine->CreateSafeAsyncWork(jsFunc, asyncResource, asyncResourceName, max_queue_size,
333         initial_thread_count, thread_finalize_data, finalizeCallback, context, callJsCallback);
334     CHECK_ENV(safeAsyncWork);
335 
336     auto ret = safeAsyncWork->Init();
337     if (ret) {
338         *result = reinterpret_cast<napi_threadsafe_function>(safeAsyncWork);
339     } else {
340         return napi_status::napi_generic_failure;
341     }
342 
343     return napi_status::napi_ok;
344 }
345 
napi_call_threadsafe_function(napi_threadsafe_function func,void * data,napi_threadsafe_function_call_mode is_blocking)346 NAPI_INNER_EXTERN napi_status napi_call_threadsafe_function(
347     napi_threadsafe_function func, void* data, napi_threadsafe_function_call_mode is_blocking)
348 {
349     CHECK_ENV(func);
350 
351     auto safeAsyncWork = reinterpret_cast<NativeSafeAsyncWork*>(func);
352     auto callMode = static_cast<NativeThreadSafeFunctionCallMode>(is_blocking);
353 
354     napi_status status = napi_status::napi_ok;
355     auto code = safeAsyncWork->Send(data, callMode);
356     switch (code) {
357         case SafeAsyncCode::SAFE_ASYNC_QUEUE_FULL:
358             status = napi_status::napi_queue_full;
359             break;
360         case SafeAsyncCode::SAFE_ASYNC_INVALID_ARGS:
361             status = napi_status::napi_invalid_arg;
362             break;
363         case SafeAsyncCode::SAFE_ASYNC_CLOSED:
364             status = napi_status::napi_closing;
365             break;
366         case SafeAsyncCode::SAFE_ASYNC_FAILED:
367             status = napi_status::napi_generic_failure;
368             break;
369         default:
370             status = napi_status::napi_ok;
371             break;
372     }
373 
374     return status;
375 }
376 
napi_acquire_threadsafe_function(napi_threadsafe_function func)377 NAPI_INNER_EXTERN napi_status napi_acquire_threadsafe_function(napi_threadsafe_function func)
378 {
379     CHECK_ENV(func);
380 
381     auto safeAsyncWork = reinterpret_cast<NativeSafeAsyncWork*>(func);
382 
383     auto ret = safeAsyncWork->Acquire();
384     if (ret != SafeAsyncCode::SAFE_ASYNC_OK) {
385         return napi_status::napi_generic_failure;
386     }
387 
388     return napi_status::napi_ok;
389 }
390 
napi_release_threadsafe_function(napi_threadsafe_function func,napi_threadsafe_function_release_mode mode)391 NAPI_INNER_EXTERN napi_status napi_release_threadsafe_function(
392     napi_threadsafe_function func, napi_threadsafe_function_release_mode mode)
393 {
394     CHECK_ENV(func);
395 
396     auto safeAsyncWork = reinterpret_cast<NativeSafeAsyncWork*>(func);
397     auto releaseMode = static_cast<NativeThreadSafeFunctionReleaseMode>(mode);
398 
399     auto ret = safeAsyncWork->Release(releaseMode);
400     if (ret != SafeAsyncCode::SAFE_ASYNC_OK) {
401         return napi_status::napi_generic_failure;
402     }
403 
404     return napi_status::napi_ok;
405 }
406 
napi_get_threadsafe_function_context(napi_threadsafe_function func,void ** result)407 NAPI_INNER_EXTERN napi_status napi_get_threadsafe_function_context(napi_threadsafe_function func, void** result)
408 {
409     CHECK_ENV(func);
410     CHECK_ENV(result);
411 
412     auto safeAsyncWork = reinterpret_cast<NativeSafeAsyncWork*>(func);
413     *result = safeAsyncWork->GetContext();
414 
415     return napi_status::napi_ok;
416 }
417 
napi_ref_threadsafe_function(napi_env env,napi_threadsafe_function func)418 NAPI_INNER_EXTERN napi_status napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func)
419 {
420     CHECK_ENV(env);
421     CHECK_ARG(env, func);
422 
423     auto safeAsyncWork = reinterpret_cast<NativeSafeAsyncWork*>(func);
424     auto ret = safeAsyncWork->Ref();
425     if (!ret) {
426         return napi_status::napi_generic_failure;
427     }
428 
429     return napi_status::napi_ok;
430 }
431 
napi_unref_threadsafe_function(napi_env env,napi_threadsafe_function func)432 NAPI_INNER_EXTERN napi_status napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func)
433 {
434     CHECK_ENV(env);
435     CHECK_ARG(env, func);
436 
437     auto safeAsyncWork = reinterpret_cast<NativeSafeAsyncWork*>(func);
438     auto ret = safeAsyncWork->Unref();
439     if (!ret) {
440         return napi_status::napi_generic_failure;
441     }
442 
443     return napi_status::napi_ok;
444 }
445 
napi_async_init(napi_env env,napi_value async_resource,napi_value async_resource_name,napi_async_context * result)446 NAPI_INNER_EXTERN napi_status napi_async_init(
447     napi_env env, napi_value async_resource, napi_value async_resource_name, napi_async_context* result)
448 {
449     CHECK_ENV(env);
450     CHECK_ARG(env, async_resource_name);
451     CHECK_ARG(env, result);
452 
453     auto asyncResource = reinterpret_cast<NativeValue*>(async_resource);
454     auto asyncResourceName = reinterpret_cast<NativeValue*>(async_resource_name);
455 
456     auto async_context = new NativeAsyncContext();
457     async_context->asyncResource = asyncResource;
458     async_context->asyncResourceName = asyncResourceName;
459 
460     *result = reinterpret_cast<napi_async_context>(async_context);
461 
462     return napi_clear_last_error(env);
463 }
464 
napi_async_destroy(napi_env env,napi_async_context async_context)465 NAPI_INNER_EXTERN napi_status napi_async_destroy(napi_env env, napi_async_context async_context)
466 {
467     CHECK_ENV(env);
468     CHECK_ARG(env, async_context);
469 
470     NativeAsyncContext* native_async_context = reinterpret_cast<NativeAsyncContext*>(async_context);
471 
472     delete native_async_context;
473 
474     return napi_clear_last_error(env);
475 }
476 
napi_open_callback_scope(napi_env env,napi_value,napi_async_context async_context_handle,napi_callback_scope * result)477 NAPI_INNER_EXTERN napi_status napi_open_callback_scope(
478     napi_env env, napi_value, napi_async_context async_context_handle, napi_callback_scope* result)
479 {
480     CHECK_ENV(env);
481     CHECK_ARG(env, result);
482 
483     auto engine = reinterpret_cast<NativeEngine*>(env);
484     auto callbackScopeManager = engine->GetCallbackScopeManager();
485     CHECK_ARG(env, callbackScopeManager);
486 
487     auto callbackScope = callbackScopeManager->Open(engine);
488     callbackScopeManager->IncrementOpenCallbackScopes();
489 
490     *result = reinterpret_cast<napi_callback_scope>(callbackScope);
491 
492     return napi_clear_last_error(env);
493 }
494 
napi_close_callback_scope(napi_env env,napi_callback_scope scope)495 NAPI_INNER_EXTERN napi_status napi_close_callback_scope(napi_env env, napi_callback_scope scope)
496 {
497     CHECK_ENV(env);
498     CHECK_ARG(env, scope);
499 
500     auto engine = reinterpret_cast<NativeEngine*>(env);
501     auto callbackScopeManager = engine->GetCallbackScopeManager();
502     CHECK_ARG(env, callbackScopeManager);
503     size_t count = callbackScopeManager->GetOpenCallbackScopes();
504     if (count == 0) {
505         return napi_callback_scope_mismatch;
506     }
507     callbackScopeManager->DecrementOpenCallbackScopes();
508 
509     auto callbackScope = reinterpret_cast<NativeCallbackScope*>(scope);
510     callbackScopeManager->Close(callbackScope);
511 
512     return napi_clear_last_error(env);
513 }
514