将复杂参数传递给 Wasm 函数

    在中,我们将演示如何从 Go 应用程序调用基于 Rust 的 WebAssembly 函数。特别是,我们将讨论如何传递字符串数据。

    Rust 函数获取字符串的内存指针,并自己构造 Rust 字符串。

    使用标准 Rust 编译器工具将 Rust 源代码编译成 WebAssembly 字节码应用程序:

    `cd rust_memory_greet cargo build --target wasm32-wasi # 输出的 WASM 将是target/wasm32-wasi/debug/rust_memory_greet_lib.wasm`.

    必须从 WasmEdge 虚拟机中调用 allocate 以获得一个指向字符串参数的指针。然后它将用这个指针调用 Rust 中的 greet 函数。在该函数返回后,Go 应用程序将调用 deallocate 来释放内存空间。

    package main import ( "fmt" "os" "strings" "github.com/second-state/WasmEdge-go/wasmedge" ) func main() { wasmedge.SetLogErrorLevel() conf := wasmedge.NewConfigure(wasmedge.WASI) store := wasmedge.NewStore() vm := wasmedge.NewVMWithConfigAndStore(conf, store) wasi := vm.GetImportModule(wasmedge.WASI) wasi.InitWasi( os.Args[1:], os.Environ(), []string{".:."}, ) err := vm.LoadWasmFile(os.Args[1]) if err != nil { fmt.Println("failed to load wasm") } vm.Validate() vm.Instantiate() subject := "WasmEdge" lengthOfSubject := len(subject) // 为 subject 分配内存,并获得其指针 // 包括一个字节,用于我们在下面添加的 NULL 结束符 allocateResult, _ := vm.Execute("allocate", int32(lengthOfSubject+1)) inputPointer := allocateResult[0].(int32) // 将 subject 写入内存 mem := store.FindMemory("memory") memData, _ := mem.GetData(uint(inputPointer), uint(lengthOfSubject+1)) copy(memData, subject) // C-字符串,以 NULL 结束 memData[lengthOfSubject] = 0 // 运行 `greet` 函数。给出指向 subject 的指针 greetResult, _ := vm.Execute("greet", inputPointer) outputPointer := greetResult[0].(int32) pageSize := mem.GetPageSize() // 读取 `greet` 函数的结果 memData, _ = mem.GetData(uint(0), uint(pageSize * 65536)) nth := 0 var output strings.Builder for { if memData[int(outputPointer) + nth] == 0 { break } output.WriteByte(memData[int(outputPointer) + nth]) nth++ } lengthOfOutput := nth fmt.Println(output.String()) // 释放 subject 以及 output vm.Execute("deallocate", inputPointer, int32(lengthOfSubject+1)) vm.Execute("deallocate", outputPointer, int32(lengthOfOutput+1)) vm.Release() store.Release() conf.Release() }

    要构建 Go SDK 示例,请运行以下命令:

    go get github.com/second-state/WasmEdge-go/wasmedge@v0.10.0 go build greet_memory.go

    现在你可以使用 Go 应用程序来运行从 Rust 编译的 WebAssembly 插件:

    $ ./greet_memory rust_memory_greet_lib.wasm Hello, WasmEdge!

    此示例中,我们将演示如何从 Go 应用程序调用。

    TinyGo 函数获取字符串的内存指针,并自己构造 TinyGo 字符串。

    使用 TinyGo 编译器工具将 Go 源代码编译成 WebAssembly 字节码应用程序:

    tinygo build -o greet.wasm -target wasi greet.go

    Go SDK 应用程序必须从 WasmEdge 虚拟机中调用 malloc 以获得一个指向字符串参数的指针。然后它将用这个指针调用 TinyGo 中的 greet 函数。在该函数返回后,Go 应用程序将调用 free 来释放内存空间。

    package main import ( "fmt" "os" "strings" "github.com/second-state/WasmEdge-go/wasmedge" ) func main() { wasmedge.SetLogErrorLevel() conf := wasmedge.NewConfigure(wasmedge.WASI) store := wasmedge.NewStore() vm := wasmedge.NewVMWithConfigAndStore(conf, store) wasi := vm.GetImportModule(wasmedge.WASI) wasi.InitWasi( os.Args[1:], os.Environ(), []string{".:."}, ) err := vm.LoadWasmFile(os.Args[1]) if err != nil { fmt.Println("failed to load wasm") } vm.Validate() vm.Instantiate() subject := "WasmEdge" lengthOfSubject := len(subject) // 为 subject 分配内存,并获得其指针 // 包括一个字节,用于我们在下面添加的 NULL 结束符 allocateResult, _ := vm.Execute("malloc", int32(lengthOfSubject+1)) inputPointer := allocateResult[0].(int32) // 将 subject 写入内存 mem := store.FindMemory("memory") memData, _ := mem.GetData(uint(inputPointer), uint(lengthOfSubject+1)) copy(memData, subject) // C-字符串,以 NULL 结束 memData[lengthOfSubject] = 0 // 运行 `greet` 函数。给出指向 subject 的指针 greetResult, _ := vm.Execute("greet", inputPointer) outputPointer := greetResult[0].(int32) pageSize := mem.GetPageSize() // 读取 `greet` 函数的结果 memData, _ = mem.GetData(uint(0), uint(pageSize * 65536)) nth := 0 var output strings.Builder for { if memData[int(outputPointer) + nth] == 0 { break } output.WriteByte(memData[int(outputPointer) + nth]) nth++ } fmt.Println(output.String()) // 释放 subject 以及 output vm.Execute("free", inputPointer) vm.Execute("free", outputPointer) vm.Release() store.Release() conf.Release() }

    要构建 Go SDK 示例,请运行以下命令:

    go get github.com/second-state/WasmEdge-go/wasmedge@v0.10.0 go build greet_memory.go

    现在你可以使用 Go 应用程序运行从 TinyGo 编译的 WebAssembly 插件:

    $ ./greet_memory greet.wasm Hello, WasmEdge!

    在中,我们将演示如何调用基于 Rust 的 WebAssembly 函数,并在 Go 应用程序中传入和传出数组。

    fib_array() 函数将一个数组作为调用参数,并用斐波那契数列填充它。或者,fib_array_return_memory() 函数返回一个斐波那契数列数组。

    对于调用参数中的数组,Rust 函数 fib_array() 需要一个内存指针并使用 from_raw_parts 构造 Rust Vec。对于数组的返回值,Rust 函数 fib_array_return_memory() 只是返回指针。

    1. #![allow(unused)]
    2. fn main() {
    3. use std::mem;
    4. use std::os::raw::{c_void, c_int};
    5. #[no_mangle]
    6. pub extern fn allocate(size: usize) -> *mut c_void {
    7. let mut buffer = Vec::with_capacity(size);
    8. mem::forget(buffer);
    9. }
    10. #[no_mangle]
    11. pub extern fn deallocate(pointer: *mut c_void, capacity: usize) {
    12. unsafe {
    13. let _ = Vec::from_raw_parts(pointer, 0, capacity);
    14. }
    15. }
    16. #[no_mangle]
    17. pub extern fn fib_array(n: i32, p: *mut c_int) -> i32 {
    18. unsafe {
    19. let mut arr = Vec::<i32>::from_raw_parts(p, 0, (4*n) as usize);
    20. for i in 0..n {
    21. if i < 2 {
    22. arr.push(i);
    23. } else {
    24. arr.push(arr[(i - 1) as usize] + arr[(i - 2) as usize]);
    25. }
    26. }
    27. mem::forget(arr);
    28. r
    29. }
    30. }
    31. #[no_mangle]
    32. pub extern fn fib_array_return_memory(n: i32) -> *mut c_int {
    33. let mut arr = Vec::with_capacity((4 * n) as usize);
    34. for i in 0..n {
    35. if i < 2 {
    36. arr.push(i);
    37. } else {
    38. arr.push(arr[(i - 1) as usize] + arr[(i - 2) as usize]);
    39. }
    40. }
    41. mem::forget(arr);
    42. pointer
    43. }
    44. }

    使用标准 Rust 编译器工具将 Rust 源代码编译成 WebAssembly 字节码应用程序:

    cd rust_access_memory cargo build --target wasm32-wasi # 输出的 WASM 将是 target/wasm32-wasi/debug/rust_access_memory_lib.wasm.

    package main import ( "fmt" "os" "unsafe" "github.com/second-state/WasmEdge-go/wasmedge" ) func main() { wasmedge.SetLogErrorLevel() conf := wasmedge.NewConfigure(wasmedge.WASI) store := wasmedge.NewStore() vm := wasmedge.NewVMWithConfigAndStore(conf, store) wasi := vm.GetImportModule(wasmedge.WASI) wasi.InitWasi( os.Args[1:], os.Environ(), []string{".:."}, ) err := vm.LoadWasmFile(os.Args[1]) if err != nil { fmt.Println("failed to load wasm") } vm.Validate() vm.Instantiate() n := int32(10) p, err := vm.Execute("allocate", 4 * n) if err != nil { fmt.Println("allocate failed:", err) } fib, err := vm.Execute("fib_array", n, p[0]) if err != nil { fmt.Println("fib_rray failed:", err) } else { fmt.Println("fib_array() returned:", fib[0]) fmt.Printf("fib_array memory at: %p\n", unsafe.Pointer((uintptr)(p[0].(int32)))) mem := store.FindMemory("memory") if mem != nil { // int32 占用 4 个字节 fibArray, err := mem.GetData(uint(p[0].(int32)), uint(n * 4)) if err == nil && fibArray != nil { fmt.Println("fibArray:", fibArray) } } } fibP, err := vm.Execute("fib_array_return_memory", n) if err != nil { fmt.Println("fib_array_return_memory failed:", err) } else { fmt.Printf("fib_array_return_memory memory at: %p\n", unsafe.Pointer((uintptr)(fibP[0].(int32)))) mem := store.FindMemory("memory") if mem != nil { // int32 占用 4 个字节 fibArrayReturnMemory, err := mem.GetData(uint(fibP[0].(int32)), uint(n * 4)) if err == nil && fibArrayReturnMemory != nil { fmt.Println("fibArrayReturnMemory:", fibArrayReturnMemory) } } } _, err = vm.Execute("deallocate", p[0].(int32), 4 * n) if err != nil { fmt.Println("free failed:", err) } exitcode := wasi.WasiGetExitCode() if exitcode != 0 { fmt.Println("Go: Running wasm failed, exit code:", exitcode) } vm.Release() store.Release() conf.Release() }

    为了构建 Go SDK 示例,请运行以下命令:

    go get github.com/second-state/WasmEdge-go/wasmedge@v0.10.0 go build run.go

    现在你可以使用 Go 应用程序运行从 Rust 编译的 WebAssembly 插件:

    $ ./run rust_access_memory_lib.wasm fib_array() returned: 34 fib_array memory at: 0x102d80 fibArray: [0 0 0 0 1 0 0 0 1 0 0 0 2 0 0 0 3 0 0 0 5 0 0 0 8 0 0 0 13 0 0 0 21 0 0 0 34 0 0 0] fib_array_return_memory memory at: 0x105430 fibArrayReturnMemory: [0 0 0 0 1 0 0 0 1 0 0 0 2 0 0 0 3 0 0 0 5 0 0 0 8 0 0 0 13 0 0 0 21 0 0 0 34 0 0 0]

    在中,我们将演示如何调用基于 TinyGo 的 WebAssembly 函数以及将数组传入和传出 Go 应用程序。

    函数 fibArray 接收一个数组作为调用参数,并将其填入一个斐波那契数列。或者,fibArrayReturnMemory 函数返回斐波那契数列的数组。

    package main import ( "fmt" "unsafe" ) func main() { println("in main") n := int32(10) arr := make([]int32, n) arrP := &arr[0] fmt.Printf("call fibArray(%d, %p) = %d\n", n, arrP, fibArray(n, arrP)) fmt.Printf("call fibArrayReturnMemory(%d) return %p\n", n, fibArrayReturnMemory(n)) } // 导出 fibArray func fibArray(n int32, p *int32) int32 { arr := unsafe.Slice(p, n) for i := int32(0); i < n; i++ { switch { case i < 2: arr[i] = i default: arr[i] = arr[i-1] + arr[i-2] } } return arr[n-1] } // 导出 fibArrayReturnMemory func fibArrayReturnMemory(n int32) *int32 { arr := make([]int32, n) for i := int32(0); i < n; i++ { switch { case i < 2: arr[i] = i default: arr[i] = arr[i-1] + arr[i-2] } } return &arr[0] }

    使用 TinyGo 编译器工具将 Go 源代码编译成 WebAssembly 字节码应用程序:

    tinygo build -o fib.wasm -target wasi fib.go

    必须从 WasmEdge 虚拟机中调用 malloc 以获得指向数组的指针。然后它将用这个指针调用TinyGo中的 fibArray() 函数。在函数返回后,Go应用程序使用 WasmEdge SDK 的 store API,从调用参数( fibArray() )或返回值( fibArrayReturnMemory() )中的指针构建一个数组。Go应用程序最终会调用 free 来释放内存空间。

    package main import ( "fmt" "os" "unsafe" "github.com/second-state/WasmEdge-go/wasmedge" ) func main() { wasmedge.SetLogErrorLevel() conf := wasmedge.NewConfigure(wasmedge.WASI) store := wasmedge.NewStore() vm := wasmedge.NewVMWithConfigAndStore(conf, store) wasi := vm.GetImportModule(wasmedge.WASI) wasi.InitWasi( os.Args[1:], os.Environ(), []string{".:."}, ) err := vm.LoadWasmFile(os.Args[1]) if err != nil { fmt.Println("failed to load wasm") } vm.Validate() vm.Instantiate() n := int32(10) p, err := vm.Execute("malloc", n) if err != nil { fmt.Println("malloc failed:", err) } fib, err := vm.Execute("fibArray", n, p[0]) if err != nil { fmt.Println("fibArray failed:", err) } else { fmt.Println("fibArray() returned:", fib[0]) fmt.Printf("fibArray memory at: %p\n", unsafe.Pointer((uintptr)(p[0].(int32)))) mem := store.FindMemory("memory") if mem != nil { // int32 占用 4 个字节 fibArray, err := mem.GetData(uint(p[0].(int32)), uint(n * 4)) if err == nil && fibArray != nil { fmt.Println("fibArray:", fibArray) } } } fibP, err := vm.Execute("fibArrayReturnMemory", n) if err != nil { fmt.Println("fibArrayReturnMemory failed:", err) } else { fmt.Printf("fibArrayReturnMemory memory at: %p\n", unsafe.Pointer((uintptr)(fibP[0].(int32)))) mem := store.FindMemory("memory") if mem != nil { // int32 占用 4 个字节 fibArrayReturnMemory, err := mem.GetData(uint(fibP[0].(int32)), uint(n * 4)) if err == nil && fibArrayReturnMemory != nil { fmt.Println("fibArrayReturnMemory:", fibArrayReturnMemory) } } } _, err = vm.Execute("free", p...) if err != nil { fmt.Println("free failed:", err) } exitcode := wasi.WasiGetExitCode() if exitcode != 0 { fmt.Println("Go: Running wasm failed, exit code:", exitcode) } vm.Release() store.Release() conf.Release() }

    要构建 Go SDK 示例,请运行以下命令:

    $ ./run fib.wasm fibArray() returned: 34 fibArray memory at: 0x14d3c fibArray: [0 0 0 0 1 0 0 0 1 0 0 0 2 0 0 0 3 0 0 0 5 0 0 0 8 0 0 0 13 0 0 0 21 0 0 0 34 0 0 0] fibArrayReturnMemory memory at: 0x14d4c fibArrayReturnMemory: [0 0 0 0 1 0 0 0 1 0 0 0 2 0 0 0 3 0 0 0 5 0 0 0 8 0 0 0 13 0 0 0 21 0 0 0 34 0 0 0]