使用JSVM-API接口进行WebAssembly模块相关开发
===============================

#### 简介

JSVM-API WebAssembly 接口提供了 WebAssembly 字节码编译、WebAssembly 函数优化、WebAssembly cache 序列化和反序列化的能力。

权限要求：WebAssembly相关接口需要应用拥有JIT权限才能执行，可参考[JSVM 申请JIT权限指导](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/jsvm-apply-jit-profile)申请对应权限。

运行限制：当前 JSVM 版本在坚盾守护模式下将禁用 WebAssembly 全部功能模块。开发者需针对此限制进行应用兼容性评估，具体技术规范详见[JSVM 坚盾守护模式](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/jsvm-secure-shield-mode)。

#### 基本概念

*   **wasm module**：表示一个 WebAssembly 模块，(WebAssembly 简称为wasm)，通过OH\_JSVM\_CompileWasmModule可以将wasm字节码或wasm cache创建为wasm module。通过 OH\_JSVM\_IsWasmModuleObject 接口可以判断一个 JSVM\_Value 是否是一个 wasm module。
*   **wasm function**：表示 wasm module 中定义的函数，wasm function 在导出后被外部代码使用。OH\_JSVM\_CompileWasmFunction 接口提供了将 wasm function 编译为优化后的机器码的能力，方便开发者对指定 wasm function 提前编译和函数粒度的并行编译。
*   **wasm cache**：对 wasm module 中的机器码进行序列化，生成的数据被称为 wasm cache。wasm cache 的创建和释放接口分别为 OH\_JSVM\_CreateWasmCache 和 OH\_JSVM\_ReleaseCache (对应的 cacheType 为 JSVM\_CACHE\_TYPE\_WASM)。

#### 接口说明

| 接口 | 功能说明 |
| :-- | :-- |
| OH\_JSVM\_CompileWasmModule | 将 wasm 字节码同步编译为 wasm module。如果提供了 cache 参数，先尝试将 cache 反序列化为 wasm module，反序列化失败后再执行编译。如果没有 JIT 权限支持，则打印一行日志提示开发者。 |
| OH\_JSVM\_CompileWasmFunction | 将 wasm module 中指定编号的函数编译为优化后的机器码，目前只使能了最高的优化等级，函数编号的合法性由接口调用者保证。如果没有 JIT 权限支持，则打印一行日志提示开发者。 |
| OH\_JSVM\_IsWasmModuleObject | 判断传入的值是否是wasm module。 |
| OH\_JSVM\_CreateWasmCache | 将 wasm module 中的机器码序列化为 wasm cache，如果 wasm module 不包含机器码，会导致序列化失败。如果没有 JIT 权限支持，则打印一行日志提示开发者。 |
| OH\_JSVM\_ReleaseCache | 释放由 JSVM 接口生成的 cache。传入的 cacheType 和 cacheData 必须匹配，否则会产生未定义行为。 |

#### code cache 校验规格说明

| 规格 | 规格说明 |
| :-- | :-- |
| 完整性校验 | 由用户保证 |
| 兼容性校验 | 校验生成 cache 的 JSVM 版本与编译选项是否与当前一致 |
| 一致性校验 | 由用户保证 |

#### 使用示例

参考 [使用JSVM-API实现JS与C/C++语言交互开发流程](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/use-jsvm-process) 了解 JSVM-API 接口开发流程。本文仅展示接口对应的 C++ 代码。

cpp 部分代码：

// hello.cpp
#include "napi/native\_api.h"
#include "ark\_runtime/jsvm.h"
#include <hilog/log.h>
#include <vector>

#ifndef CHECK\_STATUS
#define CHECK\_STATUS(cond)                           \\
    do {                                             \\
        if (!(cond)) {                               \\
            OH\_LOG\_ERROR(LOG\_APP, "CHECK FAILED");   \\
        }                                            \\
    } while (0)
#endif

// 判断一个 JSVM\_Value 是否是 wasm module
static bool IsWasmModuleObject(JSVM\_Env env, JSVM\_Value value) {
    bool result = false;
    JSVM\_Status status = OH\_JSVM\_IsWasmModuleObject(env, value, &result);
    CHECK\_STATUS(status == JSVM\_OK);
    return result;
}

