You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现Zig/Wasm与JavaScript之间的字符串类型函数绑定?

Zig/Wasm与JavaScript的字符串类型函数绑定实现方法

你已经实现了i32类型的Zig/Wasm与JS函数绑定,现在需要替换为字符串参数和返回值的sayHello函数绑定,核心难点在于Wasm无原生字符串类型,Zig的[]const u8是指针+长度的组合,需要通过共享线性内存完成字符串传递,以下是完整实现步骤:

一、修改Zig代码(new.zig)

需要处理内存分配、参数拆分与返回值组合,同时暴露内存分配函数供JS调用:

const std = @import("std");
const allocator = std.heap.wasm_allocator;

// 声明外部JS函数:接收字符串指针+长度,返回结果字符串指针
extern fn sayHello(ptr: [*]const u8, len: usize) [*]const u8;
// 声明外部JS函数:获取返回字符串的长度
extern fn getReturnStrLen() usize;

// 暴露给JS的内存分配函数:申请指定长度的内存,返回指针
export fn alloc(len: usize) [*]u8 {
    const buf = allocator.alloc(u8, len) catch unreachable;
    return buf.ptr;
}

// 封装调用逻辑,对外提供友好的字符串传递接口
export fn callSayHello(name_ptr: [*]const u8, name_len: usize) [*]const u8 {
    const name = name_ptr[0..name_len];
    // 调用JS函数,传递字符串的指针和长度
    const result_ptr = sayHello(name.ptr, name.len);
    return result_ptr;
}

// 暴露获取结果长度的函数,方便JS读取
export fn getResultLen() usize {
    return getReturnStrLen();
}

pub fn main() void {} // 空main避免编译警告

二、修改JavaScript代码(new.js)

需要完成:读取Zig传递的字符串、生成结果并写入Wasm内存、传递指针和长度:

import fs from "fs";
const source = fs.readFileSync("new.wasm");
const typedArray = new Uint8Array(source);

let returnStrLen = 0;

const importObject = {
    env: {
        sayHello: function (ptr, len) {
            // 从Wasm线性内存中读取Zig传递的字符串
            const memory = new Uint8Array(this.memory.buffer, ptr, len);
            const name = new TextDecoder().decode(memory);
            
            // 生成结果字符串
            const result = `Hello ${name}`;
            const encoder = new TextEncoder();
            const encodedResult = encoder.encode(result);
            
            // 调用Zig暴露的alloc函数,申请Wasm内存存储结果
            const resultPtr = this.alloc(encodedResult.length);
            // 将结果写入Wasm内存
            new Uint8Array(this.memory.buffer, resultPtr, encodedResult.length).set(encodedResult);
            
            // 存储结果长度,供Zig读取
            returnStrLen = encodedResult.length;
            // 返回结果字符串的指针
            return resultPtr;
        },
        getReturnStrLen: function () {
            return returnStrLen;
        }
    }
};

(async () => {
    const { instance } = await WebAssembly.instantiate(typedArray, importObject);
    const wasm = instance.exports;

    // 准备要传递给Zig的字符串:"World"
    const name = "World";
    const encoder = new TextEncoder();
    const encodedName = encoder.encode(name);
    
    // 申请Wasm内存存储输入字符串
    const namePtr = wasm.alloc(encodedName.length);
    // 将输入字符串写入Wasm内存
    new Uint8Array(wasm.memory.buffer, namePtr, encodedName.length).set(encodedName);
    
    // 调用Zig的callSayHello函数
    const resultPtr = wasm.callSayHello(namePtr, encodedName.length);
    const resultLen = wasm.getResultLen();
    
    // 读取Wasm内存中的结果字符串并输出
    const resultStr = new TextDecoder().decode(new Uint8Array(wasm.memory.buffer, resultPtr, resultLen));
    console.log(resultStr); // 输出: Hello World
})();

三、编译与运行

  1. 编译Zig代码为Wasm:
zig build-lib -O ReleaseSmall -target wasm32-freestanding new.zig -dynamic
  1. 运行Node.js代码:
node new.js

输出结果:

Hello World

关键原理说明

  • 字符串传递本质:Zig的[]const u8会被拆分为指针(内存地址)+长度两个值传递,Wasm与JS共享线性内存,因此双方都能通过指针+长度读取内存中的字符串内容。
  • 内存管理:通过Zig的wasm_allocator管理内存,JS调用Zig暴露的alloc函数申请内存,确保内存由Wasm侧统一管理,避免内存泄漏。
  • 返回值处理:由于Wasm函数仅支持单个返回值,因此返回字符串时先返回指针,再通过独立函数返回长度,最终组合为可读取的字符串。

内容的提问来源于stack exchange,提问作者Hasan A Yousef

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 20:50:39