Почему значения указателя Rust WASM и указателя JS различаются?

Допустим, у меня есть следующие определения в коде 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);

После изменения сгенерированной функции 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);
}

в консоли разработчика я получаю следующий вывод:

js addr =  1114120
rust addr = 1114124

Возникает вопрос, почему значения указателя Rust и указателя JS различаются? Также по моим наблюдениям разница между указателем Rust и указателем JS всегда равна 4. Почему это так?


person OlegTheCat    schedule 27.07.2020    source источник


Ответы (1)


Если вы посмотрите на экспорт структуры в 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, который является продаваемая версия RefCell, но, что более важно, структура с двумя полями:

pub struct WasmRefCell<T> {
    borrow: Cell<usize>,
    value: UnsafeCell<T>,
}

Здесь T - это тип Rust, поэтому внутри типа Rust вы видите этот адрес, однако то, что дано Javascript, является адресом WasmRefCell, что означает, что может быть адресом Cell<usize>, который предшествует struct в источнике: rustc не гарантирует, что он будет соответствовать исходному макету (если вы не аннотируете структуру с помощью repr(C)), но здесь у него мало причин трогать что-либо, поэтому неудивительно, что это не так.

WebAssembly - это 32-битная архитектура, поэтому size_of::<usize>() == 4, следовательно, указатель, который возвращается в JS, находится на 4 байта до места, видимого внутри структуры Rust. QED.

person Masklinn    schedule 27.07.2020