From 05adbdfb851d83a2f7e86c01991c43f2db6c7bc4 Mon Sep 17 00:00:00 2001 From: Semenov Aleksandr Date: Fri, 21 Mar 2025 23:45:18 +0300 Subject: [PATCH 1/2] [NFC] vmloader: move to standalone library build file Signed-off-by: Semenov Aleksandr --- arkoala/framework/native/meson.build | 45 +------------- arkoala/framework/native/meson_options.txt | 4 +- arkoala/framework/package.json | 10 +-- arkoala/framework/scripts/configure.mjs | 4 +- interop/src/cpp/vmloader/meson.build | 71 ++++++++++++++++++++++ interop/src/cpp/vmloader/meson_options.txt | 6 ++ interop/src/cpp/{ => vmloader}/vmloader.cc | 0 7 files changed, 87 insertions(+), 53 deletions(-) create mode 100644 interop/src/cpp/vmloader/meson.build create mode 100644 interop/src/cpp/vmloader/meson_options.txt rename interop/src/cpp/{ => vmloader}/vmloader.cc (100%) diff --git a/arkoala/framework/native/meson.build b/arkoala/framework/native/meson.build index ee0fdac7c..7144b8277 100644 --- a/arkoala/framework/native/meson.build +++ b/arkoala/framework/native/meson.build @@ -321,50 +321,7 @@ shared_library(library_use_name, ) endif -if get_option('vmloader') == true - - vmloader_cflags = ['-DKOALA_' + os.to_upper(), '-DKOALA_' + os.to_upper() + '_' + arch.to_upper()] - vmloader_ldflags = [] - - if is_ohos - vmloader_ldflags += ['-lhilog_ndk.z'] - endif - - if get_option('vmloader_apis').contains('jni') - jni_os_dir = os - if jni_os_dir == 'windows' - jni_os_dir = 'win32' - endif - include_dirs += [ - get_option('jdk_dir') / 'include', - get_option('jdk_dir') / 'include' / jni_os_dir, - ] - vmloader_cflags += [ - '-DKOALA_JNI', - ] - endif - - if get_option('vmloader_apis').contains('ets') - include_dirs += [ - interop_src / 'ets', - ] - vmloader_cflags += [ - '-DKOALA_ETS_NAPI', - ] - endif - - shared_library('vmloader', - interop_src / 'vmloader.cc', - override_options: [ - 'b_lundef=false', - ], - include_directories: include_dirs, - install: true, - install_dir: source_dir / '..' / 'build', - cpp_args: vmloader_cflags, - link_args: vmloader_ldflags, - ) - +if get_option('ArkoalaLoader') == true loader_sources = [ './src/generated/library.cc', './src/vsync.cc', diff --git a/arkoala/framework/native/meson_options.txt b/arkoala/framework/native/meson_options.txt index c3377eaee..25bb4f7b6 100644 --- a/arkoala/framework/native/meson_options.txt +++ b/arkoala/framework/native/meson_options.txt @@ -4,7 +4,5 @@ option('jdk_dir', type : 'string', value : '', description : 'A path to JDK root') option('arkoala', type : 'boolean', value : true, description : 'Whether to add arkoala to sources') -option('vmloader', type : 'boolean', value : false, +option('ArkoalaLoader', type : 'boolean', value : false, description : 'Whether to build libvmloader.so') -option('vmloader_apis', type : 'string', value : 'ets', - description : 'APIs to use in libvmloader.so') diff --git a/arkoala/framework/package.json b/arkoala/framework/package.json index e81afb154..a77943eeb 100644 --- a/arkoala/framework/package.json +++ b/arkoala/framework/package.json @@ -46,8 +46,10 @@ "configure:native-panda-macos-arm64": "cd native && node ../scripts/configure.mjs panda-macos-arm64", "compile:native-panda-macos-arm64": "npm run configure:native-panda-macos-arm64 && cd native && meson compile -C build-panda-macos-arm64 && meson install -C build-panda-macos-arm64", - "configure:native-jvm-host": "cd native && meson setup -D vm_kind=jvm build-jvm-host -D vmloader=true -D jdk_dir=$JAVA_HOME", - "compile:native-jvm-host": "npm run configure:native-jvm-host && cd native && meson compile -C build-jvm-host && meson install -C build-jvm-host", + "configure:native-vmloader-host": "meson setup native/build-vmloader-host ../../interop/src/cpp/vmloader", + "compile:native-vmloader-host": "npm run configure:native-vmloader-host && cd native && meson compile -C build-vmloader-host && meson install -C build-vmloader-host", + "configure:native-jvm-host": "npm run configure:native-vmloader-host && cd native && meson setup -D vm_kind=jvm build-jvm-host -D ArkoalaLoader=true -D jdk_dir=$JAVA_HOME", + "compile:native-jvm-host": "npm run compile:native-vmloader-host && npm run configure:native-jvm-host && cd native && meson compile -C build-jvm-host && meson install -C build-jvm-host", "configure:native-hzvm-ohos-arm64": "npm run prepare:arm64 && cd native && node ../scripts/configure.mjs hzvm-ohos-arm64", "compile:native-hzvm-ohos-arm64": "npm run configure:native-hzvm-ohos-arm64 && cd native && meson compile -C build-hzvm-ohos-arm64 && meson install -C build-hzvm-ohos-arm64", "configure:native-hzvm-ohos-arm32": "npm run prepare:arm32 && cd native && node ../scripts/configure.mjs hzvm-ohos-arm32", @@ -58,8 +60,8 @@ "compile:native-panda-ohos-arm64": "npm run configure:native-panda-ohos-arm64 && cd native && meson compile -C build-panda-ohos-arm64 && meson install -C build-panda-ohos-arm64", "configure:native-panda-ohos-arm32": "npm run prepare:arm32 && cd native && node ../scripts/configure.mjs panda-ohos-arm32", "compile:native-panda-ohos-arm32": "npm run configure:native-panda-ohos-arm32 && cd native && meson compile -C build-panda-ohos-arm32 && meson install -C build-panda-ohos-arm32", - "configure:native-panda-with-node-host": "npm run configure:native-panda-host && cd native && meson setup -D vm_kind=node -D vmloader=true -D arkoala=false build-node-host-vmloader", - "compile:native-panda-with-node-host": "npm run configure:native-panda-with-node-host && cd native && npm run compile:native-panda-host && npm run compile:native-panda-host && meson compile -C build-node-host-vmloader && meson install -C build-node-host-vmloader", + "configure:native-panda-with-node-host": "npm run configure:native-vmloader-host && npm run configure:native-panda-host && cd native && meson setup -D vm_kind=node -D ArkoalaLoader=true -D arkoala=false build-node-host-vmloader", + "compile:native-panda-with-node-host": "npm run compile:native-vmloader-host && npm run configure:native-panda-with-node-host && cd native && npm run compile:native-panda-host && npm run compile:native-panda-host && meson compile -C build-node-host-vmloader && meson install -C build-node-host-vmloader", "configure:native-panda-with-hzvm-ohos-arm64": "npm run configure:native-panda-ohos-arm64 && cd native && node ../scripts/configure.mjs hzvm-ohos-arm64-vmloader", "compile:native-panda-with-hzvm-ohos-arm64": "npm run configure:native-panda-with-hzvm-ohos-arm64 && npm run compile:native-panda-ohos-arm64 && cd native && meson compile -C build-hzvm-ohos-arm64-vmloader && meson install -C build-hzvm-ohos-arm64-vmloader && npm run copy:libcpp:arm64 --prefix ../../tools/compiler", "configure:native-panda-with-hzvm-ohos-arm32": "npm run configure:native-panda-ohos-arm32 && cd native && node ../scripts/configure.mjs hzvm-ohos-arm32-vmloader", diff --git a/arkoala/framework/scripts/configure.mjs b/arkoala/framework/scripts/configure.mjs index 90deca460..74018824d 100644 --- a/arkoala/framework/scripts/configure.mjs +++ b/arkoala/framework/scripts/configure.mjs @@ -108,9 +108,9 @@ export function configure(target) { vsenv, options: { "vm_kind": vmKind || undefined, - "arkoala": !target.includes('vmloader'), + "arkoala": !target.includes('arkoala'), "jdk_dir": isJvm ? getJdkRoot() : null, - "vmloader": target.includes('vmloader'), + "ArkoalaLoader": target.includes('ArkoalaLoader'), } }); verbose && console.log(); diff --git a/interop/src/cpp/vmloader/meson.build b/interop/src/cpp/vmloader/meson.build new file mode 100644 index 000000000..a52ef2931 --- /dev/null +++ b/interop/src/cpp/vmloader/meson.build @@ -0,0 +1,71 @@ +project('ArkoalaNativeVmloader', 'c', 'cpp', + version: '0.1', + default_options: ['cpp_std=c++17', 'buildtype=release'] +) + +oses = { 'darwin': 'macos' } # rename meson default names to convenient ones +archs = { 'x86_64': 'x64', 'aarch64': 'arm64', 'armv7-a': 'arm32', 'wasm32': 'wasm' } + +fs = import('fs') + +os = target_machine.system() +os = oses.get(os, os) +arch = target_machine.cpu() +arch = archs.get(arch, arch) + +include_dirs = [ '..', '..' / 'types' ] + +vmloader_cflags = ['-DKOALA_' + os.to_upper(), '-DKOALA_' + os.to_upper() + '_' + arch.to_upper()] +vmloader_ldflags = [] + +if os == 'ohos' + vmloader_ldflags += ['-lhilog_ndk.z'] +endif + +if os != 'windows' + vmloader_ldflags += ['-ldl'] +endif + +if get_option('vmloader_apis').contains('jni') + jni_os_dir = os + if jni_os_dir == 'windows' + jni_os_dir = 'win32' + endif + include_dirs += [ + get_option('jdk_dir') / 'include', + get_option('jdk_dir') / 'include' / jni_os_dir, + ] + vmloader_cflags += [ + '-DKOALA_JNI', + ] +endif + +if get_option('vmloader_apis').contains('ets') + include_dirs += [ + '..' / 'ets', + ] + vmloader_cflags += [ + '-DKOALA_ETS_NAPI', + ] +endif + +sources_vmloader = ['vmloader.cc'] + +# hack, need to refactor install scripts +framework_native_build_dir = meson.current_source_dir()/'..'/'..'/'..'/'..'/'arkoala'/'framework'/'build' + +if get_option('arkgen_install_path') + framework_native_build_dir = meson.current_source_dir()/'..'/'..'/'..'/'..'/'..'/'arkgen'/'native' +endif + +shared_library('vmloader', + sources_vmloader, + override_options: [ + 'b_lundef=false', + ], + include_directories: include_dirs, + install: true, + install_dir: framework_native_build_dir, + cpp_args: vmloader_cflags, + link_args: vmloader_ldflags, +) diff --git a/interop/src/cpp/vmloader/meson_options.txt b/interop/src/cpp/vmloader/meson_options.txt new file mode 100644 index 000000000..7ebfe8f19 --- /dev/null +++ b/interop/src/cpp/vmloader/meson_options.txt @@ -0,0 +1,6 @@ +option('jdk_dir', type : 'string', value : '', + description : 'A path to JDK root') +option('vmloader_apis', type : 'string', value : 'ets', + description : 'APIs to use in libvmloader.so') +option('arkgen_install_path', type : 'boolean', value : 'false', + description : 'Install so-file to idlize_arkgen') diff --git a/interop/src/cpp/vmloader.cc b/interop/src/cpp/vmloader/vmloader.cc similarity index 100% rename from interop/src/cpp/vmloader.cc rename to interop/src/cpp/vmloader/vmloader.cc -- Gitee From a1dee6d72174ab1ae83f106d1d51f01007969d26 Mon Sep 17 00:00:00 2001 From: Semenov Aleksandr Date: Sat, 22 Mar 2025 17:06:17 +0300 Subject: [PATCH 2/2] vmloader: add ani version of basic api and enable by default - vmloader: split vmloader to vm specific parts Co-authored-by: Denis Slynko Signed-off-by: Semenov Aleksandr --- arkoala-arkts/loader/webpack.config.node.js | 2 +- interop/src/cpp/ani/ani.h | 12 + .../cpp/vmloader/ani/vmloader-helpers-ani.cc | 408 ++++++++++ .../cpp/vmloader/ets/vmloader-helpers-ets.cc | 352 +++++++++ .../cpp/vmloader/jni/vmloader-helpers-jni.cc | 167 ++++ interop/src/cpp/vmloader/meson.build | 29 +- interop/src/cpp/vmloader/meson_options.txt | 2 +- interop/src/cpp/vmloader/vmloader.cc | 719 +++--------------- interop/src/cpp/vmloader/vmloader.h | 78 ++ 9 files changed, 1142 insertions(+), 627 deletions(-) create mode 100644 interop/src/cpp/vmloader/ani/vmloader-helpers-ani.cc create mode 100644 interop/src/cpp/vmloader/ets/vmloader-helpers-ets.cc create mode 100644 interop/src/cpp/vmloader/jni/vmloader-helpers-jni.cc create mode 100644 interop/src/cpp/vmloader/vmloader.h diff --git a/arkoala-arkts/loader/webpack.config.node.js b/arkoala-arkts/loader/webpack.config.node.js index 84752a3a9..9593ad727 100644 --- a/arkoala-arkts/loader/webpack.config.node.js +++ b/arkoala-arkts/loader/webpack.config.node.js @@ -75,7 +75,7 @@ function copyPluginPatterns(os, arch, isAni) { to: "." }) patterns.push({ - from: path.resolve(`../../arkoala/framework/native/build-node-host-vmloader/libvmloader.${ getExt(os) }`), + from: path.resolve(`../../arkoala/framework/native/build-vmloader-host/libvmloader.${ getExt(os) }`), to: "." }) patterns.push({ diff --git a/interop/src/cpp/ani/ani.h b/interop/src/cpp/ani/ani.h index 68884a45f..18141f723 100644 --- a/interop/src/cpp/ani/ani.h +++ b/interop/src/cpp/ani/ani.h @@ -32,6 +32,18 @@ #define ANI_FALSE 0 #define ANI_TRUE 1 +// Logger interface: +// typedef void (*ani_logger)(FILE *stream, int log_level, const char *component, const char *message); +// ani_option: +// 'option': "--logger" +// 'extra': ani_logger +// where 'log_level' can have the following values: +#define ANI_LOGLEVEL_FATAL 0 +#define ANI_LOGLEVEL_ERROR 1 +#define ANI_LOGLEVEL_WARNING 2 +#define ANI_LOGLEVEL_INFO 3 +#define ANI_LOGLEVEL_DEBUG 4 + typedef size_t ani_size; // Primitive types: diff --git a/interop/src/cpp/vmloader/ani/vmloader-helpers-ani.cc b/interop/src/cpp/vmloader/ani/vmloader-helpers-ani.cc new file mode 100644 index 000000000..c07a99cc1 --- /dev/null +++ b/interop/src/cpp/vmloader/ani/vmloader-helpers-ani.cc @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vmloader.h" + +#include "interop-logging.h" + +#include "ani.h" +#include +#include +#include + +// DO NOT USE KOALA INTEROP MECHANISMS IN THIS FILE! + +static constexpr auto OHOS_USER_LIBS = "/data/storage/el1/bundle/libs"; + +const VMLibInfo &getVMLibInfo() { + static const VMLibInfo aniVMLibInfo { +// sdkPath +#if defined(KOALA_OHOS) +#ifdef KOALA_OHOS_ARM32 + "/system/lib" +#elif KOALA_OHOS_ARM64 + "/system/lib64" +#else + OHOS_USER_LIBS +#endif +#else + getenv("PANDA_HOME") +#endif + , + +// platform +#ifdef KOALA_LINUX +#ifdef KOALA_LINUX_ARM64 + "linux_arm64_host_tools/lib" +#else + "linux_host_tools/lib" +#endif +#elif KOALA_MACOS + "macos_host_tools/lib" +#elif KOALA_WINDOWS + "_host_tools/lib" +#elif KOALA_OHOS_ARM64 + "arm64" +#elif KOALA_OHOS_ARM32 + "arm" +#else +#error "Unknown platform" +#endif + , + + // lib + "arkruntime", + + // createVM + "ANI_CreateVM" + }; + + return aniVMLibInfo; +} + +static void AniMobileLog([[maybe_unused]] FILE *stream, int level, + const char *component, const char *msg) { + switch (level) { + case ANI_LOGLEVEL_INFO: + case ANI_LOGLEVEL_DEBUG: + LOGI("ArkRuntime [%" LOG_PUBLIC "s]: %" LOG_PUBLIC "s", component, msg); + break; + case ANI_LOGLEVEL_FATAL: + case ANI_LOGLEVEL_ERROR: + case ANI_LOGLEVEL_WARNING: + default: + LOGE("ArkRuntime [%" LOG_PUBLIC "s]: %" LOG_PUBLIC "s", component, msg); + break; + } +} + +static std::pair +GetBootAndAppPandaFiles(const char *appClassPath) { + std::stringstream bootPandaFilesStream; +#if USE_SYSTEM_ARKVM + bootPandaFilesStream << SYSTEM_ARK_STDLIB_PATH; +#elif defined(KOALA_OHOS) + bootPandaFilesStream << OHOS_USER_LIBS << "/etsstdlib.abc"; +#elif defined(KOALA_LINUX) || defined(KOALA_MACOS) || defined(KOALA_WINDOWS) + bootPandaFilesStream << getVMLibInfo().sdkPath << "/ets/etsstdlib.abc"; +#endif + + std::vector files; + traverseDir(appClassPath, files); + std::sort(files.begin(), files.end()); + + std::stringstream appFilesStream; + for (size_t idx = 0, end = files.size(); idx < end; ++idx) { + if (idx > 0) { + appFilesStream << ':'; + } + appFilesStream << files[idx]; + } + auto appFiles = appFilesStream.str(); + + return {bootPandaFilesStream.str(), appFilesStream.str()}; +} + +static bool ResetErrorIfExists(ani_env *env) { + ani_boolean hasError = ANI_FALSE; + env->ExistUnhandledError(&hasError); + if (hasError == ANI_TRUE) { + env->DescribeError(); + env->ResetError(); + return true; + } + return false; +} + +using createVM_t = int (*)(const void *args, uint32_t version, void **pVM); +using getVMs_t = int (*)(void **pVM, int32_t bufLen, int32_t *nVMs); + +int loaderCreateVM(void **vm, void **env, const char *appClassPath, + const char *appLibPath) { + + auto getVMs = + reinterpret_cast(loaderFindSymbol("ANI_GetCreatedVMs")); + + if (!getVMs) { + LOGE("Cannot find %" LOG_PUBLIC "s\n", "ANI_GetCreatedVMs"); + return -1; + } + + uint32_t version = ANI_VERSION_1; + int32_t nVMs = 0; + int result = getVMs(vm, 1, &nVMs); + if (nVMs == 0 && result == 0) { + std::vector pandaVMOptions; + + auto [bootFiles, appFiles] = GetBootAndAppPandaFiles(appClassPath); + LOGE("classpath \"%s\" from %s", appFiles.c_str(), appClassPath); + std::string delimiter = ""; + if (!bootFiles.empty() && !appFiles.empty()) { + delimiter = ":"; + } + std::string bootPandaFiles = + "--ext:--boot-panda-files=" + bootFiles + delimiter + appFiles; + LOGE("ANI boot-panda-files option: \"%s\"", bootPandaFiles.c_str()); + pandaVMOptions.push_back({bootPandaFiles.c_str(), nullptr}); + + pandaVMOptions.push_back({"--ext:--gc-trigger-type=heap-trigger", nullptr}); + std::string nativeLibraryPathOption = + std::string("--ext:--native-library-path=") + appLibPath; + pandaVMOptions.push_back({nativeLibraryPathOption.c_str(), nullptr}); + pandaVMOptions.push_back({"--ext:--verification-mode=on-the-fly", nullptr}); + pandaVMOptions.push_back({"--ext:--compiler-enable-jit=false", nullptr}); + pandaVMOptions.push_back( + {"--logger", reinterpret_cast(AniMobileLog)}); + pandaVMOptions.push_back({"--ext:--enable-an", nullptr}); + ani_options optionsPtr = {pandaVMOptions.size(), pandaVMOptions.data()}; + + auto createVM = + reinterpret_cast(loaderFindSymbol(getVMLibInfo().createVM)); + + if (!createVM) { + LOGE("Cannot find %" LOG_PUBLIC "s\n", getVMLibInfo().createVM); + return -1; + } + + result = createVM(&optionsPtr, version, vm); + } + + if (result == 0) { + ani_vm *vmInstance = reinterpret_cast(*vm); + ani_env *pEnv = nullptr; + result = vmInstance->GetEnv(version, &pEnv); + *env = static_cast(pEnv); + } + + return result; +} + +static const AppInfo harnessAppInfo = { + "L@koalaui/ets-harness/src/EtsHarnessApplication/EtsHarnessApplication;", + "createApplication", + "Lstd/core/String;Lstd/core/String;ZI:L@koalaui/ets-harness/src/" + "EtsHarnessApplication/EtsHarnessApplication;", + "start", + "J:J", + "enter", + "IIJ:Z", + "emitEvent", + "IIII:Lstd/core/String;", + "restartWith", + "Lstd/core/String;:V"}; + +static const AppInfo pandaAppInfo = { + "L@koalaui/arkts-arkui/Application/Application;", + "createApplication", + "Lstd/core/String;Lstd/core/String;ZI:L@koalaui/arkts-arkui/Application/" + "Application;", + "start", + "J:J", + "enter", + "IIJ:Z", + "emitEvent", + "IIII:Lstd/core/String;", +}; + +KNativePointer loaderStartApplication(const char *appUrl, const char *appParams, + VMEntry &vmEntry) { + const auto isTestEnv = std::string(appUrl) == "EtsHarness"; + const AppInfo &appInfo = isTestEnv ? harnessAppInfo : pandaAppInfo; + + auto *env = reinterpret_cast(vmEntry.env); + + ani_class appClass{}; + auto status = env->FindClass(appInfo.className, &appClass); + if (status != ANI_OK) { + LOGE("Cannot load main class %" LOG_PUBLIC "s\n", appInfo.className); + ResetErrorIfExists(env); + return nullptr; + } + ani_static_method create{}; + status = env->Class_FindStaticMethod(appClass, appInfo.createMethodName, + appInfo.createMethodSig, &create); + if (status != ANI_OK) { + LOGE("Cannot find create method %" LOG_PUBLIC "s\n", + appInfo.createMethodName); + ResetErrorIfExists(env); + return nullptr; + } + +#if defined(KOALA_OHOS_ARM64) + ani_boolean useNativeLog = ANI_TRUE; +#else + ani_boolean useNativeLog = ANI_FALSE; +#endif + ani_string appUrlString{}; + status = env->String_NewUTF8(appUrl, strlen(appUrl), &appUrlString); + if (status != ANI_OK) { + ResetErrorIfExists(env); + return nullptr; + } + ani_string appParamsString{}; + status = env->String_NewUTF8(appParams, strlen(appParams), &appParamsString); + if (status != ANI_OK) { + ResetErrorIfExists(env); + return nullptr; + } + + ani_ref appInstance{}; + status = env->Class_CallStaticMethod_Ref( + appClass, create, &appInstance, appUrlString, appParamsString, + useNativeLog, static_cast(vmEntry.vmKind)); + if (status != ANI_OK) { + LOGE("createApplication returned null"); + ResetErrorIfExists(env); + return nullptr; + } + ani_ref app{}; + status = env->GlobalReference_Create(appInstance, &app); + if (status != ANI_OK) { + ResetErrorIfExists(env); + return nullptr; + } + vmEntry.app = (void *)app; + + ani_method start{}; + status = env->Class_FindMethod(appClass, appInfo.startMethodName, + appInfo.startMethodSig, &start); + if (status != ANI_OK) { + ResetErrorIfExists(env); + return nullptr; + } + ani_method enter{}; + status = + env->Class_FindMethod(appClass, appInfo.enterMethodName, nullptr, &enter); + if (status != ANI_OK) { + LOGE("Cannot find `enter` method %" LOG_PUBLIC "s", + appInfo.enterMethodName); + ResetErrorIfExists(env); + return nullptr; + } + vmEntry.enter = reinterpret_cast(enter); + ani_method emitEvent{}; + status = env->Class_FindMethod(appClass, appInfo.emitEventMethodName, + appInfo.emitEventMethodSig, &emitEvent); + if (status != ANI_OK) { + LOGE("Cannot find `emitEvent` method %" LOG_PUBLIC "s", + appInfo.emitEventMethodSig); + ResetErrorIfExists(env); + return nullptr; + } + vmEntry.emitEvent = reinterpret_cast(emitEvent); + + if (isTestEnv) { + ani_method restartWith{}; + status = env->Class_FindMethod(appClass, appInfo.restartWithMethodName, + appInfo.restartWithMethodSig, &restartWith); + if (status != ANI_OK) { + LOGE("Cannot find `restartWith` method %" LOG_PUBLIC "s", + appInfo.restartWithMethodSig); + ResetErrorIfExists(env); + return nullptr; + } + vmEntry.restartWith = reinterpret_cast(restartWith); + } + + ani_long ptr = 0; + // TODO: pass app entry point! + status = env->Object_CallMethod_Long( + static_cast(appInstance), start, &ptr, + reinterpret_cast(&vmEntry.foreignVMContext)); + if (status != ANI_OK) { + LOGE("Cannot start application"); + ResetErrorIfExists(env); + return nullptr; + } + return reinterpret_cast(ptr); +} + +KBoolean loaderRunApplication(const KInt arg0, const KInt arg1, + const VMEntry &vmEntry) { + ani_env *env = reinterpret_cast(vmEntry.env); + if (vmEntry.enter == nullptr) { + LOGE("Cannot find enter method"); + return -1; + } + ani_boolean result = ANI_FALSE; + auto status = env->Object_CallMethod_Boolean( + reinterpret_cast(vmEntry.app), + reinterpret_cast(vmEntry.enter), &result, + static_cast(arg0), static_cast(arg1), + reinterpret_cast(&vmEntry.foreignVMContext)); + if (status != ANI_OK) { + ResetErrorIfExists(env); + return ANI_FALSE; + } + return result; +} + +const char *loaderEmitEvent(const KInt type, const KInt target, const KInt arg0, + const KInt arg1, const VMEntry &vmEntry) { + ani_env *env = reinterpret_cast(vmEntry.env); + if (vmEntry.emitEvent == nullptr) { + LOGE("Cannot find emitEvent method"); + return "-1"; + } + ani_ref result{}; + auto status = env->Object_CallMethod_Ref( + reinterpret_cast(vmEntry.app), + reinterpret_cast(vmEntry.emitEvent), &result, + static_cast(type), static_cast(target), + static_cast(arg0), static_cast(arg1)); + if (status != ANI_OK) { + LOGE("Calling emitEvent() method gave an error"); + ResetErrorIfExists(env); + return "-1"; + } + + auto str = static_cast(result); + ani_size sz = 0; + status = env->String_GetUTF8Size(str, &sz); + if (status != ANI_OK) { + ResetErrorIfExists(env); + return "-1"; + } + auto buffer = new char[sz + 1]; + ani_size writtenChars = 0; + status = env->String_GetUTF8(str, buffer, sz + 1, &writtenChars); + if (status != ANI_OK || writtenChars != sz) { + delete[] buffer; + ResetErrorIfExists(env); + return "-1"; + } + return buffer; +} + +void loaderRestartWith(const char *page, const VMEntry &vmEntry) { + ani_env *env = reinterpret_cast(vmEntry.env); + if (vmEntry.restartWith != nullptr) { + LOGE("Cannot find restartWith method"); + return; + } + ani_string pageString{}; + auto status = env->String_NewUTF8(page, strlen(page), &pageString); + if (status != ANI_OK) { + ResetErrorIfExists(env); + return; + } + status = env->Object_CallMethod_Void( + reinterpret_cast(vmEntry.app), + reinterpret_cast(vmEntry.restartWith), pageString); + if (status != ANI_OK) { + LOGE("Calling restartWith() method gave an error"); + ResetErrorIfExists(env); + } +} diff --git a/interop/src/cpp/vmloader/ets/vmloader-helpers-ets.cc b/interop/src/cpp/vmloader/ets/vmloader-helpers-ets.cc new file mode 100644 index 000000000..7bf214013 --- /dev/null +++ b/interop/src/cpp/vmloader/ets/vmloader-helpers-ets.cc @@ -0,0 +1,352 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vmloader.h" + +#include "interop-logging.h" + +#include "etsapi.h" +#include +#include + +// DO NOT USE KOALA INTEROP MECHANISMS IN THIS FILE! + +static constexpr auto OHOS_USER_LIBS = "/data/storage/el1/bundle/libs"; + +const VMLibInfo &getVMLibInfo() { + static const VMLibInfo etsVMLibInfo { +// sdkPath +#if defined(KOALA_OHOS) +#ifdef KOALA_OHOS_ARM32 + "/system/lib" +#elif KOALA_OHOS_ARM64 + "/system/lib64" +#else + OHOS_USER_LIBS +#endif +#else + getenv("PANDA_HOME") +#endif + , + +// platform +#ifdef KOALA_LINUX +#ifdef KOALA_LINUX_ARM64 + "linux_arm64_host_tools/lib" +#else + "linux_host_tools/lib" +#endif +#elif KOALA_MACOS + "macos_host_tools/lib" +#elif KOALA_WINDOWS + "_host_tools/lib" +#elif KOALA_OHOS_ARM64 + "arm64" +#elif KOALA_OHOS_ARM32 + "arm" +#else +#error "Unknown platform" +#endif + , + + // lib + "arkruntime", + + // createVM + "ETS_CreateVM" + }; + + return etsVMLibInfo; +} + +enum PandaLog2MobileLog : int { + UNKNOWN = 0, + DEFAULT, + VERBOSE, + DEBUG, + INFO, + WARN, + ERROR, + FATAL, + SILENT, +}; + +static int ArkMobileLog(int id, int level, const char *component, + const char *fmt, const char *msg) { + switch (level) { + case PandaLog2MobileLog::DEFAULT: + case PandaLog2MobileLog::VERBOSE: + case PandaLog2MobileLog::DEBUG: + case PandaLog2MobileLog::INFO: + case PandaLog2MobileLog::SILENT: + LOGI("ArkRuntime [%" LOG_PUBLIC "s]: %" LOG_PUBLIC "s", component, msg); + break; + case PandaLog2MobileLog::UNKNOWN: + case PandaLog2MobileLog::WARN: + case PandaLog2MobileLog::ERROR: + case PandaLog2MobileLog::FATAL: + default: + LOGE("ArkRuntime [%" LOG_PUBLIC "s]: %" LOG_PUBLIC "s", component, msg); + break; + } + return 0; +} + +using createVM_t = int (*)(void **pVM, void **pEnv, void *vmInitArgs); +using getVMs_t = int (*)(void **pVM, int32_t bufLen, int32_t *nVMs); + +int loaderCreateVM(void **vm, void **env, const char *appClassPath, + const char *appLibPath) { + EtsVMInitArgs pandaVMArgs; + pandaVMArgs.version = ETS_NAPI_VERSION_1_0; + std::vector etsVMOptions; + std::vector files; + traverseDir(appClassPath, files); + std::sort(files.begin(), files.end()); + etsVMOptions = { +#if USE_SYSTEM_ARKVM + {EtsOptionType::ETS_BOOT_FILE, SYSTEM_ARK_STDLIB_PATH}, +#elif defined(KOALA_OHOS) + {EtsOptionType::ETS_BOOT_FILE, + (std::string(OHOS_USER_LIBS) + "/" + "etsstdlib.abc").c_str()}, + +#elif defined(KOALA_LINUX) || defined(KOALA_MACOS) || defined(KOALA_WINDOWS) + {EtsOptionType::ETS_BOOT_FILE, + (char *)strdup( + (std::string(getVMLibInfo().sdkPath) + "/ets/etsstdlib.abc").c_str())}, +#endif + }; + std::string all_files; + for (const std::string &path : files) { + etsVMOptions.push_back( + {EtsOptionType::ETS_BOOT_FILE, (char *)strdup(path.c_str())}); + if (all_files.size() > 0) + all_files.append(":"); + all_files.append(path); + } + LOGE("classpath \"%s\" from %s", all_files.c_str(), appClassPath); + etsVMOptions.push_back({EtsOptionType::ETS_GC_TRIGGER_TYPE, "heap-trigger"}); + etsVMOptions.push_back({EtsOptionType::ETS_NATIVE_LIBRARY_PATH, + (char *)strdup(std::string(appLibPath).c_str())}); + etsVMOptions.push_back({EtsOptionType::ETS_VERIFICATION_MODE, "on-the-fly"}); + etsVMOptions.push_back({EtsOptionType::ETS_NO_JIT, nullptr}); + etsVMOptions.push_back({EtsOptionType::ETS_MOBILE_LOG, (void *)ArkMobileLog}); + etsVMOptions.push_back({EtsOptionType::ETS_AOT, nullptr}); + // etsVMOptions.push_back({EtsOptionType::ETS_LOG_LEVEL, "info"}); + pandaVMArgs.nOptions = etsVMOptions.size(); + pandaVMArgs.options = etsVMOptions.data(); + + auto getVMs = + reinterpret_cast(loaderFindSymbol("ETS_GetCreatedVMs")); + + if (getVMs) { + // "Cannot find ETS_GetCreatedVMs" : bug ? + int32_t nVMs = 0; + int result = getVMs(vm, 1, &nVMs); + if (result) { + return result; + } + + if (nVMs != 0) { + __EtsVM *vmInstance = reinterpret_cast<__EtsVM *>(*vm); + EtsEnv *pEnv = nullptr; + vmInstance->GetEnv(&pEnv, ETS_NAPI_VERSION_1_0); + *env = static_cast(pEnv); + return 0; + } + } + + auto createVM = + reinterpret_cast(loaderFindSymbol(getVMLibInfo().createVM)); + + if (!createVM) { + LOGE("Cannot find %" LOG_PUBLIC "s\n", getVMLibInfo().createVM); + return -1; + } + + return createVM(vm, env, &pandaVMArgs); +} + +static const AppInfo harnessAppInfo = { + "@koalaui/ets-harness/src/EtsHarnessApplication/EtsHarnessApplication", + "createApplication", + "Lstd/core/String;Lstd/core/String;ZI:L@koalaui/ets-harness/src/" + "EtsHarnessApplication/EtsHarnessApplication;", + "start", + "J:J", + "enter", + "IIJ:Z", + "emitEvent", + "IIII:Lstd/core/String;", + "restartWith", + "Lstd/core/String;:V"}; + +static const AppInfo pandaAppInfo = { + "@koalaui/arkts-arkui/Application/Application", + "createApplication", + "Lstd/core/String;Lstd/core/String;ZI:L@koalaui/arkts-arkui/Application/" + "Application;", + "start", + "J:J", + "enter", + "IIJ:Z", + "emitEvent", + "IIII:Lstd/core/String;", +}; + +KNativePointer loaderStartApplication(const char *appUrl, const char *appParams, + VMEntry &vmEntry) { + + const auto isTestEnv = std::string(appUrl) == "EtsHarness"; + const AppInfo &appInfo = isTestEnv ? harnessAppInfo : pandaAppInfo; + + EtsEnv *etsEnv = (EtsEnv *)vmEntry.env; + ets_class appClass = etsEnv->FindClass(appInfo.className); + if (!appClass) { + LOGE("Cannot load main class %" LOG_PUBLIC "s\n", appInfo.className); + if (etsEnv->ErrorCheck()) { + etsEnv->ErrorDescribe(); + etsEnv->ErrorClear(); + } + return nullptr; + } + ets_method create = etsEnv->GetStaticp_method( + appClass, appInfo.createMethodName, appInfo.createMethodSig); + if (!create) { + LOGE("Cannot find create method %" LOG_PUBLIC "s\n", + appInfo.createMethodName); + if (etsEnv->ErrorCheck()) { + etsEnv->ErrorDescribe(); + etsEnv->ErrorClear(); + } + return nullptr; + } +#if defined(KOALA_OHOS_ARM64) + auto useNativeLog = true; +#else + auto useNativeLog = false; +#endif + auto app = etsEnv->NewGlobalRef(etsEnv->CallStaticObjectMethod( + appClass, create, etsEnv->NewStringUTF(appUrl), + etsEnv->NewStringUTF(appParams), useNativeLog, vmEntry.vmKind)); + if (!app) { + LOGE("createApplication returned null"); + if (etsEnv->ErrorCheck()) { + etsEnv->ErrorDescribe(); + etsEnv->ErrorClear(); + return nullptr; + } + return nullptr; + } + vmEntry.app = (void *)app; + auto start = etsEnv->Getp_method(appClass, appInfo.startMethodName, + appInfo.startMethodSig); + vmEntry.enter = (void *)(etsEnv->Getp_method( + appClass, appInfo.enterMethodName, nullptr /*appInfo.enterMethodSig */)); + if (!vmEntry.enter) { + LOGE("Cannot find enter method %" LOG_PUBLIC "s", appInfo.enterMethodName); + if (etsEnv->ErrorCheck()) { + etsEnv->ErrorDescribe(); + etsEnv->ErrorClear(); + } + return nullptr; + } + if (etsEnv->ErrorCheck()) { + etsEnv->ErrorDescribe(); + etsEnv->ErrorClear(); + return nullptr; + } + vmEntry.emitEvent = (void *)(etsEnv->Getp_method( + appClass, appInfo.emitEventMethodName, appInfo.emitEventMethodSig)); + if (!vmEntry.emitEvent) { + LOGE("Cannot find enter emitEvent %" LOG_PUBLIC "s", + appInfo.emitEventMethodSig); + if (etsEnv->ErrorCheck()) { + etsEnv->ErrorDescribe(); + etsEnv->ErrorClear(); + } + return nullptr; + } + if (isTestEnv) { + vmEntry.restartWith = (void *)(etsEnv->Getp_method( + appClass, appInfo.restartWithMethodName, appInfo.restartWithMethodSig)); + if (!vmEntry.restartWith) { + LOGE("Cannot find enter restartWith %" LOG_PUBLIC "s", + appInfo.restartWithMethodSig); + if (etsEnv->ErrorCheck()) { + etsEnv->ErrorDescribe(); + etsEnv->ErrorClear(); + } + return nullptr; + } + } + + // TODO: pass app entry point! + return reinterpret_cast(etsEnv->CallLongMethod( + (ets_object)(app), start, &vmEntry.foreignVMContext)); +} + +KBoolean loaderRunApplication(const KInt arg0, const KInt arg1, + const VMEntry &vmEntry) { + EtsEnv *etsEnv = (EtsEnv *)(vmEntry.env); + if (!vmEntry.enter) { + LOGE("Cannot find enter method"); + return -1; + } + auto result = etsEnv->CallBooleanMethod( + (ets_object)(vmEntry.app), (ets_method)(vmEntry.enter), (ets_int)arg0, + (ets_int)arg1, (int64_t)(intptr_t)(&vmEntry.foreignVMContext)); + if (etsEnv->ErrorCheck()) { + LOGE("Calling enter() method gave an error"); + etsEnv->ErrorDescribe(); + etsEnv->ErrorClear(); + } + return result; +} + +const char *loaderEmitEvent(const KInt type, const KInt target, const KInt arg0, + const KInt arg1, const VMEntry &vmEntry) { + EtsEnv *etsEnv = (EtsEnv *)(vmEntry.env); + if (!vmEntry.emitEvent) { + LOGE("Cannot find emitEvent method"); + return "-1"; + } + auto rv = (ets_string)etsEnv->CallObjectMethod( + (ets_object)(vmEntry.app), (ets_method)(vmEntry.emitEvent), (ets_int)type, + (ets_int)target, (ets_int)arg0, (ets_int)arg1); + if (etsEnv->ErrorCheck()) { + LOGE("Calling emitEvent() method gave an error"); + etsEnv->ErrorDescribe(); + etsEnv->ErrorClear(); + } + const char *result = etsEnv->GetStringUTFChars(rv, 0); + return result; +} + +void loaderRestartWith(const char *page, const VMEntry &vmEntry) { + EtsEnv *etsEnv = (EtsEnv *)(vmEntry.env); + if (!vmEntry.restartWith) { + LOGE("Cannot find restartWith method"); + return; + } + etsEnv->CallVoidMethod((ets_object)(vmEntry.app), + (ets_method)(vmEntry.restartWith), + etsEnv->NewStringUTF(page)); + if (etsEnv->ErrorCheck()) { + LOGE("Calling restartWith() method gave an error"); + etsEnv->ErrorDescribe(); + etsEnv->ErrorClear(); + } +} diff --git a/interop/src/cpp/vmloader/jni/vmloader-helpers-jni.cc b/interop/src/cpp/vmloader/jni/vmloader-helpers-jni.cc new file mode 100644 index 000000000..e0deb65ea --- /dev/null +++ b/interop/src/cpp/vmloader/jni/vmloader-helpers-jni.cc @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vmloader.h" + +#include "interop-logging.h" + +#include "jni.h" +#include +#include + +// DO NOT USE KOALA INTEROP MECHANISMS IN THIS FILE! + +const VMLibInfo &getVMLibInfo() { + static const VMLibInfo javaVMLibInfo { + getenv("JAVA_HOME"), +#if defined(KOALA_LINUX) || defined(KOALA_MACOS) + "lib/server" +#elif KOALA_WINDOWS + "bin/server" +#else +#error "Unknown platform" +#endif + , + "jvm", "JNI_CreateJavaVM", + }; + + return javaVMLibInfo; +} + +using createVM_t = int (*)(void **pVM, void **pEnv, void *vmInitArgs); + +int loaderCreateVM(void **vm, void **env, const char *appClassPath, + const char *appLibPath) { + JavaVMInitArgs javaVMArgs; + javaVMArgs.version = JNI_VERSION_10; + javaVMArgs.ignoreUnrecognized = false; + std::vector javaVMOptions; + // memory leak ? + javaVMOptions = { + {(char *)strdup( + (std::string("-Djava.class.path=") + appClassPath).c_str())}, + {(char *)strdup( + (std::string("-Djava.library.path=") + appLibPath).c_str())}, + }; + javaVMArgs.nOptions = javaVMOptions.size(); + javaVMArgs.options = javaVMOptions.data(); + auto createVM = + reinterpret_cast(loaderFindSymbol(getVMLibInfo().createVM)); + + if (!createVM) { + LOGE("Cannot find %" LOG_PUBLIC "s\n", getVMLibInfo().createVM); + return -1; + } + + return createVM(vm, env, &javaVMArgs); +} + +static const AppInfo javaAppInfo = { + "org/koalaui/arkoala/Application", + "createApplication", + "(Ljava/lang/String;Ljava/lang/String;)Lorg/koalaui/arkoala/Application;", + "start", + "()J", + "enter", + "(IIJ)Z", + "emitEvent", + "(IIII)Ljava/lang/String;", +}; + +KNativePointer loaderStartApplication(const char *appUrl, const char *appParams, + VMEntry &vmEntry) { + JNIEnv *jEnv = (JNIEnv *)(vmEntry.env); + jclass appClass = jEnv->FindClass(javaAppInfo.className); + if (!appClass) { + LOGE("Cannot load main class %s\n", javaAppInfo.className); + return nullptr; + } + jmethodID create = jEnv->GetStaticMethodID( + appClass, javaAppInfo.createMethodName, javaAppInfo.createMethodSig); + if (!create) { + LOGE("Cannot find create method %s\n", javaAppInfo.createMethodName); + return nullptr; + } + auto app = jEnv->NewGlobalRef( + jEnv->CallStaticObjectMethod(appClass, create, jEnv->NewStringUTF(appUrl), + jEnv->NewStringUTF(appParams))); + vmEntry.app = app; + auto start = jEnv->GetMethodID(appClass, javaAppInfo.startMethodName, + javaAppInfo.startMethodSig); + if (!start) { + LOGE("Cannot find start method \"%s %s\"\n", javaAppInfo.startMethodName, + javaAppInfo.startMethodSig); + return nullptr; + } + vmEntry.enter = (void *)(jEnv->GetMethodID( + appClass, javaAppInfo.enterMethodName, javaAppInfo.enterMethodSig)); + if (!vmEntry.enter) { + LOGE("Cannot find enter method %s\n", javaAppInfo.enterMethodName); + return nullptr; + } + vmEntry.emitEvent = + (void *)(jEnv->GetMethodID(appClass, javaAppInfo.emitEventMethodName, + javaAppInfo.emitEventMethodSig)); + if (!vmEntry.emitEvent) { + LOGE("Cannot find emitEvent method %s\n", javaAppInfo.emitEventMethodName); + return nullptr; + } + return reinterpret_cast(jEnv->CallLongMethod(app, start)); +} + +KBoolean loaderRunApplication(const KInt arg0, const KInt arg1, + const VMEntry &vmEntry) { + JNIEnv *jEnv = (JNIEnv *)(vmEntry.env); + auto result = jEnv->CallBooleanMethod( + (jobject)(vmEntry.app), (jmethodID)(vmEntry.enter), (jint)arg0, + (jint)arg1, (int64_t)(intptr_t)(&vmEntry.foreignVMContext)); + if (jEnv->ExceptionCheck()) { + jEnv->ExceptionDescribe(); + jEnv->ExceptionClear(); + } + return result; +} + +const char *loaderEmitEvent(const KInt type, const KInt target, const KInt arg0, + const KInt arg1, const VMEntry &vmEntry) { + JNIEnv *jEnv = (JNIEnv *)(vmEntry.env); + if (!vmEntry.emitEvent) { + LOGE("Cannot find emitEvent method"); + return "-1"; + } + auto rv = (jstring)jEnv->CallObjectMethod( + (jobject)(vmEntry.app), (jmethodID)(vmEntry.emitEvent), (jint)type, + (jint)target, (jint)arg0, (jint)arg1); + if (jEnv->ExceptionCheck()) { + jEnv->ExceptionDescribe(); + jEnv->ExceptionClear(); + } + const char *result = jEnv->GetStringUTFChars(rv, 0); + return result; +} + +void loaderRestartWith(const char *page, const VMEntry &vmEntry) { + JNIEnv *jEnv = (JNIEnv *)(vmEntry.env); + if (!vmEntry.restartWith) { + LOGE("Cannot find restartWith method"); + return; + } + jEnv->CallVoidMethod((jobject)(vmEntry.app), (jmethodID)(vmEntry.restartWith), + jEnv->NewStringUTF(page)); + if (jEnv->ExceptionCheck()) { + jEnv->ExceptionDescribe(); + jEnv->ExceptionClear(); + } +} diff --git a/interop/src/cpp/vmloader/meson.build b/interop/src/cpp/vmloader/meson.build index a52ef2931..283f34cc4 100644 --- a/interop/src/cpp/vmloader/meson.build +++ b/interop/src/cpp/vmloader/meson.build @@ -26,7 +26,11 @@ if os != 'windows' vmloader_ldflags += ['-ldl'] endif -if get_option('vmloader_apis').contains('jni') +sources_vmloader = ['vmloader.cc'] + +vmloader_apis_opt = get_option('vmloader_apis') + +if vmloader_apis_opt == 'jni' jni_os_dir = os if jni_os_dir == 'windows' jni_os_dir = 'win32' @@ -38,18 +42,33 @@ if get_option('vmloader_apis').contains('jni') vmloader_cflags += [ '-DKOALA_JNI', ] -endif -if get_option('vmloader_apis').contains('ets') + sources_vmloader += ['jni/vmloader-helpers-jni.cc'] + +elif vmloader_apis_opt == 'ets' include_dirs += [ '..' / 'ets', ] vmloader_cflags += [ '-DKOALA_ETS_NAPI', ] -endif -sources_vmloader = ['vmloader.cc'] + sources_vmloader += ['ets/vmloader-helpers-ets.cc'] + +elif vmloader_apis_opt == 'ani' + include_dirs += [ + '..' / 'ani', + ] + vmloader_cflags += [ + '-DKOALA_ANI', + ] + + sources_vmloader += ['ani/vmloader-helpers-ani.cc'] + +else + error('please specify option `vmloader_apis` correctly') + +endif # hack, need to refactor install scripts framework_native_build_dir = meson.current_source_dir()/'..'/'..'/'..'/'..'/'arkoala'/'framework'/'build' diff --git a/interop/src/cpp/vmloader/meson_options.txt b/interop/src/cpp/vmloader/meson_options.txt index 7ebfe8f19..392be4da0 100644 --- a/interop/src/cpp/vmloader/meson_options.txt +++ b/interop/src/cpp/vmloader/meson_options.txt @@ -1,6 +1,6 @@ option('jdk_dir', type : 'string', value : '', description : 'A path to JDK root') -option('vmloader_apis', type : 'string', value : 'ets', +option('vmloader_apis', type : 'string', value : 'ani', description : 'APIs to use in libvmloader.so') option('arkgen_install_path', type : 'boolean', value : 'false', description : 'Install so-file to idlize_arkgen') diff --git a/interop/src/cpp/vmloader/vmloader.cc b/interop/src/cpp/vmloader/vmloader.cc index 3a4d41f02..4bc5e33dd 100644 --- a/interop/src/cpp/vmloader/vmloader.cc +++ b/interop/src/cpp/vmloader/vmloader.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -13,34 +13,21 @@ * limitations under the License. */ -#include -#include +#include "vmloader.h" + #include +#include -#include "interop-logging.h" #include "dynamic-loader.h" -#include "koala-types.h" +#include "interop-logging.h" // DO NOT USE KOALA INTEROP MECHANISMS IN THIS FILE! -#ifdef KOALA_JNI -#include "jni.h" -#endif - -#ifdef KOALA_ETS_NAPI -#include "etsapi.h" -#endif - -#ifdef KOALA_ANI -#include "ani.h" -#endif - #if defined(KOALA_LINUX) || defined(KOALA_MACOS) || defined(KOALA_OHOS) -#include "sys/stat.h" #include "dirent.h" +#include "sys/stat.h" #endif -#define OHOS_USER_LIBS "/data/storage/el1/bundle/libs" #ifdef KOALA_OHOS_ARM32 #define USE_SYSTEM_ARKVM 1 #elif KOALA_OHOS_ARM64 @@ -62,80 +49,10 @@ #endif #endif -void traverseDir(std::string root, std::vector& paths, int depth = 0); - -struct VMLibInfo { - const char* sdkPath; - const char* platform; - const char* lib; - const char* createVM; -}; - -#ifdef KOALA_JNI -const VMLibInfo javaVMLib = { - getenv("JAVA_HOME"), - #if defined(KOALA_LINUX) || defined(KOALA_MACOS) - "lib/server" - #elif KOALA_WINDOWS - "bin/server" - #else - #error "Unknown platform" - #endif - , - "jvm", - "JNI_CreateJavaVM", -}; -#endif - -#ifdef KOALA_ETS_NAPI -const VMLibInfo pandaVMLib = { - // sdkPath - #if defined(KOALA_OHOS) - #ifdef KOALA_OHOS_ARM32 - "/system/lib" - #elif KOALA_OHOS_ARM64 - "/system/lib64" - #else - OHOS_USER_LIBS - #endif - #else - getenv("PANDA_HOME") - #endif - , - - // platform - #ifdef KOALA_LINUX - #ifdef KOALA_LINUX_ARM64 - "linux_arm64_host_tools/lib" - #else - "linux_host_tools/lib" - #endif - #elif KOALA_MACOS - "macos_host_tools/lib" - #elif KOALA_WINDOWS - "_host_tools/lib" - #elif KOALA_OHOS_ARM64 - "arm64" - #elif KOALA_OHOS_ARM32 - "arm" - #else - #error "Unknown platform" - #endif - , - - // lib - "arkruntime" - , - - // createVM - "ETS_CreateVM" -}; -#endif - struct VMInitArgs { - int version; - int nOptions; - void* options; + int version; + int nOptions; + void *options; }; #define JAVA_VM_KIND 1 @@ -143,561 +60,123 @@ struct VMInitArgs { #define ES2PANDA_KIND 3 #define PANDA_ANI_VM_KIND 4 -struct ForeignVMContext { - void* currentVMContext; - int32_t (*callSync)(void* vmContext, int32_t callback, int8_t* data, int32_t length); -}; - -struct VMEntry { - int vmKind; - void* env; - void* app; - void* enter; - void* emitEvent; - void* restartWith; - ForeignVMContext foreignVMContext; -}; - VMEntry g_vmEntry = {}; -typedef int (*createVM_t)(void** pVM, void** pEnv, void* vmInitArgs); -typedef int (*getVMs_t)(void** pVM, int32_t bufLen, int32_t* nVMs); - #ifdef KOALA_WINDOWS #define DLL_EXPORT __declspec(dllexport) #else -#define DLL_EXPORT __attribute__ ((visibility ("default"))) +#define DLL_EXPORT __attribute__((visibility("default"))) #endif -int loadES2Panda(const char* appClassPath, const char* appLibPath) { - fprintf(stderr, "native: es2panda %s\n", appClassPath); - return 0; -} - -#if defined(KOALA_ETS_NAPI) || defined(KOALA_ANI) -namespace { - -enum PandaLog2MobileLog : int { - UNKNOWN = 0, - DEFAULT, - VERBOSE, - DEBUG, - INFO, - WARN, - ERROR, - FATAL, - SILENT, -}; - -int ArkMobileLog(int id, int level, const char *component, const char *fmt, const char *msg) { - switch (level) { - case PandaLog2MobileLog::DEFAULT: - case PandaLog2MobileLog::VERBOSE: - case PandaLog2MobileLog::DEBUG: - case PandaLog2MobileLog::INFO: - case PandaLog2MobileLog::SILENT: - LOGI("ArkRuntime [%" LOG_PUBLIC "s]: %" LOG_PUBLIC "s", component, msg); - break; - case PandaLog2MobileLog::UNKNOWN: - case PandaLog2MobileLog::WARN: - case PandaLog2MobileLog::ERROR: - case PandaLog2MobileLog::FATAL: - default: - LOGE("ArkRuntime [%" LOG_PUBLIC "s]: %" LOG_PUBLIC "s", component, msg); - break; - } - return 0; -} - +int loadES2Panda(const char *appClassPath, const char *appLibPath) { + fprintf(stderr, "native: es2panda %s\n", appClassPath); + return 0; } -#endif -extern "C" DLL_EXPORT KInt LoadVirtualMachine(KInt vmKind, const char* appClassPath, const char* appLibPath, const ForeignVMContext* foreignVMContext) { - if (vmKind == ES2PANDA_KIND) { - return loadES2Panda(appClassPath, appLibPath); - } - - const VMLibInfo* thisVM = - #ifdef KOALA_JNI - (vmKind == JAVA_VM_KIND) ? &javaVMLib : - #endif - #if defined(KOALA_ETS_NAPI) || defined(KOALA_ANI) - (vmKind == PANDA_VM_KIND || vmKind == PANDA_ANI_VM_KIND) ? &pandaVMLib : - #endif - nullptr; - - if (!thisVM) { - LOGE("Unknown VM kind: %" LOG_PUBLIC "d\n (possibly %" LOG_PUBLIC "s is compiled without expected flags)", vmKind, __FILE__); - return -1; - } - - LOGI("Starting VM %" LOG_PUBLIC "d with classpath=%" LOG_PUBLIC "s native=%" LOG_PUBLIC "s", vmKind, appClassPath, appLibPath); - - std::string libPath = +void *loaderFindSymbol(const char *name) { + const auto &VMLib = getVMLibInfo(); + std::string libPath = #if USE_SYSTEM_ARKVM - std::string(thisVM->sdkPath) + "/" + libName(thisVM->lib) + std::string(VMLib.sdkPath) + "/" + libName(VMLib.lib) #elif defined(KOALA_LINUX) || defined(KOALA_MACOS) || defined(KOALA_WINDOWS) - std::string(thisVM->sdkPath) + "/" + std::string(thisVM->platform) + "/" + libName(thisVM->lib) + std::string(VMLib.sdkPath) + "/" + std::string(VMLib.platform) + "/" + + libName(VMLib.lib) #elif defined(KOALA_OHOS) - std::string(OHOS_USER_LIBS) + "/" + libName(thisVM->lib) + std::string(OHOS_USER_LIBS) + "/" + libName(VMLib.lib) #else - #error "Library path not specified for this platform" -#endif - ; - void *handle = loadLibrary(libPath); - if (!handle) { - LOGE("Cannot load library %" LOG_PUBLIC "s: %" LOG_PUBLIC "s\n", libPath.c_str(), libraryError()); - return -1; - } - - createVM_t createVM = (createVM_t)findSymbol(handle, thisVM->createVM); - getVMs_t getVMs = (getVMs_t)findSymbol(handle, "ETS_GetCreatedVMs"); - - if (!createVM) { - LOGE("Cannot find %" LOG_PUBLIC "s\n", thisVM->createVM); - return -1; - } - - void* vm = nullptr; - void* env = nullptr; - int32_t nVMs = 0; - int result = 0; - -#ifdef KOALA_JNI - if (vmKind == JAVA_VM_KIND) { - JavaVMInitArgs javaVMArgs; - javaVMArgs.version = JNI_VERSION_10; - javaVMArgs.ignoreUnrecognized = false; - std::vector javaVMOptions; - javaVMOptions = { - {(char*)strdup((std::string("-Djava.class.path=") + appClassPath).c_str())}, - {(char*)strdup((std::string("-Djava.library.path=") + appLibPath).c_str())}, - }; - javaVMArgs.nOptions = javaVMOptions.size(); - javaVMArgs.options = javaVMOptions.data(); - g_vmEntry.vmKind = JAVA_VM_KIND; - result = createVM(&vm, &env, &javaVMArgs); - } +#error "Library path not specified for this platform" #endif + ; -// For now we use ETS API for VM startup and entry. -#if defined(KOALA_ETS_NAPI) || defined(KOALA_ANI) - if (vmKind == PANDA_VM_KIND || vmKind == PANDA_ANI_VM_KIND) { - EtsVMInitArgs pandaVMArgs; - pandaVMArgs.version = ETS_NAPI_VERSION_1_0; - std::vector etsVMOptions; - std::vector files; - traverseDir(std::string(appClassPath), files); - std::sort(files.begin(), files.end()); - etsVMOptions = { -#if USE_SYSTEM_ARKVM - {EtsOptionType::ETS_BOOT_FILE, SYSTEM_ARK_STDLIB_PATH}, -#elif defined(KOALA_OHOS) - {EtsOptionType::ETS_BOOT_FILE, (std::string(OHOS_USER_LIBS) + "/" + "etsstdlib.abc").c_str() }, - -#elif defined(KOALA_LINUX) || defined(KOALA_MACOS) || defined(KOALA_WINDOWS) - {EtsOptionType::ETS_BOOT_FILE, (char*)strdup((std::string(thisVM->sdkPath) + "/ets/etsstdlib.abc").c_str())}, -#endif - }; - std::string all_files; - for (const std::string& path : files) { - etsVMOptions.push_back({EtsOptionType::ETS_BOOT_FILE, (char*)strdup(path.c_str())}); - if (all_files.size() > 0) all_files.append(":"); - all_files.append(path); - } - LOGE("classpath \"%s\" from %s", all_files.c_str(), appClassPath); - etsVMOptions.push_back({EtsOptionType::ETS_GC_TRIGGER_TYPE, "heap-trigger"}); - etsVMOptions.push_back({EtsOptionType::ETS_NATIVE_LIBRARY_PATH, (char*)strdup(std::string(appLibPath).c_str())}); - etsVMOptions.push_back({EtsOptionType::ETS_VERIFICATION_MODE, "on-the-fly"}); - etsVMOptions.push_back({EtsOptionType::ETS_NO_JIT, nullptr}); - etsVMOptions.push_back({EtsOptionType::ETS_MOBILE_LOG, (void*)ArkMobileLog}); - etsVMOptions.push_back({EtsOptionType::ETS_AOT, nullptr}); - // etsVMOptions.push_back({EtsOptionType::ETS_LOG_LEVEL, "info"}); - pandaVMArgs.nOptions = etsVMOptions.size(); - pandaVMArgs.options = etsVMOptions.data(); - g_vmEntry.vmKind = vmKind; - - result = getVMs ? getVMs(&vm, 1, &nVMs) : 0; - if (nVMs != 0) { - __EtsVM* vmInstance = (__EtsVM*)vm; - EtsEnv* pEnv = nullptr; - vmInstance->GetEnv(&pEnv, ETS_NAPI_VERSION_1_0); - env = static_cast(pEnv); - - } else { - result = createVM(&vm, &env, &pandaVMArgs); - } - } -#endif + void *handle = loadLibrary(libPath); + if (!handle) { + LOGE("Cannot load library %" LOG_PUBLIC "s: %" LOG_PUBLIC "s\n", + libPath.c_str(), libraryError()); + return nullptr; + } - if (result != 0) { - LOGE("Error creating a VM of kind %" LOG_PUBLIC "d: %" LOG_PUBLIC "d\n", vmKind, result); - return result; - } - g_vmEntry.env = env; - g_vmEntry.foreignVMContext = *foreignVMContext; - return 0; + return findSymbol(handle, name); } -struct AppInfo { - const char* className; - const char* createMethodName; - const char* createMethodSig; - const char* startMethodName; - const char* startMethodSig; - const char* enterMethodName; - const char* enterMethodSig; - const char* emitEventMethodName; - const char* emitEventMethodSig; - const char* restartWithMethodName; - const char* restartWithMethodSig; -}; - -#ifdef KOALA_JNI -const AppInfo javaAppInfo = { - "org/koalaui/arkoala/Application", - "createApplication", - "(Ljava/lang/String;Ljava/lang/String;)Lorg/koalaui/arkoala/Application;", - "start", - "()J", - "enter", - "(IIJ)Z", - "emitEvent", - "(IIII)Ljava/lang/String;", -}; -#endif - -#ifdef KOALA_USE_PANDA_VM -const AppInfo pandaAppInfo = { - "@koalaui/arkts-arkui/Application/Application", - "createApplication", - "Lstd/core/String;Lstd/core/String;ZI:L@koalaui/arkts-arkui/Application/Application;", - "start", - "J:J", - "enter", - "IIJ:Z", - "emitEvent", - "IIII:Lstd/core/String;", -}; -const AppInfo harnessAppInfo = { - "@koalaui/ets-harness/src/EtsHarnessApplication/EtsHarnessApplication", - "createApplication", - "Lstd/core/String;Lstd/core/String;ZI:L@koalaui/ets-harness/src/EtsHarnessApplication/EtsHarnessApplication;", - "start", - "J:J", - "enter", - "IIJ:Z", - "emitEvent", - "IIII:Lstd/core/String;", - "restartWith", - "Lstd/core/String;:V" -}; -#endif - -extern "C" DLL_EXPORT KNativePointer StartApplication(const char* appUrl, const char* appParams) { - const auto isTestEnv = std::string(appUrl) == "EtsHarness"; - const AppInfo* appInfo = - #ifdef KOALA_JNI - (g_vmEntry.vmKind == JAVA_VM_KIND) ? &javaAppInfo : - #endif - #if defined(KOALA_ETS_NAPI) || defined(KOALA_ANI) - (g_vmEntry.vmKind == PANDA_VM_KIND || g_vmEntry.vmKind == PANDA_ANI_VM_KIND) ? isTestEnv ? &harnessAppInfo : &pandaAppInfo : - #endif - nullptr; - - if (!appInfo) { - LOGE("No appInfo provided for VM kind %" LOG_PUBLIC "d (recompile vmloader.cc with the missing flags)\n", g_vmEntry.vmKind); - return nullptr; - } - - LOGI("Starting application %" LOG_PUBLIC "s with params %" LOG_PUBLIC "s", appUrl, appParams); -#ifdef KOALA_JNI - if (g_vmEntry.vmKind == JAVA_VM_KIND) { - JNIEnv* jEnv = (JNIEnv*)(g_vmEntry.env); - jclass appClass = jEnv->FindClass(appInfo->className); - if (!appClass) { - LOGE("Cannot load main class %s\n", appInfo->className); - return nullptr; - } - jmethodID create = jEnv->GetStaticMethodID(appClass, appInfo->createMethodName, appInfo->createMethodSig); - if (!create) { - LOGE("Cannot find create method %s\n", appInfo->createMethodName); - return nullptr; - } - auto app = jEnv->NewGlobalRef(jEnv->CallStaticObjectMethod(appClass, create, jEnv->NewStringUTF(appUrl), jEnv->NewStringUTF(appParams))); - g_vmEntry.app = app; - auto start = jEnv->GetMethodID(appClass, appInfo->startMethodName, appInfo->startMethodSig); - if (!start) { - LOGE("Cannot find start method \"%s %s\"\n", appInfo->startMethodName, appInfo->startMethodSig); - return nullptr; - } - g_vmEntry.enter = (void*)(jEnv->GetMethodID(appClass, appInfo->enterMethodName, appInfo->enterMethodSig)); - if (!g_vmEntry.enter) { - LOGE("Cannot find enter method %s\n", appInfo->enterMethodName); - return nullptr; - } - g_vmEntry.emitEvent = (void*)(jEnv->GetMethodID(appClass, appInfo->emitEventMethodName, appInfo->emitEventMethodSig)); - if (!g_vmEntry.emitEvent) { - LOGE("Cannot find emitEvent method %s\n", appInfo->emitEventMethodName); - return nullptr; - } - return reinterpret_cast(jEnv->CallLongMethod( - app, start)); - } -#endif -#if defined(KOALA_ETS_NAPI) || defined(KOALA_ANI) - if (g_vmEntry.vmKind == PANDA_VM_KIND || g_vmEntry.vmKind == PANDA_ANI_VM_KIND) { - EtsEnv* etsEnv = (EtsEnv*)g_vmEntry.env; - ets_class appClass = etsEnv->FindClass(appInfo->className); - if (!appClass) { - LOGE("Cannot load main class %" LOG_PUBLIC "s\n", appInfo->className); - if (etsEnv->ErrorCheck()) { - etsEnv->ErrorDescribe(); - etsEnv->ErrorClear(); - } - return nullptr; - } - ets_method create = etsEnv->GetStaticp_method(appClass, appInfo->createMethodName, appInfo->createMethodSig); - if (!create) { - LOGE("Cannot find create method %" LOG_PUBLIC "s\n", appInfo->createMethodName); - if (etsEnv->ErrorCheck()) { - etsEnv->ErrorDescribe(); - etsEnv->ErrorClear(); - } - return nullptr; - } -#if defined (KOALA_OHOS_ARM64) - auto useNativeLog = true; -#else - auto useNativeLog = false; -#endif - auto app = etsEnv->NewGlobalRef(etsEnv->CallStaticObjectMethod( - appClass, create, - etsEnv->NewStringUTF(appUrl), etsEnv->NewStringUTF(appParams), - useNativeLog, - g_vmEntry.vmKind - )); - if (!app) { - LOGE("createApplication returned null"); - if (etsEnv->ErrorCheck()) { - etsEnv->ErrorDescribe(); - etsEnv->ErrorClear(); - return nullptr; - } - return nullptr; - } - g_vmEntry.app = (void*)app; - auto start = etsEnv->Getp_method(appClass, appInfo->startMethodName, appInfo->startMethodSig); - g_vmEntry.enter = (void*)(etsEnv->Getp_method(appClass, appInfo->enterMethodName, nullptr /*appInfo->enterMethodSig */)); - if (!g_vmEntry.enter) { - LOGE("Cannot find enter method %" LOG_PUBLIC "s", appInfo->enterMethodName); - if (etsEnv->ErrorCheck()) { - etsEnv->ErrorDescribe(); - etsEnv->ErrorClear(); - } - return nullptr; - } - if (etsEnv->ErrorCheck()) { - etsEnv->ErrorDescribe(); - etsEnv->ErrorClear(); - return nullptr; - } - g_vmEntry.emitEvent = (void*)(etsEnv->Getp_method(appClass, appInfo->emitEventMethodName, appInfo->emitEventMethodSig)); - if (!g_vmEntry.emitEvent) { - LOGE("Cannot find enter emitEvent %" LOG_PUBLIC "s", appInfo->emitEventMethodSig); - if (etsEnv->ErrorCheck()) { - etsEnv->ErrorDescribe(); - etsEnv->ErrorClear(); - } - return nullptr; - } - if (isTestEnv) { - g_vmEntry.restartWith = (void*)(etsEnv->Getp_method(appClass, appInfo->restartWithMethodName, appInfo->restartWithMethodSig)); - if (!g_vmEntry.restartWith) { - LOGE("Cannot find enter restartWith %" LOG_PUBLIC "s", appInfo->restartWithMethodSig); - if (etsEnv->ErrorCheck()) { - etsEnv->ErrorDescribe(); - etsEnv->ErrorClear(); - } - return nullptr; - } - } - // TODO: pass app entry point! - return reinterpret_cast(etsEnv->CallLongMethod((ets_object)(app), start, &g_vmEntry.foreignVMContext)); - } -#endif - return nullptr; +extern "C" DLL_EXPORT KInt LoadVirtualMachine( + KInt vmKind, const char *appClassPath, const char *appLibPath, + const ForeignVMContext *foreignVMContext) { + if (vmKind == ES2PANDA_KIND) { + return loadES2Panda(appClassPath, appLibPath); + } + + LOGI("Starting VM %" LOG_PUBLIC "d with classpath=%" LOG_PUBLIC + "s native=%" LOG_PUBLIC "s", + vmKind, appClassPath, appLibPath); + + void *vm = nullptr; + void *env = nullptr; + int result = loaderCreateVM(&vm, &env, appClassPath, appLibPath); + g_vmEntry.vmKind = vmKind; + g_vmEntry.env = env; + g_vmEntry.foreignVMContext = *foreignVMContext; + return result; } -extern "C" DLL_EXPORT KBoolean RunApplication(const KInt arg0, const KInt arg1) { -#ifdef KOALA_JNI - if (g_vmEntry.vmKind == JAVA_VM_KIND) { - JNIEnv* jEnv = (JNIEnv*)(g_vmEntry.env); - auto result = jEnv->CallBooleanMethod( - (jobject)(g_vmEntry.app), - (jmethodID)(g_vmEntry.enter), - (jint)arg0, - (jint)arg1, - (int64_t)(intptr_t)(&g_vmEntry.foreignVMContext) - ); - if (jEnv->ExceptionCheck()) { - jEnv->ExceptionDescribe(); - jEnv->ExceptionClear(); - } - return result; - } -#endif -#if defined(KOALA_ETS_NAPI) || defined(KOALA_ANI) - if (g_vmEntry.vmKind == PANDA_VM_KIND || g_vmEntry.vmKind == PANDA_ANI_VM_KIND) { - EtsEnv* etsEnv = (EtsEnv*)(g_vmEntry.env); - if (!g_vmEntry.enter) { - LOGE("Cannot find enter method"); - return -1; - } - auto result = etsEnv->CallBooleanMethod( - (ets_object)(g_vmEntry.app), - (ets_method)(g_vmEntry.enter), - (ets_int)arg0, - (ets_int)arg1, - (int64_t)(intptr_t)(&g_vmEntry.foreignVMContext) - ); - if (etsEnv->ErrorCheck()) { - LOGE("Calling enter() method gave an error"); - etsEnv->ErrorDescribe(); - etsEnv->ErrorClear(); - } - return result; - } -#endif - - return 1; +extern "C" DLL_EXPORT KNativePointer StartApplication(const char *appUrl, + const char *appParams) { + LOGI("Starting application %" LOG_PUBLIC "s with params %" LOG_PUBLIC "s", + appUrl, appParams); + return loaderStartApplication(appUrl, appParams, g_vmEntry); } -extern "C" DLL_EXPORT const char* EmitEvent(const KInt type, const KInt target, const KInt arg0, const KInt arg1) { -#ifdef KOALA_JNI - if (g_vmEntry.vmKind == JAVA_VM_KIND) { - JNIEnv* jEnv = (JNIEnv*)(g_vmEntry.env); - if (!g_vmEntry.emitEvent) { - LOGE("Cannot find emitEvent method"); - return "-1"; - } - auto rv = (jstring)jEnv->CallObjectMethod( - (jobject)(g_vmEntry.app), - (jmethodID)(g_vmEntry.emitEvent), - (jint)type, - (jint)target, - (jint)arg0, - (jint)arg1 - ); - if (jEnv->ExceptionCheck()) { - jEnv->ExceptionDescribe(); - jEnv->ExceptionClear(); - } - const char *result = jEnv->GetStringUTFChars(rv, 0); - return result; - } -#endif +extern "C" DLL_EXPORT KBoolean RunApplication(const KInt arg0, + const KInt arg1) { + return loaderRunApplication(arg0, arg1, g_vmEntry); +} -#if defined(KOALA_ETS_NAPI) || defined(KOALA_ANI) - if (g_vmEntry.vmKind == PANDA_VM_KIND || g_vmEntry.vmKind == PANDA_ANI_VM_KIND) { - EtsEnv* etsEnv = (EtsEnv*)(g_vmEntry.env); - if (!g_vmEntry.emitEvent) { - LOGE("Cannot find emitEvent method"); - return "-1"; - } - auto rv = (ets_string)etsEnv->CallObjectMethod( - (ets_object)(g_vmEntry.app), - (ets_method)(g_vmEntry.emitEvent), - (ets_int)type, - (ets_int)target, - (ets_int)arg0, - (ets_int)arg1 - ); - if (etsEnv->ErrorCheck()) { - LOGE("Calling emitEvent() method gave an error"); - etsEnv->ErrorDescribe(); - etsEnv->ErrorClear(); - } - const char *result = etsEnv->GetStringUTFChars(rv, 0); - return result; - } -#endif - return "-1"; +extern "C" DLL_EXPORT const char *EmitEvent(const KInt type, const KInt target, + const KInt arg0, const KInt arg1) { + return loaderEmitEvent(type, target, arg0, arg1, g_vmEntry); } -extern "C" DLL_EXPORT void RestartWith(const char* page) { -#ifdef KOALA_JNI - if (g_vmEntry.vmKind == JAVA_VM_KIND) { - JNIEnv* jEnv = (JNIEnv*)(g_vmEntry.env); - if (!g_vmEntry.restartWith) { - LOGE("Cannot find restartWith method"); - return; - } - jEnv->CallVoidMethod( - (jobject)(g_vmEntry.app), - (jmethodID)(g_vmEntry.restartWith), - jEnv->NewStringUTF(page) - ); - if (jEnv->ExceptionCheck()) { - jEnv->ExceptionDescribe(); - jEnv->ExceptionClear(); - } - } -#endif -#if defined(KOALA_ETS_NAPI) || defined(KOALA_ANI) - if (g_vmEntry.vmKind == PANDA_VM_KIND) { - EtsEnv* etsEnv = (EtsEnv*)(g_vmEntry.env); - if (!g_vmEntry.restartWith) { - LOGE("Cannot find restartWith method"); - return; - } - etsEnv->CallVoidMethod( - (ets_object)(g_vmEntry.app), - (ets_method)(g_vmEntry.restartWith), - etsEnv->NewStringUTF(page) - ); - if (etsEnv->ErrorCheck()) { - LOGE("Calling restartWith() method gave an error"); - etsEnv->ErrorDescribe(); - etsEnv->ErrorClear(); - } - } -#endif +extern "C" DLL_EXPORT void RestartWith(const char *page) { + loaderRestartWith(page, g_vmEntry); } -void traverseDir(std::string root, std::vector& paths, int depth) { - if (depth >= 50) { - return; - } +void traverseDir(const char *root, std::vector &paths, int depth) { + if (depth >= 50) { + return; + } #if defined(KOALA_LINUX) || defined(KOALA_MACOS) || defined(KOALA_OHOS) - std::string suffix = ".abc"; - #if defined(KOALA_OHOS) - suffix += ".so"; - #endif - DIR* directory = opendir(root.c_str()); - if (!directory) { - LOGE("Cannot open dir %" LOG_PUBLIC "s\n", root.c_str()); - return; - } - struct dirent* ent = NULL; - struct stat statbuf; - - LOGI("Searching for *%" LOG_PUBLIC "s in %" LOG_PUBLIC "s\n", suffix.c_str(), root.c_str()); - while ((ent = readdir(directory)) != nullptr) { - std::string filename = std::string(ent->d_name); - if (filename == "." || filename == "..") { - continue; - } - std::string filepath = root + "/" + filename; - int rv = stat(filepath.c_str(), &statbuf); - if (rv < 0) continue; - if (filepath.size() >= suffix.size() && filepath.substr(filepath.size() - suffix.size()) == suffix && (statbuf.st_mode & S_IFMT) == S_IFREG) { - paths.push_back(filepath); - } - if ((statbuf.st_mode & S_IFMT) == S_IFDIR) { - traverseDir(filepath, paths, depth + 1); - } - } - closedir(directory); + std::string suffix = ".abc"; +#if defined(KOALA_OHOS) + suffix += ".so"; +#endif + DIR *directory = opendir(root); + if (!directory) { + LOGE("Cannot open dir %" LOG_PUBLIC "s\n", root); + return; + } + struct dirent *ent = NULL; + struct stat statbuf; + + LOGI("Searching for *%" LOG_PUBLIC "s in %" LOG_PUBLIC "s\n", suffix.c_str(), + root); + while ((ent = readdir(directory)) != nullptr) { + std::string filename = std::string(ent->d_name); + if (filename == "." || filename == "..") { + continue; + } + std::string filepath = root; + filepath += "/" + filename; + int rv = stat(filepath.c_str(), &statbuf); + if (rv < 0) + continue; + if (filepath.size() >= suffix.size() && + filepath.substr(filepath.size() - suffix.size()) == suffix && + (statbuf.st_mode & S_IFMT) == S_IFREG) { + paths.push_back(filepath); + } + if ((statbuf.st_mode & S_IFMT) == S_IFDIR) { + traverseDir(filepath.c_str(), paths, depth + 1); + } + } + closedir(directory); #endif } diff --git a/interop/src/cpp/vmloader/vmloader.h b/interop/src/cpp/vmloader/vmloader.h new file mode 100644 index 000000000..c03c1a5d4 --- /dev/null +++ b/interop/src/cpp/vmloader/vmloader.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _INTEROP_SRC_CPP_VMLOADER_VMLOADER_H +#define _INTEROP_SRC_CPP_VMLOADER_VMLOADER_H + +#include "koala-types.h" + +#include +#include +#include + +void traverseDir(const char *root, std::vector &paths, + int depth = 0); + +struct VMLibInfo { + const char *sdkPath; + const char *platform; + const char *lib; + const char *createVM; +}; + +const VMLibInfo &getVMLibInfo(); +int loaderCreateVM(void **vm, void **env, const char *appClassPath, + const char *appLibPath); +void *loaderFindSymbol(const char *name); + +struct AppInfo { + const char *className; + const char *createMethodName; + const char *createMethodSig; + const char *startMethodName; + const char *startMethodSig; + const char *enterMethodName; + const char *enterMethodSig; + const char *emitEventMethodName; + const char *emitEventMethodSig; + const char *restartWithMethodName; + const char *restartWithMethodSig; +}; + +struct ForeignVMContext { + void *currentVMContext; + int32_t (*callSync)(void *vmContext, int32_t callback, int8_t *data, + int32_t length); +}; + +struct VMEntry { + int vmKind; + void *env; + void *app; + void *enter; + void *emitEvent; + void *restartWith; + ForeignVMContext foreignVMContext; +}; + +KNativePointer loaderStartApplication(const char *appUrl, const char *appParams, + VMEntry &vmEntry); +KBoolean loaderRunApplication(const KInt arg0, const KInt arg1, + const VMEntry &vmEntry); +const char *loaderEmitEvent(const KInt type, const KInt target, const KInt arg0, + const KInt arg1, const VMEntry &vmEntrys); +void loaderRestartWith(const char *page, const VMEntry &vmEntrys); + +#endif // _INTEROP_SRC_CPP_VMLOADER_VMLOADER_H -- Gitee