字符串的
漂流 › 我们的 WAT [#255]
字符串的 漂流 › 我们的 WAT [#255]
被设计为两种不同的表示,一种是利于压缩和网络传输的 Webassembly Binary format,一种是便于人类阅读的 WAT (Webassembly Textual format);这两种格式是等价的。我们关心 后端编译结果的细节,因此 WAT 是更合适的选择。
> moon build --target wasm --output-wat
在 target/wasm/release/build/lib/lib.wat 可以找到 WAT 文件。
现在我们得到了编译产物,在查看之前,先用 wasmtime 跑跑看。传递参数 --invoke foo 表示调用 lib.wat 中导出的 foo 函数:
> wasmtime --invoke foo target/wasm/release/build/lib/lib.wat
10000
看起来 foo 返回了一个数字,但我们想要的是一个字符串,这是什么?
让我们来看看 WAT 的内容,好在 WAT 相当短(鉴于源代码也相当短且简单,这是预期中的),只需要扫一眼,马上就能明白发生了什么。
target/wasm/release/build/lib/lib.wat
(data (memory $moonbit.memory) (offset (i32.const 10000))
"\FF\FF\FF\FF\F3\02\00\00A\00B\00\00\00\00\03\00\00\00\00\00\00\00\00")
(memory $moonbit.memory 1)
(table $moonbit.global 0 0 funcref )
(elem (table $moonbit.global) (offset (i32.const 0))
funcref)
(func $jinser/tour-of-moonbit-string/lib.foo (result i32) (; #2 ;)
(i32.const 10000)) (; #3 ;)
(export "foo" (func $jinser/tour-of-moonbit-string/lib.foo)) (; #1 ;)
(start $*init*/1)
(func $*init*/1)
这里发生的事情是,函数 $jinser/tour-of-moonbit-string/lib.foo 被导出为 "foo"(#1),该函数的返回值类型为 i32(#2),是一个常数值 10000(#3)。
这虽然很好地解释了 wasmtime 为何返回这个数字,但还没有解释这个数字是什么。不用多说,我想你已经注意到了 WAT 第一行的可疑指令。看来我们又找到线索了!要使猜测更有说服力,让我们来试着解释一下这句指令做了什么。
根据 spec 中对 data segment 的描述,参考其形式化定义
\[\begin {array}{llll} \phantom {data segment} & data &::=& \{ \text {init}~vec(byte), \text {mode}~datamode \} \\ \phantom {data segment mode} & \text {datamode} &::=& passive \\&&|& active~\{ \text {mem}~memidx, \text {offset}~expr \} \\ \end {array} \]得到以下信息:
- \(data\)
(data mode init)此指令用于声明一段数据。这里是 active data segment ,意味着复制 init 到 WebAssembly 模块的线性内存中。 - \(datamode (active~\{\text {mem}\})\)
(memory $moonbit.memory)指定数据要放置的内存段,这里引用了名为$moonbit.memory的内存。 - \(datamode (active~\{\text {offset}\})\)
(offset (i32.const 10000))指定了数据在内存中应该存放位置的偏移量,这里使用的是(i32.const 10000),表示将数据放置在内存地址 10000。 - \(\text {init}\)
"\FF\FF\FF\FF\F3\02\00\00A\00B\00\00\00\00\03\00\00\00\00\00\00\00\00"这是要写入到内存中的字节序列。每个\XX表示一个字节,使用十六进制表示法。
foo 返回的值 10000,就是这段数据的 offset 了。
MoonBit