// 由 C 字符串创建 JSVM string
static JSVM\_Value CreateString(JSVM\_Env env, const char \*str) {
    JSVM\_Value jsvmStr;
    JSVM\_Status status = OH\_JSVM\_CreateStringUtf8(env, str, JSVM\_AUTO\_LENGTH, &jsvmStr);
    CHECK\_STATUS(status == JSVM\_OK);
    return jsvmStr;
}

// 由 C int32\_t 创建 JSVM number
static JSVM\_Value CreateInt32(JSVM\_Env env, int32\_t val) {
    JSVM\_Value jsvmInt32;
    JSVM\_Status status = OH\_JSVM\_CreateInt32(env, val, &jsvmInt32);
    CHECK\_STATUS(status == JSVM\_OK);
    return jsvmInt32;
}

// 对 wasm module 进行实例化
static JSVM\_Value InstantiateWasmModule(JSVM\_Env env, JSVM\_Value wasmModule) {
    JSVM\_Status status = JSVM\_OK;
    JSVM\_Value globalThis;
    status = OH\_JSVM\_GetGlobal(env, &globalThis);
    CHECK\_STATUS(status == JSVM\_OK);

    JSVM\_Value webAssembly;
    status = OH\_JSVM\_GetProperty(env, globalThis, CreateString(env, "WebAssembly"), &webAssembly);
    CHECK\_STATUS(status == JSVM\_OK);

    JSVM\_Value webAssemblyInstance;
    status = OH\_JSVM\_GetProperty(env, webAssembly, CreateString(env, "Instance"), &webAssemblyInstance);
    CHECK\_STATUS(status == JSVM\_OK);

    JSVM\_Value instance;
    JSVM\_Value argv\[\] = {wasmModule};
    status = OH\_JSVM\_NewInstance(env, webAssemblyInstance, 1, argv, &instance);
    CHECK\_STATUS(status == JSVM\_OK);
    return instance;
}

// 获取 wasm 字节码 (add 模块)
static std::vector<uint8\_t> GetAddWasmBuffer() {
    /\* 以下 wasmBuffer 对应的 wasm 字节码文本格式如下所示，只包含了一个函数 add
       (module
         (func $add (param $lhs i32) (param $rhs i32) (result i32)
           local.get $lhs
           local.get $rhs
           i32.add
         )
         (export "add" (func $add))
       )
     \*/
    std::vector<uint8\_t> wasmBuffer = {0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01,
                                       0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07,
                                       0x07, 0x01, 0x03, 0x61, 0x64, 0x64, 0x00, 0x00, 0x0a, 0x09, 0x01,
                                       0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x6a, 0x0b};
    return wasmBuffer;
}

// 验证 wasm instance 功能 (add 模块)
static void VerifyAddWasmInstance(JSVM\_Env env, JSVM\_Value wasmInstance) {
    JSVM\_Status status = JSVM\_OK;
    // 从 wasm instance 获取 exports.add 函数
    JSVM\_Value exports;
    status = OH\_JSVM\_GetProperty(env, wasmInstance, CreateString(env, "exports"), &exports);
    CHECK\_STATUS(status == JSVM\_OK);

    JSVM\_Value add;
    status = OH\_JSVM\_GetProperty(env, exports, CreateString(env, "add"), &add);
    CHECK\_STATUS(status == JSVM\_OK);

    // 执行 exports.add(1, 2)，期望得到结果 3
    JSVM\_Value undefined;
    OH\_JSVM\_GetUndefined(env, &undefined);
    JSVM\_Value one = CreateInt32(env, 1);
    JSVM\_Value two = CreateInt32(env, 2);
    JSVM\_Value argv\[\] = {one, two};
    JSVM\_Value result;
    status = OH\_JSVM\_CallFunction(env, undefined, add, 2, argv, &result);
    CHECK\_STATUS(status == JSVM\_OK);
    int32\_t resultInt32 = 0;
    OH\_JSVM\_GetValueInt32(env, result, &resultInt32);
    CHECK\_STATUS(resultInt32 == 3);
}

