JSVM-API Development Process
To implement cross-language interaction using JSVM-API, you need to register and load modules based on the JSVM-API mechanism first.
-
ArkTS/JS: Import the .so library and call C++ APIs.
-
Native: Implement module registration via a .cpp file. You need to declare the name of the library to register and define the mappings between the native and JS/ArkTS APIs in the callback registered.
The following demonstrates how to implement cross-language interaction by implementing RunJsVm() in ArkTS/JS code and RunJsVm() in native code.
Creating a Native C++ Project
For details, see Creating an NDK Project.
Implementing Native APIs
By referring to Node-API Development Process, the following code provides a demo for implementing a native method by following "JSVM-API Development Process".
-
In the index.d.ts file, declare the JS methods.
// entry/src/main/cpp/types/libentry/index.d.ts export const runTest: () => void; -
Associate index.d.ts with .cpp in the oh-package.json5 file.
{ "name": "libentry.so", "types": "./index.d.ts", "version": "", "description": "Please describe the basic information." } -
Set CMake packaging parameters in the CMakeLists.txt file.
# entry/src/main/cpp/CMakeLists.txt cmake_minimum_required(VERSION 3.4.1) project(JSVMDemo) set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) # Set logging information. add_definitions( "-DLOG_DOMAIN=0xd0d0" ) add_definitions( "-DLOG_TAG=\"testTag\"" ) include_directories(${NATIVERENDER_ROOT_PATH} ${NATIVERENDER_ROOT_PATH}/include) # Add a shared library named entry from the source file hello.cpp. add_library(entry SHARED hello.cpp) # Link the entry library with the specified shared libraries. target_link_libraries(entry PUBLIC libace_napi.z.so libjsvm.so libhilog_ndk.z.so) -
Create entry/src/main/cpp/hello.cpp and implement runTest() on the native side. The code is as follows:
// entry/src/main/cpp/hello.cpp #include "napi/native_api.h" #include "hilog/log.h" #include "ark_runtime/jsvm.h" #define LOG_DOMAIN 0x3200 #define LOG_TAG "APP" static int g_aa = 0; #define CHECK_RET(theCall) \ do { \ JSVM_Status cond = theCall; \ if ((cond) != JSVM_OK) { \ const JSVM_ExtendedErrorInfo *info; \ OH_JSVM_GetLastErrorInfo(env, &info); \ OH_LOG_ERROR(LOG_APP, "jsvm fail file: %{public}s line: %{public}d ret = %{public}d message = %{public}s", \ __FILE__, __LINE__, cond, info != nullptr ? info->errorMessage : ""); \ return -1; \ } \ } while (0) #define CHECK(theCall) \ do { \ JSVM_Status cond = theCall; \ if ((cond) != JSVM_OK) { \ OH_LOG_ERROR(LOG_APP, "jsvm fail file: %{public}s line: %{public}d ret = %{public}d", __FILE__, __LINE__, \ cond); \ return -1; \ } \ } while (0) // Call theCall and check whether the return value is JSVM_OK. // If not, call OH_JSVM_GetLastErrorInfo to handle the error and return retVal. #define JSVM_CALL_BASE(env, theCall, retVal) \ do { \ JSVM_Status cond = theCall; \ if (cond != JSVM_OK) { \ const JSVM_ExtendedErrorInfo *info; \ OH_JSVM_GetLastErrorInfo(env, &info); \ OH_LOG_ERROR(LOG_APP, "jsvm fail file: %{public}s line: %{public}d ret = %{public}d message = %{public}s", \ __FILE__, __LINE__, cond, info != nullptr ? info->errorMessage : ""); \ return retVal; \ } \ } while (0) // Simplified version of JSVM_CALL_BASE, which returns nullptr. #define JSVM_CALL(theCall) JSVM_CALL_BASE(env, theCall, nullptr) // Define OH_JSVM_StrictEquals. static JSVM_Value IsStrictEquals(JSVM_Env env, JSVM_CallbackInfo info) { // Obtain the two parameters passed from JS. size_t argc = 2; JSVM_Value args[2] = {nullptr}; JSVM_CALL(OH_JSVM_GetCbInfo(env, info, &argc, args, nullptr, nullptr)); // Call OH_JSVM_StrictEquals to check whether two given JS values are strictly equal. bool result = false; JSVM_Status status = OH_JSVM_StrictEquals(env, args[0], args[1], &result); if (status != JSVM_OK) { OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_StrictEquals: failed"); } else { OH_LOG_INFO(LOG_APP, "JSVM OH_JSVM_StrictEquals: success: %{public}d", result); } JSVM_Value isStrictEqual; JSVM_CALL(OH_JSVM_GetBoolean(env, result, &isStrictEqual)); return isStrictEqual; } // Register the IsStrictEquals callback. static JSVM_CallbackStruct param[] = { {.data = nullptr, .callback = IsStrictEquals}, }; static JSVM_CallbackStruct *method = param; // Set a property descriptor named isStrictEquals and associate it with a callback. This allows the isStrictEquals callback to be called from JS. static JSVM_PropertyDescriptor descriptor[] = { {"isStrictEquals", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}, }; // Call the C++ code from JS. const char *srcCallNative = R"JS( let data = '123'; let value = 123; isStrictEquals(data,value);)JS"; static int32_t TestJSVM() { JSVM_InitOptions initOptions = {0}; JSVM_VM vm; JSVM_Env env = nullptr; JSVM_VMScope vmScope; JSVM_EnvScope envScope; JSVM_HandleScope handleScope; JSVM_Value result; // Initialize the JSVM instance. if (g_aa == 0) { g_aa++; CHECK(OH_JSVM_Init(&initOptions)); } // Create a JSVM environment. CHECK(OH_JSVM_CreateVM(nullptr, &vm)); CHECK(OH_JSVM_CreateEnv(vm, sizeof(descriptor) / sizeof(descriptor[0]), descriptor, &env)); CHECK(OH_JSVM_OpenVMScope(vm, &vmScope)); CHECK_RET(OH_JSVM_OpenEnvScope(env, &envScope)); CHECK_RET(OH_JSVM_OpenHandleScope(env, &handleScope)); // Call the test function through the script. JSVM_Script script; JSVM_Value jsSrc; CHECK_RET(OH_JSVM_CreateStringUtf8(env, srcCallNative, JSVM_AUTO_LENGTH, &jsSrc)); CHECK_RET(OH_JSVM_CompileScript(env, jsSrc, nullptr, 0, true, nullptr, &script)); CHECK_RET(OH_JSVM_RunScript(env, script, &result)); // Destroy the JSVM environment. CHECK_RET(OH_JSVM_CloseHandleScope(env, handleScope)); CHECK_RET(OH_JSVM_CloseEnvScope(env, envScope)); CHECK(OH_JSVM_CloseVMScope(vm, vmScope)); CHECK(OH_JSVM_DestroyEnv(env)); CHECK(OH_JSVM_DestroyVM(vm)); return 0; } static napi_value RunTest(napi_env env, napi_callback_info info) { TestJSVM(); return nullptr; } // Initialize the module. EXTERN_C_START static napi_value Init(napi_env env, napi_value exports) { // Implement the mappings between the ArkTS and C++ APIs. napi_property_descriptor desc[] = { {"runTest", nullptr, RunTest, nullptr, nullptr, nullptr, napi_default, nullptr} }; // Register the native RunJsVm function with the JS exports object, making the native function available to JS. napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); return exports; } EXTERN_C_END static napi_module demoModule = { .nm_version = 1, .nm_flags = 0, .nm_filename = nullptr, .nm_register_func = Init, .nm_modname = "entry", .nm_priv = ((void *)0), .reserved = {0}, }; extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); }
Calling C/C++ APIs in ArkTS
// Import the native APIs.
import napitest from 'libentry.so';
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
// runtest
napitest.runTest();
})
}
.width('100%')
}
.height('100%')
}
}
Expected result:
JSVM OH_JSVM_StrictEquals: success: 0