1 /*
2  * Copyright (c) 2024 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 <string>
17 #include <unordered_map>
18 #include <mutex>
19 #include <condition_variable>
20 #include <memory>
21 
22 #include "ark_interop_internal.h"
23 #include "ark_interop_log.h"
24 #include "ark_interop_napi.h"
25 #include "ark_interop_external.h"
26 #include "jsnapi.h"
27 #include "native_engine/impl/ark/ark_native_engine.h"
28 #ifdef __OHOS__
29 #include "uv_loop_handler.h"
30 #endif
31 
32 struct ARKTS_Engine_ {
33     panda::EcmaVM* vm;
34     ArkNativeEngine* engine;
35     std::unordered_map<std::string, std::string> loadedAbcs;
36 #ifdef __OHOS__
37     std::shared_ptr<OHOS::AppExecFwk::EventHandler> eventHandler;
38     std::shared_ptr<OHOS::AppExecFwk::EventRunner> newRunner;
39 #endif
40     uint64_t threadId;
41 };
42 
43 #ifdef __OHOS__
44 static constexpr char TIMER_TASK[] = "uv_timer_task";
45 
OnReadable(int32_t)46 void UVLoopHandler::OnReadable(int32_t)
47 {
48     OnTriggered();
49 }
50 
OnWritable(int32_t)51 void UVLoopHandler::OnWritable(int32_t)
52 {
53     OnTriggered();
54 }
55 
OnTriggered()56 void UVLoopHandler::OnTriggered()
57 {
58     uv_run(uvLoop_, UV_RUN_NOWAIT);
59     auto eventHandler = GetOwner();
60     if (!eventHandler) {
61         return;
62     }
63 
64     int32_t timeout = uv_backend_timeout(uvLoop_);
65     if (timeout < 0) {
66         if (haveTimerTask_) {
67             eventHandler->RemoveTask(TIMER_TASK);
68         }
69         return;
70     }
71 
72     int64_t timeStamp = static_cast<int64_t>(uv_now(uvLoop_)) + timeout;
73     // we don't check timestamp in emulator for computer clock is inaccurate
74 #ifndef RUNTIME_EMULATOR
75     if (timeStamp == lastTimeStamp_) {
76         return;
77     }
78 #endif
79 
80     if (haveTimerTask_) {
81         eventHandler->RemoveTask(TIMER_TASK);
82     }
83 
84     auto callback = [wp = weak_from_this()] {
85         auto sp = wp.lock();
86         if (sp) {
87             // Timer task is triggered, so there is no timer task now.
88             sp->haveTimerTask_ = false;
89             sp->OnTriggered();
90         }
91     };
92     eventHandler->PostTask(callback, TIMER_TASK, timeout);
93     lastTimeStamp_ = timeStamp;
94     haveTimerTask_ = true;
95 }
96 #endif
97 
ARKTS_CreateEngine()98 ARKTS_Engine ARKTS_CreateEngine()
99 {
100     panda::RuntimeOption options;
101     options.SetLogLevel(panda::RuntimeOption::LOG_LEVEL::INFO);
102     auto vm = panda::JSNApi::CreateJSVM(options);
103     if (!vm) {
104         LOGE("create EcmaVM failed");
105         return nullptr;
106     }
107     auto engine = new ArkNativeEngine(vm, nullptr);
108     if (!engine) {
109         LOGE("alloc ArkEngine failed");
110         panda::JSNApi::DestroyJSVM(vm);
111         return nullptr;
112     }
113 
114     auto result = new ARKTS_Engine_;
115     if (!result) {
116         LOGE("alloc ARKTS_Engine_ failed");
117         panda::JSNApi::DestroyJSVM(vm);
118         delete engine;
119         return nullptr;
120     }
121     result->vm = vm;
122     result->engine = engine;
123 #ifdef __OHOS__
124     result->eventHandler = ARKTS_GetOrCreateEventHandler(P_CAST(vm, ARKTS_Env));
125     if (!result->eventHandler) {
126         ARKTS_DestroyEngine(result);
127         return nullptr;
128     }
129 #endif
130     auto loop = engine->GetUVLoop();
131     if (loop == nullptr) {
132         if (!engine->ReinitUVLoop()) {
133             LOGE("init uv loop failed");
134             ARKTS_DestroyEngine(result);
135             return nullptr;
136         }
137         loop = engine->GetUVLoop();
138     }
139     panda::JSNApi::SetLoop(vm, loop);
140 
141 #ifdef __OHOS__
142     uv_run(loop, UV_RUN_NOWAIT);
143     auto fd = uv_backend_fd(loop);
144     uint32_t events = OHOS::AppExecFwk::FILE_DESCRIPTOR_INPUT_EVENT | OHOS::AppExecFwk::FILE_DESCRIPTOR_OUTPUT_EVENT;
145     result->eventHandler->AddFileDescriptorListener(fd, events, std::make_shared<UVLoopHandler>(loop), "uvLoopTask");
146 #endif
147 
148     result->threadId = ARKTS_GetPosixThreadId();
149 
150     return result;
151 }
152 
ARKTS_GetNAPIEnv(ARKTS_Engine engine)153 void* ARKTS_GetNAPIEnv(ARKTS_Engine engine)
154 {
155     ARKTS_ASSERT_P(engine, "engine is null");
156     return engine->engine;
157 }
158 
ARKTSInnerDisposeEngine(ARKTS_Engine engine)159 static void ARKTSInnerDisposeEngine(ARKTS_Engine engine)
160 {
161     delete engine->engine;
162     if (engine->vm) {
163         auto env = P_CAST(engine->vm, ARKTS_Env);
164         ARKTS_DisposeJSContext(env);
165         ARKTS_DisposeEventHandler(env);
166         panda::JSNApi::DestroyJSVM(engine->vm);
167     }
168     engine->engine = nullptr;
169     engine->vm = nullptr;
170 }
171 
ARKTS_DestroyEngine(ARKTS_Engine engine)172 void ARKTS_DestroyEngine(ARKTS_Engine engine)
173 {
174     ARKTS_ASSERT_V(ARKTS_GetPosixThreadId() != ARKTS_GetThreadIdOfEngine(engine),
175         "can not destroy engine at it's running thread");
176 #ifdef __OHOS__
177     std::condition_variable cv;
178     std::atomic<bool> isComplete = false;
179     engine->eventHandler->PostTask([engine, &cv, &isComplete] {
180         ARKTSInnerDisposeEngine(engine);
181         isComplete = true;
182         cv.notify_one();
183     });
184     std::mutex mutex;
185     std::unique_lock lock(mutex);
186     while (!isComplete) {
187         cv.wait_for(lock, std::chrono::seconds(1));
188     }
189     engine->eventHandler->RemoveAllFileDescriptorListeners();
190     if (engine->newRunner) {
191         engine->newRunner->Stop();
192     }
193 #else
194     ARKTSInnerDisposeEngine(engine);
195 #endif
196     delete engine;
197 }
198 
ARKTS_GetContext(ARKTS_Engine engine)199 ARKTS_Env ARKTS_GetContext(ARKTS_Engine engine)
200 {
201     return P_CAST(engine->vm, ARKTS_Env);
202 }
203 
ARKTS_LoadEntryFromAbc(ARKTS_Engine engine,const char * filePath,const char * entryPoint,bool forceReload)204 bool ARKTS_LoadEntryFromAbc(ARKTS_Engine engine, const char* filePath, const char* entryPoint, bool forceReload)
205 {
206     ARKTS_ASSERT_F(engine, "engine is null");
207     ARKTS_ASSERT_F(filePath, "filePath is null");
208     ARKTS_ASSERT_F(entryPoint, "entryPoint is null");
209 
210     if (!forceReload) {
211         auto existed = engine->loadedAbcs.find(entryPoint);
212         if (existed != engine->loadedAbcs.end()) {
213             if (existed->second == filePath) {
214                 return true;
215             } else {
216                 LOGE("can't shadow loaded entryPoint from another .abc, entryPoint: %{public}s", entryPoint);
217                 return false;
218             }
219         }
220     }
221     if (access(filePath, R_OK) != 0) {
222         LOGE("no such file: %{public}s", filePath);
223         return false;
224     }
225     auto vm = engine->vm;
226     panda::JSNApi::NotifyLoadModule(vm);
227     auto success = panda::JSNApi::Execute(vm, filePath, entryPoint);
228     if (success) {
229         engine->loadedAbcs[entryPoint] = filePath;
230     }
231     return success;
232 }
233 
ARKTS_ImportFromEntry(ARKTS_Engine engine,const char * entryPoint,const char * importName)234 ARKTS_Value ARKTS_ImportFromEntry(ARKTS_Engine engine, const char* entryPoint, const char* importName)
235 {
236     ARKTS_ASSERT_P(engine, "engine is null");
237     ARKTS_ASSERT_P(entryPoint, "entryPoint is null");
238     ARKTS_ASSERT_P(importName, "importName is null");
239 
240     if (engine->loadedAbcs.find(entryPoint) == engine->loadedAbcs.end()) {
241         return ARKTS_CreateUndefined();
242     }
243 
244     auto vm = engine->vm;
245     auto value = panda::JSNApi::GetExportObject(vm, entryPoint, importName);
246     if (value->IsHole()) {
247         return ARKTS_CreateUndefined();
248     }
249 
250     return ARKTS_FromHandle(value);
251 }
252 
ARKTS_Require(ARKTS_Env env,const char * target,bool isNativeModule,bool isAppModule,const char * relativePath)253 ARKTS_Value ARKTS_Require(
254     ARKTS_Env env, const char* target, bool isNativeModule, bool isAppModule, const char* relativePath)
255 {
256     ARKTS_ASSERT_P(env, "env is null");
257 
258     auto global = ARKTS_GetGlobalConstant(env);
259     auto targetValue = ARKTS_CreateUtf8(env, target, -1);
260 
261     ARKTS_ASSERT_P(ARKTS_IsHeapObject(global), "js global is null");
262     if (!isNativeModule) {
263         auto funcName = ARKTS_CreateUtf8(env, "requireInternal", -1);
264         auto funcValue = ARKTS_GetProperty(env, global, funcName);
265         ARKTS_ASSERT_P(ARKTS_IsCallable(env, funcName), "global func requireInternal is undefined");
266         return ARKTS_Call(env, funcValue, ARKTS_CreateUndefined(), 1, &targetValue);
267     }
268     auto funcName = ARKTS_CreateUtf8(env, "requireNapi", -1);
269     auto funcValue = ARKTS_GetProperty(env, global, funcName);
270     ARKTS_ASSERT_P(ARKTS_IsCallable(env, funcValue), "global func requireNapi is undefined");
271 
272     if (relativePath) {
273         ARKTS_Value args[] = { targetValue, ARKTS_CreateBool(isAppModule), ARKTS_CreateUndefined(),
274             ARKTS_CreateUtf8(env, relativePath, -1) };
275         return ARKTS_Call(env, funcValue, ARKTS_CreateUndefined(), sizeof(args) / sizeof(ARKTS_Value), args);
276     } else {
277         ARKTS_Value args[] = { targetValue, ARKTS_CreateBool(isAppModule) };
278         return ARKTS_Call(env, funcValue, ARKTS_CreateUndefined(), sizeof(args) / sizeof(ARKTS_Value), args);
279     }
280 }
281 
282 #ifdef __OHOS__
ARKTS_CreateEngineWithNewThread()283 ARKTS_Engine ARKTS_CreateEngineWithNewThread()
284 {
285     ARKTS_Engine result = nullptr;
286     auto newRunner = OHOS::AppExecFwk::EventRunner::Create(true);
287     if (!newRunner) {
288         return nullptr;
289     }
290     auto handler = std::make_shared<OHOS::AppExecFwk::EventHandler>(newRunner);
291     if (!handler) {
292         newRunner->Stop();
293         return nullptr;
294     }
295     std::mutex mutex;
296     std::unique_lock lock(mutex);
297     std::condition_variable cv;
298     std::atomic<bool> createComplete = false;
299     auto postSuccess = handler->PostTask([&result, &cv, &createComplete] {
300         result = ARKTS_CreateEngine();
301         createComplete = true;
302         cv.notify_one();
303     });
304     if (!postSuccess) {
305         newRunner->Stop();
306         return nullptr;
307     }
308     while (!createComplete) {
309         cv.wait_for(lock, std::chrono::milliseconds(1));
310     }
311     if (!result) {
312         newRunner->Stop();
313         return nullptr;
314     }
315     result->newRunner = newRunner;
316     return result;
317 }
318 #endif
319 
ARKTS_GetThreadIdOfEngine(ARKTS_Engine engine)320 uint64_t ARKTS_GetThreadIdOfEngine(ARKTS_Engine engine)
321 {
322     return engine->threadId;
323 }
324