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 "native_engine.h"
17
18 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM)
19 #include <sys/epoll.h>
20 #endif
21 #include <uv.h>
22
23 #include "utils/log.h"
24
25 namespace {
26 const char* g_errorMessages[] = {
27 nullptr,
28 "Invalid parameter",
29 "Need object",
30 "Need string",
31 "Need string or symbol",
32 "Need function",
33 "Need number",
34 "Need boolean",
35 "Need array",
36 "Generic failure",
37 "An exception is blocking",
38 "Asynchronous work cancelled",
39 "Escape called twice",
40 "Handle scope mismatch",
41 "Callback scope mismatch",
42 "Asynchronous work queue is full",
43 "Asynchronous work handle is closing",
44 "Need bigint",
45 "Need date",
46 "Need arraybuffer",
47 "Need detachable arraybuffer",
48 };
49 } // namespace
50
NativeEngine(void * jsEngine)51 NativeEngine::NativeEngine(void* jsEngine) : jsEngine_(jsEngine) {}
52
Init()53 void NativeEngine::Init()
54 {
55 moduleManager_ = NativeModuleManager::GetInstance();
56 referenceManager_ = new NativeReferenceManager();
57 scopeManager_ = new NativeScopeManager();
58 callbackScopeManager_ = new NativeCallbackScopeManager();
59 loop_ = uv_loop_new();
60 if (loop_ == nullptr) {
61 return;
62 }
63 tid_ = pthread_self();
64 uv_async_init(loop_, &uvAsync_, nullptr);
65 uv_sem_init(&uvSem_, 0);
66 lastException_ = nullptr;
67 }
68
~NativeEngine()69 NativeEngine::~NativeEngine()
70 {
71 if (cleanEnv_ != nullptr) {
72 cleanEnv_();
73 }
74 }
75
Deinit()76 void NativeEngine::Deinit()
77 {
78 if (referenceManager_ != nullptr) {
79 delete referenceManager_;
80 referenceManager_ = nullptr;
81 }
82 if (scopeManager_ != nullptr) {
83 delete scopeManager_;
84 scopeManager_ = nullptr;
85 }
86
87 SetStopping(true);
88 uv_sem_destroy(&uvSem_);
89 uv_close((uv_handle_t*)&uvAsync_, nullptr);
90 uv_run(loop_, UV_RUN_ONCE);
91 uv_loop_delete(loop_);
92 }
93
GetScopeManager()94 NativeScopeManager* NativeEngine::GetScopeManager()
95 {
96 return scopeManager_;
97 }
98
GetReferenceManager()99 NativeReferenceManager* NativeEngine::GetReferenceManager()
100 {
101 return referenceManager_;
102 }
103
GetModuleManager()104 NativeModuleManager* NativeEngine::GetModuleManager()
105 {
106 return moduleManager_;
107 }
108
GetCallbackScopeManager()109 NativeCallbackScopeManager* NativeEngine::GetCallbackScopeManager()
110 {
111 return callbackScopeManager_;
112 }
113
GetUVLoop() const114 uv_loop_t* NativeEngine::GetUVLoop() const
115 {
116 return loop_;
117 }
118
GetTid() const119 pthread_t NativeEngine::GetTid() const
120 {
121 return tid_;
122 }
123
Loop(LoopMode mode,bool needSync)124 void NativeEngine::Loop(LoopMode mode, bool needSync)
125 {
126 bool more = true;
127 switch (mode) {
128 case LOOP_DEFAULT:
129 more = uv_run(loop_, UV_RUN_DEFAULT);
130 break;
131 case LOOP_ONCE:
132 more = uv_run(loop_, UV_RUN_ONCE);
133 break;
134 case LOOP_NOWAIT:
135 more = uv_run(loop_, UV_RUN_NOWAIT);
136 break;
137 default:
138 return;
139 }
140 if (more == false) {
141 more = uv_loop_alive(loop_);
142 }
143
144 if (needSync) {
145 uv_sem_post(&uvSem_);
146 }
147 }
148
CreateAsyncWork(NativeValue * asyncResource,NativeValue * asyncResourceName,NativeAsyncExecuteCallback execute,NativeAsyncCompleteCallback complete,void * data)149 NativeAsyncWork* NativeEngine::CreateAsyncWork(NativeValue* asyncResource, NativeValue* asyncResourceName,
150 NativeAsyncExecuteCallback execute, NativeAsyncCompleteCallback complete, void* data)
151 {
152 (void)asyncResource;
153 (void)asyncResourceName;
154 return new NativeAsyncWork(this, execute, complete, data);
155 }
156
CreateAsyncWork(NativeAsyncExecuteCallback execute,NativeAsyncCompleteCallback complete,void * data)157 NativeAsyncWork* NativeEngine::CreateAsyncWork(NativeAsyncExecuteCallback execute,
158 NativeAsyncCompleteCallback complete,
159 void* data)
160 {
161 return new NativeAsyncWork(this, execute, complete, data);
162 }
163
CreateSafeAsyncWork(NativeValue * func,NativeValue * asyncResource,NativeValue * asyncResourceName,size_t maxQueueSize,size_t threadCount,void * finalizeData,NativeFinalize finalizeCallback,void * context,NativeThreadSafeFunctionCallJs callJsCallback)164 NativeSafeAsyncWork* NativeEngine::CreateSafeAsyncWork(NativeValue* func, NativeValue* asyncResource,
165 NativeValue* asyncResourceName, size_t maxQueueSize, size_t threadCount, void* finalizeData,
166 NativeFinalize finalizeCallback, void* context, NativeThreadSafeFunctionCallJs callJsCallback)
167 {
168 return new NativeSafeAsyncWork(this, func, asyncResource, asyncResourceName, maxQueueSize, threadCount,
169 finalizeData, finalizeCallback, context, callJsCallback);
170 }
171
InitAsyncWork(NativeAsyncExecuteCallback execute,NativeAsyncCompleteCallback complete,void * data)172 void NativeEngine::InitAsyncWork(NativeAsyncExecuteCallback execute,
173 NativeAsyncCompleteCallback complete,
174 void* data)
175 {
176 asyncWorker_ = std::make_unique<NativeAsyncWork>(this, execute, complete, data);
177 asyncWorker_->Init();
178 }
179
SendAsyncWork(void * data)180 bool NativeEngine::SendAsyncWork(void* data)
181 {
182 if (!asyncWorker_) {
183 HILOG_ERROR("asyncWorker_ is nullptr");
184 return false;
185 }
186 asyncWorker_->Send(data);
187 return true;
188 }
189
CloseAsyncWork()190 void NativeEngine::CloseAsyncWork()
191 {
192 if (!asyncWorker_) {
193 HILOG_ERROR("asyncWorker_ is nullptr");
194 return;
195 }
196 asyncWorker_->Close();
197 }
198
GetLastError()199 NativeErrorExtendedInfo* NativeEngine::GetLastError()
200 {
201 return &lastError_;
202 }
203
SetLastError(int errorCode,uint32_t engineErrorCode,void * engineReserved)204 void NativeEngine::SetLastError(int errorCode, uint32_t engineErrorCode, void* engineReserved)
205 {
206 lastError_.errorCode = errorCode;
207 lastError_.engineErrorCode = engineErrorCode;
208 lastError_.message = g_errorMessages[lastError_.errorCode];
209 lastError_.reserved = engineReserved;
210 }
211
ClearLastError()212 void NativeEngine::ClearLastError()
213 {
214 lastError_.errorCode = 0;
215 lastError_.engineErrorCode = 0;
216 lastError_.message = nullptr;
217 lastError_.reserved = nullptr;
218 }
219
IsExceptionPending() const220 bool NativeEngine::IsExceptionPending() const
221 {
222 return !(lastException_ == nullptr);
223 }
224
GetAndClearLastException()225 NativeValue* NativeEngine::GetAndClearLastException()
226 {
227 NativeValue* temp = lastException_;
228 lastException_ = nullptr;
229 return temp;
230 }
231
EncodeToUtf8(NativeValue * nativeValue,char * buffer,int32_t * written,size_t bufferSize,int32_t * nchars)232 void NativeEngine::EncodeToUtf8(NativeValue* nativeValue,
233 char* buffer,
234 int32_t* written,
235 size_t bufferSize,
236 int32_t* nchars)
237 {
238 if (nativeValue == nullptr || nchars == nullptr || written == nullptr) {
239 HILOG_ERROR("NativeEngine EncodeToUtf8 args is nullptr");
240 return;
241 }
242
243 auto nativeString = reinterpret_cast<NativeString*>(nativeValue->GetInterface(NativeString::INTERFACE_ID));
244
245 if (nativeString == nullptr) {
246 HILOG_ERROR("nativeValue GetInterface is nullptr");
247 return;
248 }
249 *written = nativeString->EncodeWriteUtf8(buffer, bufferSize, nchars);
250 }
251
252 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM)
CheckUVLoop()253 void NativeEngine::CheckUVLoop()
254 {
255 checkUVLoop_ = true;
256 uv_thread_create(&uvThread_, NativeEngine::UVThreadRunner, this);
257 }
258
CancelCheckUVLoop()259 void NativeEngine::CancelCheckUVLoop()
260 {
261 checkUVLoop_ = false;
262 RunCleanup();
263 uv_async_send(&uvAsync_);
264 uv_sem_post(&uvSem_);
265 uv_thread_join(&uvThread_);
266 }
267
PostLoopTask()268 void NativeEngine::PostLoopTask()
269 {
270 postTask_(true);
271 uv_sem_wait(&uvSem_);
272 }
273
UVThreadRunner(void * nativeEngine)274 void NativeEngine::UVThreadRunner(void* nativeEngine)
275 {
276 auto engine = static_cast<NativeEngine*>(nativeEngine);
277 engine->PostLoopTask();
278 while (engine->checkUVLoop_) {
279 int32_t fd = uv_backend_fd(engine->loop_);
280 int32_t timeout = uv_backend_timeout(engine->loop_);
281 struct epoll_event ev;
282 int32_t result = epoll_wait(fd, &ev, 1, timeout);
283 if (!engine->checkUVLoop_) {
284 HILOG_INFO("break thread after epoll wait");
285 break;
286 }
287 if (result >= 0) {
288 engine->PostLoopTask();
289 } else {
290 HILOG_ERROR("epoll wait fail: result: %{public}d, errno: %{public}d", result, errno);
291 }
292 if (!engine->checkUVLoop_) {
293 HILOG_INFO("break thread after post loop task");
294 break;
295 }
296 }
297 }
298 #endif
299
SetPostTask(PostTask postTask)300 void NativeEngine::SetPostTask(PostTask postTask)
301 {
302 HILOG_INFO("SetPostTask in");
303 postTask_ = postTask;
304 }
305
TriggerPostTask()306 void NativeEngine::TriggerPostTask()
307 {
308 if (postTask_ == nullptr) {
309 HILOG_ERROR("postTask_ is nullptr");
310 return;
311 }
312 postTask_(false);
313 }
314
GetJsEngine()315 void* NativeEngine::GetJsEngine()
316 {
317 return jsEngine_;
318 }
319
320 // register init worker func
SetInitWorkerFunc(InitWorkerFunc func)321 void NativeEngine::SetInitWorkerFunc(InitWorkerFunc func)
322 {
323 initWorkerFunc_ = func;
324 }
SetGetAssetFunc(GetAssetFunc func)325 void NativeEngine::SetGetAssetFunc(GetAssetFunc func)
326 {
327 getAssetFunc_ = func;
328 }
SetOffWorkerFunc(OffWorkerFunc func)329 void NativeEngine::SetOffWorkerFunc(OffWorkerFunc func)
330 {
331 offWorkerFunc_ = func;
332 }
SetWorkerAsyncWorkFunc(NativeAsyncExecuteCallback executeCallback,NativeAsyncCompleteCallback completeCallback)333 void NativeEngine::SetWorkerAsyncWorkFunc(NativeAsyncExecuteCallback executeCallback,
334 NativeAsyncCompleteCallback completeCallback)
335 {
336 nativeAsyncExecuteCallback_ = executeCallback;
337 nativeAsyncCompleteCallback_ = completeCallback;
338 }
339 // call init worker func
CallInitWorkerFunc(NativeEngine * engine)340 bool NativeEngine::CallInitWorkerFunc(NativeEngine* engine)
341 {
342 if (initWorkerFunc_ != nullptr) {
343 initWorkerFunc_(engine);
344 return true;
345 }
346 return false;
347 }
CallGetAssetFunc(const std::string & uri,std::vector<uint8_t> & content,std::string & ami)348 bool NativeEngine::CallGetAssetFunc(const std::string& uri, std::vector<uint8_t>& content, std::string& ami)
349 {
350 if (getAssetFunc_ != nullptr) {
351 getAssetFunc_(uri, content, ami);
352 return true;
353 }
354 return false;
355 }
CallOffWorkerFunc(NativeEngine * engine)356 bool NativeEngine::CallOffWorkerFunc(NativeEngine* engine)
357 {
358 if (offWorkerFunc_ != nullptr) {
359 offWorkerFunc_(engine);
360 return true;
361 }
362 return false;
363 }
364
365 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM)
CallDebuggerPostTaskFunc(std::function<void ()> && task)366 void NativeEngine::CallDebuggerPostTaskFunc(std::function<void()>&& task)
367 {
368 if (debuggerPostTaskFunc_ != nullptr) {
369 debuggerPostTaskFunc_(std::move(task));
370 }
371 }
372
SetDebuggerPostTaskFunc(DebuggerPostTask func)373 void NativeEngine::SetDebuggerPostTaskFunc(DebuggerPostTask func)
374 {
375 debuggerPostTaskFunc_ = func;
376 }
377 #endif
378
CallWorkerAsyncWorkFunc(NativeEngine * engine)379 bool NativeEngine::CallWorkerAsyncWorkFunc(NativeEngine* engine)
380 {
381 if (nativeAsyncExecuteCallback_ != nullptr && nativeAsyncCompleteCallback_ != nullptr) {
382 engine->InitAsyncWork(nativeAsyncExecuteCallback_, nativeAsyncCompleteCallback_, nullptr);
383 return true;
384 }
385 return false;
386 }
387
AddCleanupHook(CleanupCallback fun,void * arg)388 void NativeEngine::AddCleanupHook(CleanupCallback fun, void* arg)
389 {
390 HILOG_INFO("%{public}s, start.", __func__);
391 auto insertion_info = cleanup_hooks_.emplace(CleanupHookCallback { fun, arg, cleanup_hook_counter_++ });
392 if (insertion_info.second != true) {
393 HILOG_ERROR("AddCleanupHook Failed.");
394 }
395 HILOG_INFO("%{public}s, end.", __func__);
396 }
397
RemoveCleanupHook(CleanupCallback fun,void * arg)398 void NativeEngine::RemoveCleanupHook(CleanupCallback fun, void* arg)
399 {
400 HILOG_INFO("%{public}s, start.", __func__);
401 CleanupHookCallback hook { fun, arg, 0 };
402 cleanup_hooks_.erase(hook);
403 HILOG_INFO("%{public}s, end.", __func__);
404 }
405
RunCleanup()406 void NativeEngine::RunCleanup()
407 {
408 HILOG_INFO("%{public}s, start.", __func__);
409 CleanupHandles();
410 // sync clean up
411 while (!cleanup_hooks_.empty()) {
412 HILOG_INFO("NativeEngine::RunCleanup cleanup_hooks is not empty");
413 // Copy into a vector, since we can't sort an unordered_set in-place.
414 std::vector<CleanupHookCallback> callbacks(cleanup_hooks_.begin(), cleanup_hooks_.end());
415 // We can't erase the copied elements from `cleanup_hooks_` yet, because we
416 // need to be able to check whether they were un-scheduled by another hook.
417
418 std::sort(callbacks.begin(), callbacks.end(), [](const CleanupHookCallback& a, const CleanupHookCallback& b) {
419 // Sort in descending order so that the most recently inserted callbacks are run first.
420 return a.insertion_order_counter_ > b.insertion_order_counter_;
421 });
422 HILOG_INFO("NativeEngine::RunCleanup cleanup_hooks callbacks size:%{public}d", (int32_t)callbacks.size());
423 for (const CleanupHookCallback& cb : callbacks) {
424 if (cleanup_hooks_.count(cb) == 0) {
425 // This hook was removed from the `cleanup_hooks_` set during another
426 // hook that was run earlier. Nothing to do here.
427 continue;
428 }
429 cb.fn_(cb.arg_);
430 cleanup_hooks_.erase(cb);
431 }
432 CleanupHandles();
433 }
434 HILOG_INFO("%{public}s, end.", __func__);
435 }
436
CleanupHandles()437 void NativeEngine::CleanupHandles()
438 {
439 HILOG_INFO("%{public}s, start.", __func__);
440 while (request_waiting_ != 0) {
441 HILOG_INFO("%{public}s, request waiting:%{public}d.", __func__, request_waiting_);
442 uv_run(loop_, UV_RUN_ONCE);
443 }
444 HILOG_INFO("%{public}s, end.", __func__);
445 }
446
IncreaseWaitingRequestCounter()447 void NativeEngine::IncreaseWaitingRequestCounter()
448 {
449 request_waiting_++;
450 HILOG_INFO("%{public}s, request waiting:%{public}d.", __func__, request_waiting_);
451 }
452
DecreaseWaitingRequestCounter()453 void NativeEngine::DecreaseWaitingRequestCounter()
454 {
455 request_waiting_--;
456 HILOG_INFO("%{public}s, request waiting:%{public}d.", __func__, request_waiting_);
457 }
458
RegisterWorkerFunction(const NativeEngine * engine)459 void NativeEngine::RegisterWorkerFunction(const NativeEngine* engine)
460 {
461 if (engine == nullptr) {
462 return;
463 }
464 SetInitWorkerFunc(engine->initWorkerFunc_);
465 SetGetAssetFunc(engine->getAssetFunc_);
466 SetOffWorkerFunc(engine->offWorkerFunc_);
467 SetWorkerAsyncWorkFunc(engine->nativeAsyncExecuteCallback_, engine->nativeAsyncCompleteCallback_);
468 }
469
RunScript(const char * path)470 NativeValue* NativeEngine::RunScript(const char* path)
471 {
472 std::vector<uint8_t> scriptContent;
473 std::string pathStr(path);
474 std::string ami;
475 if (!CallGetAssetFunc(pathStr, scriptContent, ami)) {
476 HILOG_ERROR("Get asset error");
477 return nullptr;
478 }
479 HILOG_INFO("asset size is %{public}zu", scriptContent.size());
480 return RunActor(scriptContent, ami.c_str());
481 }