• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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