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