Refactor the ComponentValue impls for tuples (#4217)

These helper functions are also useful for implementing this trait on
user-defined record and tuple types.
This commit is contained in:
Jamey Sharp
2022-06-06 08:44:37 -07:00
committed by GitHub
parent acfeda4d80
commit 82f7dd67e0

View File

@@ -1433,6 +1433,7 @@ unsafe impl<T: ComponentValue> ComponentValue for WasmList<T> {
} }
} }
/// Round `a` up to the next multiple of `align`, assuming that `align` is a power of 2.
#[inline] #[inline]
const fn align_to(a: usize, align: u32) -> usize { const fn align_to(a: usize, align: u32) -> usize {
debug_assert!(align.is_power_of_two()); debug_assert!(align.is_power_of_two());
@@ -1440,6 +1441,43 @@ const fn align_to(a: usize, align: u32) -> usize {
(a + (align - 1)) & !(align - 1) (a + (align - 1)) & !(align - 1)
} }
/// For a field of type T starting after `offset` bytes, updates the offset to reflect the correct
/// alignment and size of T. Returns the correctly aligned offset for the start of the field.
#[inline]
pub fn next_field<T: ComponentValue>(offset: &mut usize) -> usize {
*offset = align_to(*offset, T::align());
let result = *offset;
*offset += T::size();
result
}
/// Verify that the given wasm type is a tuple with the expected fields in the right order.
#[inline]
fn typecheck_tuple(
ty: &InterfaceType,
types: &ComponentTypes,
op: Op,
expected: &[fn(&InterfaceType, &ComponentTypes, Op) -> Result<()>],
) -> Result<()> {
match ty {
InterfaceType::Tuple(t) => {
let tuple = &types[*t];
if tuple.types.len() != expected.len() {
bail!(
"expected {}-tuple, found {}-tuple",
expected.len(),
tuple.types.len()
);
}
for (ty, check) in tuple.types.iter().zip(expected) {
check(ty, types, op)?;
}
Ok(())
}
other => bail!("expected `tuple` found `{}`", desc(other)),
}
}
unsafe impl<T> ComponentValue for Option<T> unsafe impl<T> ComponentValue for Option<T>
where where
T: ComponentValue, T: ComponentValue,
@@ -1683,19 +1721,7 @@ macro_rules! impl_component_ty_for_tuples {
types: &ComponentTypes, types: &ComponentTypes,
op: Op, op: Op,
) -> Result<()> { ) -> Result<()> {
match ty { typecheck_tuple(ty, types, op, &[$($t::typecheck),*])
InterfaceType::Tuple(t) => {
let tuple = &types[*t];
if tuple.types.len() != $n {
bail!("expected {}-tuple, found {}-tuple", $n, tuple.types.len());
}
let mut tuple = tuple.types.iter();
$($t::typecheck(tuple.next().unwrap(), types, op)?;)*
debug_assert!(tuple.next().is_none());
Ok(())
}
other => bail!("expected `tuple` found `{}`", desc(other)),
}
} }
fn lower<U>( fn lower<U>(
@@ -1712,7 +1738,7 @@ macro_rules! impl_component_ty_for_tuples {
#[inline] #[inline]
fn size() -> usize { fn size() -> usize {
let mut size = 0; let mut size = 0;
$(size = align_to(size, $t::align()) + $t::size();)* $(next_field::<$t>(&mut size);)*
size size
} }
@@ -1727,12 +1753,7 @@ macro_rules! impl_component_ty_for_tuples {
let ($($t,)*) = self; let ($($t,)*) = self;
// TODO: this requires that `offset` is aligned which we may not // TODO: this requires that `offset` is aligned which we may not
// want to do // want to do
$( $($t.store(memory, next_field::<$t>(&mut offset))?;)*
offset = align_to(offset, $t::align());
$t.store(memory, offset)?;
offset += $t::size();
)*
drop(offset); // silence warning about last assignment
Ok(()) Ok(())
} }
@@ -1741,12 +1762,8 @@ macro_rules! impl_component_ty_for_tuples {
} }
fn load(memory: &Memory<'_>, bytes: &[u8]) -> Result<Self> { fn load(memory: &Memory<'_>, bytes: &[u8]) -> Result<Self> {
let mut _offset = 0; let mut offset = 0;
$( $(let $t = $t::load(memory, &bytes[next_field::<$t>(&mut offset)..][..$t::size()])?;)*
_offset = align_to(_offset, $t::align());
let $t = $t::load(memory, &bytes[_offset..][..$t::size()])?;
_offset += $t::size();
)*
Ok(($($t,)*)) Ok(($($t,)*))
} }
} }