1# 使用JSVM-API实现JS与C/C++语言交互开发流程 2 3使用JSVM-API实现跨语言交互,首先需要按照JSVM-API的机制实现模块的注册和加载等相关动作。 4 5- ArkTS/JS侧:实现C++方法的调用。代码比较简单,import一个对应的so库后,即可调用C++方法。 6 7- Native侧:.cpp文件,实现模块的注册。需要提供注册lib库的名称,并在注册回调方法中定义接口的映射关系,即Native方法及对应的JS/ArkTS接口名称等。 8 9此处以在ArkTS/JS侧实现RunJsVm()接口、在Native侧实现RunJsVm()接口,从而实现跨语言交互为例,呈现使用JSVM-API进行跨语言交互的流程。 10 11## 创建Native C++工程 12 13具体见[创建NDK工程](create-with-ndk.md) 14 15## Native侧方法的实现 16 17- 设置模块注册信息 18 19 具体见[设置模块注册信息](use-napi-process.md#native侧方法的实现) 20 21- 模块初始化 22 23 实现ArkTS接口与C++接口的绑定和映射。 24 25 ```cpp 26 // entry/src/main/cpp/hello.cpp 27 EXTERN_C_START 28 // 模块初始化 29 static napi_value Init(napi_env env, napi_value exports) 30 { 31 // ArkTS接口与C++接口的绑定和映射 32 napi_property_descriptor desc[] = { 33 {"runJsVm", nullptr, RunJsVm, nullptr, nullptr, nullptr, napi_default, nullptr}, 34 }; 35 // 在exports对象上挂载RunJsVm的Native方法 36 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 37 return exports; 38 } 39 EXTERN_C_END 40 ``` 41 42- 在index.d.ts文件中,提供JS侧的接口方法。 43 44 ```ts 45 // entry/src/main/cpp/types/libentry/index.d.ts 46 export const runJsVm: (jsCode: string) => void; 47 ``` 48 49- 在oh-package.json5文件中将index.d.ts与cpp文件关联起来。 50 51 ```json 52 { 53 "name": "libentry.so", 54 "types": "./index.d.ts", 55 "version": "", 56 "description": "Please describe the basic information." 57 } 58 ``` 59 60- 在CMakeLists.txt文件中配置CMake打包参数。 61 62 ```text 63 # entry/src/main/cpp/CMakeLists.txt 64 cmake_minimum_required(VERSION 3.4.1) 65 project(JSVMDemo) 66 67 set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) 68 # 日志打印配置 69 add_definitions( "-DLOG_DOMAIN=0xd0d0" ) 70 add_definitions( "-DLOG_TAG=\"testTag\"" ) 71 include_directories(${NATIVERENDER_ROOT_PATH} 72 ${NATIVERENDER_ROOT_PATH}/include) 73 74 # 添加名为entry的库 75 add_library(entry SHARED hello.cpp) 76 # 构建此可执行文件需要链接的库 77 target_link_libraries(entry PUBLIC libace_napi.z.so libjsvm.so libhilog_ndk.z.so) 78 ``` 79 80- 实现Native侧的RunJsVm接口。具体代码如下: 81 82 ```cpp 83 // entry/src/main/cpp/hello.cpp 84 #include "napi/native_api.h" 85 #include "ark_runtime/jsvm.h" 86 #include <sys/stat.h> 87 #include <thread> 88 #include <unistd.h> 89 #include <bits/alltypes.h> 90 #include <hilog/log.h> 91 static int g_aa = 0; 92 static JSVM_Value Assert(JSVM_Env env, JSVM_CallbackInfo info) 93 { 94 size_t argc = 2; 95 JSVM_Value args[2] = {nullptr}; 96 OH_JSVM_GetCbInfo(env, info, &argc, args, nullptr, nullptr); 97 bool isStrictEquals; 98 if (argc > 1) { 99 OH_JSVM_StrictEquals(env, args[0], args[1], &isStrictEquals); 100 } else { 101 JSVM_Value valTrue; 102 OH_JSVM_GetBoolean(env, true, &valTrue); 103 OH_JSVM_StrictEquals(env, args[0], valTrue, &isStrictEquals); 104 } 105 if (isStrictEquals) { 106 OH_LOG_INFO(LOG_APP, "JSVM API TEST RESULT: PASS"); 107 } else { 108 OH_LOG_ERROR(LOG_APP, "JSVM API TEST RESULT: FAILED"); 109 } 110 JSVM_Value result; 111 OH_JSVM_GetBoolean(env, isStrictEquals, &result); 112 return result; 113 } 114 115 // Assert方法注册回调 116 static JSVM_CallbackStruct param[] = { 117 {.data = nullptr, .callback = Assert}, 118 }; 119 static JSVM_CallbackStruct *method = param; 120 // Assert方法别名,TS侧调用 121 static JSVM_PropertyDescriptor descriptor[] = { 122 {"assert", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 123 }; 124 125 // 获取JavaScript代码字符串 126 static std::string getCodeString(napi_env nEnv, napi_callback_info nInfo) { 127 size_t argc = 1; 128 napi_value argv[1] = {nullptr}; 129 napi_get_cb_info(nEnv, nInfo, &argc, argv, nullptr, nullptr); 130 std::string jsCodeStr; 131 size_t result = 0; 132 napi_get_value_string_utf8(nEnv, argv[0], nullptr, NAPI_AUTO_LENGTH, &result); 133 const size_t bufSize = result + 1; 134 char *buf = new char[bufSize]; 135 napi_status status = napi_get_value_string_utf8(nEnv, argv[0], buf, bufSize, &result); 136 if (status == napi_ok) { 137 jsCodeStr = buf; 138 } 139 free(buf); 140 return jsCodeStr; 141 } 142 143 // javascript代码执行失败的处理 144 void RunScriptFail(napi_env &nEnv, JSVM_Env &env, JSVM_Status &res) { 145 JSVM_Value exceptionValue; 146 JSVM_Status status = OH_JSVM_GetAndClearLastException(env, &exceptionValue); 147 if (status == JSVM_OK) { 148 JSVM_Value message; 149 OH_JSVM_GetNamedProperty(env, exceptionValue, "message", &message); 150 size_t length; 151 OH_JSVM_GetValueStringUtf8(env, message, nullptr, 0, &length); 152 char *buffer = new char[length + 1]; 153 OH_JSVM_GetValueStringUtf8(env, message, buffer, length + 1, nullptr); 154 napi_throw_error(nEnv, std::to_string(static_cast<int>(status)).c_str(), buffer); 155 delete[] buffer; 156 } else { 157 napi_throw_error(nEnv, std::to_string(static_cast<int>(status)).c_str(), nullptr); 158 OH_LOG_INFO(LOG_APP, "JSVM RunJsVm failed Error code: %{public}d", static_cast<int>(res)); 159 } 160 } 161 // RunJsVm函数创建JavaScript VM,创建JS执行的上下文环境来执行JS代码获取JSVM_Value。 162 static napi_value RunJsVm(napi_env nEnv, napi_callback_info nInfo) { 163 std::string jsCodeStr = getCodeString(nEnv, nInfo); 164 JSVM_InitOptions initOptions; 165 memset(&initOptions, 0, sizeof(initOptions)); 166 // InitOptions外部引用赋值 167 initOptions.externalReferences = nullptr; 168 if (g_aa == 0) { 169 g_aa++; 170 // 初始化JavaScript引擎实例 171 OH_JSVM_Init(&initOptions); 172 } 173 JSVM_VM vm; 174 JSVM_CreateVMOptions options; 175 memset(&options, 0, sizeof(options)); 176 // 创建JavaScript引擎实例 177 OH_JSVM_CreateVM(&options, &vm); 178 JSVM_VMScope vmScope; 179 // 打开一个新的VM scope,引擎实例只能在scope范围内使用,可以保证引擎实例不被销毁 180 OH_JSVM_OpenVMScope(vm, &vmScope); 181 JSVM_Env env; 182 // 创建一个新的JS执行上下文环境,并注册指定的Native函数 183 JSVM_Status res = OH_JSVM_CreateEnv(vm, sizeof(descriptor) / sizeof(descriptor[0]), descriptor, &env); 184 if (res != JSVM_OK) { 185 OH_LOG_INFO(LOG_APP, "JSVM API OH_JSVM_CreateEnv failed res is %{public}d", static_cast<int>(res)); 186 } 187 JSVM_EnvScope envScope; 188 // 打开一个新的Env scope,Env只能在scope范围内使用 189 OH_JSVM_OpenEnvScope(env, &envScope); 190 JSVM_HandleScope handleScope; 191 // 打开一个Handle scope 192 OH_JSVM_OpenHandleScope(env, &handleScope); 193 std::string sourceCodeStr = jsCodeStr; 194 napi_value nResult = nullptr; 195 JSVM_Value jsVmResult; 196 OH_LOG_INFO(LOG_APP, "JSVM API RunJsVm %{public}s", sourceCodeStr.c_str()); 197 // 根据传入的JavaScript代码字符串判断是否调用获取自定义结构数据的方法 198 if (strcmp(sourceCodeStr.c_str(), "defineClass") == 0) { 199 // DefineClass预留 200 OH_LOG_INFO(LOG_APP, "JSVM API DefineClass called"); 201 } else { 202 JSVM_Value sourceCodeValue = nullptr; 203 OH_JSVM_CreateStringUtf8(env, sourceCodeStr.c_str(), sourceCodeStr.size(), &sourceCodeValue); 204 JSVM_Script script; 205 // 编译JavaScript代码字符串并返回编译后的脚本 206 OH_JSVM_CompileScript(env, sourceCodeValue, nullptr, 0, true, nullptr, &script); 207 // 执行JavaScript代码字符串 208 res = OH_JSVM_RunScript(env, script, &jsVmResult); 209 if (res != JSVM_OK) { 210 RunScriptFail(nEnv, env, res); 211 napi_get_boolean(nEnv, false, &nResult); 212 } else { 213 napi_get_boolean(nEnv, true, &nResult); 214 } 215 } 216 // 关闭Handle scope 217 OH_JSVM_CloseHandleScope(env, handleScope); 218 // 关闭Env scope 219 OH_JSVM_CloseEnvScope(env, envScope); 220 // 关闭VM scope 221 OH_JSVM_CloseVMScope(vm, vmScope); 222 // 销毁一个JS执行上下文环境 223 OH_JSVM_DestroyEnv(env); 224 // 销毁JavaScript引擎实例 225 OH_JSVM_DestroyVM(vm); 226 return nResult; 227 } 228 ``` 229 230## ArkTS侧调用C/C++方法实现 231 232```ts 233import hilog from '@ohos.hilog' 234// 通过import的方式,引入Native能力。 235import napitest from 'libentry.so' 236// test assert 237try { 238 let data = false; 239 let script: string = `assert(${data});`; 240 let result = napitest.runJsVm(script); 241 hilog.info(0x0000, 'testJSVM', 'Test JSVM Assert:%{public}s', result); 242} catch (error) { 243 hilog.error(0x0000, 'testJSVM', 'Test JSVM Assert error: %{public}s', error); 244} 245``` 246