harmony 鸿蒙Working with Wasm Using JSVM-API

  • 2025-06-12
  • 浏览 (4)

Working with Wasm Using JSVM-API

Introduction

JSVM-API provides APIs for compiling the WebAssembly (Wasm) bytecode, optimizing Wasm functions, and serializing and deserializing Wasm caches. > NOTE
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.

Basic Concepts

  • 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.
  • 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.
  • 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.

Available APIs

API Description
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.
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.
OH_JSVM_IsWasmModuleObject Checks whether the input value is a Wasm module.
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.
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.

Code Cache Verification Specifications

Specification Description
Integrity verification Executed by the developer.
Compatibility verification Checks whether the JSVM version and compilation options of the generated cache are the same as the current one.
Consistency verification Executed by the developer.

Example

If you are just starting out with JSVM-API, see JSVM-API Development Process. The following demonstrates only the C++ code involved in the APIs for Wasm.

CPP code:

// hello.cpp
#include "napi/native_api.h"
#include "ark_runtime/jsvm.h"
#include <hilog/log.h>

#ifndef CHECK
#define CHECK(cond)                                  \
    do {                                             \
        if (!(cond)) {                               \
            OH_LOG_ERROR(LOG_APP, "CHECK FAILED");   \
            abort();                                 \
        }                                            \
    } while (0)
#endif

// Check whether a JSVM_Value is a Wasm module.
static bool IsWasmModuleObject(JSVM_Env env, JSVM_Value value) {
    bool result;
    JSVM_Status status = OH_JSVM_IsWasmModuleObject(env, value, &result);
    CHECK(status == JSVM_OK);
    return result;
}

// Create a JSVM string from a C 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 == JSVM_OK);
    return jsvmStr;
}

// Create a JSVM number from a C int32_t value.
static JSVM_Value CreateInt32(JSVM_Env env, int32_t val) {
    JSVM_Value jsvmInt32;
    JSVM_Status status = OH_JSVM_CreateInt32(env, val, &jsvmInt32);
    CHECK(status == JSVM_OK);
    return jsvmInt32;
}

// Instantiate the 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 == JSVM_OK);

    JSVM_Value webAssembly;
    status = OH_JSVM_GetProperty(env, globalThis, CreateString(env, "WebAssembly"), &webAssembly);
    CHECK(status == JSVM_OK);

    JSVM_Value webAssemblyInstance;
    status = OH_JSVM_GetProperty(env, webAssembly, CreateString(env, "Instance"), &webAssemblyInstance);
    CHECK(status == JSVM_OK);

    JSVM_Value instance;
    JSVM_Value argv[] = {wasmModule};
    status = OH_JSVM_NewInstance(env, webAssemblyInstance, 1, argv, &instance);
    CHECK(status == JSVM_OK);
    return instance;
}

// Obtain the Wasm bytecode (add module).
static std::vector<uint8_t> GetAddWasmBuffer() {
    // The following is the text format of the Wasm bytecode corresponding to wasmBuffer, which contains only the add function.
    // (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;
}

// Verify the Wasm instance function (add module).
static void VerifyAddWasmInstance(JSVM_Env env, JSVM_Value wasmInstance) {
    JSVM_Status status = JSVM_OK;
    // Obtain the exports.add function from the Wasm instance.
    JSVM_Value exports;
    status = OH_JSVM_GetProperty(env, wasmInstance, CreateString(env, "exports"), &exports);
    CHECK(status == JSVM_OK);

    JSVM_Value add;
    status = OH_JSVM_GetProperty(env, exports, CreateString(env, "add"), &add);
    CHECK(status == JSVM_OK);

    // Run the exports.add(1, 2). The expected result is 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 == JSVM_OK);
    int32_t resultInt32;
    OH_JSVM_GetValueInt32(env, result, &resultInt32);
    CHECK(resultInt32 == 3);
}

