• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     engine->eventHandler->PostTask([engine, &cv] {
179         ARKTSInnerDisposeEngine(engine);
180         cv.notify_one();
181     });
182     std::mutex mutex;
183     std::unique_lock lock(mutex);
184     cv.wait(lock);
185     engine->eventHandler->RemoveAllFileDescriptorListeners();
186     if (engine->newRunner) {
187         engine->newRunner->Stop();
188     }
189 #else
190     ARKTSInnerDisposeEngine(engine);
191 #endif
192     delete engine;
193 }
194 
ARKTS_GetContext(ARKTS_Engine engine)195 ARKTS_Env ARKTS_GetContext(ARKTS_Engine engine)
196 {
197     return P_CAST(engine->vm, ARKTS_Env);
198 }
199 
ARKTS_LoadEntryFromAbc(ARKTS_Engine engine,const char * filePath,const char * entryPoint,bool forceReload)200 bool ARKTS_LoadEntryFromAbc(ARKTS_Engine engine, const char* filePath, const char* entryPoint, bool forceReload)
201 {
202     ARKTS_ASSERT_F(engine, "engine is null");
203     ARKTS_ASSERT_F(filePath, "filePath is null");
204     ARKTS_ASSERT_F(entryPoint, "entryPoint is null");
205 
206     if (!forceReload) {
207         auto existed = engine->loadedAbcs.find(entryPoint);
208         if (existed != engine->loadedAbcs.end()) {
209             if (existed->second == filePath) {
210                 return true;
211             } else {
212                 LOGE("can't shadow loaded entryPoint from another .abc, entryPoint: %{public}s", entryPoint);
213                 return false;
214             }
215         }
216     }
217     if (access(filePath, R_OK) != 0) {
218         LOGE("no such file: %{public}s", filePath);
219         return false;
220     }
221     auto vm = engine->vm;
222     panda::JSNApi::NotifyLoadModule(vm);
223     auto success = panda::JSNApi::Execute(vm, filePath, entryPoint);
224     if (success) {
225         engine->loadedAbcs[entryPoint] = filePath;
226     }
227     return success;
228 }
229 
ARKTS_ImportFromEntry(ARKTS_Engine engine,const char * entryPoint,const char * importName)230 ARKTS_Value ARKTS_ImportFromEntry(ARKTS_Engine engine, const char* entryPoint, const char* importName)
231 {
232     ARKTS_ASSERT_P(engine, "engine is null");
233     ARKTS_ASSERT_P(entryPoint, "entryPoint is null");
234     ARKTS_ASSERT_P(importName, "importName is null");
235 
236     if (engine->loadedAbcs.find(entryPoint) == engine->loadedAbcs.end()) {
237         return ARKTS_CreateUndefined();
238     }
239 
240     auto vm = engine->vm;
241     auto value = panda::JSNApi::GetExportObject(vm, entryPoint, importName);
242     if (value->IsHole()) {
243         return ARKTS_CreateUndefined();
244     }
245 
246     return ARKTS_FromHandle(value);
247 }
248 
ARKTS_Require(ARKTS_Env env,const char * target,bool isNativeModule,bool isAppModule,const char * relativePath)249 ARKTS_Value ARKTS_Require(
250     ARKTS_Env env, const char* target, bool isNativeModule, bool isAppModule, const char* relativePath)
251 {
252     ARKTS_ASSERT_P(env, "env is null");
253 
254     auto global = ARKTS_GetGlobalConstant(env);
255     auto targetValue = ARKTS_CreateUtf8(env, target, -1);
256 
257     ARKTS_ASSERT_P(ARKTS_IsHeapObject(global), "js global is null");
258     if (!isNativeModule) {
259         auto funcName = ARKTS_CreateUtf8(env, "requireInternal", -1);
260         auto funcValue = ARKTS_GetProperty(env, global, funcName);
261         ARKTS_ASSERT_P(ARKTS_IsCallable(env, funcName), "global func requireInternal is undefined");
262         return ARKTS_Call(env, funcValue, ARKTS_CreateUndefined(), 1, &targetValue);
263     }
264     auto funcName = ARKTS_CreateUtf8(env, "requireNapi", -1);
265     auto funcValue = ARKTS_GetProperty(env, global, funcName);
266     ARKTS_ASSERT_P(ARKTS_IsCallable(env, funcValue), "global func requireNapi is undefined");
267 
268     if (relativePath) {
269         ARKTS_Value args[] = { targetValue, ARKTS_CreateBool(isAppModule), ARKTS_CreateUndefined(),
270             ARKTS_CreateUtf8(env, relativePath, -1) };
271         return ARKTS_Call(env, funcValue, ARKTS_CreateUndefined(), sizeof(args) / sizeof(ARKTS_Value), args);
272     } else {
273         ARKTS_Value args[] = { targetValue, ARKTS_CreateBool(isAppModule) };
274         return ARKTS_Call(env, funcValue, ARKTS_CreateUndefined(), sizeof(args) / sizeof(ARKTS_Value), args);
275     }
276 }
277 
278 #ifdef __OHOS__
ARKTS_CreateEngineWithNewThread()279 ARKTS_Engine ARKTS_CreateEngineWithNewThread()
280 {
281     ARKTS_Engine result = nullptr;
282     auto newRunner = OHOS::AppExecFwk::EventRunner::Create(true);
283     if (!newRunner) {
284         return nullptr;
285     }
286     auto handler = std::make_shared<OHOS::AppExecFwk::EventHandler>(newRunner);
287     if (!handler) {
288         newRunner->Stop();
289         return nullptr;
290     }
291     std::mutex mutex;
292     std::unique_lock lock(mutex);
293     std::condition_variable cv;
294     auto postSuccess = handler->PostTask([&result, &cv] {
295         result = ARKTS_CreateEngine();
296         cv.notify_one();
297     });
298     if (!postSuccess) {
299         newRunner->Stop();
300         return nullptr;
301     }
302     auto status = cv.wait_for(lock, std::chrono::seconds(1));
303     if (status == std::cv_status::timeout) {
304         handler->PostTask([&result] {
305             if (result) {
306                 ARKTS_DestroyEngine(result);
307             }
308         });
309         newRunner->Stop();
310         return nullptr;
311     }
312     if (!result) {
313         newRunner->Stop();
314         return nullptr;
315     }
316     result->newRunner = newRunner;
317     return result;
318 }
319 #endif
320 
ARKTS_GetThreadIdOfEngine(ARKTS_Engine engine)321 uint64_t ARKTS_GetThreadIdOfEngine(ARKTS_Engine engine)
322 {
323     return engine->threadId;
324 }
325