Open WJ-Yuan opened 11 months ago
不论是二进制还是文本格式,WebAssembly 代码中的基本单元是一个模块。在文本格式中,一个模块被表示为一个大的 S-表达式。
S-表达式 一个 () 代表一个模块,比如:
S-表达式
()
(module (memory 1) (func))
这个表达式中,表示一棵根节点为模块(module)的树,该树有两个孩子节点,分别是 属性为 1 的内存(memory)节点 和 一个函数(func)节点。
模块(module)
内存(memory)
函数(func)
(module)
( func <signature> <locals> <body> )
签名是由一系列参数类型声明,及其后面的返回值类型声明列表组成。 参数格式为 (param <类型>),返回值格式为 (result <类型>)。
(param <类型>)
(result <类型>)
每一个参数都有一个显式声明的类型,wasm 当前有四个可用类型:
接受两个 32 位整数,返回一个 64 位浮点数的函数如下::
(func (param i32) (param i32) (result f64) ...)
局部变量和参数能够被函数体使用 local.get 和 local.set 指令进行读写。
local.get
local.set
(func (param i32) (param f32) (local f64) local.get 0 local.get 1 local.get 2)
local.get 0 会得到 i32 类型的参数 local.get 1 会得到 f32 类型的参数 local.get 2 会得到 f64 类型的局部变量
由于使用数字索引来指向某个条目容易让人混淆,因此,也可以通过别名的方式来访问它们,方法就是在类型声明的前面添加一个使用美元符号($)作为前缀的名字。
(func (param $p1 i32) (param $p2 f32) (local $loc i32) …)
(module (func (param $lhs i32) (param $rhs i32) (result i32) local.get $lhs local.get $rhs i32.add))
这个函数获取两个参数,然后相加,最后返回其结果。
命名了一个 $add 函数
(func $add … )
(export "add" (func $add))
这里的 add 是 JavaScript 中用来区别这个函数的名字,而$add 则是指出模块中的哪个 WebAssembly 函数将会被导出
(module (func $add (param $lhs i32) (param $rhs i32) (result i32) local.get $lhs local.get $rhs i32.add) (export "add" (func $add)) )
虽然浏览器把 wasm 编译为某种更高效的东西,但是,wasm 的执行是以栈式机器定义的。也就是说,其基本理念是每种类型的指令都是在栈上执行数值的入栈出栈操作。
例如,local.get 被定义为把它读到的局部变量值压入到栈上,然后 i32.add 从栈上取出两个 i32 类型值(它的含义是把前面压入栈上的两个值取出来)计算它们的和(以 2^32 求模),最后把结果压入栈上。
当函数被调用的时候,它是从一个空栈开始的。随着函数体指令的执行,栈会逐步填满和清空。例如,在执行了下面的函数之后:
(func (param $p i32) local.get $p local.get $p i32.add)
栈上只包含一个 i32 类型值——表达式 ($p + $p) 的结果,该结果是由 i32.add 得到的。函数的返回值就是栈上留下的那个最终值。
(module (func $getAnswer (result i32) i32.const 42) (func (export "getAnswerPlus1") (result i32) call $getAnswer i32.const 1 i32.add))
i32.const 只是定义一个 32 位整数并把它压入栈。
(module (import "console" "log" (func $log (param i32))) (func (export "logIt") i32.const 13 call $log))
我们要求从 console 模块导入 log 函数。导入的函数就像普通函数一样:它们拥有一个 WebAssembly 验证机制,会静态检查的签名,可以被设置一个索引,能够被命名和被调用。
var importObject = { console: { log: function (arg) { console.log(arg); }, }, }; fetchAndInstantiate("logger.wasm", importObject).then(function (instance) { instance.exports.logIt(); });
wat2wasm simple.wat -o simple.wasm
wat2wasm simple.wat -v
理解 wasm 格式
S-表达式
不论是二进制还是文本格式,WebAssembly 代码中的基本单元是一个模块。在文本格式中,一个模块被表示为一个大的 S-表达式。
S-表达式
一个()
代表一个模块,比如:最简单的模块
向模块中添加功能
签名
签名是由一系列参数类型声明,及其后面的返回值类型声明列表组成。 参数格式为
(param <类型>)
,返回值格式为(result <类型>)
。每一个参数都有一个显式声明的类型,wasm 当前有四个可用类型:
接受两个 32 位整数,返回一个 64 位浮点数的函数如下::
局部变量和参数获取
局部变量和参数能够被函数体使用
local.get
和local.set
指令进行读写。由于使用数字索引来指向某个条目容易让人混淆,因此,也可以通过别名的方式来访问它们,方法就是在类型声明的前面添加一个使用美元符号($)作为前缀的名字。
函数体
命名函数
导出函数
栈式机器
虽然浏览器把 wasm 编译为某种更高效的东西,但是,wasm 的执行是以栈式机器定义的。也就是说,其基本理念是每种类型的指令都是在栈上执行数值的入栈出栈操作。
例如,local.get 被定义为把它读到的局部变量值压入到栈上,然后 i32.add 从栈上取出两个 i32 类型值(它的含义是把前面压入栈上的两个值取出来)计算它们的和(以 2^32 求模),最后把结果压入栈上。
当函数被调用的时候,它是从一个空栈开始的。随着函数体指令的执行,栈会逐步填满和清空。例如,在执行了下面的函数之后:
栈上只包含一个 i32 类型值——表达式 ($p + $p) 的结果,该结果是由 i32.add 得到的。函数的返回值就是栈上留下的那个最终值。
基本原则
在同一模块里的函数调用其他函数成员
从 Javascript 中导入函数
.wat 转为 .wasm
wat2wasm simple.wat -o simple.wasm
查看 .wat