为什么 Rust WASM 指针和 JS 指针的值不同?

Ole*_*Cat 7 javascript rust webassembly wasm-bindgen

假设我在 Rust 代码中有以下定义:

#[wasm_bindgen]
pub struct RustType {
    foo: usize
}

#[wasm_bindgen]
impl RustType {
    #[wasm_bindgen(constructor)]
    pub fn new() -> Self {
        Self { foo: 100 }
    }
}

#[wasm_bindgen]
pub fn print_addr(obj: &RustType) {
    console_log!("rust addr = {}", obj as *const _ as u32);
}
Run Code Online (Sandbox Code Playgroud)

JS 代码创建一个实例RustType并将其传递给print_addr函数:

var obj = new RustType();
print_addr(obj);
Run Code Online (Sandbox Code Playgroud)

修改生成的print_addr函数后index_bg.js如下:

export function print_addr(obj) {
    _assertClass(obj, RustType);
    console.log("js addr = ", obj.ptr); // <== added this line
    if (obj.ptr === 0) {
        throw new Error('Attempt to use a moved value');
    }
    wasm.print_addr(obj.ptr);
}
Run Code Online (Sandbox Code Playgroud)

在开发控制台中我得到以下输出:

js addr =  1114120
rust addr = 1114124
Run Code Online (Sandbox Code Playgroud)

问题是为什么 Rust 指针和 JS 指针的值不同?另外根据我的观察,Rust 指针和 JS 指针之间的差异始终等于 4。为什么会这样呢?

Mas*_*inn 7

如果您查看将结构导出到 JS,在页面下方您可以看到某些函数的生成代码。看起来相关的一个是:

#[export_name = "foo_new"]
pub extern "C" fn __wasm_bindgen_generated_Foo_new(arg0: i32) -> u32
    let ret = Foo::new(arg0);
    Box::into_raw(Box::new(WasmRefCell::new(ret))) as u32
}
Run Code Online (Sandbox Code Playgroud)

所以我们有一个通过 Box 的指针,nbd,但是您可以看到Foo(暴露给 JavaScript 的结构)被包装在 a 中WasmRefCell,这是RefCell 的供应版本,但更重要的是具有两个字段的结构:

pub struct WasmRefCell<T> {
    borrow: Cell<usize>,
    value: UnsafeCell<T>,
}
Run Code Online (Sandbox Code Playgroud)

T是 Rust 类型,因此在 Rust 类型内部您可以看到该地址,但是提供给 Javascript 的是 的地址WasmRefCell,这意味着它可能Cell<usize>是源中结构体之前的的地址:rustc 不保证它将匹配源布局(除非您用 注释结构repr(C)),但在这里它没有理由接触任何东西,所以它不匹配也就不足为奇了。

WebAssembly 是 32 位架构,因此size_of::<usize>() == 4,返回给 JS 的指针位于 Rust 结构内部可见位置之前 4 个字节。量子ED。