Files
wasmtime/tests/all/component_model/dynamic.rs
Lann 0029ff95ac Use floats for wasmtime::component::Val::Float* (#5510)
The definitions of `wasmtime::component::Val::Float{32,64}` mirrored
`wasmtime::Val::F{32,64}` by using integers as their wrapped types,
storing the bit representation of their floating point values.
This was necessary for the core Wasm `f32`/`f64` types because Rust
floats don't have guaranteed NaN bit representations.

The component model `float32`/`float64` types require NaN
canonicalization, so we can use normal Rust `f{32,64}` instead.

Closes #5480
2023-01-03 20:23:38 +00:00

516 lines
16 KiB
Rust

use super::{make_echo_component, make_echo_component_with_params, Param, Type};
use anyhow::Result;
use component_test_util::FuncExt;
use wasmtime::component::{self, Component, Linker, Val};
use wasmtime::Store;
#[test]
fn primitives() -> Result<()> {
let engine = super::engine();
let mut store = Store::new(&engine, ());
let mut output = [Val::Bool(false)];
for (input, ty, param) in [
(Val::Bool(true), "bool", Param(Type::U8, Some(0))),
(Val::S8(-42), "s8", Param(Type::S8, Some(0))),
(Val::U8(42), "u8", Param(Type::U8, Some(0))),
(Val::S16(-4242), "s16", Param(Type::S16, Some(0))),
(Val::U16(4242), "u16", Param(Type::U16, Some(0))),
(Val::S32(-314159265), "s32", Param(Type::I32, Some(0))),
(Val::U32(314159265), "u32", Param(Type::I32, Some(0))),
(Val::S64(-31415926535897), "s64", Param(Type::I64, Some(0))),
(Val::U64(31415926535897), "u64", Param(Type::I64, Some(0))),
(
Val::Float32(3.14159265),
"float32",
Param(Type::F32, Some(0)),
),
(
Val::Float64(3.14159265),
"float64",
Param(Type::F64, Some(0)),
),
(Val::Char('🦀'), "char", Param(Type::I32, Some(0))),
] {
let component = Component::new(&engine, make_echo_component_with_params(ty, &[param]))?;
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
let func = instance.get_func(&mut store, "echo").unwrap();
func.call_and_post_return(&mut store, &[input.clone()], &mut output)?;
assert_eq!(input, output[0]);
}
// Sad path: type mismatch
let component = Component::new(
&engine,
make_echo_component_with_params("float64", &[Param(Type::F64, Some(0))]),
)?;
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
let func = instance.get_func(&mut store, "echo").unwrap();
let err = func
.call_and_post_return(&mut store, &[Val::U64(42)], &mut output)
.unwrap_err();
assert!(err.to_string().contains("type mismatch"), "{err}");
// Sad path: arity mismatch (too many)
let err = func
.call_and_post_return(
&mut store,
&[Val::Float64(3.14159265), Val::Float64(3.14159265)],
&mut output,
)
.unwrap_err();
assert!(
err.to_string().contains("expected 1 argument(s), got 2"),
"{err}"
);
// Sad path: arity mismatch (too few)
let err = func
.call_and_post_return(&mut store, &[], &mut output)
.unwrap_err();
assert!(
err.to_string().contains("expected 1 argument(s), got 0"),
"{err}"
);
let err = func
.call_and_post_return(&mut store, &output, &mut [])
.unwrap_err();
assert!(
err.to_string().contains("expected 1 results(s), got 0"),
"{err}"
);
Ok(())
}
#[test]
fn strings() -> Result<()> {
let engine = super::engine();
let mut store = Store::new(&engine, ());
let component = Component::new(&engine, make_echo_component("string", 8))?;
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
let func = instance.get_func(&mut store, "echo").unwrap();
let input = Val::String(Box::from("hello, component!"));
let mut output = [Val::Bool(false)];
func.call_and_post_return(&mut store, &[input.clone()], &mut output)?;
assert_eq!(input, output[0]);
Ok(())
}
#[test]
fn lists() -> Result<()> {
let engine = super::engine();
let mut store = Store::new(&engine, ());
let component = Component::new(&engine, make_echo_component("(list u32)", 8))?;
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
let func = instance.get_func(&mut store, "echo").unwrap();
let ty = &func.params(&store)[0];
let input = ty.unwrap_list().new_val(Box::new([
Val::U32(32343),
Val::U32(79023439),
Val::U32(2084037802),
]))?;
let mut output = [Val::Bool(false)];
func.call_and_post_return(&mut store, &[input.clone()], &mut output)?;
assert_eq!(input, output[0]);
// Sad path: type mismatch
let err = ty
.unwrap_list()
.new_val(Box::new([
Val::U32(32343),
Val::U32(79023439),
Val::Float32(3.14159265),
]))
.unwrap_err();
assert!(err.to_string().contains("type mismatch"), "{err}");
Ok(())
}
#[test]
fn records() -> Result<()> {
let engine = super::engine();
let mut store = Store::new(&engine, ());
let component = Component::new(
&engine,
make_echo_component_with_params(
r#"(record (field "A" u32) (field "B" float64) (field "C" (record (field "D" bool) (field "E" u32))))"#,
&[
Param(Type::I32, Some(0)),
Param(Type::F64, Some(8)),
Param(Type::U8, Some(16)),
Param(Type::I32, Some(20)),
],
),
)?;
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
let func = instance.get_func(&mut store, "echo").unwrap();
let ty = &func.params(&store)[0];
let inner_type = &ty.unwrap_record().fields().nth(2).unwrap().ty;
let input = ty.unwrap_record().new_val([
("A", Val::U32(32343)),
("B", Val::Float64(3.14159265)),
(
"C",
inner_type
.unwrap_record()
.new_val([("D", Val::Bool(false)), ("E", Val::U32(2084037802))])?,
),
])?;
let mut output = [Val::Bool(false)];
func.call_and_post_return(&mut store, &[input.clone()], &mut output)?;
assert_eq!(input, output[0]);
// Sad path: type mismatch
let err = ty
.unwrap_record()
.new_val([
("A", Val::S32(32343)),
("B", Val::Float64(3.14159265)),
(
"C",
inner_type
.unwrap_record()
.new_val([("D", Val::Bool(false)), ("E", Val::U32(2084037802))])?,
),
])
.unwrap_err();
assert!(err.to_string().contains("type mismatch"), "{err}");
// Sad path: too many fields
let err = ty
.unwrap_record()
.new_val([
("A", Val::U32(32343)),
("B", Val::Float64(3.14159265)),
(
"C",
inner_type
.unwrap_record()
.new_val([("D", Val::Bool(false)), ("E", Val::U32(2084037802))])?,
),
("F", Val::Bool(true)),
])
.unwrap_err();
assert!(
err.to_string().contains("expected 3 value(s); got 4"),
"{err}"
);
// Sad path: too few fields
let err = ty
.unwrap_record()
.new_val([("A", Val::U32(32343)), ("B", Val::Float64(3.14159265))])
.unwrap_err();
assert!(
err.to_string().contains("expected 3 value(s); got 2"),
"{err}"
);
Ok(())
}
#[test]
fn variants() -> Result<()> {
let engine = super::engine();
let mut store = Store::new(&engine, ());
let component = Component::new(
&engine,
make_echo_component_with_params(
r#"(variant (case "A" u32) (case "B" float64) (case "C" (record (field "D" bool) (field "E" u32))))"#,
&[
Param(Type::U8, Some(0)),
Param(Type::I64, Some(8)),
Param(Type::I32, None),
],
),
)?;
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
let func = instance.get_func(&mut store, "echo").unwrap();
let ty = &func.params(&store)[0];
let input = ty
.unwrap_variant()
.new_val("B", Some(Val::Float64(3.14159265)))?;
let mut output = [Val::Bool(false)];
func.call_and_post_return(&mut store, &[input.clone()], &mut output)?;
assert_eq!(input, output[0]);
// Do it again, this time using case "C"
let component = Component::new(
&engine,
make_echo_component_with_params(
r#"(variant (case "A" u32) (case "B" float64) (case "C" (record (field "D" bool) (field "E" u32))))"#,
&[
Param(Type::U8, Some(0)),
Param(Type::I64, Some(8)),
Param(Type::I32, Some(12)),
],
),
)?;
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
let func = instance.get_func(&mut store, "echo").unwrap();
let ty = &func.params(&store)[0];
let c_type = &ty.unwrap_variant().cases().nth(2).unwrap().ty.unwrap();
let input = ty.unwrap_variant().new_val(
"C",
Some(
c_type
.unwrap_record()
.new_val([("D", Val::Bool(true)), ("E", Val::U32(314159265))])?,
),
)?;
func.call_and_post_return(&mut store, &[input.clone()], &mut output)?;
assert_eq!(input, output[0]);
// Sad path: type mismatch
let err = ty
.unwrap_variant()
.new_val("B", Some(Val::U64(314159265)))
.unwrap_err();
assert!(err.to_string().contains("type mismatch"), "{err}");
let err = ty.unwrap_variant().new_val("B", None).unwrap_err();
assert!(
err.to_string().contains("expected a payload for case `B`"),
"{err}"
);
// Sad path: unknown case
let err = ty
.unwrap_variant()
.new_val("D", Some(Val::U64(314159265)))
.unwrap_err();
assert!(err.to_string().contains("unknown variant case"), "{err}");
let err = ty.unwrap_variant().new_val("D", None).unwrap_err();
assert!(err.to_string().contains("unknown variant case"), "{err}");
// Make sure we lift variants which have cases of different sizes with the correct alignment
let component = Component::new(
&engine,
make_echo_component_with_params(
r#"
(record
(field "A" (variant
(case "A" u32)
(case "B" float64)
(case "C" (record (field "D" bool) (field "E" u32)))))
(field "B" u32))"#,
&[
Param(Type::U8, Some(0)),
Param(Type::I64, Some(8)),
Param(Type::I32, None),
Param(Type::I32, Some(16)),
],
),
)?;
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
let func = instance.get_func(&mut store, "echo").unwrap();
let ty = &func.params(&store)[0];
let a_type = &ty.unwrap_record().fields().nth(0).unwrap().ty;
let input = ty.unwrap_record().new_val([
(
"A",
a_type
.unwrap_variant()
.new_val("A", Some(Val::U32(314159265)))?,
),
("B", Val::U32(628318530)),
])?;
func.call_and_post_return(&mut store, &[input.clone()], &mut output)?;
assert_eq!(input, output[0]);
Ok(())
}
#[test]
fn flags() -> Result<()> {
let engine = super::engine();
let mut store = Store::new(&engine, ());
let component = Component::new(
&engine,
make_echo_component_with_params(
r#"(flags "A" "B" "C" "D" "E")"#,
&[Param(Type::U8, Some(0))],
),
)?;
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
let func = instance.get_func(&mut store, "echo").unwrap();
let ty = &func.params(&store)[0];
let input = ty.unwrap_flags().new_val(&["B", "D"])?;
let mut output = [Val::Bool(false)];
func.call_and_post_return(&mut store, &[input.clone()], &mut output)?;
assert_eq!(input, output[0]);
// Sad path: unknown flags
let err = ty.unwrap_flags().new_val(&["B", "D", "F"]).unwrap_err();
assert!(err.to_string().contains("unknown flag"), "{err}");
Ok(())
}
#[test]
fn everything() -> Result<()> {
// This serves to test both nested types and storing parameters on the heap (i.e. exceeding `MAX_STACK_PARAMS`)
let engine = super::engine();
let mut store = Store::new(&engine, ());
let component = Component::new(
&engine,
make_echo_component_with_params(
r#"
(record
(field "A" u32)
(field "B" (enum "a" "b"))
(field "C" (record (field "D" bool) (field "E" u32)))
(field "F" (list (flags "G" "H" "I")))
(field "J" (variant
(case "K" u32)
(case "L" float64)
(case "M" (record (field "N" bool) (field "O" u32)))))
(field "P" s8)
(field "Q" s16)
(field "R" s32)
(field "S" s64)
(field "T" float32)
(field "U" float64)
(field "V" string)
(field "W" char)
(field "Y" (tuple u32 u32))
(field "Z" (union u32 float64))
(field "AA" (option u32))
(field "BB" (result string (error string)))
)"#,
&[
Param(Type::I32, Some(0)),
Param(Type::U8, Some(4)),
Param(Type::U8, Some(5)),
Param(Type::I32, Some(8)),
Param(Type::I32, Some(12)),
Param(Type::I32, Some(16)),
Param(Type::U8, Some(20)),
Param(Type::I64, Some(28)),
Param(Type::I32, Some(32)),
Param(Type::S8, Some(36)),
Param(Type::S16, Some(38)),
Param(Type::I32, Some(40)),
Param(Type::I64, Some(48)),
Param(Type::F32, Some(56)),
Param(Type::F64, Some(64)),
Param(Type::I32, Some(72)),
Param(Type::I32, Some(76)),
Param(Type::I32, Some(80)),
Param(Type::I32, Some(84)),
Param(Type::I32, Some(88)),
Param(Type::I64, Some(96)),
Param(Type::U8, Some(104)),
Param(Type::I32, Some(108)),
Param(Type::U8, Some(112)),
Param(Type::I32, Some(116)),
Param(Type::I32, Some(120)),
],
),
)?;
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
let func = instance.get_func(&mut store, "echo").unwrap();
let ty = &func.params(&store)[0];
let types = ty
.unwrap_record()
.fields()
.map(|field| field.ty)
.collect::<Box<[component::Type]>>();
let (b_type, c_type, f_type, j_type, y_type, z_type, aa_type, bb_type) = (
&types[1], &types[2], &types[3], &types[4], &types[13], &types[14], &types[15], &types[16],
);
let f_element_type = &f_type.unwrap_list().ty();
let input = ty.unwrap_record().new_val([
("A", Val::U32(32343)),
("B", b_type.unwrap_enum().new_val("b")?),
(
"C",
c_type
.unwrap_record()
.new_val([("D", Val::Bool(false)), ("E", Val::U32(2084037802))])?,
),
(
"F",
f_type.unwrap_list().new_val(Box::new([f_element_type
.unwrap_flags()
.new_val(&["G", "I"])?]))?,
),
(
"J",
j_type
.unwrap_variant()
.new_val("L", Some(Val::Float64(3.14159265)))?,
),
("P", Val::S8(42)),
("Q", Val::S16(4242)),
("R", Val::S32(42424242)),
("S", Val::S64(424242424242424242)),
("T", Val::Float32(3.14159265)),
("U", Val::Float64(3.14159265)),
("V", Val::String(Box::from("wow, nice types"))),
("W", Val::Char('🦀')),
(
"Y",
y_type
.unwrap_tuple()
.new_val(Box::new([Val::U32(42), Val::U32(24)]))?,
),
(
"Z",
z_type.unwrap_union().new_val(1, Val::Float64(3.14159265))?,
),
(
"AA",
aa_type.unwrap_option().new_val(Some(Val::U32(314159265)))?,
),
(
"BB",
bb_type
.unwrap_result()
.new_val(Ok(Some(Val::String(Box::from("no problem")))))?,
),
])?;
let mut output = [Val::Bool(false)];
func.call_and_post_return(&mut store, &[input.clone()], &mut output)?;
assert_eq!(input, output[0]);
Ok(())
}