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