Golang开发wasm程序
wasm以小巧安全的特性受到广泛的关注,但是他也有很多的局限性。
这些局限性可以通过Host Function
进行扩展,使得wasm与host能够进行交互,完成更多的功能。
Host Function
需要import
到wasm中,同样wasm中的function需要export
到host中才能调用。
本篇主要记录下在Golang下,wasm function
如何与host function
进行交互。
环境依赖
- 使用wazero或者wasmtime-go作为wasm的runtime
- Tinygo编译Golang为wasm文件
为什么需要Host Function
Host Function
指在Host
程序里定义的方法。某些方面wasm还不太完善,比如默认情况下不能在终端上打印信息,比如fmt.Println("Hello")
,此时就可以在host
程序中创建一个Print(string)
方法,让wasm调用这个Print(string)
方法。
在Golang中需要wasm runtime将Host Function
(比如Print(string)
)加载到wasm中即可运行,这个操作从wasm角度看,为import
。
show me code
wasm与host进行交互涉及到import
和export
操作,通过代码具体展示何时使用import
和export
,以及在tinygo中又该如何使用。
1. Golang创建wasm模块
1 | package main |
- 1标记的空
main
函数是tinygo编译wasm时需要的 - 2标记的是将
add
方法导出到host
,这样就可以在host程序中调用wasm模块
使用tinygo将golang编译为wasm模块tinygo build -o hello.wasm -scheduler=none --no-debug -target wasi ./hello.go
2. 编写host
程序调用wasm模块
host
模块是用Wazero
runtime编写,首先创建一个WebAssembly Runtime,然后实例化一个WebAssembly module,这样host
程序就能调用wasm function
了。代码如下:
1 | package main |
- 1标记的是创建一个WebAssembly Runtime
- 2标记的是加载一个wasm模块
- 3标记的是实例化一个WebAssembly module
- 4标记的是获取一个
wasm function
add的引用 - 5标记的是在host程序中调用wasm
然后可以像运行正常Go程序那样执行go run main.go
,输出是result: 42
这个基础例子展示了wasm模块的常规用法,主要展示了host程序如何调用wasm模块。
- 但是wasm模块如何调用
host function
呢? - 在Go编写的wasm模块中,使用
export
关键字标记host程序可调用的wasm function
,那host function
又是如何标记被wasm模块调用的呢?
带着这两个疑问看下面这一节内容。
export import 傻傻分不清楚
上面说wasm局限性时提到无法在终端打印信息,需要在host function
中定义一个Print(string)
方法,然后在wasm模块中调用,现在就在host
程序中定义一个在终端打印数字的方法hostLogUint32
。
hostLogUint32
wasm模块调用host function
需要在host程序中和wasm中都进行改动
- host程序中的改动
- 定义一个
logUint32
方法 - export
logUint32
方法到Wasm runtime种,为了方便标记logUint32
export的名字为hostLogUint32
- wasm程序中的改动
- import
hostLogUint32
方法 - 在wasm模块中调用
hostLogUint32
方法在终端打印一个数字
看下具体的代码改动,在main.go
中添加logUint32
方法
1 | func logUint32(value uint32) { |
然后在runtime中export logUint32
方法为hostLogUint32
,代码如下:
1 | _, errEnv := wasmRuntime.NewModuleBuilder("env"). |
接下来在wasm模块中import hostLogUint32
,代码如下:
1 | //export hostLogUint32 |
这里是不是有人开始疑惑了,不是说好import嘛,怎么还是export呀???我当时可是困扰了好久好久。。。
这里//export hostLogUint32
其实是wasm模块import host function
,可以注意到这里的hostLogUint32
并没有方法体,这个可以用来区分是export还是import
最后就是在wasm模块中调用host function
了,代码如下:
1 | package main |
程序准备好之后就可以运行,由于wasm模块也发生了变化,所以需要重新编译wasm文件,执行如下命令:
1 | tinygo build -o hello.wasm -scheduler=none --no-debug -target wasi ./hello.go |
结论
写这篇文章的主要目的其实想理解tinygo中export的用法,因为在参考wasm相关教程中host function
需要import到wasm中才能使用,但是tinygo中并没有提供import
关键字,这使我一度怀疑代码有问题,为了消除疑虑所以才查找了很多资料。
我的理解是tinygo中的export
关键字有时间充当将wasm模块中的方法export到host的功能,有时也充当将host function
中的方法import到wasm模块的功能,那这两种场景如何区分呢?是看带有export
关键字的方法是否有方法体
,如何有就是export
,没有则是import
。这个结果从hostLogUint32
的解析图中可以猜想到,我们还可以将编译之后的wasm文件进行反编译进行验证。
wasm文件反编译使用的是wasm-decompile
命令,具体命令为wasm-decompile log.wasm -o log.dcmp
,查看log.dcmp
文件可看到import和export标注的方法。