目前还没有关于indirect_call
的使用示例可以在网上找到。根据语义文档,我尝试了以下方法:
(call_indirect
(i32.const 0)
(i32.const 0)
)
数字是随机的,但与我预期的运行时错误不同,我得到了解析错误。
call_indirect
的正确语法是什么?
目前还没有关于indirect_call
的使用示例可以在网上找到。根据语义文档,我尝试了以下方法:
(call_indirect
(i32.const 0)
(i32.const 0)
)
数字是随机的,但与我预期的运行时错误不同,我得到了解析错误。
call_indirect
的正确语法是什么?
call_indirect
的正确语法似乎是:
(call_indirect $fsig
(i32.const 0)
)
$fsig
是在 type
部分定义的预期函数签名,参数是函数地址 (或者说它在 table
中的索引)。
以以下调用函数指针的 C 代码示例为例:
typedef void(*fp)();
void dispatch(fp x) {
x();
}
它编译成
(module
(type $FUNCSIG$v (func))
(table 0 anyfunc)
(memory $0 1)
(export "memory" (memory $0))
(export "dispatch" (func $dispatch))
(func $dispatch (param $0 i32)
(call_indirect $FUNCSIG$v
(get_local $0)
)
)
)
下面是一个更完整的示例,我们实际上调用了一个返回值的函数test
:
(module
(type $FUNCSIG$i (func (result i32)))
(table 1 anyfunc)
(elem (i32.const 0) $test)
(memory $0 1)
(func $test (type $FUNCSIG$i) (result i32)
(i32.const 42)
)
(func $main (result i32)
(call_indirect $FUNCSIG$i
(i32.const 0)
)
)
)
这是文档的一部分:
表格基本上是可调整大小的引用数组,可以从WebAssembly代码中通过索引访问。为了看到为什么需要表格,我们首先需要观察调用指令需要一个静态函数索引,因此只能调用一个函数——但是如果被调用者是运行时值呢?
- 在JavaScript中,我们经常看到这种情况:函数是一等公民。
- 在C/C++中,我们使用函数指针。
- 在C ++中,我们使用虚拟函数。
WebAssembly需要一种类型的调用指令来实现这一点,因此我们提供了call_indirect指令,它需要一个动态函数操作数。问题在于,目前在WebAssembly中我们可以给操作数提供的唯一类型是i32/i64/f32/f64。
WebAssembly可以添加一个名为anyfunc的类型(“any”表示该类型可以保存任何签名的函数),但不幸的是出于安全原因,无法将此anyfunc类型存储在线性内存中。线性内存以字节形式公开存储值的原始内容,这将允许WASM内容任意观察和破坏原始函数地址,这是不能在Web上允许的。
解决方案是将函数引用存储在表格中,并传递索引而非函数地址作为参数,这些索引只是i32值。因此,call_indirect的操作数可以是i32索引值。
因此,简单来说,call_indirect
需要签名进行验证,并以指向相应表格中所需函数的i32
值作为参数。与其他WASM指令一样,参数可以从堆栈中弹出或从嵌套指令中检索。因此,我们可以有以下内容:
call_indirect (type 123) ;; Type as an index in the table,
;; argument on the stack from previous instructions.
或者
call_indirect $funcSignature ;; The same, but using a type name instead of index.
或者
(call_indirect $funcSignature ;; The argument is provided by the nested instructions.
(get_local $0)
)