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模块是用Wazeroruntime编写,首先创建一个WebAssembly Runtime,然后实例化一个WebAssembly module,这样host程序就能调用wasm function了。代码如下:
1 | package main |
- 1标记的是创建一个WebAssembly Runtime
- 2标记的是加载一个wasm模块
- 3标记的是实例化一个WebAssembly module
- 4标记的是获取一个
wasm functionadd的引用 - 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种,为了方便标记logUint32export的名字为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标注的方法。
