指定 FFI export 名称

为了方便从外部调用 MoonBit 生成的 WASM 函数,可否新增下面这样的语法:

pub fn on_complete "on_message_complete" (ptr: Int) -> Int { ... }

输出如下 WAT:

(export "fantix/mmhttp/http1::on_complete" (func $236))
(export "on_message_complete" (func $236))
...

当该模块被作为依赖关系嵌入其他 MoonBit 工程中时,编译生成的结果不会包含 on_complete 的导出,但是会包含 on_message_complete 的导出:

(export "on_message_complete" (func $236))
...

其中参数和返回值类型的限制,同 FFI import 的限制一致:

fn llhttp_resume(ptr: Int) = "llhttp" "llhttp_resume"

使用场景是在用 wasm-merge 来合并两个 wasm 文件,一个是 MoonBit 生成的,另一个是用 C(WASI)写的(llhttp 是一个 HTTP 1.1 协议解析器)。二者之间互有调用,从 mbt 调 C 因为有上述 FFI import 的语法支持就很容易,而从 C 调 mbt 则需额外用 WAT 写一段桥接的代码,如下:

 (import "mmhttp" "fantix/mmhttp/http1::on_complete" (func $on_message_complete (param i32) (result i32)))
 (func (export "on_message_complete") (param i32) (result i32) (local.get 0) (call $on_message_complete))

(llhttp 导入了 on_message_complete 函数)

更有甚者,如果用户的工程只是把 mmhttp 作为一个依赖关系的话,用户还需要自行编写所有桥接代码,维护十分不易。如果能有像一开始提议的功能,那么即使是在底层依赖中,也可以顺利将函数 export 到外部(不管是 host 系统,还是 wasm-merge 进来的第三方 wasm 模块)。

我自己分析这种语法可能带来的坏处就是,不同依赖关系可能会在同一个项目中,尝试 export 同一个名称。这种情况在早期应该十分罕见(同质化的 FFI 库),报错即可;后期可在 moon.mod.json 中允许用户关闭某个模块的 FFI export。个人意见仅供参考。

现在的导出是在顶层lib的pub才会导出,感觉应该是可行的,冲突只需要检测当前package

感谢张老师确认!如果不允许【非顶层 lib】导出指定名称的函数,那我还有一个问题:如何构建并发行一个包含 export 的【顶层 lib】?

目前如果 moon new 创建的是 lib 而非 exec 格式的项目的话,moon build 是无法构建【顶层 lib】的,只有 moon test 才可以创建以测试为目的的 wasm 包。这是不是意味着,lib 就只能作为依赖关系,依从某个 exec 项目才能完成构建(但那样 lib 就无法 export 了)?还是说这只是尚未完成的功能,需要看下一步 lib 链接方式的设计才能实现,比如需要再等等 component model 提议?

我目前的做法是,用 moon test 构建【顶层 lib】"mmhttp"的 wasm 包,然后手动与 llhttp 的 wasm 包合并,但是再下一步就走不下去了,因为其他项目最终是要 moon add fantix/mmhttp 的,而我发行的却是一个 .wasm 文件;其他项目如果自行构建 mmhttp 的话,又牵扯到前述【非顶层 lib】不能导出函数的问题,从而无法与 llhttp 合并,所以得让用户自己在顶层重新包装一遍需要导出的函数。于是才有了之前的提议,希望能从【非顶层 lib】导出函数,用户只需 moon add fantix/mmhttpmoon build,然后把生成的 wasm 与 llhttp 的 wasm 做一下 wasm-merge 即可。

当然,如果抛开【非顶层 lib】导出函数不谈,单说【允许自定义导出函数的别名】这一点也是有用处的,因为这将省去手写的 WAT 桥接代码。

1 个赞