为什么 Rust WASM 指针和 JS 指针的值不同?
Why do the values of Rust WASM pointer and JS pointer differ?
假设我在 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);
}
JS代码创建了一个RustType
实例并将其传递给print_addr
函数:
var obj = new RustType();
print_addr(obj);
在index_bg.js
中修改生成的print_addr
函数后,像这样:
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);
}
在开发控制台中,我得到以下输出:
js addr = 1114120
rust addr = 1114124
问题是为什么Rust指针和JS指针的值不同?另外根据我的观察,Rust 指针和 JS 指针之间的 diff 总是等于 4。为什么会这样?
如果您查看 exporting a struct to 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
}
所以我们有一个通过 Box 的指针,nbd,但是您可以看到 Foo
(暴露给 Javascript 的结构)被包裹在 WasmRefCell
中,这是 a vendored version of RefCell 但更重要的是具有 两个 字段的结构:
pub struct WasmRefCell<T> {
borrow: Cell<usize>,
value: UnsafeCell<T>,
}
这里 T
是 Rust 类型,所以在 Rust 类型内部你会看到那个地址,但是给 Javascript 的是 WasmRefCell
的地址,这意味着它可能 是 Cell<usize>
的地址,它在源代码中位于结构之前:rustc 不保证它会匹配源布局(除非你用 [=17 注释结构=]) 但这里它几乎没有理由触摸任何东西,所以它没有触摸也就不足为奇了。
WebAssembly 是一个 32 位架构,所以 size_of::<usize>() == 4
,因此返回给 JS 的指针在 Rust 结构内部可见位置之前 4 个字节。 QED.
假设我在 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);
}
JS代码创建了一个RustType
实例并将其传递给print_addr
函数:
var obj = new RustType();
print_addr(obj);
在index_bg.js
中修改生成的print_addr
函数后,像这样:
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);
}
在开发控制台中,我得到以下输出:
js addr = 1114120
rust addr = 1114124
问题是为什么Rust指针和JS指针的值不同?另外根据我的观察,Rust 指针和 JS 指针之间的 diff 总是等于 4。为什么会这样?
如果您查看 exporting a struct to 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
}
所以我们有一个通过 Box 的指针,nbd,但是您可以看到 Foo
(暴露给 Javascript 的结构)被包裹在 WasmRefCell
中,这是 a vendored version of RefCell 但更重要的是具有 两个 字段的结构:
pub struct WasmRefCell<T> {
borrow: Cell<usize>,
value: UnsafeCell<T>,
}
这里 T
是 Rust 类型,所以在 Rust 类型内部你会看到那个地址,但是给 Javascript 的是 WasmRefCell
的地址,这意味着它可能 是 Cell<usize>
的地址,它在源代码中位于结构之前:rustc 不保证它会匹配源布局(除非你用 [=17 注释结构=]) 但这里它几乎没有理由触摸任何东西,所以它没有触摸也就不足为奇了。
WebAssembly 是一个 32 位架构,所以 size_of::<usize>() == 4
,因此返回给 JS 的指针在 Rust 结构内部可见位置之前 4 个字节。 QED.