// WebAssembly demo 主函数
static JSVM\_Value WasmDemo(JSVM\_Env env, JSVM\_CallbackInfo info) {
    JSVM\_Status status = JSVM\_OK;
    std::vector<uint8\_t> wasmBuffer = GetAddWasmBuffer();
    uint8\_t \*wasmBytecode = wasmBuffer.data();
    size\_t wasmBytecodeLength = wasmBuffer.size();
    JSVM\_Value wasmModule;
    // 根据 wasm 字节码得到 wasm module
    status = OH\_JSVM\_CompileWasmModule(env, wasmBytecode, wasmBytecodeLength, NULL, 0, NULL, &wasmModule);
    CHECK\_STATUS(status == JSVM\_OK);
    CHECK\_STATUS(IsWasmModuleObject(env, wasmModule));

    // 对当前 wasm module 中定义的第一个函数 (即 add) 执行编译优化
    int32\_t functionIndex = 0;
    // 注意：当前只支持 high level optimization，即传入 JSVM\_WASM\_OPT\_BASELINE 和传入 JSVM\_WASM\_OPT\_HIGH 效果是一样的
    status = OH\_JSVM\_CompileWasmFunction(env, wasmModule, functionIndex, JSVM\_WASM\_OPT\_HIGH);
    CHECK\_STATUS(status == JSVM\_OK);
    // 对编译得到的 wasm module 进行实例化
    JSVM\_Value wasmInstance = InstantiateWasmModule(env, wasmModule);
    // 对实例化的 wasm instance 中的函数进行功能验证
    VerifyAddWasmInstance(env, wasmInstance);

    // 创建 wasm cache
    const uint8\_t \*wasmCacheData = NULL;
    size\_t wasmCacheLength = 0;
    status = OH\_JSVM\_CreateWasmCache(env, wasmModule, &wasmCacheData, &wasmCacheLength);
    CHECK\_STATUS(status == JSVM\_OK);
    // 期望 wasm cache 创建成功
    CHECK\_STATUS(wasmCacheData != NULL);
    CHECK\_STATUS(wasmCacheLength > 0);

    // 通过将 wasm cache 赋值来模拟 cache 持久化，实际使用场景可能将 wasm cache 保存到文件
    std::vector<uint8\_t> cacheBuffer(wasmCacheData, wasmCacheData + wasmCacheLength);

    // cache 一旦保存完成后，需要显式释放，以免发生内存泄露
    // 注意：传入的 JSVM\_CacheType 必须匹配
    status = OH\_JSVM\_ReleaseCache(env, wasmCacheData, JSVM\_CACHE\_TYPE\_WASM);
    CHECK\_STATUS(status == JSVM\_OK);

    // 使用 wasm code 反序列化来生成 wasm module
    bool cacheRejected = false;
    JSVM\_Value wasmModule2;
    status = OH\_JSVM\_CompileWasmModule(env, wasmBytecode, wasmBytecodeLength, cacheBuffer.data(), cacheBuffer.size(),
                                       &cacheRejected, &wasmModule2);
   
    // 传入的 wasm cache 如果是匹配的，且内部校验通过 (如版本)，则会接受 cache
    CHECK\_STATUS(!cacheRejected);
    CHECK\_STATUS(IsWasmModuleObject(env, wasmModule2));

    // 对反序列化得到的 wasmModule2 进行同样的操作：函数编译、实例化、验证功能，期望也都是通过的
    status = OH\_JSVM\_CompileWasmFunction(env, wasmModule2, functionIndex, JSVM\_WASM\_OPT\_HIGH);
    CHECK\_STATUS(status == JSVM\_OK);

    JSVM\_Value wasmInstance2 = InstantiateWasmModule(env, wasmModule2);
    VerifyAddWasmInstance(env, wasmInstance2);

    JSVM\_Value result;
    OH\_JSVM\_GetBoolean(env, true, &result);
    return result;
}

// WasmDemo 方法注册回调
static JSVM\_CallbackStruct param\[\] = {
    {.data = nullptr, .callback = WasmDemo}
};
static JSVM\_CallbackStruct \*method = param;
// 将 C++ callback WasmDemo 函数注册为 JSVM globalThis.wasmDemo 属性，供 JS 侧调用
static JSVM\_PropertyDescriptor descriptor\[\] = {
    {"wasmDemo", nullptr, method++, nullptr, nullptr, nullptr, JSVM\_DEFAULT},
};

// 样例测试js
const char \*srcCallNative = R"JS(wasmDemo())JS";

预期输出：无报错