• 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 
ARKTSInner_DisposeEngine(ARKTS_Engine engine)98 static void ARKTSInner_DisposeEngine(ARKTS_Engine engine)
99 {
100 #ifdef __OHOS__
101     if (auto loop = engine->engine->GetUVLoop()) {
102         auto fd = uv_backend_fd(loop);
103         engine->eventHandler->RemoveFileDescriptorListener(fd);
104     }
105 #endif
106     ARKTS_DisposeJSContext(reinterpret_cast<ARKTS_Env>(engine->vm));
107     delete engine->engine;
108     if (engine->vm) {
109         auto env = P_CAST(engine->vm, ARKTS_Env);
110         ARKTS_DisposeEventHandler(env);
111         panda::JSNApi::DestroyJSVM(engine->vm);
112     }
113     engine->engine = nullptr;
114     engine->vm = nullptr;
115 }
116 
ARKTSInner_InitEngineLoop(ARKTS_Engine result,ArkNativeEngine * engine,EcmaVM * vm)117 static bool ARKTSInner_InitEngineLoop(ARKTS_Engine result, ArkNativeEngine* engine, EcmaVM* vm)
118 {
119 #ifdef __OHOS__
120     result->eventHandler = ARKTS_GetOrCreateEventHandler(P_CAST(vm, ARKTS_Env));
121     if (!result->eventHandler) {
122         return false;
123     }
124 #endif
125     auto loop = engine->GetUVLoop();
126     if (!loop) {
127         if (!engine->ReinitUVLoop()) {
128             LOGE("init uv loop failed");
129             return false;
130         }
131         loop = engine->GetUVLoop();
132     }
133     panda::JSNApi::SetLoop(vm, loop);
134     if (!ARKTSInner_InitLoop(reinterpret_cast<napi_env>(engine), reinterpret_cast<ARKTS_Env>(vm), loop)) {
135         LOGE("init async loop failed");
136         return false;
137     }
138 
139 #ifdef __OHOS__
140     uv_run(loop, UV_RUN_NOWAIT);
141     auto fd = uv_backend_fd(loop);
142     uint32_t events = OHOS::AppExecFwk::FILE_DESCRIPTOR_INPUT_EVENT | OHOS::AppExecFwk::FILE_DESCRIPTOR_OUTPUT_EVENT;
143     result->eventHandler->AddFileDescriptorListener(fd, events, std::make_shared<UVLoopHandler>(loop), "uvLoopTask");
144 #endif
145     return true;
146 }
147 
ARKTS_CreateEngine()148 ARKTS_Engine ARKTS_CreateEngine()
149 {
150     panda::RuntimeOption options;
151     options.SetLogLevel(panda::RuntimeOption::LOG_LEVEL::INFO);
152     auto vm = panda::JSNApi::CreateJSVM(options);
153     if (!vm) {
154         LOGE("create EcmaVM failed");
155         return nullptr;
156     }
157     auto engine = new ArkNativeEngine(vm, nullptr);
158     if (!engine) {
159         LOGE("alloc ArkEngine failed");
160         panda::JSNApi::DestroyJSVM(vm);
161         return nullptr;
162     }
163 
164     auto result = new ARKTS_Engine_;
165     if (!result) {
166         LOGE("alloc ARKTS_Engine_ failed");
167         panda::JSNApi::DestroyJSVM(vm);
168         delete engine;
169         return nullptr;
170     }
171     result->vm = vm;
172     result->engine = engine;
173     result->threadId = ARKTS_GetPosixThreadId();
174 
175     if (!ARKTSInner_InitEngineLoop(result, engine, vm)) {
176         ARKTSInner_DisposeEngine(result);
177         delete result;
178         return nullptr;
179     }
180 
181     return result;
182 }
183 
ARKTS_GetNAPIEnv(ARKTS_Engine engine)184 void* ARKTS_GetNAPIEnv(ARKTS_Engine engine)
185 {
186     ARKTS_ASSERT_P(engine, "engine is null");
187     return engine->engine;
188 }
189 
ARKTS_DestroyEngine(ARKTS_Engine engine)190 void ARKTS_DestroyEngine(ARKTS_Engine engine)
191 {
192     ARKTS_ASSERT_V(engine, "engine is null");
193     ARKTS_ASSERT_V(ARKTS_GetPosixThreadId() != ARKTS_GetThreadIdOfEngine(engine),
194         "can not destroy engine at it's running thread");
195 #ifdef __OHOS__
196     std::condition_variable cv;
197     std::atomic<bool> isComplete = false;
198     engine->eventHandler->PostTask([engine, &cv, &isComplete] {
199         ARKTSInner_DisposeEngine(engine);
200         isComplete = true;
201         cv.notify_one();
202     });
203     std::mutex mutex;
204     std::unique_lock lock(mutex);
205     while (!isComplete) {
206         cv.wait_for(lock, std::chrono::seconds(1));
207     }
208     engine->eventHandler->RemoveAllFileDescriptorListeners();
209     if (engine->newRunner) {
210         engine->newRunner->Stop();
211     }
212 #else
213     ARKTSInner_DisposeEngine(engine);
214 #endif
215     delete engine;
216 }
217 
ARKTS_GetContext(ARKTS_Engine engine)218 ARKTS_Env ARKTS_GetContext(ARKTS_Engine engine)
219 {
220     ARKTS_ASSERT_P(engine, "engine is null");
221     return P_CAST(engine->vm, ARKTS_Env);
222 }
223 
ARKTS_LoadEntryFromAbc(ARKTS_Engine engine,const char * filePath,const char * entryPoint,bool forceReload)224 bool ARKTS_LoadEntryFromAbc(ARKTS_Engine engine, const char* filePath, const char* entryPoint, bool forceReload)
225 {
226     ARKTS_ASSERT_F(engine, "engine is null");
227     ARKTS_ASSERT_F(filePath, "filePath is null");
228     ARKTS_ASSERT_F(entryPoint, "entryPoint is null");
229 
230     if (!forceReload) {
231         auto existed = engine->loadedAbcs.find(entryPoint);
232         if (existed != engine->loadedAbcs.end()) {
233             if (existed->second == filePath) {
234                 return true;
235             } else {
236                 LOGE("can't shadow loaded entryPoint from another .abc, entryPoint: %{public}s", entryPoint);
237                 return false;
238             }
239         }
240     }
241     if (access(filePath, R_OK) != 0) {
242         LOGE("no such file: %{public}s", filePath);
243         return false;
244     }
245     auto vm = engine->vm;
246     panda::JSNApi::NotifyLoadModule(vm);
247     auto success = panda::JSNApi::Execute(vm, filePath, entryPoint);
248     if (success) {
249         engine->loadedAbcs[entryPoint] = filePath;
250     }
251     return success;
252 }
253 
ARKTS_ImportFromEntry(ARKTS_Engine engine,const char * entryPoint,const char * importName)254 ARKTS_Value ARKTS_ImportFromEntry(ARKTS_Engine engine, const char* entryPoint, const char* importName)
255 {
256     ARKTS_ASSERT_P(engine, "engine is null");
257     ARKTS_ASSERT_P(entryPoint, "entryPoint is null");
258     ARKTS_ASSERT_P(importName, "importName is null");
259 
260     if (engine->loadedAbcs.find(entryPoint) == engine->loadedAbcs.end()) {
261         return ARKTS_CreateUndefined();
262     }
263 
264     auto vm = engine->vm;
265     auto value = panda::JSNApi::GetExportObject(vm, entryPoint, importName);
266     if (value->IsHole()) {
267         return ARKTS_CreateUndefined();
268     }
269 
270     return ARKTS_FromHandle(value);
271 }
272 
ARKTS_Require(ARKTS_Env env,const char * target,bool isNativeModule,bool isAppModule,const char * relativePath)273 ARKTS_Value ARKTS_Require(
274     ARKTS_Env env, const char* target, bool isNativeModule, bool isAppModule, const char* relativePath)
275 {
276     ARKTS_ASSERT_P(env, "env is null");
277 
278     auto global = ARKTS_GetGlobalConstant(env);
279     auto targetValue = ARKTS_CreateUtf8(env, target, -1);
280 
281     ARKTS_ASSERT_P(ARKTS_IsHeapObject(global), "js global is null");
282     if (!isNativeModule) {
283         auto funcName = ARKTS_CreateUtf8(env, "requireInternal", -1);
284         auto funcValue = ARKTS_GetProperty(env, global, funcName);
285         ARKTS_ASSERT_P(ARKTS_IsCallable(env, funcName), "global func requireInternal is undefined");
286         return ARKTS_Call(env, funcValue, ARKTS_CreateUndefined(), 1, &targetValue);
287     }
288     auto funcName = ARKTS_CreateUtf8(env, "requireNapi", -1);
289     auto funcValue = ARKTS_GetProperty(env, global, funcName);
290     ARKTS_ASSERT_P(ARKTS_IsCallable(env, funcValue), "global func requireNapi is undefined");
291 
292     if (relativePath) {
293         ARKTS_Value args[] = { targetValue, ARKTS_CreateBool(isAppModule), ARKTS_CreateUndefined(),
294             ARKTS_CreateUtf8(env, relativePath, -1) };
295         return ARKTS_Call(env, funcValue, ARKTS_CreateUndefined(), sizeof(args) / sizeof(ARKTS_Value), args);
296     } else {
297         ARKTS_Value args[] = { targetValue, ARKTS_CreateBool(isAppModule) };
298         return ARKTS_Call(env, funcValue, ARKTS_CreateUndefined(), sizeof(args) / sizeof(ARKTS_Value), args);
299     }
300 }
301 
302 #ifdef __OHOS__
ARKTS_CreateEngineWithNewThread()303 ARKTS_Engine ARKTS_CreateEngineWithNewThread()
304 {
305     ARKTS_Engine result = nullptr;
306     auto newRunner = OHOS::AppExecFwk::EventRunner::Create(true);
307     if (!newRunner) {
308         return nullptr;
309     }
310     auto handler = std::make_shared<OHOS::AppExecFwk::EventHandler>(newRunner);
311     if (!handler) {
312         newRunner->Stop();
313         return nullptr;
314     }
315     std::mutex mutex;
316     std::unique_lock lock(mutex);
317     std::condition_variable cv;
318     std::atomic<bool> createComplete = false;
319     auto postSuccess = handler->PostTask([&result, &cv, &createComplete] {
320         result = ARKTS_CreateEngine();
321         createComplete = true;
322         cv.notify_one();
323     });
324     if (!postSuccess) {
325         newRunner->Stop();
326         return nullptr;
327     }
328     while (!createComplete) {
329         cv.wait_for(lock, std::chrono::milliseconds(1));
330     }
331     if (!result) {
332         newRunner->Stop();
333         return nullptr;
334     }
335     result->newRunner = newRunner;
336     return result;
337 }
338 #endif
339 
ARKTS_GetThreadIdOfEngine(ARKTS_Engine engine)340 uint64_t ARKTS_GetThreadIdOfEngine(ARKTS_Engine engine)
341 {
342     ARKTS_ASSERT_I(engine, "engine is null");
343     return engine->threadId;
344 }
345