1# 使用JSVM-API实现JS与C/C++语言交互开发流程 2<!--Kit: NDK Development--> 3<!--Subsystem: arkcompiler--> 4<!--Owner: @yuanxiaogou; @string_sz--> 5<!--Designer: @knightaoko--> 6<!--Tester: @test_lzz--> 7<!--Adviser: @fang-jinxu--> 8 9使用JSVM-API实现跨语言交互,首先需按其机制注册和加载模块。 10 11- ArkTS/JS侧:实现C++方法的调用。代码比较简单,import一个对应的so库后,即可调用C++方法。 12 13- Native侧:.cpp文件,实现模块的注册。需要提供注册lib库的名称,并在注册回调方法中定义接口的映射关系,即Native方法及对应的JS/ArkTS接口名称等。 14 15此处以在ArkTS/JS侧和Native侧实现RunJsVm()接口实现跨语言交互为例,展示使用JSVM-API进行跨语言交互的流程。 16 17## 创建Native C++工程 18 19具体见[创建NDK工程](create-with-ndk.md) 20 21## Native侧方法的实现 22 23参考[使用Node-API实现跨语言交互开发流程](use-napi-process.md#native侧方法的实现),以下代码提供了“使用JSVM-API实现JS与C/C++语言交互开发流程”Native侧方法实现的一个demo。 24 25- 在index.d.ts文件中,提供JS侧的接口方法。 26 27 ```ts 28 // entry/src/main/cpp/types/libentry/index.d.ts 29 export const runTest: () => void; 30 ``` 31 <!-- @[export_native](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/JSVMAPI/JsvmProcess/entry/src/main/cpp/types/libentry/Index.d.ts) --> 32 33- 在oh-package.json5文件中将index.d.ts与cpp文件关联起来。 34 35 ```json 36 { 37 "name": "libentry.so", 38 "types": "./index.d.ts", 39 "version": "", 40 "description": "Please describe the basic information." 41 } 42 ``` 43 44- 在CMakeLists.txt文件中配置CMake打包参数。 45 46 ```text 47 # entry/src/main/cpp/CMakeLists.txt 48 cmake_minimum_required(VERSION 3.4.1) 49 project(JSVMDemo) 50 51 set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) 52 # 日志打印配置 53 add_definitions( "-DLOG_DOMAIN=0xd0d0" ) 54 add_definitions( "-DLOG_TAG=\"testTag\"" ) 55 include_directories(${NATIVERENDER_ROOT_PATH} 56 ${NATIVERENDER_ROOT_PATH}/include) 57 58 # 添加名为entry的库 59 add_library(entry SHARED hello.cpp) 60 # 构建此可执行文件需要链接的库 61 target_link_libraries(entry PUBLIC libace_napi.z.so libjsvm.so libhilog_ndk.z.so) 62 ``` 63 64- 新建entry/src/main/cpp/hello.cpp,实现Native侧的runTest接口。具体代码如下: 65 66 ```cpp 67 // entry/src/main/cpp/hello.cpp 68 #include "napi/native_api.h" 69 #include "hilog/log.h" 70 #include "ark_runtime/jsvm.h" 71 72 #define LOG_DOMAIN 0x3200 73 #define LOG_TAG "APP" 74 75 static int g_aa = 0; 76 77 #define CHECK_RET(theCall) \ 78 do { \ 79 JSVM_Status cond = theCall; \ 80 if ((cond) != JSVM_OK) { \ 81 const JSVM_ExtendedErrorInfo *info; \ 82 OH_JSVM_GetLastErrorInfo(env, &info); \ 83 OH_LOG_ERROR(LOG_APP, "jsvm fail file: %{public}s line: %{public}d ret = %{public}d message = %{public}s", \ 84 __FILE__, __LINE__, cond, info != nullptr ? info->errorMessage : ""); \ 85 return -1; \ 86 } \ 87 } while (0) 88 89 #define CHECK(theCall) \ 90 do { \ 91 JSVM_Status cond = theCall; \ 92 if ((cond) != JSVM_OK) { \ 93 OH_LOG_ERROR(LOG_APP, "jsvm fail file: %{public}s line: %{public}d ret = %{public}d", __FILE__, __LINE__, \ 94 cond); \ 95 return -1; \ 96 } \ 97 } while (0) 98 99 // 用于调用theCall并检查其返回值是否为JSVM_OK。 100 // 如果不是,则调用OH_JSVM_GetLastErrorInfo处理错误并返回retVal。 101 #define JSVM_CALL_BASE(env, theCall, retVal) \ 102 do { \ 103 JSVM_Status cond = theCall; \ 104 if (cond != JSVM_OK) { \ 105 const JSVM_ExtendedErrorInfo *info; \ 106 OH_JSVM_GetLastErrorInfo(env, &info); \ 107 OH_LOG_ERROR(LOG_APP, "jsvm fail file: %{public}s line: %{public}d ret = %{public}d message = %{public}s", \ 108 __FILE__, __LINE__, cond, info != nullptr ? info->errorMessage : ""); \ 109 return retVal; \ 110 } \ 111 } while (0) 112 113 // JSVM_CALL_BASE的简化版本,返回nullptr 114 #define JSVM_CALL(theCall) JSVM_CALL_BASE(env, theCall, nullptr) 115 116 // OH_JSVM_StrictEquals的样例方法 117 static JSVM_Value IsStrictEquals(JSVM_Env env, JSVM_CallbackInfo info) { 118 // 接受两个入参 119 size_t argc = 2; 120 JSVM_Value args[2] = {nullptr}; 121 JSVM_CALL(OH_JSVM_GetCbInfo(env, info, &argc, args, nullptr, nullptr)); 122 // 调用OH_JSVM_StrictEquals接口判断给定的两个JavaScript value是否严格相等 123 bool result = false; 124 JSVM_Status status = OH_JSVM_StrictEquals(env, args[0], args[1], &result); 125 if (status != JSVM_OK) { 126 OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_StrictEquals: failed"); 127 } else { 128 OH_LOG_INFO(LOG_APP, "JSVM OH_JSVM_StrictEquals: success: %{public}d", result); 129 } 130 JSVM_Value isStrictEqual; 131 JSVM_CALL(OH_JSVM_GetBoolean(env, result, &isStrictEqual)); 132 return isStrictEqual; 133 } 134 // IsStrictEquals注册回调 135 static JSVM_CallbackStruct param[] = { 136 {.data = nullptr, .callback = IsStrictEquals}, 137 }; 138 static JSVM_CallbackStruct *method = param; 139 // IsStrictEquals方法别名,供JS调用 140 static JSVM_PropertyDescriptor descriptor[] = { 141 {"isStrictEquals", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 142 }; 143 // 样例测试js 144 const char *srcCallNative = R"JS( let data = '123'; 145 let value = 123; 146 isStrictEquals(data,value);)JS"; 147 148 static int32_t TestJSVM() { 149 JSVM_InitOptions initOptions = {0}; 150 JSVM_VM vm; 151 JSVM_Env env = nullptr; 152 JSVM_VMScope vmScope; 153 JSVM_EnvScope envScope; 154 JSVM_HandleScope handleScope; 155 JSVM_Value result; 156 // 初始化JavaScript引擎实例 157 if (g_aa == 0) { 158 g_aa++; 159 CHECK(OH_JSVM_Init(&initOptions)); 160 } 161 // 创建JSVM环境 162 CHECK(OH_JSVM_CreateVM(nullptr, &vm)); 163 CHECK(OH_JSVM_CreateEnv(vm, sizeof(descriptor) / sizeof(descriptor[0]), descriptor, &env)); 164 CHECK(OH_JSVM_OpenVMScope(vm, &vmScope)); 165 CHECK_RET(OH_JSVM_OpenEnvScope(env, &envScope)); 166 CHECK_RET(OH_JSVM_OpenHandleScope(env, &handleScope)); 167 168 // 通过script调用测试函数 169 JSVM_Script script; 170 JSVM_Value jsSrc; 171 CHECK_RET(OH_JSVM_CreateStringUtf8(env, srcCallNative, JSVM_AUTO_LENGTH, &jsSrc)); 172 CHECK_RET(OH_JSVM_CompileScript(env, jsSrc, nullptr, 0, true, nullptr, &script)); 173 CHECK_RET(OH_JSVM_RunScript(env, script, &result)); 174 175 // 销毁JSVM环境 176 CHECK_RET(OH_JSVM_CloseHandleScope(env, handleScope)); 177 CHECK_RET(OH_JSVM_CloseEnvScope(env, envScope)); 178 CHECK(OH_JSVM_CloseVMScope(vm, vmScope)); 179 CHECK(OH_JSVM_DestroyEnv(env)); 180 CHECK(OH_JSVM_DestroyVM(vm)); 181 return 0; 182 } 183 184 static napi_value RunTest(napi_env env, napi_callback_info info) 185 { 186 TestJSVM(); 187 return nullptr; 188 } 189 190 // 模块初始化 191 EXTERN_C_START 192 static napi_value Init(napi_env env, napi_value exports) { 193 // 实现ArkTS接口与C++接口的绑定和映射 194 napi_property_descriptor desc[] = { 195 {"runTest", nullptr, RunTest, nullptr, nullptr, nullptr, napi_default, nullptr} 196 }; 197 // 在exports对象上挂载RunJsVm的Native方法 198 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 199 return exports; 200 } 201 EXTERN_C_END 202 203 static napi_module demoModule = { 204 .nm_version = 1, 205 .nm_flags = 0, 206 .nm_filename = nullptr, 207 .nm_register_func = Init, 208 .nm_modname = "entry", 209 .nm_priv = ((void *)0), 210 .reserved = {0}, 211 }; 212 213 extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); } 214 ``` 215 <!-- @[oh_jsvm_process](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/JSVMAPI/JsvmProcess/entry/src/main/cpp/hello.cpp) --> 216 217## ArkTS侧调用C/C++方法实现 218 219```ts 220import hilog from '@ohos.hilog'; 221// 通过import的方式,引入Native能力。 222import napitest from 'libentry.so'; 223 224@Entry 225@Component 226struct Index { 227 @State message: string = 'Hello World'; 228 229 build() { 230 Row() { 231 Column() { 232 Text(this.message) 233 .fontSize(50) 234 .fontWeight(FontWeight.Bold) 235 .onClick(() => { 236 // runtest 237 napitest.runTest(); 238 }) 239 } 240 .width('100%') 241 } 242 .height('100%') 243 } 244} 245``` 246<!-- @[call_native_cpp](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/JSVMAPI/JsvmProcess/entry/src/main/ets/pages/Index.ets) --> 247 248预期输出结果 249```ts 250JSVM OH_JSVM_StrictEquals: success: 0 251```