Expose raw list accessors for all integer types (#4330)

This commit extends the `WasmList<T>` type to have an
`as_slice`-lookalike method (now renamed to `as_le_slice`) for all
integer types rather than just the `u8` type. With the guarantees of the
component model it's known that all lists are aligned in linear memory.
Additionally linear memories themselves are also generally guaranteed to
be aligned. This means that hosts where the primitive integer alignment
is at most the size (which I think is basically all host platforms) can
get a raw view into memory for the wasm linear memory for slices of
these types.

Note, though, that the remaining caveat after alignment is endianness.
Big-endian hosts need to be aware that the integers aren't stored in a
native format. Previously tools like wit-bindgen have added an `Le<T>`
wrapper but for now I've opted to instead use a method that has "le" in
the name - `as_le_slice`. I'm hoping that this is a clear enough
indicator for users to little-endian conversions as appropriate when
reading the values within the slice.
This commit is contained in:
Alex Crichton
2022-06-28 10:23:58 -05:00
committed by GitHub
parent 2efdd5c46b
commit fc38f39bd2
2 changed files with 215 additions and 12 deletions

View File

@@ -932,7 +932,7 @@ fn many_parameters() -> Result<()> {
.as_slice(),
);
let (memory, pointer) = func.call(&mut store, input)?;
let memory = memory.as_slice(&store);
let memory = memory.as_le_slice(&store);
let mut actual = &memory[pointer as usize..][..72];
assert_eq!(i8::from_le_bytes(*actual.take_n::<1>()), input.0);
@@ -1780,7 +1780,7 @@ fn fancy_list() -> Result<()> {
(Some(200), Err("general kenobi")),
];
let (ptr, len, list) = func.call(&mut store, (&input,))?;
let memory = list.as_slice(&store);
let memory = list.as_le_slice(&store);
let ptr = usize::try_from(ptr).unwrap();
let len = usize::try_from(len).unwrap();
let mut array = &memory[ptr..][..len * 16];
@@ -1990,3 +1990,167 @@ fn drop_component_still_works() -> Result<()> {
Ok(())
}
#[test]
fn raw_slice_of_various_types() -> Result<()> {
let component = r#"
(component
(core module $m
(memory (export "memory") 1)
(func (export "list8") (result i32)
(call $setup_list (i32.const 16))
)
(func (export "list16") (result i32)
(call $setup_list (i32.const 8))
)
(func (export "list32") (result i32)
(call $setup_list (i32.const 4))
)
(func (export "list64") (result i32)
(call $setup_list (i32.const 2))
)
(func $setup_list (param i32) (result i32)
(i32.store offset=0 (i32.const 100) (i32.const 8))
(i32.store offset=4 (i32.const 100) (local.get 0))
i32.const 100
)
(data (i32.const 8) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f")
)
(core instance $i (instantiate $m))
(func (export "list-u8") (result (list u8))
(canon lift (core func $i "list8") (memory $i "memory"))
)
(func (export "list-i8") (result (list s8))
(canon lift (core func $i "list8") (memory $i "memory"))
)
(func (export "list-u16") (result (list u16))
(canon lift (core func $i "list16") (memory $i "memory"))
)
(func (export "list-i16") (result (list s16))
(canon lift (core func $i "list16") (memory $i "memory"))
)
(func (export "list-u32") (result (list u32))
(canon lift (core func $i "list32") (memory $i "memory"))
)
(func (export "list-i32") (result (list s32))
(canon lift (core func $i "list32") (memory $i "memory"))
)
(func (export "list-u64") (result (list u64))
(canon lift (core func $i "list64") (memory $i "memory"))
)
(func (export "list-i64") (result (list s64))
(canon lift (core func $i "list64") (memory $i "memory"))
)
)
"#;
let (mut store, instance) = {
let engine = super::engine();
let component = Component::new(&engine, component)?;
let mut store = Store::new(&engine, ());
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
(store, instance)
};
let list = instance
.get_typed_func::<(), WasmList<u8>, _>(&mut store, "list-u8")?
.call_and_post_return(&mut store, ())?;
assert_eq!(
list.as_le_slice(&store),
[
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f,
]
);
let list = instance
.get_typed_func::<(), WasmList<i8>, _>(&mut store, "list-i8")?
.call_and_post_return(&mut store, ())?;
assert_eq!(
list.as_le_slice(&store),
[
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f,
]
);
let list = instance
.get_typed_func::<(), WasmList<u16>, _>(&mut store, "list-u16")?
.call_and_post_return(&mut store, ())?;
assert_eq!(
list.as_le_slice(&store),
[
u16::to_le(0x01_00),
u16::to_le(0x03_02),
u16::to_le(0x05_04),
u16::to_le(0x07_06),
u16::to_le(0x09_08),
u16::to_le(0x0b_0a),
u16::to_le(0x0d_0c),
u16::to_le(0x0f_0e),
]
);
let list = instance
.get_typed_func::<(), WasmList<i16>, _>(&mut store, "list-i16")?
.call_and_post_return(&mut store, ())?;
assert_eq!(
list.as_le_slice(&store),
[
i16::to_le(0x01_00),
i16::to_le(0x03_02),
i16::to_le(0x05_04),
i16::to_le(0x07_06),
i16::to_le(0x09_08),
i16::to_le(0x0b_0a),
i16::to_le(0x0d_0c),
i16::to_le(0x0f_0e),
]
);
let list = instance
.get_typed_func::<(), WasmList<u32>, _>(&mut store, "list-u32")?
.call_and_post_return(&mut store, ())?;
assert_eq!(
list.as_le_slice(&store),
[
u32::to_le(0x03_02_01_00),
u32::to_le(0x07_06_05_04),
u32::to_le(0x0b_0a_09_08),
u32::to_le(0x0f_0e_0d_0c),
]
);
let list = instance
.get_typed_func::<(), WasmList<i32>, _>(&mut store, "list-i32")?
.call_and_post_return(&mut store, ())?;
assert_eq!(
list.as_le_slice(&store),
[
i32::to_le(0x03_02_01_00),
i32::to_le(0x07_06_05_04),
i32::to_le(0x0b_0a_09_08),
i32::to_le(0x0f_0e_0d_0c),
]
);
let list = instance
.get_typed_func::<(), WasmList<u64>, _>(&mut store, "list-u64")?
.call_and_post_return(&mut store, ())?;
assert_eq!(
list.as_le_slice(&store),
[
u64::to_le(0x07_06_05_04_03_02_01_00),
u64::to_le(0x0f_0e_0d_0c_0b_0a_09_08),
]
);
let list = instance
.get_typed_func::<(), WasmList<i64>, _>(&mut store, "list-i64")?
.call_and_post_return(&mut store, ())?;
assert_eq!(
list.as_le_slice(&store),
[
i64::to_le(0x07_06_05_04_03_02_01_00),
i64::to_le(0x0f_0e_0d_0c_0b_0a_09_08),
]
);
Ok(())
}