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