使用JSVM-API实现JS与C/C++语言交互开发流程
============================

使用JSVM-API实现跨语言交互，首先需按其机制注册和加载模块。

*   ArkTS/JS侧：实现C++方法的调用。代码比较简单，import一个对应的so库后，即可调用C++方法。
    
*   Native侧：.cpp文件，实现模块的注册。需要提供注册lib库的名称，并在注册回调方法中定义接口的映射关系，即Native方法及对应的JS/ArkTS接口名称等。
    

此处以在ArkTS/JS侧和Native侧实现RunTest()接口实现跨语言交互为例，展示使用JSVM-API进行跨语言交互的流程。

#### 创建Native C++工程

具体见[创建NDK工程](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/create-with-ndk)

#### Native侧方法的实现

参考[使用Node-API实现跨语言交互开发流程](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/use-napi-process#native侧方法的实现)，以下代码提供了“使用JSVM-API实现JS与C/C++语言交互开发流程”Native侧方法实现的一个demo。

*   在index.d.ts文件中，提供JS侧的接口方法。
    
    export const runTest: () => void;
    
*   在oh-package.json5文件中将index.d.ts与cpp文件关联起来。
    
    {
      "name": "libentry.so",
      "types": "./index.d.ts",
      "version": "",
      "description": "Please describe the basic information."
    }
    
*   在CMakeLists.txt文件中配置CMake打包参数。
    
    \# entry/src/main/cpp/CMakeLists.txt
    cmake\_minimum\_required(VERSION 3.4.1)
    project(JSVMDemo)
    
    set(NATIVERENDER\_ROOT\_PATH ${CMAKE\_CURRENT\_SOURCE\_DIR})
    # 日志打印配置
    add\_definitions( "-DLOG\_DOMAIN=0xd0d0" )
    add\_definitions( "-DLOG\_TAG=\\"testTag\\"" )
    include\_directories(${NATIVERENDER\_ROOT\_PATH}
                        ${NATIVERENDER\_ROOT\_PATH}/include)
    
    # 添加名为entry的库
    add\_library(entry SHARED hello.cpp)
    # 构建此可执行文件需要链接的库
    target\_link\_libraries(entry PUBLIC libace\_napi.z.so libjsvm.so libhilog\_ndk.z.so)
    
*   新建entry/src/main/cpp/hello.cpp，实现Native侧的runTest接口。具体代码如下：
    
    #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)
    
    // 用于调用theCall并检查其返回值是否为JSVM\_OK。
    // 如果不是，则调用OH\_JSVM\_GetLastErrorInfo处理错误并返回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)
    
    // JSVM\_CALL\_BASE的简化版本，返回nullptr
    #define JSVM\_CALL(theCall) JSVM\_CALL\_BASE(env, theCall, nullptr)
    
    // OH\_JSVM\_StrictEquals的样例方法
    static JSVM\_Value IsStrictEquals(JSVM\_Env env, JSVM\_CallbackInfo info)
    {
        // 接受两个入参
        size\_t argc = 2;
        JSVM\_Value args\[2\] = {nullptr};
        JSVM\_CALL(OH\_JSVM\_GetCbInfo(env, info, &argc, args, nullptr, nullptr));
        // 调用OH\_JSVM\_StrictEquals接口判断给定的两个JavaScript value是否严格相等
        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;
    }
    // IsStrictEquals注册回调
    static JSVM\_CallbackStruct param\[\] = {
        {.data = nullptr, .callback = IsStrictEquals},
    };
    static JSVM\_CallbackStruct \*method = param;
    // IsStrictEquals方法别名，供JS调用
    static JSVM\_PropertyDescriptor descriptor\[\] = {
        {"isStrictEquals", nullptr, method++, nullptr, nullptr, nullptr, JSVM\_DEFAULT},
    };
    // 样例测试js
    const char \*SRC\_CALL\_NATIVE = 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;
        // 初始化JavaScript引擎实例
        if (g\_aa == 0) {
            g\_aa++;
            CHECK(OH\_JSVM\_Init(&initOptions));
        }
        // 创建JSVM环境
        CHECK(OH\_JSVM\_CreateVM(nullptr, &vm));
        CHECK(OH\_JSVM\_OpenVMScope(vm, &vmScope));
        CHECK(OH\_JSVM\_CreateEnv(vm, sizeof(descriptor) / sizeof(descriptor\[0\]), descriptor, &env));
        CHECK\_RET(OH\_JSVM\_OpenEnvScope(env, &envScope));
        CHECK\_RET(OH\_JSVM\_OpenHandleScope(env, &handleScope));
    
        // 通过script调用测试函数
        JSVM\_Script script;
        JSVM\_Value jsSrc;
        CHECK\_RET(OH\_JSVM\_CreateStringUtf8(env, SRC\_CALL\_NATIVE, JSVM\_AUTO\_LENGTH, &jsSrc));
        CHECK\_RET(OH\_JSVM\_CompileScript(env, jsSrc, nullptr, 0, true, nullptr, &script));
        CHECK\_RET(OH\_JSVM\_RunScript(env, script, &result));
    
        // 销毁JSVM环境
        CHECK\_RET(OH\_JSVM\_CloseHandleScope(env, handleScope));
        CHECK\_RET(OH\_JSVM\_CloseEnvScope(env, envScope));
        CHECK(OH\_JSVM\_DestroyEnv(env));
        CHECK(OH\_JSVM\_CloseVMScope(vm, vmScope));
        CHECK(OH\_JSVM\_DestroyVM(vm));
        return 0;
    }
    
    static napi\_value RunTest(napi\_env env, napi\_callback\_info info)
    {
        TestJSVM();
        return nullptr;
    }
    
    // 模块注册信息，供arkts侧调用
    EXTERN\_C\_START
    static napi\_value Init(napi\_env env, napi\_value exports)
    {
        napi\_property\_descriptor desc\[\] = {{"runTest", nullptr, RunTest, nullptr, nullptr, nullptr, napi\_default, nullptr}};
        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); }
    

#### ArkTS侧调用C/C++方法实现

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(() => {
            try {
              napitest.runTest();
              this.message = 'success';
            } catch (error) {
              console.error('An error occurred: ', error);
              this.message = 'fail';
            }
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

预期输出结果

JSVM OH\_JSVM\_StrictEquals: success: 0