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