• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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## Code Cache Verification Specifications
26| Specification       | Description                                        |
27| ---------- | ------------------------------------------------ |
28| Integrity verification | Executed by the developer.                                       |
29| Compatibility verification | Checks whether the JSVM version and compilation options of the generated cache are the same as the current one.|
30| Consistency verification | Executed by the developer.                                       |
31
32## Example
33
34If 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.
35
36CPP code:
37
38```cpp
39// hello.cpp
40#include "napi/native_api.h"
41#include "ark_runtime/jsvm.h"
42#include <hilog/log.h>
43
44#ifndef CHECK
45#define CHECK(cond)                                  \
46    do {                                             \
47        if (!(cond)) {                               \
48            OH_LOG_ERROR(LOG_APP, "CHECK FAILED");   \
49            abort();                                 \
50        }                                            \
51    } while (0)
52#endif
53
54// Check whether a JSVM_Value is a Wasm module.
55static bool IsWasmModuleObject(JSVM_Env env, JSVM_Value value) {
56    bool result;
57    JSVM_Status status = OH_JSVM_IsWasmModuleObject(env, value, &result);
58    CHECK(status == JSVM_OK);
59    return result;
60}
61
62// Create a JSVM string from a C string.
63static JSVM_Value CreateString(JSVM_Env env, const char *str) {
64    JSVM_Value jsvmStr;
65    JSVM_Status status = OH_JSVM_CreateStringUtf8(env, str, JSVM_AUTO_LENGTH, &jsvmStr);
66    CHECK(status == JSVM_OK);
67    return jsvmStr;
68}
69
70// Create a JSVM number from a C int32_t value.
71static JSVM_Value CreateInt32(JSVM_Env env, int32_t val) {
72    JSVM_Value jsvmInt32;
73    JSVM_Status status = OH_JSVM_CreateInt32(env, val, &jsvmInt32);
74    CHECK(status == JSVM_OK);
75    return jsvmInt32;
76}
77
78// Instantiate the Wasm module.
79static JSVM_Value InstantiateWasmModule(JSVM_Env env, JSVM_Value wasmModule) {
80    JSVM_Status status = JSVM_OK;
81    JSVM_Value globalThis;
82    status = OH_JSVM_GetGlobal(env, &globalThis);
83    CHECK(status == JSVM_OK);
84
85    JSVM_Value webAssembly;
86    status = OH_JSVM_GetProperty(env, globalThis, CreateString(env, "WebAssembly"), &webAssembly);
87    CHECK(status == JSVM_OK);
88
89    JSVM_Value webAssemblyInstance;
90    status = OH_JSVM_GetProperty(env, webAssembly, CreateString(env, "Instance"), &webAssemblyInstance);
91    CHECK(status == JSVM_OK);
92
93    JSVM_Value instance;
94    JSVM_Value argv[] = {wasmModule};
95    status = OH_JSVM_NewInstance(env, webAssemblyInstance, 1, argv, &instance);
96    CHECK(status == JSVM_OK);
97    return instance;
98}
99
100// Obtain the Wasm bytecode (add module).
101static std::vector<uint8_t> GetAddWasmBuffer() {
102    // The following is the text format of the Wasm bytecode corresponding to wasmBuffer, which contains only the add function.
103    // (module
104    //   (func $add (param $lhs i32) (param $rhs i32) (result i32)
105    //     local.get $lhs
106    //     local.get $rhs
107    //     i32.add
108    //   )
109    //   (export "add" (func $add))
110    // )
111    std::vector<uint8_t> wasmBuffer = {0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01,
112                                       0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07,
113                                       0x07, 0x01, 0x03, 0x61, 0x64, 0x64, 0x00, 0x00, 0x0a, 0x09, 0x01,
114                                       0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x6a, 0x0b};
115    return wasmBuffer;
116}
117
118// Verify the Wasm instance function (add module).
119static void VerifyAddWasmInstance(JSVM_Env env, JSVM_Value wasmInstance) {
120    JSVM_Status status = JSVM_OK;
121    // Obtain the exports.add function from the Wasm instance.
122    JSVM_Value exports;
123    status = OH_JSVM_GetProperty(env, wasmInstance, CreateString(env, "exports"), &exports);
124    CHECK(status == JSVM_OK);
125
126    JSVM_Value add;
127    status = OH_JSVM_GetProperty(env, exports, CreateString(env, "add"), &add);
128    CHECK(status == JSVM_OK);
129
130    // Run the exports.add(1, 2). The expected result is 3.
131    JSVM_Value undefined;
132    OH_JSVM_GetUndefined(env, &undefined);
133    JSVM_Value one = CreateInt32(env, 1);
134    JSVM_Value two = CreateInt32(env, 2);
135    JSVM_Value argv[] = {one, two};
136    JSVM_Value result;
137    status = OH_JSVM_CallFunction(env, undefined, add, 2, argv, &result);
138    CHECK(status == JSVM_OK);
139    int32_t resultInt32;
140    OH_JSVM_GetValueInt32(env, result, &resultInt32);
141    CHECK(resultInt32 == 3);
142}
143
144// Wasm demo main function.
145static JSVM_Value WasmDemo(JSVM_Env env, JSVM_CallbackInfo info) {
146    JSVM_Status status = JSVM_OK;
147    std::vector<uint8_t> wasmBuffer = GetAddWasmBuffer();
148    uint8_t *wasmBytecode = wasmBuffer.data();
149    size_t wasmBytecodeLength = wasmBuffer.size();
150    JSVM_Value wasmModule;
151    // Obtain the Wasm module based on the Wasm bytecode.
152    status = OH_JSVM_CompileWasmModule(env, wasmBytecode, wasmBytecodeLength, NULL, 0, NULL, &wasmModule);
153    CHECK(status == JSVM_OK);
154    CHECK(IsWasmModuleObject(env, wasmModule));
155
156    // Perform compilation optimization on the first function (add) defined in the Wasm module.
157    int32_t functionIndex = 0;
158    // 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.
159    status = OH_JSVM_CompileWasmFunction(env, wasmModule, functionIndex, JSVM_WASM_OPT_HIGH);
160    CHECK(status == JSVM_OK);
161    // Instantiate the compiled Wasm module.
162    JSVM_Value wasmInstance = InstantiateWasmModule(env, wasmModule);
163    // Verify the function in the instantiated Wasm instance.
164    VerifyAddWasmInstance(env, wasmInstance);
165
166    // Create a Wasm cache.
167    const uint8_t *wasmCacheData = NULL;
168    size_t wasmCacheLength = 0;
169    status = OH_JSVM_CreateWasmCache(env, wasmModule, &wasmCacheData, &wasmCacheLength);
170    CHECK(status == JSVM_OK);
171    // The Wasm cache is created successfully.
172    CHECK(wasmCacheData != NULL);
173    CHECK(wasmCacheLength > 0);
174
175    // Assign a value to the Wasm cache to simulate cache persistence. In actual scenarios, the Wasm cache may be saved to a file.
176    std::vector<uint8_t> cacheBuffer(wasmCacheData, wasmCacheData + wasmCacheLength);
177
178    // Once the cache is saved, it needs to be released explicitly to avoid memory leaks.
179    // Note that the input JSVM_CacheType must match the cache data.
180    status = OH_JSVM_ReleaseCache(env, wasmCacheData, JSVM_CACHE_TYPE_WASM);
181    CHECK(status == JSVM_OK);
182
183    // Deserialize the Wasm code to generate a Wasm module.
184    bool cacheRejected;
185    JSVM_Value wasmModule2;
186    status = OH_JSVM_CompileWasmModule(env, wasmBytecode, wasmBytecodeLength, cacheBuffer.data(), cacheBuffer.size(),
187                                       &cacheRejected, &wasmModule2);
188    CHECK(status == JSVM_OK);
189    // If the input Wasm cache is matched and the internal verification (such as the version) is successful, the cache will be accepted.
190    CHECK(cacheRejected == false);
191    CHECK(IsWasmModuleObject(env, wasmModule2));
192
193    // For wasmModule2 (obtained through deserialization), perform the same operations, including function compilation, instantiation, and verification.
194    status = OH_JSVM_CompileWasmFunction(env, wasmModule2, functionIndex, JSVM_WASM_OPT_HIGH);
195    CHECK(status == JSVM_OK);
196    JSVM_Value wasmInstance2 = InstantiateWasmModule(env, wasmModule);
197    VerifyAddWasmInstance(env, wasmInstance2);
198
199    JSVM_Value result;
200    OH_JSVM_GetBoolean(env, true, &result);
201    return result;
202}
203
204// Register a WasmDemo callback.
205static JSVM_CallbackStruct param[] = {
206    {.data = nullptr, .callback = WasmDemo}
207};
208static JSVM_CallbackStruct *method = param;
209// Register the C++ WasmDemo callback as a JSVM globalThis.wasmDemo property for the JS to call.
210static JSVM_PropertyDescriptor descriptor[] = {
211    {"wasmDemo", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT},
212};
213
214// Call the C++ callback from JS.
215const char *srcCallNative = R"JS(wasmDemo())JS";
216```
217