1# 使用JSVM-API接口进行WebAssembly模块相关开发 2 3 4## 简介 5 6JSVM-API WebAssembly 接口提供了 WebAssembly 字节码编译、WebAssembly 函数优化、WebAssembly cache 序列化和反序列化的能力。 7 8## 基本概念 9 10- **wasm module**:表示一个 WebAssembly 模块,(WebAssembly 简称为wasm),通过 `OH_JSVM_CompileWasmModule` 接口可以从 wasm 字节码或 wasm cache 创建 wasm module。通过 `OH_JSVM_IsWasmModuleObject` 接口可以判断一个 JSVM_Value 是否是一个 wasm module。 11- **wasm function**:表示 wasm module 中定义的函数,wasm function 在导出后被外部代码使用。`OH_JSVM_CompileWasmFunction` 接口提供了将 wasm function 编译为优化后的机器码的能力,方便开发者对指定 wasm function 提前编译和函数粒度的并行编译。 12- **wasm cache**:对 wasm module 中的机器码进行序列化,生成的数据被称为 wasm cache。wasm cache 的创建和释放接口分别为 `OH_JSVM_CreateWasmCache` 和 `OH_JSVM_ReleaseCache` (对应的 cacheType 为 `JSVM_CACHE_TYPE_WASM`)。 13 14## 接口说明 15 16| 接口 | 功能说明 | 17| --------------------------- | ------------------------------------------------------------------------------------ | 18| OH_JSVM_CompileWasmModule | 将 wasm 字节码同步编译为 wasm module。如果提供了 cache 参数,先尝试将 cache 反序列为 wasm module,反序列化失败时再执行编译。 | 19| OH_JSVM_CompileWasmFunction | 将 wasm module 中指定编号的函数编译为优化后的机器码,目前只使能了最高的优化等级,函数编号的合法性由接口调用者保证。 | 20| OH_JSVM_IsWasmModuleObject | 判断传入的值是否是一个 wasm module。 | 21| OH_JSVM_CreateWasmCache | 将 wasm module 中的机器码序列化为 wasm cache,如果 wasm module 不包含机器码,则会序列化失败。 | 22| OH_JSVM_ReleaseCache | 释放由 JSVM 接口生成的 cache。传入的 cacheType 和 cacheData 必须匹配,否则会产生未定义行为。 | 23 24## 使用示例 25 26JSVM-API 接口开发流程参考 [使用JSVM-API实现JS与C/C++语言交互开发流程](use-jsvm-process.md),本文仅对接口对应 C++ 相关代码进行展示。 27 28cpp 部分代码: 29 30```cpp 31// hello.cpp 32#include "napi/native_api.h" 33#include "ark_runtime/jsvm.h" 34#include <hilog/log.h> 35 36#ifndef CHECK 37#define CHECK(cond) \ 38 do { \ 39 if (!(cond)) { \ 40 OH_LOG_ERROR(LOG_APP, "CHECK FAILED"); \ 41 abort(); \ 42 } \ 43 } while (0) 44#endif 45 46// 判断一个 JSVM_Value 是否是 wasm module 47static bool IsWasmModuleObject(JSVM_Env env, JSVM_Value value) { 48 bool result; 49 JSVM_Status status = OH_JSVM_IsWasmModuleObject(env, value, &result); 50 CHECK(status == JSVM_OK); 51 return result; 52} 53 54// 由 C 字符串创建 JSVM string 55static JSVM_Value CreateString(JSVM_Env env, const char *str) { 56 JSVM_Value jsvmStr; 57 JSVM_Status status = OH_JSVM_CreateStringUtf8(env, str, JSVM_AUTO_LENGTH, &jsvmStr); 58 CHECK(status == JSVM_OK); 59 return jsvmStr; 60} 61 62// 由 C int32_t 创建 JSVM number 63static JSVM_Value CreateInt32(JSVM_Env env, int32_t val) { 64 JSVM_Value jsvmInt32; 65 JSVM_Status status = OH_JSVM_CreateInt32(env, val, &jsvmInt32); 66 CHECK(status == JSVM_OK); 67 return jsvmInt32; 68} 69 70// 对 wasm module 进行实例化 71static JSVM_Value InstantiateWasmModule(JSVM_Env env, JSVM_Value wasmModule) { 72 JSVM_Status status = JSVM_OK; 73 JSVM_Value globalThis; 74 status = OH_JSVM_GetGlobal(env, &globalThis); 75 CHECK(status == JSVM_OK); 76 77 JSVM_Value webAssembly; 78 status = OH_JSVM_GetProperty(env, globalThis, CreateString(env, "WebAssembly"), &webAssembly); 79 CHECK(status == JSVM_OK); 80 81 JSVM_Value webAssemblyInstance; 82 status = OH_JSVM_GetProperty(env, webAssembly, CreateString(env, "Instance"), &webAssemblyInstance); 83 CHECK(status == JSVM_OK); 84 85 JSVM_Value instance; 86 JSVM_Value argv[] = {wasmModule}; 87 status = OH_JSVM_NewInstance(env, WebAssemblyInstance, 1, argv, &instance); 88 CHECK(status == JSVM_OK); 89 return instance; 90} 91 92// 获取 wasm 字节码 (add 模块) 93static std::vector<uint8_t> GetAddWasmBuffer() { 94 // 以下 wasmBuffer 对应的 wasm 字节码文本格式如下所示,只包含了一个函数 add 95 // (module 96 // (func $add (param $lhs i32) (param $rhs i32) (result i32) 97 // local.get $lhs 98 // local.get $rhs 99 // i32.add 100 // ) 101 // (export "add" (func $add)) 102 // ) 103 std::vector<uint8_t> wasmBuffer = {0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01, 104 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 105 0x07, 0x01, 0x03, 0x61, 0x64, 0x64, 0x00, 0x00, 0x0a, 0x09, 0x01, 106 0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x6a, 0x0b}; 107 return wasmBuffer; 108} 109 110// 验证 wasm instance 功能 (add 模块) 111static void VerifyAddWasmInstance(JSVM_Env env, JSVM_Value wasmInstance) { 112 JSVM_Status status = JSVM_OK; 113 // 从 wasm instance 获取 exports.add 函数 114 JSVM_Value exports; 115 status = OH_JSVM_GetProperty(env, wasmInstance, CreateString(env, "exports"), &exports); 116 CHECK(status == JSVM_OK); 117 118 JSVM_Value add; 119 status = OH_JSVM_GetProperty(env, exports, CreateString(env, "add"), &add); 120 CHECK(status == JSVM_OK); 121 122 // 执行 exports.add(1, 2),期望得到结果 3 123 JSVM_Value undefined; 124 OH_JSVM_GetUndefined(env, &undefined); 125 JSVM_Value one = CreateInt32(env, 1); 126 JSVM_Value two = CreateInt32(env, 2); 127 JSVM_Value argv[] = {one, two}; 128 JSVM_Value result; 129 status = OH_JSVM_CallFunction(env, undefined, add, 2, argv, &result); 130 CHECK(status == JSVM_OK); 131 int32_t resultInt32; 132 OH_JSVM_GetValueInt32(env, result, &resultInt32); 133 CHECK(resultInt32 == 3); 134} 135 136// WebAssembly demo 主函数 137static JSVM_Value WasmDemo(JSVM_Env env, JSVM_CallbackInfo info) { 138 JSVM_Status status = JSVM_OK; 139 std::vector<uint8_t> wasmBuffer = GetAddWasmBuffer(); 140 uint8_t *wasmBytecode = wasmBuffer.data(); 141 size_t wasmBytecodeLength = wasmBuffer.size(); 142 JSVM_Value wasmModule; 143 // 根据 wasm 字节码得到 wasm module 144 status = OH_JSVM_CompileWasmModule(env, wasmBytecode, wasmBytecodeLength, NULL, 0, NULL, &wasmModule); 145 CHECK(status == JSVM_OK); 146 CHECK(IsWasmModuleObject(env, wasmModule)); 147 148 // 对当前 wasm module 中定义的第一个函数 (即 add) 执行编译优化 149 int32_t functionIndex = 0; 150 // 注意:当前只支持 high level optimization,即传入 JSVM_WASM_OPT_BASELINE 和传入 JSVM_WASM_OPT_HIGH 效果是一样的 151 status = OH_JSVM_CompileWasmFunction(env, wasmModule, functionIndex, JSVM_WASM_OPT_HIGH); 152 CHECK(status == JSVM_OK); 153 // 对编译得到的 wasm module 进行实例化 154 JSVM_Value wasmInstance = InstantiateWasmModule(env, wasmModule); 155 // 对实例化的 wasm instance 中的函数进行功能验证 156 VerifyAddWasmInstance(env, wasmInstance); 157 158 // 创建 wasm cache 159 const uint8_t *wasmCacheData = NULL; 160 size_t wasmCacheLength = 0; 161 status = OH_JSVM_CreateWasmCache(env, wasmModule, &wasmCacheData, &wasmCacheLength); 162 CHECK(status == JSVM_OK); 163 // 期望 wasm cache 创建成功 164 CHECK(wasmCacheData != NULL); 165 CHECK(wasmCacheLength > 0); 166 167 // 通过将 wasm cache 赋值来模拟 cache 持久化,实际使用场景可能将 wasm cache 保存到文件 168 std::vector<uint8_t> cacheBuffer(wasmCacheData, wasmCacheData + wasmCacheLength); 169 170 // cache 一旦保存完成后,需要显式释放,以免发生内存泄露 171 // 注意:传入的 JSVM_CacheType 必须匹配 172 status = OH_JSVM_ReleaseCache(env, wasmCacheData, JSVM_CACHE_TYPE_WASM); 173 CHECK(status == JSVM_OK); 174 175 // 使用 wasm code 反序列化来生成 wasm module 176 bool cacheRejected; 177 JSVM_Value wasmModule2; 178 status = OH_JSVM_CompileWasmModule(env, wasmBytecode, wasmBytecodeLength, cacheBuffer.data(), cacheBuffer.size(), 179 &cacheRejected, &wasmModule2); 180 CHECK(status == JSVM_OK); 181 // 传入的 wasm cache 如果是匹配的,且内部校验通过 (如版本),则会接受 cache 182 CHECK(cacheRejected == false); 183 CHECK(IsWasmModuleObject(env, wasmModule2)); 184 185 // 对反序列化得到的 wasmModule2 进行同样的操作:函数编译、实例化、验证功能,期望也都是通过的 186 status = OH_JSVM_CompileWasmFunction(env, wasmModule2, functionIndex, JSVM_WASM_OPT_HIGH); 187 CHECK(status == JSVM_OK); 188 JSVM_Value wasmInstance2 = InstantiateWasmModule(env, wasmModule); 189 VerifyAddWasmInstance(env, wasmInstance2); 190 191 JSVM_Value result; 192 OH_JSVM_GetBoolean(env, true, &result); 193 return result; 194} 195 196// WasmDemo 方法注册回调 197static JSVM_CallbackStruct param[] = { 198 {.data = nullptr, .callback = WasmDemo} 199}; 200static JSVM_CallbackStruct *method = param; 201// 将 C++ callback WasmDemo 函数注册为 JSVM globalThis.wasmDemo 属性,供 JS 侧调用 202static JSVM_PropertyDescriptor descriptor[] = { 203 {"wasmDemo", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 204}; 205 206// 样例测试js 207const char *srcCallNative = R"JS(wasmDemo())JS"; 208```