• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 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 <cstring>
17 #include <vector>
18 #include <algorithm>
19 
20 #include "interop-logging.h"
21 #include "dynamic-loader.h"
22 #include "koala-types.h"
23 
24 // DO NOT USE KOALA INTEROP MECHANISMS IN THIS FILE!
25 
26 #ifdef KOALA_JNI
27 #include "jni.h"
28 #endif
29 
30 #ifdef KOALA_ETS_NAPI
31 #include "etsapi.h"
32 #endif
33 
34 #if defined(KOALA_LINUX) || defined(KOALA_MACOS) || defined(KOALA_OHOS)
35 #include "sys/stat.h"
36 #include "dirent.h"
37 #endif
38 
39 #define OHOS_USER_LIBS "/data/storage/el1/bundle/libs"
40 #ifdef KOALA_OHOS_ARM32
41 #define USE_SYSTEM_ARKVM 1
42 #elif KOALA_OHOS_ARM64
43 #define USE_SYSTEM_ARKVM 1
44 #else
45 #define USE_SYSTEM_ARKVM 0
46 #endif
47 
48 #if USE_SYSTEM_ARKVM
49 #define SYSTEM_ARK_STDLIB_PATH "/system/etc/etsstdlib.abc"
50 #endif
51 
52 void traverseDir(std::string root, std::vector<std::string>& paths, int depth = 0);
53 
54 struct VMLibInfo {
55     const char* sdkPath;
56     const char* platform;
57     const char* lib;
58     const char* createVM;
59 };
60 
61 #ifdef KOALA_JNI
62 const VMLibInfo javaVMLib = {
63     getenv("JAVA_HOME"),
64     #if defined(KOALA_LINUX) || defined(KOALA_MACOS)
65     "lib/server"
66     #elif KOALA_WINDOWS
67     "bin/server"
68     #else
69     #error "Unknown platform"
70     #endif
71     ,
72     "jvm",
73     "JNI_CreateJavaVM",
74 };
75 #endif
76 
77 #ifdef KOALA_ETS_NAPI
78 const VMLibInfo pandaVMLib = {
79     // sdkPath
80     #if defined(KOALA_OHOS)
81         #ifdef KOALA_OHOS_ARM32
82             "/system/lib"
83         #elif KOALA_OHOS_ARM64
84             "/system/lib64"
85         #else
86             OHOS_USER_LIBS
87         #endif
88     #else
89         getenv("PANDA_HOME")
90     #endif
91     ,
92 
93     // platform
94     #ifdef KOALA_LINUX
95         #ifdef KOALA_LINUX_ARM64
96             "linux_arm64_host_tools/lib"
97         #else
98             "linux_host_tools/lib"
99         #endif
100     #elif KOALA_MACOS
101         "macos_host_tools/lib"
102     #elif KOALA_WINDOWS
103         "_host_tools/lib"
104     #elif KOALA_OHOS_ARM64
105         "arm64"
106     #elif KOALA_OHOS_ARM32
107         "arm"
108     #else
109         #error "Unknown platform"
110     #endif
111     ,
112 
113     // lib
114     "arkruntime"
115     ,
116 
117     // createVM
118     "ETS_CreateVM"
119 };
120 #endif
121 
122 struct VMInitArgs {
123     int version;
124     int nOptions;
125     void* options;
126 };
127 
128 #define JAVA_VM_KIND 1
129 #define PANDA_VM_KIND 2
130 #define ES2PANDA_KIND 3
131 
132 struct ForeignVMContext {
133     void* currentVMContext;
134     int32_t (*callSync)(void* vmContext, int32_t callback, int8_t* data, int32_t length);
135 };
136 
137 struct VMEntry {
138     int vmKind;
139     void* env;
140     void* app;
141     void* enter;
142     void* emitEvent;
143     void* restartWith;
144     ForeignVMContext foreignVMContext;
145 };
146 
147 VMEntry g_vmEntry = {};
148 
149 typedef int (*createVM_t)(void** pVM, void** pEnv, void* vmInitArgs);
150 typedef int (*getVMs_t)(void** pVM, int32_t bufLen, int32_t* nVMs);
151 
152 #ifdef KOALA_WINDOWS
153 #define DLL_EXPORT __declspec(dllexport)
154 #else
155 #define DLL_EXPORT __attribute__ ((visibility ("default")))
156 #endif
157 
loadES2Panda(const char * appClassPath,const char * appLibPath)158 int loadES2Panda(const char* appClassPath, const char* appLibPath) {
159     fprintf(stderr, "native: es2panda %s\n", appClassPath);
160     return 0;
161 }
162 
ArkMobileLog(int id,int level,const char * component,const char * fmt,const char * msg)163 static int ArkMobileLog(int id, int level, const char *component, const char *fmt, const char *msg) {
164     LOGE("ArkMobileLog: %" LOG_PUBLIC "s", msg);
165     return 0;
166 }
167 
LoadVirtualMachine(KInt vmKind,const char * appClassPath,const char * appLibPath,const ForeignVMContext * foreignVMContext)168 extern "C" DLL_EXPORT KInt LoadVirtualMachine(KInt vmKind, const char* appClassPath, const char* appLibPath, const ForeignVMContext* foreignVMContext) {
169     if (vmKind == ES2PANDA_KIND) {
170         return loadES2Panda(appClassPath, appLibPath);
171     }
172 
173     const VMLibInfo* thisVM =
174         #ifdef KOALA_JNI
175         (vmKind == JAVA_VM_KIND) ? &javaVMLib :
176         #endif
177         #ifdef KOALA_ETS_NAPI
178         (vmKind == PANDA_VM_KIND) ? &pandaVMLib :
179         #endif
180         nullptr;
181 
182     if (!thisVM) {
183         LOGE("Unknown VM kind: %" LOG_PUBLIC "d\n (possibly %" LOG_PUBLIC "s is compiled without expected flags)", vmKind, __FILE__);
184         return -1;
185     }
186 
187     LOGI("Starting VM %" LOG_PUBLIC "d with classpath=%" LOG_PUBLIC "s native=%" LOG_PUBLIC "s", vmKind, appClassPath, appLibPath);
188 
189     std::string libPath =
190 #if USE_SYSTEM_ARKVM
191         std::string(thisVM->sdkPath) + "/" + libName(thisVM->lib)
192 #elif defined(KOALA_LINUX) || defined(KOALA_MACOS) || defined(KOALA_WINDOWS)
193         std::string(thisVM->sdkPath) + "/" + std::string(thisVM->platform) + "/" + libName(thisVM->lib)
194 #elif defined(KOALA_OHOS)
195         std::string(OHOS_USER_LIBS) + "/" + libName(thisVM->lib)
196 #else
197         #error "Library path not specified for this platform"
198 #endif
199         ;
200     void *handle = loadLibrary(libPath);
201     if (!handle) {
202         LOGE("Cannot load library %" LOG_PUBLIC "s: %" LOG_PUBLIC "s\n", libPath.c_str(), libraryError());
203         return -1;
204     }
205 
206     createVM_t createVM = (createVM_t)findSymbol(handle, thisVM->createVM);
207     getVMs_t getVMs = (getVMs_t)findSymbol(handle, "ETS_GetCreatedVMs");
208 
209     if (!createVM) {
210         LOGE("Cannot find %" LOG_PUBLIC "s\n", thisVM->createVM);
211         return -1;
212     }
213 
214     void* vm = nullptr;
215     void* env = nullptr;
216     int32_t nVMs = 0;
217     int result = 0;
218 
219 #ifdef KOALA_JNI
220     if (vmKind == JAVA_VM_KIND) {
221         JavaVMInitArgs javaVMArgs;
222         javaVMArgs.version = JNI_VERSION_10;
223         javaVMArgs.ignoreUnrecognized = false;
224         std::vector<JavaVMOption> javaVMOptions;
225         javaVMOptions = {
226             {(char*)strdup((std::string("-Djava.class.path=") + appClassPath).c_str())},
227             {(char*)strdup((std::string("-Djava.library.path=") + appLibPath).c_str())},
228         };
229         javaVMArgs.nOptions = javaVMOptions.size();
230         javaVMArgs.options = javaVMOptions.data();
231         g_vmEntry.vmKind = JAVA_VM_KIND;
232         result = createVM(&vm, &env, &javaVMArgs);
233     }
234 #endif
235 
236 #ifdef KOALA_ETS_NAPI
237     if (vmKind == PANDA_VM_KIND) {
238         EtsVMInitArgs pandaVMArgs;
239         pandaVMArgs.version = ETS_NAPI_VERSION_1_0;
240         std::vector<EtsVMOption> etsVMOptions;
241         std::vector<std::string> files;
242         traverseDir(std::string(appClassPath), files);
243         std::sort(files.begin(), files.end());
244         etsVMOptions = {
245 #if USE_SYSTEM_ARKVM
246             {EtsOptionType::ETS_BOOT_FILE, SYSTEM_ARK_STDLIB_PATH},
247 #elif defined(KOALA_OHOS)
248             {EtsOptionType::ETS_BOOT_FILE, (std::string(OHOS_USER_LIBS) + "/" + "etsstdlib.abc").c_str() },
249 
250 #elif defined(KOALA_LINUX) || defined(KOALA_MACOS) || defined(KOALA_WINDOWS)
251             {EtsOptionType::ETS_BOOT_FILE, (char*)strdup((std::string(thisVM->sdkPath) + "/ets/etsstdlib.abc").c_str())},
252 #endif
253         };
254         for (const std::string& path : files) {
255             etsVMOptions.push_back({EtsOptionType::ETS_BOOT_FILE, (char*)strdup(path.c_str())});
256         }
257         etsVMOptions.push_back({EtsOptionType::ETS_NATIVE_LIBRARY_PATH, (char*)strdup(std::string(appLibPath).c_str())});
258         etsVMOptions.push_back({EtsOptionType::ETS_VERIFICATION_MODE, "on-the-fly"});
259         etsVMOptions.push_back({EtsOptionType::ETS_NO_JIT, nullptr});
260         etsVMOptions.push_back({EtsOptionType::ETS_MOBILE_LOG, (void*)ArkMobileLog});
261         etsVMOptions.push_back({EtsOptionType::ETS_AOT, nullptr});
262         pandaVMArgs.nOptions = etsVMOptions.size();
263         pandaVMArgs.options = etsVMOptions.data();
264         g_vmEntry.vmKind = PANDA_VM_KIND;
265 
266         result = getVMs ? getVMs(&vm, 1, &nVMs) : 0;
267         if (nVMs != 0) {
268             __EtsVM* vmInstance = (__EtsVM*)vm;
269             EtsEnv* pEnv = nullptr;
270             vmInstance->GetEnv(&pEnv, ETS_NAPI_VERSION_1_0);
271             env = static_cast<void*>(pEnv);
272         } else {
273             result = createVM(&vm, &env, &pandaVMArgs);
274         }
275 
276     }
277 #endif
278 
279     if (result != 0) {
280         LOGE("Error creating a VM of kind %" LOG_PUBLIC "d: %" LOG_PUBLIC "d\n", vmKind, result);
281         return result;
282     }
283     g_vmEntry.env = env;
284     g_vmEntry.foreignVMContext = *foreignVMContext;
285     return 0;
286 }
287 
288 struct AppInfo {
289     const char* className;
290     const char* createMethodName;
291     const char* createMethodSig;
292     const char* startMethodName;
293     const char* startMethodSig;
294     const char* enterMethodName;
295     const char* enterMethodSig;
296     const char* emitEventMethodName;
297     const char* emitEventMethodSig;
298     const char* restartWithMethodName;
299     const char* restartWithMethodSig;
300 };
301 
302 #ifdef KOALA_JNI
303 const AppInfo javaAppInfo = {
304     "org/koalaui/arkoala/Application",
305     "createApplication",
306     "(Ljava/lang/String;Ljava/lang/String;)Lorg/koalaui/arkoala/Application;",
307     "start",
308     "()J",
309     "enter",
310     "(IIJ)Z",
311     "emitEvent",
312     "(IIII)Ljava/lang/String;",
313 };
314 #endif
315 
316 #ifdef KOALA_ETS_NAPI
317 const AppInfo pandaAppInfo = {
318     "@koalaui/arkts-arkui/Application/Application",
319     "createApplication",
320     "Lstd/core/String;Lstd/core/String;Z:L@koalaui/arkts-arkui/Application/Application;",
321     "start",
322     ":J",
323     "enter",
324     "IIJ:Z",
325     "emitEvent",
326     "IIII:Lstd/core/String;",
327 };
328 const AppInfo harnessAppInfo = {
329     "@koalaui/ets-harness/src/EtsHarnessApplication/EtsHarnessApplication",
330     "createApplication",
331     "Lstd/core/String;Lstd/core/String;Z:L@koalaui/ets-harness/src/EtsHarnessApplication/EtsHarnessApplication;",
332     "start",
333     ":J",
334     "enter",
335     "II:Z",
336     "emitEvent",
337     "IIII:Lstd/core/String;",
338     "restartWith",
339     "Lstd/core/String;:V"
340 };
341 #endif
342 
StartApplication(const char * appUrl,const char * appParams)343 extern "C" DLL_EXPORT KNativePointer StartApplication(const char* appUrl, const char* appParams) {
344     const auto isTestEnv = std::string(appUrl) == "EtsHarness";
345     const AppInfo* appInfo =
346         #ifdef KOALA_JNI
347         (g_vmEntry.vmKind == JAVA_VM_KIND) ? &javaAppInfo :
348         #endif
349         #ifdef KOALA_ETS_NAPI
350         (g_vmEntry.vmKind == PANDA_VM_KIND) ? isTestEnv ? &harnessAppInfo : &pandaAppInfo :
351         #endif
352         nullptr;
353 
354     if (!appInfo) {
355         LOGE("No appInfo provided for VM kind %" LOG_PUBLIC "d (recompile vmloader.cc with the missing flags)\n", g_vmEntry.vmKind);
356         return nullptr;
357     }
358 
359     LOGI("Starting application %" LOG_PUBLIC "s with params %" LOG_PUBLIC "s", appUrl, appParams);
360 
361 #ifdef KOALA_JNI
362     if (g_vmEntry.vmKind == JAVA_VM_KIND) {
363         JNIEnv* jEnv = (JNIEnv*)(g_vmEntry.env);
364         jclass appClass = jEnv->FindClass(appInfo->className);
365         if (!appClass) {
366             LOGE("Cannot load main class %s\n", appInfo->className);
367             return nullptr;
368         }
369         jmethodID create = jEnv->GetStaticMethodID(appClass, appInfo->createMethodName, appInfo->createMethodSig);
370         if (!create) {
371             LOGE("Cannot find create method %s\n", appInfo->createMethodName);
372             return nullptr;
373         }
374         auto app = jEnv->NewGlobalRef(jEnv->CallStaticObjectMethod(appClass, create, jEnv->NewStringUTF(appUrl), jEnv->NewStringUTF(appParams)));
375         g_vmEntry.app = app;
376         auto start = jEnv->GetMethodID(appClass, appInfo->startMethodName, appInfo->startMethodSig);
377         if (!start) {
378             LOGE("Cannot find start method \"%s %s\"\n", appInfo->startMethodName, appInfo->startMethodSig);
379             return nullptr;
380         }
381         g_vmEntry.enter = (void*)(jEnv->GetMethodID(appClass, appInfo->enterMethodName, appInfo->enterMethodSig));
382         if (!g_vmEntry.enter) {
383             LOGE("Cannot find enter method %s\n", appInfo->enterMethodName);
384             return nullptr;
385         }
386         g_vmEntry.emitEvent = (void*)(jEnv->GetMethodID(appClass, appInfo->emitEventMethodName, appInfo->emitEventMethodSig));
387         if (!g_vmEntry.emitEvent) {
388             LOGE("Cannot find emitEvent method %s\n", appInfo->emitEventMethodName);
389             return nullptr;
390         }
391         return reinterpret_cast<KNativePointer>(jEnv->CallLongMethod(
392             app, start));
393     }
394 #endif
395 #ifdef KOALA_ETS_NAPI
396     if (g_vmEntry.vmKind == PANDA_VM_KIND) {
397         EtsEnv* etsEnv = (EtsEnv*)g_vmEntry.env;
398         ets_class appClass = etsEnv->FindClass(appInfo->className);
399         if (!appClass) {
400             LOGE("Cannot load main class %" LOG_PUBLIC "s\n", appInfo->className);
401             return nullptr;
402         }
403         ets_method create = etsEnv->GetStaticp_method(appClass, appInfo->createMethodName, appInfo->createMethodSig);
404         if (!create) {
405             LOGE("Cannot find create method %" LOG_PUBLIC "s\n", appInfo->createMethodName);
406             if (etsEnv->ErrorCheck()) {
407                 etsEnv->ErrorDescribe();
408                 etsEnv->ErrorClear();
409             }
410             return nullptr;
411         }
412 #if defined (KOALA_OHOS_ARM64)
413         auto useNativeLog = true;
414 #else
415         auto useNativeLog = false;
416 #endif
417         auto app = etsEnv->NewGlobalRef(etsEnv->CallStaticObjectMethod(
418             appClass, create,
419             etsEnv->NewStringUTF(appUrl), etsEnv->NewStringUTF(appParams),
420             useNativeLog
421             ));
422         if (!app) {
423             LOGE("createApplication returned null");
424             if (etsEnv->ErrorCheck()) {
425                 etsEnv->ErrorDescribe();
426                 etsEnv->ErrorClear();
427             }
428             return nullptr;
429         }
430         g_vmEntry.app = (void*)app;
431         auto start = etsEnv->Getp_method(appClass, appInfo->startMethodName, appInfo->startMethodSig);
432         g_vmEntry.enter = (void*)(etsEnv->Getp_method(appClass, appInfo->enterMethodName, nullptr /*appInfo->enterMethodSig */));
433         if (!g_vmEntry.enter) {
434             LOGE("Cannot find enter method %" LOG_PUBLIC "s", appInfo->enterMethodName);
435             if (etsEnv->ErrorCheck()) {
436                 etsEnv->ErrorDescribe();
437                 etsEnv->ErrorClear();
438             }
439             return nullptr;
440         }
441         g_vmEntry.emitEvent = (void*)(etsEnv->Getp_method(appClass, appInfo->emitEventMethodName, appInfo->emitEventMethodSig));
442         if (!g_vmEntry.emitEvent) {
443             LOGE("Cannot find enter emitEvent %" LOG_PUBLIC "s", appInfo->emitEventMethodSig);
444             if (etsEnv->ErrorCheck()) {
445                 etsEnv->ErrorDescribe();
446                 etsEnv->ErrorClear();
447             }
448             return nullptr;
449         }
450         if (isTestEnv) {
451             g_vmEntry.restartWith = (void*)(etsEnv->Getp_method(appClass, appInfo->restartWithMethodName, appInfo->restartWithMethodSig));
452             if (!g_vmEntry.restartWith) {
453                 LOGE("Cannot find enter restartWith %" LOG_PUBLIC "s", appInfo->restartWithMethodSig);
454                 if (etsEnv->ErrorCheck()) {
455                     etsEnv->ErrorDescribe();
456                     etsEnv->ErrorClear();
457                 }
458                 return nullptr;
459             }
460         }
461         // TODO: pass app entry point!
462         return reinterpret_cast<KNativePointer>(etsEnv->CallLongMethod((ets_object)(app), start));
463     }
464 #endif
465     return nullptr;
466 }
467 
RunApplication(const KInt arg0,const KInt arg1)468 extern "C" DLL_EXPORT KBoolean RunApplication(const KInt arg0, const KInt arg1) {
469 #ifdef KOALA_JNI
470     if (g_vmEntry.vmKind == JAVA_VM_KIND) {
471         JNIEnv* jEnv = (JNIEnv*)(g_vmEntry.env);
472         auto result = jEnv->CallBooleanMethod(
473             (jobject)(g_vmEntry.app),
474             (jmethodID)(g_vmEntry.enter),
475             (jint)arg0,
476             (jint)arg1,
477             (int64_t)(intptr_t)(&g_vmEntry.foreignVMContext)
478         );
479         if (jEnv->ExceptionCheck()) {
480             jEnv->ExceptionDescribe();
481             jEnv->ExceptionClear();
482         }
483         return result;
484     }
485 #endif
486 #ifdef KOALA_ETS_NAPI
487     if (g_vmEntry.vmKind == PANDA_VM_KIND) {
488         EtsEnv* etsEnv = (EtsEnv*)(g_vmEntry.env);
489         if (!g_vmEntry.enter) {
490             LOGE("Cannot find enter method");
491             return -1;
492         }
493         auto result = etsEnv->CallBooleanMethod(
494             (ets_object)(g_vmEntry.app),
495             (ets_method)(g_vmEntry.enter),
496             (ets_int)arg0,
497             (ets_int)arg1,
498             (int64_t)(intptr_t)(&g_vmEntry.foreignVMContext)
499         );
500         if (etsEnv->ErrorCheck()) {
501             LOGE("Calling enter() method gave an error");
502             etsEnv->ErrorDescribe();
503             etsEnv->ErrorClear();
504         }
505         return result;
506     }
507     #endif
508     return 1;
509 }
510 
EmitEvent(const KInt type,const KInt target,const KInt arg0,const KInt arg1)511 extern "C" DLL_EXPORT const char* EmitEvent(const KInt type, const KInt target, const KInt arg0, const KInt arg1) {
512 #ifdef KOALA_JNI
513     if (g_vmEntry.vmKind == JAVA_VM_KIND) {
514         JNIEnv* jEnv = (JNIEnv*)(g_vmEntry.env);
515         if (!g_vmEntry.emitEvent) {
516             LOGE("Cannot find emitEvent method");
517             return "-1";
518         }
519         auto rv = (jstring)jEnv->CallObjectMethod(
520             (jobject)(g_vmEntry.app),
521             (jmethodID)(g_vmEntry.emitEvent),
522             (jint)type,
523             (jint)target,
524             (jint)arg0,
525             (jint)arg1
526         );
527         if (jEnv->ExceptionCheck()) {
528             jEnv->ExceptionDescribe();
529             jEnv->ExceptionClear();
530         }
531         const char *result = jEnv->GetStringUTFChars(rv, 0);
532         return result;
533     }
534 #endif
535 #ifdef KOALA_ETS_NAPI
536     if (g_vmEntry.vmKind == PANDA_VM_KIND) {
537         EtsEnv* etsEnv = (EtsEnv*)(g_vmEntry.env);
538         if (!g_vmEntry.emitEvent) {
539             LOGE("Cannot find emitEvent method");
540             return "-1";
541         }
542         auto rv = (ets_string)etsEnv->CallObjectMethod(
543             (ets_object)(g_vmEntry.app),
544             (ets_method)(g_vmEntry.emitEvent),
545             (ets_int)type,
546             (ets_int)target,
547             (ets_int)arg0,
548             (ets_int)arg1
549         );
550         if (etsEnv->ErrorCheck()) {
551             LOGE("Calling emitEvent() method gave an error");
552             etsEnv->ErrorDescribe();
553             etsEnv->ErrorClear();
554         }
555         const char *result = etsEnv->GetStringUTFChars(rv, 0);
556         return result;
557     }
558     #endif
559     return "-1";
560 }
561 
RestartWith(const char * page)562 extern "C" DLL_EXPORT void RestartWith(const char* page) {
563 #ifdef KOALA_JNI
564     if (g_vmEntry.vmKind == JAVA_VM_KIND) {
565         JNIEnv* jEnv = (JNIEnv*)(g_vmEntry.env);
566         if (!g_vmEntry.restartWith) {
567             LOGE("Cannot find restartWith method");
568             return;
569         }
570         jEnv->CallVoidMethod(
571             (jobject)(g_vmEntry.app),
572             (jmethodID)(g_vmEntry.restartWith),
573             jEnv->NewStringUTF(page)
574         );
575         if (jEnv->ExceptionCheck()) {
576             jEnv->ExceptionDescribe();
577             jEnv->ExceptionClear();
578         }
579     }
580 #endif
581 #ifdef KOALA_ETS_NAPI
582     if (g_vmEntry.vmKind == PANDA_VM_KIND) {
583         EtsEnv* etsEnv = (EtsEnv*)(g_vmEntry.env);
584         if (!g_vmEntry.restartWith) {
585             LOGE("Cannot find restartWith method");
586             return;
587         }
588         etsEnv->CallVoidMethod(
589             (ets_object)(g_vmEntry.app),
590             (ets_method)(g_vmEntry.restartWith),
591             etsEnv->NewStringUTF(page)
592         );
593         if (etsEnv->ErrorCheck()) {
594             LOGE("Calling restartWith() method gave an error");
595             etsEnv->ErrorDescribe();
596             etsEnv->ErrorClear();
597         }
598     }
599     #endif
600 }
601 
traverseDir(std::string root,std::vector<std::string> & paths,int depth)602 void traverseDir(std::string root, std::vector<std::string>& paths, int depth) {
603     if (depth >= 50) {
604         return;
605     }
606 #if defined(KOALA_LINUX) || defined(KOALA_MACOS) || defined(KOALA_OHOS)
607     std::string suffix = ".abc";
608     #if defined(KOALA_OHOS)
609     suffix += ".so";
610     #endif
611     DIR* directory = opendir(root.c_str());
612     if (!directory) {
613         LOGE("Cannot open dir %" LOG_PUBLIC "s\n", root.c_str());
614         return;
615     }
616     struct dirent* ent = NULL;
617     struct stat statbuf;
618 
619     LOGI("Searching for *%" LOG_PUBLIC "s in %" LOG_PUBLIC "s\n", suffix.c_str(), root.c_str());
620     while ((ent = readdir(directory)) != nullptr) {
621         std::string filename = std::string(ent->d_name);
622         if (filename == "." || filename == "..") {
623             continue;
624         }
625         std::string filepath = root + "/" + filename;
626         int rv = stat(filepath.c_str(), &statbuf);
627         if (rv < 0) continue;
628         if (filepath.size() >= suffix.size() && filepath.substr(filepath.size() - suffix.size()) == suffix && (statbuf.st_mode & S_IFMT) == S_IFREG) {
629             paths.push_back(filepath);
630         }
631         if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
632             traverseDir(filepath, paths, depth + 1);
633         }
634     }
635     closedir(directory);
636 #endif
637 }
638