1 /*
2 * Copyright (c) 2021-2022 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 "native_engine/native_engine.h"
17
18 #include <uv.h>
19 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) && !defined(IOS_PLATFORM) && !defined(LINUX_PLATFORM)
20 #include <sys/epoll.h>
21 #endif
22 #ifdef IOS_PLATFORM
23 #include <sys/event.h>
24 #endif
25
26 #include "ecmascript/napi/include/jsnapi.h"
27 #include "native_engine/native_utils.h"
28 #include "unicode/ucnv.h"
29 #include "utils/log.h"
30
31 constexpr size_t NAME_BUFFER_SIZE = 64;
32 static constexpr auto PANDA_MAIN_FUNCTION = "_GLOBAL::func_main_0";
33 static constexpr int32_t API11 = 11;
34
35 using panda::JSValueRef;
36 using panda::Local;
37 using panda::LocalScope;
38 using panda::ObjectRef;
39 using panda::StringRef;
40
41 namespace {
42 const char* g_errorMessages[] = {
43 nullptr,
44 "Invalid parameter",
45 "Need object",
46 "Need string",
47 "Need string or symbol",
48 "Need function",
49 "Need number",
50 "Need boolean",
51 "Need array",
52 "Generic failure",
53 "An exception is blocking",
54 "Asynchronous work cancelled",
55 "Escape called twice",
56 "Handle scope mismatch",
57 "Callback scope mismatch",
58 "Asynchronous work queue is full",
59 "Asynchronous work handle is closing",
60 "Need bigint",
61 "Need date",
62 "Need arraybuffer",
63 "Need detachable arraybuffer",
64 };
65 } // namespace
66
67 static GetContainerScopeIdCallback getContainerScopeIdFunc_;
68 static ContainerScopeCallback initContainerScopeFunc_;
69 static ContainerScopeCallback finishContainerScopeFunc_;
70
71 std::mutex NativeEngine::g_alivedEngineMutex_;
72 std::unordered_set<NativeEngine*> NativeEngine::g_alivedEngine_;
73 uint64_t NativeEngine::g_lastEngineId_ = 1;
74 std::mutex NativeEngine::g_mainThreadEngineMutex_;
75 NativeEngine* NativeEngine::g_mainThreadEngine_;
76
NativeEngine(void * jsEngine)77 NativeEngine::NativeEngine(void* jsEngine) : jsEngine_(jsEngine)
78 {
79 SetMainThreadEngine(this);
80 SetAlived();
81 InitUvField();
82 }
83
InitUvField()84 void NativeEngine::InitUvField()
85 {
86 if (memset_s(&uvAsync_, sizeof(uvAsync_), 0, sizeof(uvAsync_)) != EOK) {
87 HILOG_ERROR("failed to init uvAsync_");
88 return;
89 }
90 if (memset_s(&uvSem_, sizeof(uvSem_), 0, sizeof(uvSem_)) != EOK) {
91 HILOG_ERROR("failed to init uvSem_");
92 return;
93 }
94 #if !defined(PREVIEW)
95 if (memset_s(&uvThread_, sizeof(uvThread_), 0, sizeof(uvThread_)) != EOK) {
96 HILOG_ERROR("failed to init uvThread_");
97 return;
98 }
99 #endif
100 }
101
~NativeEngine()102 NativeEngine::~NativeEngine()
103 {
104 HILOG_INFO("NativeEngine::~NativeEngine");
105 isInDestructor_ = true;
106 if (cleanEnv_ != nullptr) {
107 cleanEnv_();
108 }
109 std::lock_guard<std::mutex> insLock(instanceDataLock_);
110 FinalizerInstanceData();
111 }
112
ThreadSafeCallback(napi_env env,napi_value jsCallback,void * context,void * data)113 static void ThreadSafeCallback(napi_env env, napi_value jsCallback, void* context, void* data)
114 {
115 if (data != nullptr) {
116 CallbackWrapper *cbw = static_cast<CallbackWrapper *>(data);
117 cbw->cb();
118 delete cbw;
119 cbw = nullptr;
120 data = nullptr;
121 }
122 }
123
CreateDefaultFunction(void)124 void NativeEngine::CreateDefaultFunction(void)
125 {
126 std::unique_lock<std::shared_mutex> writeLock(eventMutex_);
127 if (defaultFunc_) {
128 return;
129 }
130 napi_env env = reinterpret_cast<napi_env>(this);
131 napi_value resourceName = nullptr;
132 napi_create_string_utf8(env, "call_default_threadsafe_function", NAPI_AUTO_LENGTH, &resourceName);
133 napi_create_threadsafe_function(env, nullptr, nullptr, resourceName, 0, 1,
134 nullptr, nullptr, nullptr, ThreadSafeCallback, &defaultFunc_);
135 }
136
DestoryDefaultFunction(bool release)137 void NativeEngine::DestoryDefaultFunction(bool release)
138 {
139 std::unique_lock<std::shared_mutex> writeLock(eventMutex_);
140 if (!defaultFunc_) {
141 return;
142 }
143 if (release) {
144 napi_release_threadsafe_function(defaultFunc_, napi_tsfn_abort);
145 } else {
146 NativeSafeAsyncWork* work = reinterpret_cast<NativeSafeAsyncWork*>(defaultFunc_);
147 delete work; // only free mem due to uv_loop is invalid
148 }
149 defaultFunc_ = nullptr;
150 }
151
Init()152 void NativeEngine::Init()
153 {
154 HILOG_DEBUG("NativeEngine::Init");
155 moduleManager_ = NativeModuleManager::GetInstance();
156 referenceManager_ = new NativeReferenceManager();
157 callbackScopeManager_ = new NativeCallbackScopeManager();
158 tid_ = pthread_self();
159 sysTid_ = GetCurSysTid();
160
161 loop_ = new (std::nothrow)uv_loop_t;
162 if (loop_ == nullptr) {
163 HILOG_ERROR("failed to create uv_loop, async task interface would not work");
164 return;
165 }
166 if (uv_loop_init(loop_) != EOK) {
167 HILOG_ERROR("failed to init uv_loop, async task interface would not work");
168 delete loop_;
169 loop_ = nullptr;
170 return;
171 }
172 uv_async_init(loop_, &uvAsync_, nullptr);
173 uv_sem_init(&uvSem_, 0);
174 CreateDefaultFunction();
175 }
176
Deinit()177 void NativeEngine::Deinit()
178 {
179 HILOG_INFO("NativeEngine::Deinit");
180 if (loop_ != nullptr) {
181 DestoryDefaultFunction(true);
182 uv_sem_destroy(&uvSem_);
183 uv_close((uv_handle_t*)&uvAsync_, nullptr);
184 }
185
186 RunCleanup();
187 if (referenceManager_ != nullptr) {
188 delete referenceManager_;
189 referenceManager_ = nullptr;
190 }
191
192 SetUnalived();
193 SetStopping(true);
194 if (loop_ == nullptr) {
195 return;
196 }
197 if (uv_loop_close(loop_) != EOK) {
198 HILOG_WARN("faild to close uv_loop, rerun it.");
199 // execute works posted from finalize_cb
200 uv_run(loop_, UV_RUN_DEFAULT);
201 if (uv_loop_close(loop_) != EOK) {
202 // it maybe up to fatal level later
203 HILOG_ERROR("faild to close uv_loop, after reran.");
204 }
205 };
206 delete loop_;
207 loop_ = nullptr;
208 }
209
GetReferenceManager()210 NativeReferenceManager* NativeEngine::GetReferenceManager()
211 {
212 return referenceManager_;
213 }
214
GetModuleManager()215 NativeModuleManager* NativeEngine::GetModuleManager()
216 {
217 return moduleManager_;
218 }
219
GetCallbackScopeManager()220 NativeCallbackScopeManager* NativeEngine::GetCallbackScopeManager()
221 {
222 return callbackScopeManager_;
223 }
224
GetUVLoop() const225 uv_loop_t* NativeEngine::GetUVLoop() const
226 {
227 return loop_;
228 }
229
GetTid() const230 pthread_t NativeEngine::GetTid() const
231 {
232 return tid_;
233 }
234
GetCurSysTid()235 ThreadId NativeEngine::GetCurSysTid()
236 {
237 return reinterpret_cast<ThreadId>(panda::JSNApi::GetCurrentThreadId());
238 }
239
240 // should only be called if process is forked, other case would cause fd_leak
ReinitUVLoop()241 bool NativeEngine::ReinitUVLoop()
242 {
243 if (defaultFunc_ != nullptr) {
244 DestoryDefaultFunction(false);
245 }
246
247 if (loop_ != nullptr) {
248 delete loop_; // only free mem due to uv_loop is invalid
249 loop_ = nullptr;
250 }
251
252 tid_ = pthread_self();
253 sysTid_ = GetCurSysTid();
254
255 loop_ = new (std::nothrow)uv_loop_t;
256 if (loop_ == nullptr) {
257 HILOG_ERROR("failed to create uv_loop, async task interface would not work");
258 return false;
259 }
260 if (uv_loop_init(loop_) != EOK) {
261 HILOG_ERROR("failed to init uv_loop, async task interface would not work");
262 delete loop_;
263 loop_ = nullptr;
264 return false;
265 }
266
267 uv_async_init(loop_, &uvAsync_, nullptr);
268 uv_sem_init(&uvSem_, 0);
269 CreateDefaultFunction();
270
271 return true;
272 }
273
Loop(LoopMode mode,bool needSync)274 void NativeEngine::Loop(LoopMode mode, bool needSync)
275 {
276 if (loop_ == nullptr) {
277 HILOG_ERROR("uv loop is nullptr");
278 return;
279 }
280 bool more = true;
281 switch (mode) {
282 case LoopMode::LOOP_DEFAULT:
283 more = uv_run(loop_, UV_RUN_DEFAULT);
284 break;
285 case LoopMode::LOOP_ONCE:
286 more = uv_run(loop_, UV_RUN_ONCE);
287 break;
288 case LoopMode::LOOP_NOWAIT:
289 more = uv_run(loop_, UV_RUN_NOWAIT);
290 break;
291 default:
292 return;
293 }
294 if (more == false) {
295 uv_loop_alive(loop_);
296 }
297
298 if (needSync) {
299 uv_sem_post(&uvSem_);
300 }
301 }
302
303 // should only call once in life cycle of ArkNativeEngine(NativeEngine)
SetAlived()304 void NativeEngine::SetAlived()
305 {
306 if (id_ != 0) {
307 HILOG_FATAL("id of native engine cannot set twice");
308 }
309 std::lock_guard<std::mutex> alivedEngLock(g_alivedEngineMutex_);
310 g_alivedEngine_.emplace(this);
311 // must be protected by g_alivedEngineMutex_
312 id_ = g_lastEngineId_++;
313 return;
314 }
315
CreateAsyncWork(napi_value asyncResource,napi_value asyncResourceName,NativeAsyncExecuteCallback execute,NativeAsyncCompleteCallback complete,void * data)316 NativeAsyncWork* NativeEngine::CreateAsyncWork(napi_value asyncResource, napi_value asyncResourceName,
317 NativeAsyncExecuteCallback execute, NativeAsyncCompleteCallback complete, void* data)
318 {
319 (void)asyncResource;
320 (void)asyncResourceName;
321 char name[NAME_BUFFER_SIZE] = {0};
322 if (asyncResourceName != nullptr) {
323 auto val = LocalValueFromJsValue(asyncResourceName);
324 [[maybe_unused]] size_t strLength = 0;
325 auto vm = GetEcmaVm();
326 LocalScope scope(vm);
327 auto str = val->ToString(vm);
328 char* buffer = name;
329 if (buffer == nullptr) {
330 strLength = static_cast<size_t>(str->Utf8Length(vm, true) - 1);
331 } else if (NAME_BUFFER_SIZE != 0) {
332 int copied = str->WriteUtf8(vm, buffer, NAME_BUFFER_SIZE - 1, true) - 1;
333 buffer[copied] = '\0';
334 strLength = static_cast<size_t>(copied);
335 } else {
336 strLength = 0;
337 }
338 }
339 return new NativeAsyncWork(this, execute, complete, name, data);
340 }
341
CreateAsyncWork(const std::string & asyncResourceName,NativeAsyncExecuteCallback execute,NativeAsyncCompleteCallback complete,void * data)342 NativeAsyncWork* NativeEngine::CreateAsyncWork(const std::string& asyncResourceName, NativeAsyncExecuteCallback execute,
343 NativeAsyncCompleteCallback complete, void* data)
344 {
345 return new NativeAsyncWork(this, execute, complete, asyncResourceName, data);
346 }
347
CreateSafeAsyncWork(napi_value func,napi_value asyncResource,napi_value asyncResourceName,size_t maxQueueSize,size_t threadCount,void * finalizeData,NativeFinalize finalizeCallback,void * context,NativeThreadSafeFunctionCallJs callJsCallback)348 NativeSafeAsyncWork* NativeEngine::CreateSafeAsyncWork(napi_value func, napi_value asyncResource,
349 napi_value asyncResourceName, size_t maxQueueSize, size_t threadCount, void* finalizeData,
350 NativeFinalize finalizeCallback, void* context, NativeThreadSafeFunctionCallJs callJsCallback)
351 {
352 return new NativeSafeAsyncWork(this, func, asyncResource, asyncResourceName, maxQueueSize, threadCount,
353 finalizeData, finalizeCallback, context, callJsCallback);
354 }
355
GetLastError()356 NativeErrorExtendedInfo* NativeEngine::GetLastError()
357 {
358 return &lastError_;
359 }
360
SetLastError(int errorCode,uint32_t engineErrorCode,void * engineReserved)361 void NativeEngine::SetLastError(int errorCode, uint32_t engineErrorCode, void* engineReserved)
362 {
363 lastError_.errorCode = errorCode;
364 lastError_.engineErrorCode = engineErrorCode;
365 lastError_.message = g_errorMessages[lastError_.errorCode];
366 lastError_.reserved = engineReserved;
367 }
368
SubEncodeToUtf8(const EcmaVM * vm,Local<JSValueRef> & nativeValue,Local<StringRef> & nativeString,char * buffer,int32_t * written,size_t bufferSize,int32_t * nchars)369 static void SubEncodeToUtf8(const EcmaVM* vm,
370 Local<JSValueRef>& nativeValue,
371 Local<StringRef>& nativeString,
372 char* buffer,
373 int32_t* written,
374 size_t bufferSize,
375 int32_t* nchars)
376 {
377 int32_t length = static_cast<int32_t>(nativeString->Length(vm));
378 int32_t pos = 0;
379 int32_t writableSize = static_cast<int32_t>(bufferSize);
380 int32_t i = 0;
381 panda::Local<ObjectRef> strObj = nativeValue->ToObject(vm);
382 for (; i < length; i++) {
383 panda::Local<StringRef> str = strObj->Get(vm, i)->ToString(vm);
384 int32_t len = str->Utf8Length(vm) - 1;
385 if (len > writableSize) {
386 break;
387 }
388 str->WriteUtf8(vm, (buffer + pos), writableSize);
389 writableSize -= len;
390 pos += len;
391 }
392 *nchars = i;
393 HILOG_DEBUG("EncodeWriteUtf8 the result of buffer: %{public}s", buffer);
394 *written = pos;
395 }
396
EncodeToUtf8(napi_value value,char * buffer,int32_t * written,size_t bufferSize,int32_t * nchars)397 void NativeEngine::EncodeToUtf8(napi_value value, char* buffer, int32_t* written, size_t bufferSize, int32_t* nchars)
398 {
399 auto nativeValue = LocalValueFromJsValue(value);
400 if (nativeValue->IsNull() || nchars == nullptr || written == nullptr) {
401 HILOG_ERROR("NativeEngine EncodeToUtf8 args is nullptr");
402 return;
403 }
404
405 auto vm = GetEcmaVm();
406 LocalScope scope(vm);
407 auto nativeString = nativeValue->ToString(vm);
408 if (!nativeString->IsString(vm)) {
409 HILOG_ERROR("nativeValue not is string");
410 return;
411 }
412
413 if (buffer == nullptr) {
414 HILOG_ERROR("buffer is null");
415 return;
416 }
417
418 SubEncodeToUtf8(vm, nativeValue, nativeString, buffer, written, bufferSize, nchars);
419 }
420
SubEncodeToChinese(const EcmaVM * vm,Local<JSValueRef> & nativeValue,Local<StringRef> & nativeString,std::string & buffer,const char * encode)421 static void SubEncodeToChinese(const EcmaVM* vm,
422 Local<JSValueRef>& nativeValue,
423 Local<StringRef>& nativeString,
424 std::string& buffer,
425 const char* encode)
426 {
427 int32_t length = static_cast<int32_t>(nativeString->Length(vm));
428 int32_t pos = 0;
429 const int32_t writableSize = 22; // 22 : encode max bytes of the ucnv_convent function;
430 std::string tempBuf = "";
431 tempBuf.resize(writableSize + 1);
432 UErrorCode errorCode = U_ZERO_ERROR;
433 const char* encFrom = "utf8";
434 panda::Local<ObjectRef> strObj = nativeValue->ToObject(vm);
435 for (int32_t i = 0; i < length; i++) {
436 panda::Local<StringRef> str = strObj->Get(vm, i)->ToString(vm);
437 int32_t len = str->Utf8Length(vm) - 1;
438 if ((pos + len) >= writableSize) {
439 char outBuf[writableSize] = {0};
440 ucnv_convert(encode, encFrom, outBuf, writableSize, tempBuf.c_str(), pos, &errorCode);
441 if (errorCode != U_ZERO_ERROR) {
442 HILOG_ERROR("ucnv_convert is failed : ErrorCode = %{public}d", static_cast<int32_t>(errorCode));
443 return;
444 }
445 buffer += outBuf;
446 tempBuf.clear();
447 pos = 0;
448 }
449 str->WriteUtf8(vm, (tempBuf.data() + pos), pos + len + 1);
450 pos += len;
451 }
452 if (pos > 0) {
453 char outBuf[writableSize] = {0};
454 ucnv_convert(encode, encFrom, outBuf, writableSize, tempBuf.c_str(), pos, &errorCode);
455 if (errorCode != U_ZERO_ERROR) {
456 HILOG_ERROR("ucnv_convert is failed : ErrorCode = %{public}d", static_cast<int32_t>(errorCode));
457 return;
458 }
459 buffer += outBuf;
460 }
461 }
462
EncodeToChinese(napi_value value,std::string & buffer,const std::string & encoding)463 void NativeEngine::EncodeToChinese(napi_value value, std::string& buffer, const std::string& encoding)
464 {
465 if (value == nullptr) {
466 HILOG_ERROR("nativeValue GetInterface is nullptr");
467 return;
468 }
469
470 auto nativeValue = LocalValueFromJsValue(value);
471 auto vm = GetEcmaVm();
472 LocalScope scope(vm);
473 auto nativeString = nativeValue->ToString(vm);
474 if (!nativeString->IsString(vm)) {
475 HILOG_ERROR("nativeValue not is string");
476 return;
477 }
478
479 auto encode = encoding.c_str();
480 if (encode == nullptr) {
481 HILOG_ERROR("encoding is nullptr");
482 return;
483 }
484
485 SubEncodeToChinese(vm, nativeValue, nativeString, buffer, encode);
486 }
487
488 #if !defined(PREVIEW)
CheckUVLoop()489 void NativeEngine::CheckUVLoop()
490 {
491 checkUVLoop_ = true;
492 uv_thread_create(&uvThread_, NativeEngine::UVThreadRunner, this);
493 }
494
UVThreadRunner(void * nativeEngine)495 void NativeEngine::UVThreadRunner(void* nativeEngine)
496 {
497 std::string name("UVLoop");
498 #ifdef IOS_PLATFORM
499 pthread_setname_np(name.c_str());
500 #else
501 pthread_setname_np(pthread_self(), name.c_str());
502 #endif
503 auto engine = static_cast<NativeEngine*>(nativeEngine);
504 engine->PostLoopTask();
505 while (engine->checkUVLoop_) {
506 int32_t fd = uv_backend_fd(engine->loop_);
507 int32_t timeout = uv_backend_timeout(engine->loop_);
508 int32_t result = -1;
509 #ifdef IOS_PLATFORM
510 struct kevent events[1];
511 struct timespec spec;
512 static const int32_t mSec = 1000;
513 static const int32_t uSec = 1000000;
514 if (timeout != -1) {
515 spec.tv_sec = timeout / mSec;
516 spec.tv_nsec = (timeout % mSec) * uSec;
517 }
518 result = kevent(fd, NULL, 0, events, 1, timeout == -1 ? NULL : &spec);
519
520 #else
521 struct epoll_event ev;
522 result = epoll_wait(fd, &ev, 1, timeout);
523 #endif
524
525 if (!engine->checkUVLoop_) {
526 HILOG_INFO("break thread after epoll wait");
527 break;
528 }
529 if (result >= 0) {
530 engine->PostLoopTask();
531 } else {
532 HILOG_ERROR("epoll wait fail: result: %{public}d, errno: %{public}d", result, errno);
533 }
534 if (!engine->checkUVLoop_) {
535 HILOG_INFO("break thread after post loop task");
536 break;
537 }
538 }
539 }
540
CancelCheckUVLoop()541 void NativeEngine::CancelCheckUVLoop()
542 {
543 checkUVLoop_ = false;
544 uv_async_send(&uvAsync_);
545 uv_sem_post(&uvSem_);
546 uv_thread_join(&uvThread_);
547 }
548
PostLoopTask()549 void NativeEngine::PostLoopTask()
550 {
551 postTask_(true);
552 uv_sem_wait(&uvSem_);
553 }
554 #endif
555
SetPostTask(PostTask postTask)556 void NativeEngine::SetPostTask(PostTask postTask)
557 {
558 postTask_ = postTask;
559 }
560
TriggerPostTask()561 void NativeEngine::TriggerPostTask()
562 {
563 if (postTask_ == nullptr) {
564 HILOG_ERROR("postTask_ is nullptr");
565 return;
566 }
567 postTask_(false);
568 }
569
GetJsEngine()570 void* NativeEngine::GetJsEngine()
571 {
572 return jsEngine_;
573 }
574
575 // register init worker func
SetInitWorkerFunc(InitWorkerFunc func)576 void NativeEngine::SetInitWorkerFunc(InitWorkerFunc func)
577 {
578 initWorkerFunc_ = func;
579 }
GetInitWorkerFunc() const580 InitWorkerFunc NativeEngine::GetInitWorkerFunc() const
581 {
582 return initWorkerFunc_;
583 }
SetGetAssetFunc(GetAssetFunc func)584 void NativeEngine::SetGetAssetFunc(GetAssetFunc func)
585 {
586 getAssetFunc_ = func;
587 }
GetGetAssetFunc() const588 GetAssetFunc NativeEngine::GetGetAssetFunc() const
589 {
590 return getAssetFunc_;
591 }
SetOffWorkerFunc(OffWorkerFunc func)592 void NativeEngine::SetOffWorkerFunc(OffWorkerFunc func)
593 {
594 offWorkerFunc_ = func;
595 }
GetOffWorkerFunc() const596 OffWorkerFunc NativeEngine::GetOffWorkerFunc() const
597 {
598 return offWorkerFunc_;
599 }
600
601 // call init worker func
CallInitWorkerFunc(NativeEngine * engine)602 bool NativeEngine::CallInitWorkerFunc(NativeEngine* engine)
603 {
604 if (initWorkerFunc_ != nullptr) {
605 initWorkerFunc_(engine);
606 return true;
607 }
608 return false;
609 }
610
CallGetAssetFunc(const std::string & uri,uint8_t ** buffer,size_t * bufferSize,std::vector<uint8_t> & content,std::string & ami,bool & useSecureMem,bool isRestrictedWorker)611 bool NativeEngine::CallGetAssetFunc(const std::string& uri, uint8_t** buffer, size_t* bufferSize,
612 std::vector<uint8_t>& content, std::string& ami, bool &useSecureMem, bool isRestrictedWorker)
613 {
614 if (getAssetFunc_ != nullptr) {
615 getAssetFunc_(uri, buffer, bufferSize, content, ami, useSecureMem, isRestrictedWorker);
616 return true;
617 }
618 return false;
619 }
620
CallOffWorkerFunc(NativeEngine * engine)621 bool NativeEngine::CallOffWorkerFunc(NativeEngine* engine)
622 {
623 if (offWorkerFunc_ != nullptr) {
624 offWorkerFunc_(engine);
625 return true;
626 }
627 return false;
628 }
629
630 // adapt worker to ace container
SetGetContainerScopeIdFunc(GetContainerScopeIdCallback func)631 void NativeEngine::SetGetContainerScopeIdFunc(GetContainerScopeIdCallback func)
632 {
633 getContainerScopeIdFunc_ = func;
634 }
SetInitContainerScopeFunc(ContainerScopeCallback func)635 void NativeEngine::SetInitContainerScopeFunc(ContainerScopeCallback func)
636 {
637 initContainerScopeFunc_ = func;
638 }
SetFinishContainerScopeFunc(ContainerScopeCallback func)639 void NativeEngine::SetFinishContainerScopeFunc(ContainerScopeCallback func)
640 {
641 finishContainerScopeFunc_ = func;
642 }
GetContainerScopeIdFunc()643 int32_t NativeEngine::GetContainerScopeIdFunc()
644 {
645 int32_t scopeId = -1;
646 if (getContainerScopeIdFunc_ != nullptr) {
647 scopeId = getContainerScopeIdFunc_();
648 }
649 return scopeId;
650 }
InitContainerScopeFunc(int32_t id)651 bool NativeEngine::InitContainerScopeFunc(int32_t id)
652 {
653 if (initContainerScopeFunc_ != nullptr) {
654 initContainerScopeFunc_(id);
655 return true;
656 }
657 return false;
658 }
FinishContainerScopeFunc(int32_t id)659 bool NativeEngine::FinishContainerScopeFunc(int32_t id)
660 {
661 if (finishContainerScopeFunc_ != nullptr) {
662 finishContainerScopeFunc_(id);
663 return true;
664 }
665 return false;
666 }
667
668 #if !defined(PREVIEW)
CallDebuggerPostTaskFunc(std::function<void ()> && task)669 void NativeEngine::CallDebuggerPostTaskFunc(std::function<void()>&& task)
670 {
671 if (debuggerPostTaskFunc_ != nullptr) {
672 debuggerPostTaskFunc_(std::move(task));
673 }
674 }
675
SetDebuggerPostTaskFunc(DebuggerPostTask func)676 void NativeEngine::SetDebuggerPostTaskFunc(DebuggerPostTask func)
677 {
678 debuggerPostTaskFunc_ = func;
679 }
680 #endif
681
SetHostEngine(NativeEngine * engine)682 void NativeEngine::SetHostEngine(NativeEngine* engine)
683 {
684 hostEngine_ = engine;
685 }
686
GetHostEngine() const687 NativeEngine* NativeEngine::GetHostEngine() const
688 {
689 return hostEngine_;
690 }
691
SetApiVersion(int32_t apiVersion)692 void NativeEngine::SetApiVersion(int32_t apiVersion)
693 {
694 apiVersion_ = apiVersion;
695 }
696
GetApiVersion()697 int32_t NativeEngine::GetApiVersion()
698 {
699 return apiVersion_;
700 }
701
IsApplicationApiVersionAPI11Plus()702 bool NativeEngine::IsApplicationApiVersionAPI11Plus()
703 {
704 return apiVersion_ > API11;
705 }
706
AddCleanupHook(CleanupCallback fun,void * arg)707 napi_status NativeEngine::AddCleanupHook(CleanupCallback fun, void* arg)
708 {
709 auto insertion_info = cleanupHooks_.emplace(arg,
710 std::pair<CleanupCallback, uint64_t>(fun, cleanupHookCounter_++));
711 if (insertion_info.second != true) {
712 HILOG_ERROR("AddCleanupHook Failed.");
713 return napi_generic_failure;
714 }
715 return napi_ok;
716 }
717
RemoveCleanupHook(CleanupCallback fun,void * arg)718 napi_status NativeEngine::RemoveCleanupHook(CleanupCallback fun, void* arg)
719 {
720 auto cleanupHook = cleanupHooks_.find(arg);
721 if (cleanupHook != cleanupHooks_.end() && cleanupHook->second.first == fun) {
722 cleanupHooks_.erase(arg);
723 return napi_ok;
724 }
725 HILOG_ERROR("RemoveCleanupHook Failed.");
726 return napi_generic_failure;
727 }
728
RunCleanup()729 void NativeEngine::RunCleanup()
730 {
731 HILOG_DEBUG("%{public}s, start.", __func__);
732 CleanupHandles();
733 // sync clean up
734 while (!cleanupHooks_.empty()) {
735 HILOG_DEBUG("NativeEngine::RunCleanup cleanupHooks_ is not empty");
736 using CleanupCallbackTuple = std::pair<void*, std::pair<CleanupCallback, uint64_t>>;
737 // Copy into a vector, since we can't sort an unordered_set in-place.
738 std::vector<CleanupCallbackTuple> callbacks(cleanupHooks_.begin(), cleanupHooks_.end());
739 // We can't erase the copied elements from `cleanupHooks_` yet, because we
740 // need to be able to check whether they were un-scheduled by another hook.
741
742 std::sort(callbacks.begin(), callbacks.end(), [](const CleanupCallbackTuple& a, const CleanupCallbackTuple& b) {
743 // Sort in descending order so that the most recently inserted callbacks are run first.
744 return a.second.second > b.second.second;
745 });
746 HILOG_DEBUG(
747 "NativeEngine::RunCleanup cleanup_hooks callbacks size:%{public}d", (int32_t)callbacks.size());
748 for (const CleanupCallbackTuple& cb : callbacks) {
749 void* data = cb.first;
750 if (cleanupHooks_.find(data) == cleanupHooks_.end()) {
751 // This hook was removed from the `cleanupHooks_` set during another
752 // hook that was run earlier. Nothing to do here.
753 continue;
754 }
755 CleanupCallback fun = cb.second.first;
756 if (fun != nullptr) {
757 fun(data);
758 }
759 cleanupHooks_.erase(data);
760 }
761 CleanupHandles();
762 }
763
764 // make sure tsfn relese by itself
765 uv_run(loop_, UV_RUN_NOWAIT);
766
767 // Close all unclosed uv handles
768 auto const ensureClosing = [](uv_handle_t *handle, void *arg) {
769 if (!uv_is_closing(handle)) {
770 uv_close(handle, nullptr);
771 }
772 };
773 uv_walk(loop_, ensureClosing, nullptr);
774
775 while (uv_run(loop_, UV_RUN_DEFAULT) != 0) {};
776
777 HILOG_DEBUG("%{public}s, end.", __func__);
778 }
779
CleanupHandles()780 void NativeEngine::CleanupHandles()
781 {
782 while (requestWaiting_.load() > 0) {
783 HILOG_INFO("%{public}s, request waiting:%{public}d.", __func__,
784 requestWaiting_.load(std::memory_order_relaxed));
785 uv_run(loop_, UV_RUN_ONCE);
786 }
787 }
788
IncreaseWaitingRequestCounter()789 void NativeEngine::IncreaseWaitingRequestCounter()
790 {
791 requestWaiting_++;
792 }
793
DecreaseWaitingRequestCounter()794 void NativeEngine::DecreaseWaitingRequestCounter()
795 {
796 requestWaiting_--;
797 }
798
HasWaitingRequest()799 bool NativeEngine::HasWaitingRequest()
800 {
801 return requestWaiting_.load() != 0;
802 }
803
IncreaseListeningCounter()804 void NativeEngine::IncreaseListeningCounter()
805 {
806 listeningCounter_++;
807 }
808
DecreaseListeningCounter()809 void NativeEngine::DecreaseListeningCounter()
810 {
811 listeningCounter_--;
812 }
813
HasListeningCounter()814 bool NativeEngine::HasListeningCounter()
815 {
816 return listeningCounter_.load() != 0;
817 }
818
RegisterWorkerFunction(const NativeEngine * engine)819 void NativeEngine::RegisterWorkerFunction(const NativeEngine* engine)
820 {
821 if (engine == nullptr) {
822 return;
823 }
824 SetInitWorkerFunc(engine->GetInitWorkerFunc());
825 SetGetAssetFunc(engine->GetGetAssetFunc());
826 SetOffWorkerFunc(engine->GetOffWorkerFunc());
827 }
828
829 // this interface for restrictedWorker with entryPoint to execute mergeabc
RunScriptForAbc(const char * path,char * entryPoint)830 napi_value NativeEngine::RunScriptForAbc(const char* path, char* entryPoint)
831 {
832 EcmaVM* vm = const_cast<EcmaVM*>(GetEcmaVm());
833 panda::EscapeLocalScope scope(vm);
834 std::string normalizedPath = panda::JSNApi::NormalizePath(path);
835 uint8_t* scriptContent = nullptr;
836 size_t scriptContentSize = 0;
837 std::vector<uint8_t> content;
838 std::string ami;
839 if (!GetAbcBuffer(normalizedPath.c_str(), &scriptContent, &scriptContentSize,
840 content, ami, true)) {
841 HILOG_ERROR("RunScript: GetAbcBuffer failed");
842 return nullptr;
843 }
844 HILOG_DEBUG("RunScriptForAbc: GetAmi: %{private}s", ami.c_str());
845 // if buffer is empty, return directly.
846 if (scriptContentSize == 0) {
847 HILOG_ERROR("asset size is %{public}zu", scriptContentSize);
848 ThrowException("RunScriptForAbc: abc file is empty.");
849 return nullptr;
850 }
851 panda::JSNApi::Execute(vm, ami, entryPoint, false, true);
852 if (panda::JSNApi::HasPendingException(vm)) {
853 HandleUncaughtException();
854 return nullptr;
855 }
856 Local<JSValueRef> undefObj = JSValueRef::Undefined(vm);
857 return JsValueFromLocalValue(scope.Escape(undefObj));
858 }
859
RunScript(const char * path,char * entryPoint)860 napi_value NativeEngine::RunScript(const char* path, char* entryPoint)
861 {
862 uint8_t* scriptContent = nullptr;
863 size_t scriptContentSize = 0;
864 std::vector<uint8_t> content;
865 std::string ami;
866 if (!GetAbcBuffer(path, &scriptContent, &scriptContentSize, content, ami)) {
867 HILOG_ERROR("RunScript: GetAbcBuffer failed");
868 return nullptr;
869 }
870 HILOG_DEBUG("RunScript: GetAmi: %{private}s", ami.c_str());
871 // if buffer is empty, return directly.
872 if (scriptContentSize == 0) {
873 HILOG_ERROR("RunScript: buffer size is empty, please check abc path");
874 return nullptr;
875 }
876 return RunActor(scriptContent, scriptContentSize, ami.c_str(), entryPoint, false);
877 }
878
RunScriptInRestrictedThread(const char * path)879 napi_value NativeEngine::RunScriptInRestrictedThread(const char* path)
880 {
881 auto vm = GetEcmaVm();
882 panda::EscapeLocalScope scope(vm);
883 uint8_t* scriptContent = nullptr;
884 size_t scriptContentSize = 0;
885 std::vector<uint8_t> content;
886 std::string ami;
887 if (!GetAbcBuffer(path, &scriptContent, &scriptContentSize, content, ami, true)) {
888 HILOG_ERROR("RunScriptInRestrictedThread: GetAbcBuffer failed");
889 return nullptr;
890 }
891 HILOG_DEBUG("RunScriptInRestrictedThread: GetAmi: %{private}s", ami.c_str());
892 panda::JSNApi::Execute(vm, ami, PANDA_MAIN_FUNCTION, false, true);
893 if (panda::JSNApi::HasPendingException(vm)) {
894 HandleUncaughtException();
895 return nullptr;
896 }
897 Local<JSValueRef> undefObj = JSValueRef::Undefined(vm);
898 return JsValueFromLocalValue(scope.Escape(undefObj));
899 }
900
901 /* buffer, bufferSize is for secureMem; content is for normalMem.
902 * If output is not secureMem, fullfill buffer, bufferSize with content data and size.
903 */
GetAbcBuffer(const char * path,uint8_t ** buffer,size_t * bufferSize,std::vector<uint8_t> & content,std::string & ami,bool isRestrictedWorker)904 bool NativeEngine::GetAbcBuffer(const char* path, uint8_t **buffer, size_t* bufferSize,
905 std::vector<uint8_t>& content, std::string& ami, bool isRestrictedWorker)
906 {
907 std::string pathStr(path);
908 bool useSecureMem = false;
909 if (!CallGetAssetFunc(pathStr, buffer, bufferSize, content, ami, useSecureMem, isRestrictedWorker)) {
910 HILOG_ERROR("Get asset error");
911 return false;
912 }
913 if (!useSecureMem) {
914 *buffer = content.data();
915 *bufferSize = content.size();
916 }
917 return true;
918 }
919
SetInstanceData(void * data,NativeFinalize finalize_cb,void * hint)920 void NativeEngine::SetInstanceData(void* data, NativeFinalize finalize_cb, void* hint)
921 {
922 HILOG_DEBUG("NativeEngineWraper::%{public}s, start.", __func__);
923 std::lock_guard<std::mutex> insLock(instanceDataLock_);
924 FinalizerInstanceData();
925 instanceDataInfo_.engine = this;
926 instanceDataInfo_.callback = finalize_cb;
927 instanceDataInfo_.nativeObject = data;
928 instanceDataInfo_.hint = hint;
929 }
930
GetInstanceData(void ** data)931 void NativeEngine::GetInstanceData(void** data)
932 {
933 HILOG_DEBUG("NativeEngineWraper::%{public}s, start.", __func__);
934 std::lock_guard<std::mutex> insLock(instanceDataLock_);
935 if (data) {
936 *data = instanceDataInfo_.nativeObject;
937 }
938 }
939
FinalizerInstanceData(void)940 void NativeEngine::FinalizerInstanceData(void)
941 {
942 if (instanceDataInfo_.engine != nullptr && instanceDataInfo_.callback != nullptr) {
943 instanceDataInfo_.callback(instanceDataInfo_.engine, instanceDataInfo_.nativeObject, instanceDataInfo_.hint);
944 }
945 instanceDataInfo_.engine = nullptr;
946 instanceDataInfo_.callback = nullptr;
947 instanceDataInfo_.nativeObject = nullptr;
948 instanceDataInfo_.hint = nullptr;
949 }
950
GetModuleFileName()951 const char* NativeEngine::GetModuleFileName()
952 {
953 HILOG_DEBUG("%{public}s, start.", __func__);
954 if (moduleFileName_.empty()) {
955 NativeModuleManager* moduleManager = GetModuleManager();
956 HILOG_DEBUG("NativeEngineWraper::GetFileName GetModuleManager");
957 if (moduleManager != nullptr) {
958 std::string moduleFileName = moduleManager->GetModuleFileName(moduleName_.c_str(), isAppModule_);
959 HILOG_INFO("NativeEngineWraper::GetFileName end filename:%{public}s", moduleFileName.c_str());
960 SetModuleFileName(moduleFileName);
961 }
962 }
963 return moduleFileName_.c_str();
964 }
965
SetModuleName(std::string & moduleName)966 void NativeEngine::SetModuleName(std::string& moduleName)
967 {
968 moduleName_ = moduleName;
969 }
970
SetModuleFileName(std::string & moduleFileName)971 void NativeEngine::SetModuleFileName(std::string& moduleFileName)
972 {
973 moduleFileName_ = moduleFileName;
974 }
975
SetExtensionInfos(std::unordered_map<std::string,int32_t> && extensionInfos)976 void NativeEngine::SetExtensionInfos(std::unordered_map<std::string, int32_t>&& extensionInfos)
977 {
978 extensionInfos_ = extensionInfos;
979 }
980
GetExtensionInfos()981 const std::unordered_map<std::string, int32_t>& NativeEngine::GetExtensionInfos()
982 {
983 return extensionInfos_;
984 }
985
SetModuleLoadChecker(const std::shared_ptr<ModuleCheckerDelegate> & moduleCheckerDelegate)986 void NativeEngine::SetModuleLoadChecker(const std::shared_ptr<ModuleCheckerDelegate>& moduleCheckerDelegate)
987 {
988 NativeModuleManager* moduleManager = GetModuleManager();
989 if (!moduleManager) {
990 HILOG_ERROR("SetModuleLoadChecker failed, moduleManager is nullptr");
991 return;
992 }
993 moduleManager->SetModuleLoadChecker(moduleCheckerDelegate);
994 }
995
RunEventLoop(napi_event_mode mode)996 napi_status NativeEngine::RunEventLoop(napi_event_mode mode)
997 {
998 HILOG_DEBUG("%{public}s, start.", __func__);
999 if (loop_ == nullptr) {
1000 HILOG_ERROR("nullptr loop in native engine");
1001 return napi_status::napi_invalid_arg;
1002 }
1003
1004 if (!IsNativeThread()) {
1005 HILOG_ERROR("current thread is not native thread");
1006 return napi_status::napi_generic_failure;
1007 }
1008
1009 std::unique_lock<std::mutex> lock(loopRunningMutex_);
1010 if (isLoopRunning_) {
1011 HILOG_DEBUG("loop is already undered running state");
1012 return napi_status::napi_ok;
1013 }
1014 isLoopRunning_ = true;
1015 lock.unlock();
1016 HILOG_DEBUG("uv loop is running with mode %{public}d", mode);
1017 if (mode == napi_event_mode_default) {
1018 uv_run(loop_, UV_RUN_DEFAULT);
1019 }
1020 if (mode == napi_event_mode_nowait) {
1021 uv_run(loop_, UV_RUN_NOWAIT);
1022 }
1023 HILOG_DEBUG("uv loop is stopped");
1024 lock.lock();
1025 isLoopRunning_ = false;
1026 return napi_status::napi_ok;
1027 }
1028
StopEventLoop()1029 napi_status NativeEngine::StopEventLoop()
1030 {
1031 HILOG_DEBUG("%{public}s, start.", __func__);
1032 if (loop_ == nullptr) {
1033 HILOG_ERROR("nullptr loop in native engine");
1034 return napi_status::napi_invalid_arg;
1035 }
1036
1037 if (!IsNativeThread()) {
1038 HILOG_ERROR("current thread is not native thread");
1039 return napi_status::napi_generic_failure;
1040 }
1041 std::unique_lock<std::mutex> lock(loopRunningMutex_);
1042 if (!isLoopRunning_) {
1043 HILOG_DEBUG("loop is already undered stop state");
1044 return napi_status::napi_ok;
1045 }
1046 HILOG_DEBUG("uv loop is running");
1047 uv_stop(loop_);
1048 HILOG_DEBUG("uv loop is stopped");
1049 return napi_status::napi_ok;
1050 }
1051
ThrowException(const char * msg)1052 void NativeEngine::ThrowException(const char* msg)
1053 {
1054 auto vm = GetEcmaVm();
1055 Local<panda::JSValueRef> error = panda::Exception::Error(vm, StringRef::NewFromUtf8(vm, msg));
1056 panda::JSNApi::ThrowException(vm, error);
1057 }
1058
SendEvent(const std::function<void ()> & cb,napi_event_priority priority)1059 napi_status NativeEngine::SendEvent(const std::function<void()> &cb, napi_event_priority priority)
1060 {
1061 std::shared_lock<std::shared_mutex> readLock(eventMutex_);
1062 if (defaultFunc_) {
1063 auto safeAsyncWork = reinterpret_cast<NativeSafeAsyncWork*>(defaultFunc_);
1064 return safeAsyncWork->SendEvent(cb, priority);
1065 } else {
1066 HILOG_ERROR("default function is nullptr!");
1067 return napi_status::napi_generic_failure;
1068 }
1069 }
1070