1# Working with Wasm Using JSVM-API 2 3 4## Introduction 5 6JSVM-API provides APIs for compiling the WebAssembly (Wasm) bytecode, optimizing Wasm functions, and serializing and deserializing Wasm caches. 7> **NOTE**<br>To run the Wasm bytecode, the application must have the JIT permission. For details about how to apply for the permission, see [Requesting the JIT Permission](jsvm-apply-jit-profile.md). 8 9## Basic Concepts 10 11- Wasm module: a binary format that contains compiled Wasm code. You can use **OH_JSVM_CompileWasmModule** to create a Wasm module from Wasm bytecode or Wasm cache, and use **OH_JSVM_IsWasmModuleObject** to check whether a **JSVM_Value** is a Wasm module. 12- Wasm function: a function defined in a Wasm module. The functions in a Wasm module can be used by external code after being imported. You can use **OH_JSVM_CompileWasmFunction** to convert Wasm bytecode into the format that JSVM can execute efficiently. 13- Wasm cache: data generated by serializing the bytecode in a Wasm module. The cache holds the compiled Wasm code so that it can be reused, eliminating the need for recompiling the code. You can use **OH_JSVM_CreateWasmCache** (with **cacheType** set to **JSVM_CACHE_TYPE_WASM**) to create a Wasm cache instance and use **OH_JSVM_ReleaseCache** to release it. 14 15## Available APIs 16 17| API | Description | 18| --------------------------- | ------------------------------------------------------------------------------------ | 19| OH_JSVM_CompileWasmModule | Compiles the Wasm bytecode into a Wasm module. If the **cache** parameter is passed in, the cache will be deserialized into a Wasm module first. The compilation is performed when the deserialization fails.| 20| OH_JSVM_CompileWasmFunction | Compiles the function with the specified ID in a Wasm module into the optimized machine code. Currently, only the highest optimization level is enabled. The validity of the function ID is ensured by the caller. | 21| OH_JSVM_IsWasmModuleObject | Checks whether the input value is a Wasm module. | 22| OH_JSVM_CreateWasmCache | Serializes the machine code in a Wasm module into a Wasm cache. If the Wasm module does not contain machine code, the serialization will fail. | 23| OH_JSVM_ReleaseCache | Releases a Wasm cache instance created by JSVM-API. The **cacheType** and **cacheData** passed in must match. Otherwise, undefined behavior may occur. | 24 25## Example 26 27If you are just starting out with JSVM-API, see [JSVM-API Development Process](use-jsvm-process.md). The following demonstrates only the C++ code involved in the APIs for Wasm. 28 29CPP code: 30 31```cpp 32// hello.cpp 33#include "napi/native_api.h" 34#include "ark_runtime/jsvm.h" 35#include <hilog/log.h> 36 37#ifndef CHECK 38#define CHECK(cond) \ 39 do { \ 40 if (!(cond)) { \ 41 OH_LOG_ERROR(LOG_APP, "CHECK FAILED"); \ 42 abort(); \ 43 } \ 44 } while (0) 45#endif 46 47// Check whether a JSVM_Value is a Wasm module. 48static bool IsWasmModuleObject(JSVM_Env env, JSVM_Value value) { 49 bool result; 50 JSVM_Status status = OH_JSVM_IsWasmModuleObject(env, value, &result); 51 CHECK(status == JSVM_OK); 52 return result; 53} 54 55// Create a JSVM string from a C string. 56static JSVM_Value CreateString(JSVM_Env env, const char *str) { 57 JSVM_Value jsvmStr; 58 JSVM_Status status = OH_JSVM_CreateStringUtf8(env, str, JSVM_AUTO_LENGTH, &jsvmStr); 59 CHECK(status == JSVM_OK); 60 return jsvmStr; 61} 62 63// Create a JSVM number from a C int32_t value. 64static JSVM_Value CreateInt32(JSVM_Env env, int32_t val) { 65 JSVM_Value jsvmInt32; 66 JSVM_Status status = OH_JSVM_CreateInt32(env, val, &jsvmInt32); 67 CHECK(status == JSVM_OK); 68 return jsvmInt32; 69} 70 71// Instantiate the Wasm module. 72static JSVM_Value InstantiateWasmModule(JSVM_Env env, JSVM_Value wasmModule) { 73 JSVM_Status status = JSVM_OK; 74 JSVM_Value globalThis; 75 status = OH_JSVM_GetGlobal(env, &globalThis); 76 CHECK(status == JSVM_OK); 77 78 JSVM_Value webAssembly; 79 status = OH_JSVM_GetProperty(env, globalThis, CreateString(env, "WebAssembly"), &webAssembly); 80 CHECK(status == JSVM_OK); 81 82 JSVM_Value webAssemblyInstance; 83 status = OH_JSVM_GetProperty(env, webAssembly, CreateString(env, "Instance"), &webAssemblyInstance); 84 CHECK(status == JSVM_OK); 85 86 JSVM_Value instance; 87 JSVM_Value argv[] = {wasmModule}; 88 status = OH_JSVM_NewInstance(env, WebAssemblyInstance, 1, argv, &instance); 89 CHECK(status == JSVM_OK); 90 return instance; 91} 92 93// Obtain the Wasm bytecode (add module). 94static std::vector<uint8_t> GetAddWasmBuffer() { 95 // The following is the text format of the Wasm bytecode corresponding to wasmBuffer, which contains only the add function. 96 // (module 97 // (func $add (param $lhs i32) (param $rhs i32) (result i32) 98 // local.get $lhs 99 // local.get $rhs 100 // i32.add 101 // ) 102 // (export "add" (func $add)) 103 // ) 104 std::vector<uint8_t> wasmBuffer = {0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01, 105 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 106 0x07, 0x01, 0x03, 0x61, 0x64, 0x64, 0x00, 0x00, 0x0a, 0x09, 0x01, 107 0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x6a, 0x0b}; 108 return wasmBuffer; 109} 110 111// Verify the Wasm instance function (add module). 112static void VerifyAddWasmInstance(JSVM_Env env, JSVM_Value wasmInstance) { 113 JSVM_Status status = JSVM_OK; 114 // Obtain the exports.add function from the Wasm instance. 115 JSVM_Value exports; 116 status = OH_JSVM_GetProperty(env, wasmInstance, CreateString(env, "exports"), &exports); 117 CHECK(status == JSVM_OK); 118 119 JSVM_Value add; 120 status = OH_JSVM_GetProperty(env, exports, CreateString(env, "add"), &add); 121 CHECK(status == JSVM_OK); 122 123 // Run the exports.add(1, 2). The expected result is 3. 124 JSVM_Value undefined; 125 OH_JSVM_GetUndefined(env, &undefined); 126 JSVM_Value one = CreateInt32(env, 1); 127 JSVM_Value two = CreateInt32(env, 2); 128 JSVM_Value argv[] = {one, two}; 129 JSVM_Value result; 130 status = OH_JSVM_CallFunction(env, undefined, add, 2, argv, &result); 131 CHECK(status == JSVM_OK); 132 int32_t resultInt32; 133 OH_JSVM_GetValueInt32(env, result, &resultInt32); 134 CHECK(resultInt32 == 3); 135} 136 137// Wasm demo main function. 138static JSVM_Value WasmDemo(JSVM_Env env, JSVM_CallbackInfo info) { 139 JSVM_Status status = JSVM_OK; 140 std::vector<uint8_t> wasmBuffer = GetAddWasmBuffer(); 141 uint8_t *wasmBytecode = wasmBuffer.data(); 142 size_t wasmBytecodeLength = wasmBuffer.size(); 143 JSVM_Value wasmModule; 144 // Obtain the Wasm module based on the Wasm bytecode. 145 status = OH_JSVM_CompileWasmModule(env, wasmBytecode, wasmBytecodeLength, NULL, 0, NULL, &wasmModule); 146 CHECK(status == JSVM_OK); 147 CHECK(IsWasmModuleObject(env, wasmModule)); 148 149 // Perform compilation optimization on the first function (add) defined in the Wasm module. 150 int32_t functionIndex = 0; 151 // Currently, only high-level optimization is supported. That is, the effect is the same no matter whether JSVM_WASM_OPT_BASELINE or JSVM_WASM_OPT_HIGH is passed in. 152 status = OH_JSVM_CompileWasmFunction(env, wasmModule, functionIndex, JSVM_WASM_OPT_HIGH); 153 CHECK(status == JSVM_OK); 154 // Instantiate the compiled Wasm module. 155 JSVM_Value wasmInstance = InstantiateWasmModule(env, wasmModule); 156 // Verify the function in the instantiated Wasm instance. 157 VerifyAddWasmInstance(env, wasmInstance); 158 159 // Create a Wasm cache. 160 const uint8_t *wasmCacheData = NULL; 161 size_t wasmCacheLength = 0; 162 status = OH_JSVM_CreateWasmCache(env, wasmModule, &wasmCacheData, &wasmCacheLength); 163 CHECK(status == JSVM_OK); 164 // The Wasm cache is created successfully. 165 CHECK(wasmCacheData != NULL); 166 CHECK(wasmCacheLength > 0); 167 168 // Assign a value to the Wasm cache to simulate cache persistence. In actual scenarios, the Wasm cache may be saved to a file. 169 std::vector<uint8_t> cacheBuffer(wasmCacheData, wasmCacheData + wasmCacheLength); 170 171 // Once the cache is saved, it needs to be released explicitly to avoid memory leaks. 172 // Note that the input JSVM_CacheType must match the cache data. 173 status = OH_JSVM_ReleaseCache(env, wasmCacheData, JSVM_CACHE_TYPE_WASM); 174 CHECK(status == JSVM_OK); 175 176 // Deserialize the Wasm code to generate a Wasm module. 177 bool cacheRejected; 178 JSVM_Value wasmModule2; 179 status = OH_JSVM_CompileWasmModule(env, wasmBytecode, wasmBytecodeLength, cacheBuffer.data(), cacheBuffer.size(), 180 &cacheRejected, &wasmModule2); 181 CHECK(status == JSVM_OK); 182 // If the input Wasm cache is matched and the internal verification (such as the version) is successful, the cache will be accepted. 183 CHECK(cacheRejected == false); 184 CHECK(IsWasmModuleObject(env, wasmModule2)); 185 186 // For wasmModule2 (obtained through deserialization), perform the same operations, including function compilation, instantiation, and verification. 187 status = OH_JSVM_CompileWasmFunction(env, wasmModule2, functionIndex, JSVM_WASM_OPT_HIGH); 188 CHECK(status == JSVM_OK); 189 JSVM_Value wasmInstance2 = InstantiateWasmModule(env, wasmModule); 190 VerifyAddWasmInstance(env, wasmInstance2); 191 192 JSVM_Value result; 193 OH_JSVM_GetBoolean(env, true, &result); 194 return result; 195} 196 197// Register a WasmDemo callback. 198static JSVM_CallbackStruct param[] = { 199 {.data = nullptr, .callback = WasmDemo} 200}; 201static JSVM_CallbackStruct *method = param; 202// Register the C++ WasmDemo callback as a JSVM globalThis.wasmDemo property for the JS to call. 203static JSVM_PropertyDescriptor descriptor[] = { 204 {"wasmDemo", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 205}; 206 207// Call the C++ callback from JS. 208const char *srcCallNative = R"JS(wasmDemo())JS"; 209``` 210