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