// Wasm demo main function.
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;
    // Obtain the Wasm module based on the Wasm bytecode.
    status = OH_JSVM_CompileWasmModule(env, wasmBytecode, wasmBytecodeLength, NULL, 0, NULL, &wasmModule);
    CHECK(status == JSVM_OK);
    CHECK(IsWasmModuleObject(env, wasmModule));

    // Perform compilation optimization on the first function (add) defined in the Wasm module.
    int32_t functionIndex = 0;
    // 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.
    status = OH_JSVM_CompileWasmFunction(env, wasmModule, functionIndex, JSVM_WASM_OPT_HIGH);
    CHECK(status == JSVM_OK);
    // Instantiate the compiled Wasm module.
    JSVM_Value wasmInstance = InstantiateWasmModule(env, wasmModule);
    // Verify the function in the instantiated Wasm instance.
    VerifyAddWasmInstance(env, wasmInstance);

    // Create a Wasm cache.
    const uint8_t *wasmCacheData = NULL;
    size_t wasmCacheLength = 0;
    status = OH_JSVM_CreateWasmCache(env, wasmModule, &wasmCacheData, &wasmCacheLength);
    CHECK(status == JSVM_OK);
    // The Wasm cache is created successfully.
    CHECK(wasmCacheData != NULL);
    CHECK(wasmCacheLength > 0);

    // Assign a value to the Wasm cache to simulate cache persistence. In actual scenarios, the Wasm cache may be saved to a file.
    std::vector<uint8_t> cacheBuffer(wasmCacheData, wasmCacheData + wasmCacheLength);

    // Once the cache is saved, it needs to be released explicitly to avoid memory leaks.
    // Note that the input JSVM_CacheType must match the cache data.
    status = OH_JSVM_ReleaseCache(env, wasmCacheData, JSVM_CACHE_TYPE_WASM);
    CHECK(status == JSVM_OK);

    // Deserialize the Wasm code to generate a Wasm module.
    bool cacheRejected;
    JSVM_Value wasmModule2;
    status = OH_JSVM_CompileWasmModule(env, wasmBytecode, wasmBytecodeLength, cacheBuffer.data(), cacheBuffer.size(),
                                       &cacheRejected, &wasmModule2);
    CHECK(status == JSVM_OK);
    // If the input Wasm cache is matched and the internal verification (such as the version) is successful, the cache will be accepted.
    CHECK(cacheRejected == false);
    CHECK(IsWasmModuleObject(env, wasmModule2));

    // For wasmModule2 (obtained through deserialization), perform the same operations, including function compilation, instantiation, and verification.
    status = OH_JSVM_CompileWasmFunction(env, wasmModule2, functionIndex, JSVM_WASM_OPT_HIGH);
    CHECK(status == JSVM_OK);
    JSVM_Value wasmInstance2 = InstantiateWasmModule(env, wasmModule);
    VerifyAddWasmInstance(env, wasmInstance2);

    JSVM_Value result;
    OH_JSVM_GetBoolean(env, true, &result);
    return result;
}

// Register a WasmDemo callback.
static JSVM_CallbackStruct param[] = {
    {.data = nullptr, .callback = WasmDemo}
};
static JSVM_CallbackStruct *method = param;
// Register the C++ WasmDemo callback as a JSVM globalThis.wasmDemo property for the JS to call.
static JSVM_PropertyDescriptor descriptor[] = {
    {"wasmDemo", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT},
};

// Call the C++ callback from JS.
const char *srcCallNative = R"JS(wasmDemo())JS";

你可能感兴趣的鸿蒙文章

harmony 鸿蒙Node-API

harmony 鸿蒙Building an NDK Project with CMake

harmony 鸿蒙Building an NDK Project with the DevEco Studio Template

harmony 鸿蒙NDK Project Building Overview

harmony 鸿蒙Building an NDK Project with Prebuilt Libraries

harmony 鸿蒙C/C++ Library Mechanisms

harmony 鸿蒙CPU Features

harmony 鸿蒙Creating an NDK Project

harmony 鸿蒙C/C++ Memory Error Detection

harmony 鸿蒙Debugging in DevEco Studio

0  赞