#![cfg(not(miri))] use super::{Param, Type, make_echo_component, make_echo_component_with_params}; use wasmtime::Result; use wasmtime::component::types::{self, Case, ComponentItem, Field}; use wasmtime::component::{Component, Linker, ResourceType, Val}; use wasmtime::{Module, Store}; use wasmtime_component_util::REALLOC_AND_FREE; #[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(&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(&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( &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(&mut store, &[], &mut output).unwrap_err(); assert!( err.to_string().contains("expected 1 argument(s), got 0"), "{err}" ); let err = func.call(&mut store, &output, &mut []).unwrap_err(); assert!( err.to_string().contains("expected 1 result(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("hello, component!".into()); let mut output = [Val::Bool(false)]; func.call(&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 input = Val::List(vec![ Val::U32(32343), Val::U32(79023439), Val::U32(2084037802), ]); let mut output = [Val::Bool(false)]; func.call(&mut store, &[input.clone()], &mut output)?; assert_eq!(input, output[0]); // Sad path: type mismatch let err = Val::List(vec![ Val::U32(32343), Val::U32(79023439), Val::Float32(3.14159265), ]); let err = func.call(&mut store, &[err], &mut output).unwrap_err(); assert!(err.to_string().contains("type mismatch"), "{err}"); Ok(()) } #[test] fn maps() -> Result<()> { let engine = super::map_engine(); let mut store = Store::new(&engine, ()); let component = Component::new(&engine, make_echo_component("(map u32 string)", 8))?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "echo").unwrap(); let input_map = vec![ (Val::U32(1), Val::String("one".into())), (Val::U32(2), Val::String("two".into())), (Val::U32(3), Val::String("three".into())), ]; let input = Val::Map(input_map); let mut output = [Val::Bool(false)]; func.call(&mut store, &[input.clone()], &mut output)?; // Maps should round-trip correctly match &output[0] { Val::Map(output_map) => { assert_eq!(output_map.len(), 3); assert!( output_map .iter() .any(|(k, v)| *k == Val::U32(1) && *v == Val::String("one".into())) ); assert!( output_map .iter() .any(|(k, v)| *k == Val::U32(2) && *v == Val::String("two".into())) ); assert!( output_map .iter() .any(|(k, v)| *k == Val::U32(3) && *v == Val::String("three".into())) ); } _ => panic!("expected map"), } // Sad path: type mismatch (wrong key type) // Need to create a fresh instance because errors can leave the instance in a bad state let mut store2 = Store::new(&engine, ()); let instance2 = Linker::new(&engine).instantiate(&mut store2, &component)?; let func2 = instance2.get_func(&mut store2, "echo").unwrap(); let err_map = vec![(Val::String("key".into()), Val::String("value".into()))]; let err = Val::Map(err_map); let err = func2.call(&mut store2, &[err], &mut output).unwrap_err(); assert!(err.to_string().contains("type mismatch"), "{err}"); // Sad path: type mismatch (wrong value type) let mut store3 = Store::new(&engine, ()); let instance3 = Linker::new(&engine).instantiate(&mut store3, &component)?; let func3 = instance3.get_func(&mut store3, "echo").unwrap(); let err_map2 = vec![(Val::U32(1), Val::U32(42))]; let err = Val::Map(err_map2); let err = func3.call(&mut store3, &[err], &mut output).unwrap_err(); assert!(err.to_string().contains("type mismatch"), "{err}"); // Test empty map let empty_map = vec![]; let input = Val::Map(empty_map); func.call(&mut store, &[input.clone()], &mut output)?; match &output[0] { Val::Map(output_map) => assert_eq!(output_map.len(), 0), _ => panic!("expected map"), } Ok(()) } #[test] fn maps_complex_types() -> Result<()> { let engine = super::map_engine(); let mut store = Store::new(&engine, ()); // Test map> let component = Component::new(&engine, make_echo_component("(map string (list u32))", 8))?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "echo").unwrap(); let input_map = vec![ ( Val::String("first".into()), Val::List(vec![Val::U32(1), Val::U32(2), Val::U32(3)]), ), ( Val::String("second".into()), Val::List(vec![Val::U32(4), Val::U32(5)]), ), ]; let input = Val::Map(input_map); let mut output = [Val::Bool(false)]; func.call(&mut store, &[input.clone()], &mut output)?; // Verify round-trip match &output[0] { Val::Map(output_map) => { assert_eq!(output_map.len(), 2); // Check first entry let first_entry = output_map .iter() .find(|(k, _)| *k == Val::String("first".into())); match first_entry { Some((_, Val::List(list))) => { assert_eq!(list.len(), 3); assert_eq!(list[0], Val::U32(1)); assert_eq!(list[1], Val::U32(2)); assert_eq!(list[2], Val::U32(3)); } _ => panic!("expected list"), } // Check second entry let second_entry = output_map .iter() .find(|(k, _)| *k == Val::String("second".into())); match second_entry { Some((_, Val::List(list))) => { assert_eq!(list.len(), 2); assert_eq!(list[0], Val::U32(4)); assert_eq!(list[1], Val::U32(5)); } _ => panic!("expected list"), } } _ => panic!("expected map"), } Ok(()) } #[test] fn maps_equality() -> Result<()> { let map1 = vec![ (Val::U32(1), Val::String("one".into())), (Val::U32(2), Val::String("two".into())), ]; let map2 = vec![ (Val::U32(1), Val::String("one".into())), (Val::U32(2), Val::String("two".into())), ]; // Maps with same content in same order should be equal assert_eq!(Val::Map(map1.clone()), Val::Map(map2)); // Different values should not be equal let map3 = vec![ (Val::U32(1), Val::String("different".into())), (Val::U32(2), Val::String("two".into())), ]; assert_ne!(Val::Map(map1.clone()), Val::Map(map3)); // Different keys should not be equal let map4 = vec![ (Val::U32(3), Val::String("one".into())), (Val::U32(2), Val::String("two".into())), ]; assert_ne!(Val::Map(map1), Val::Map(map4)); // Empty maps should be equal let empty1: Vec<(Val, Val)> = vec![]; let empty2: Vec<(Val, Val)> = vec![]; assert_eq!(Val::Map(empty1), Val::Map(empty2)); Ok(()) } #[test] fn maps_duplicate_keys() -> Result<()> { let engine = super::map_engine(); let mut store = Store::new(&engine, ()); let component = Component::new(&engine, make_echo_component("(map u32 string)", 8))?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "echo").unwrap(); // Create a map with duplicate keys - Vec preserves all entries let input_map = vec![ (Val::U32(1), Val::String("first".into())), (Val::U32(1), Val::String("last".into())), // Duplicate key (Val::U32(2), Val::String("two".into())), ]; let input = Val::Map(input_map); let mut output = [Val::Bool(false)]; func.call(&mut store, &[input.clone()], &mut output)?; // Verify all entries are preserved (Vec doesn't deduplicate) match &output[0] { Val::Map(output_map) => { // Should have 3 entries (Vec preserves duplicates) assert_eq!(output_map.len(), 3); } _ => panic!("expected map"), } Ok(()) } #[test] fn maps_all_primitive_types() -> Result<()> { let engine = super::map_engine(); let mut store = Store::new(&engine, ()); // Test map let component = Component::new(&engine, make_echo_component("(map u32 u32)", 8))?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "echo").unwrap(); let input_map = vec![(Val::U32(1), Val::U32(100)), (Val::U32(2), Val::U32(200))]; let input = Val::Map(input_map); let mut output = [Val::Bool(false)]; func.call(&mut store, &[input.clone()], &mut output)?; assert_eq!(input, output[0]); // Test map let component = Component::new(&engine, make_echo_component("(map string u32)", 8))?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "echo").unwrap(); let input_map = vec![ (Val::String("one".into()), Val::U32(1)), (Val::String("two".into()), Val::U32(2)), ]; let input = Val::Map(input_map); func.call(&mut store, &[input.clone()], &mut output)?; assert_eq!(input, output[0]); Ok(()) } #[test] fn maps_alignment() -> Result<()> { let engine = super::map_engine(); let mut store = Store::new(&engine, ()); // Test map - key_size=1, value_align=8 // This would fail with the alignment bug because value would be at offset 1 instead of 8 let component = Component::new(&engine, make_echo_component("(map u8 u64)", 8))?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "echo").unwrap(); let input_map = vec![ (Val::U8(1), Val::U64(100)), (Val::U8(2), Val::U64(200)), (Val::U8(3), Val::U64(300)), ]; let input = Val::Map(input_map); let mut output = [Val::Bool(false)]; func.call(&mut store, &[input.clone()], &mut output)?; assert_eq!(input, output[0]); // Test map - key_size=4, value_align=8 // This would fail with the alignment bug because value would be at offset 4 instead of 8 let component = Component::new(&engine, make_echo_component("(map u32 u64)", 8))?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "echo").unwrap(); let input_map = vec![(Val::U32(1), Val::U64(1000)), (Val::U32(2), Val::U64(2000))]; let input = Val::Map(input_map); func.call(&mut store, &[input.clone()], &mut output)?; assert_eq!(input, output[0]); // Test map - key_size=1, value_align=4 // This would fail with the alignment bug because value would be at offset 1 instead of 4 let component = Component::new(&engine, make_echo_component("(map u8 u32)", 8))?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "echo").unwrap(); let input_map = vec![(Val::U8(10), Val::U32(100)), (Val::U8(20), Val::U32(200))]; let input = Val::Map(input_map); func.call(&mut store, &[input.clone()], &mut output)?; assert_eq!(input, output[0]); // Test map - key_size=2, value_align=8 // This would fail with the alignment bug because value would be at offset 2 instead of 8 let component = Component::new(&engine, make_echo_component("(map u16 u64)", 8))?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "echo").unwrap(); let input_map = vec![ (Val::U16(1), Val::U64(10000)), (Val::U16(2), Val::U64(20000)), ]; let input = Val::Map(input_map); func.call(&mut store, &[input.clone()], &mut output)?; assert_eq!(input, output[0]); Ok(()) } #[test] fn maps_large() -> Result<()> { let engine = super::map_engine(); let mut store = Store::new(&engine, ()); let component = Component::new(&engine, make_echo_component("(map u32 string)", 8))?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "echo").unwrap(); // Create a map with many entries let input_map: Vec<(Val, Val)> = (0..100) .map(|i| (Val::U32(i), Val::String(format!("value_{i}")))) .collect(); let input = Val::Map(input_map); let mut output = [Val::Bool(false)]; func.call(&mut store, &[input.clone()], &mut output)?; // Verify all entries are present match &output[0] { Val::Map(output_map) => { assert_eq!(output_map.len(), 100); for i in 0..100 { assert!(output_map.iter().any(|(k, v)| { *k == Val::U32(i) && *v == Val::String(format!("value_{i}")) })); } } _ => panic!("expected map"), } 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#" (type $c' (record (field "D" bool) (field "E" u32) )) (export $c "c" (type $c')) (type $Foo' (record (field "A" u32) (field "B" float64) (field "C" $c) )) "#, &[ 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 input = Val::Record(vec![ ("A".into(), Val::U32(32343)), ("B".into(), Val::Float64(3.14159265)), ( "C".into(), Val::Record(vec![ ("D".into(), Val::Bool(false)), ("E".into(), Val::U32(2084037802)), ]), ), ]); let mut output = [Val::Bool(false)]; func.call(&mut store, &[input.clone()], &mut output)?; assert_eq!(input, output[0]); // Sad path: type mismatch let err = Val::Record(vec![ ("A".into(), Val::S32(32343)), ("B".into(), Val::Float64(3.14159265)), ( "C".into(), Val::Record(vec![ ("D".into(), Val::Bool(false)), ("E".into(), Val::U32(2084037802)), ]), ), ]); let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "echo").unwrap(); let err = func.call(&mut store, &[err], &mut output).unwrap_err(); assert!(err.to_string().contains("type mismatch"), "{err}"); // Sad path: too many fields let err = Val::Record(vec![ ("A".into(), Val::U32(32343)), ("B".into(), Val::Float64(3.14159265)), ( "C".into(), Val::Record(vec![ ("D".into(), Val::Bool(false)), ("E".into(), Val::U32(2084037802)), ]), ), ("F".into(), Val::Bool(true)), ]); let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "echo").unwrap(); let err = func.call(&mut store, &[err], &mut output).unwrap_err(); assert!( err.to_string().contains("expected 3 fields, got 4"), "{err}" ); // Sad path: too few fields let err = Val::Record(vec![ ("A".into(), Val::U32(32343)), ("B".into(), Val::Float64(3.14159265)), ]); let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "echo").unwrap(); let err = func.call(&mut store, &[err], &mut output).unwrap_err(); assert!( err.to_string().contains("expected 3 fields, got 2"), "{err}" ); Ok(()) } #[test] fn variants() -> Result<()> { let engine = super::engine(); let mut store = Store::new(&engine, ()); let fragment = r#" (type $c' (record (field "D" bool) (field "E" u32))) (export $c "c" (type $c')) (type $Foo' (variant (case "A" u32) (case "B" float64) (case "C" $c) )) "#; let component = Component::new( &engine, make_echo_component_with_params( fragment, &[ 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 input = Val::Variant("B".into(), Some(Box::new(Val::Float64(3.14159265)))); let mut output = [Val::Bool(false)]; func.call(&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( fragment, &[ 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 input = Val::Variant( "C".into(), Some(Box::new(Val::Record(vec![ ("D".into(), Val::Bool(true)), ("E".into(), Val::U32(314159265)), ]))), ); func.call(&mut store, &[input.clone()], &mut output)?; assert_eq!(input, output[0]); // Sad path: type mismatch let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "echo").unwrap(); let err = Val::Variant("B".into(), Some(Box::new(Val::U64(314159265)))); let err = func.call(&mut store, &[err], &mut output).unwrap_err(); assert!(err.to_string().contains("type mismatch"), "{err}"); let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "echo").unwrap(); let err = Val::Variant("B".into(), None); let err = func.call(&mut store, &[err], &mut output).unwrap_err(); assert!( err.to_string().contains("expected a payload for case `B`"), "{err}" ); // Sad path: unknown case let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "echo").unwrap(); let err = Val::Variant("D".into(), Some(Box::new(Val::U64(314159265)))); let err = func.call(&mut store, &[err], &mut output).unwrap_err(); assert!(err.to_string().contains("unknown variant case"), "{err}"); let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "echo").unwrap(); let err = Val::Variant("D".into(), None); let err = func.call(&mut store, &[err], &mut output).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#" (type $c' (record (field "D" bool) (field "E" u32))) (export $c "c" (type $c')) (type $a' (variant (case "A" u32) (case "B" float64) (case "C" $c) )) (export $a "a" (type $a')) (type $Foo' (record (field "A" $a) (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 input = Val::Record(vec![ ( "A".into(), Val::Variant("A".into(), Some(Box::new(Val::U32(314159265)))), ), ("B".into(), Val::U32(628318530)), ]); func.call(&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 input = Val::Flags(vec!["B".into(), "D".into()]); let mut output = [Val::Bool(false)]; func.call(&mut store, &[input.clone()], &mut output)?; assert_eq!(input, output[0]); // Sad path: unknown flags let err = Val::Flags(vec!["B".into(), "D".into(), "F".into()]); let err = func.call(&mut store, &[err], &mut output).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#" (type $b' (enum "a" "b")) (export $b "b" (type $b')) (type $c' (record (field "D" bool) (field "E" u32))) (export $c "c" (type $c')) (type $f' (flags "G" "H" "I")) (export $f "f" (type $f')) (type $m' (record (field "N" bool) (field "O" u32))) (export $m "m" (type $m')) (type $j' (variant (case "K" u32) (case "L" float64) (case "M" $m) )) (export $j "j" (type $j')) (type $Foo' (record (field "A" u32) (field "B" $b) (field "C" $c) (field "F" (list $f)) (field "J" $j) (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 "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 input = Val::Record(vec![ ("A".into(), Val::U32(32343)), ("B".into(), Val::Enum("b".to_string())), ( "C".into(), Val::Record(vec![ ("D".to_string(), Val::Bool(false)), ("E".to_string(), Val::U32(2084037802)), ]), ), ( "F".into(), Val::List(vec![Val::Flags(vec!["G".to_string(), "I".to_string()])]), ), ( "J".into(), Val::Variant("L".to_string(), Some(Box::new(Val::Float64(3.14159265)))), ), ("P".into(), Val::S8(42)), ("Q".into(), Val::S16(4242)), ("R".into(), Val::S32(42424242)), ("S".into(), Val::S64(424242424242424242)), ("T".into(), Val::Float32(3.14159265)), ("U".into(), Val::Float64(3.14159265)), ("V".into(), Val::String("wow, nice types".to_string())), ("W".into(), Val::Char('🦀')), ("Y".into(), Val::Tuple(vec![Val::U32(42), Val::U32(24)])), ( "AA".into(), Val::Option(Some(Box::new(Val::U32(314159265)))), ), ( "BB".into(), Val::Result(Ok(Some(Box::new(Val::String("no problem".to_string()))))), ), ]); let mut output = [Val::Bool(false)]; func.call(&mut store, &[input.clone()], &mut output)?; assert_eq!(input, output[0]); Ok(()) } #[test] fn introspection() -> Result<()> { let engine = super::engine(); let component = Component::new( &engine, format!( r#" (component (import "res" (type $res (sub resource))) (import "ai" (instance $i)) (import "bi" (instance $i2 (export "m" (core module)))) (alias export $i2 "m" (core module $m)) (type $t (func (param "a" u32) (result u32))) (component $a (core module $m (func (export "add-five") (param i32) (result i32) local.get 0 i32.const 5 i32.add) ) (core instance $m (instantiate $m)) (func (export "add-five") (type $t) (canon lift (core func $m "add-five"))) ) (component $b (import "interface-v1" (instance $i (export "add-five" (func (type $t))))) (core module $m (func $add-five (import "interface-0.1.0" "add-five") (param i32) (result i32)) (func) ;; causes index out of bounds (func (export "run") (result i32) i32.const 0 call $add-five) ) (core func $add-five (canon lower (func $i "add-five"))) (core instance $i (instantiate 0 (with "interface-0.1.0" (instance (export "add-five" (func $add-five)) )) )) (func (result u32) (canon lift (core func $i "run"))) (export "run" (func 1)) ) (instance $a (instantiate $a)) (instance $b (instantiate $b (with "interface-v1" (instance $a)))) (export "run" (func $b "run")) (component $c (component $c (export "m" (core module $m)) ) (instance $c (instantiate $c)) (export "i" (instance $c)) ) (instance $c (instantiate $c)) (export "i" (instance $c)) (export "r" (instance $i)) (export "r2" (instance $i2)) (type $b' (enum "a" "b")) (export $b "b" (type $b')) (type $c' (record (field "D" bool) (field "E" u32))) (export $c "c" (type $c')) (type $f' (flags "G" "H" "I")) (export $f "f" (type $f')) (type $m' (record (field "N" bool) (field "O" u32))) (export $m "m" (type $m')) (type $j' (variant (case "K" u32) (case "L" float64) (case "M" $m) )) (export $j "j" (type $j')) (type $Foo' (record (field "A" u32) (field "B" $b) (field "C" $c) (field "F" (list $f)) (field "J" $j) (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 "AA" (option u32)) (field "BB" (result string (error string))) (field "CC" (own $res)) )) (export $Foo "foo" (type $Foo')) (core module $m2 (func (export "f") (param i32) (result i32) local.get 0 ) (memory (export "memory") 1) {REALLOC_AND_FREE} ) (core instance $i3 (instantiate $m2)) (func (export "fn") (param "x" (option $Foo)) (result (option (tuple u32 u32))) (canon lift (core func $i3 "f") (memory $i3 "memory") (realloc (func $i3 "realloc")) ) ) ) "# ), )?; struct MyType; let mut linker = Linker::<()>::new(&engine); linker .root() .resource("res", ResourceType::host::(), |_, _| Ok(()))?; linker.instance("ai")?; linker .instance("bi")? .module("m", &Module::new(&engine, "(module)")?)?; let component_ty = linker.substituted_component_type(&component)?; let mut imports = component_ty.imports(linker.engine()); assert_eq!(imports.len(), 3); let (name, res_ty) = imports.next().unwrap(); assert_eq!(name, "res"); let ComponentItem::Resource(res_ty) = res_ty else { panic!("`res` import item of wrong type") }; assert_eq!(res_ty, ResourceType::host::()); let (name, ai_ty) = imports.next().unwrap(); assert_eq!(name, "ai"); let ComponentItem::ComponentInstance(ai_ty) = ai_ty else { panic!("`ai` import item of wrong type") }; assert_eq!(ai_ty.exports(linker.engine()).len(), 0); let (name, bi_ty) = imports.next().unwrap(); assert_eq!(name, "bi"); let ComponentItem::ComponentInstance(bi_ty) = bi_ty else { panic!("`bi` import item of wrong type") }; let mut bi_exports = bi_ty.exports(linker.engine()); assert_eq!(bi_exports.len(), 1); let (name, bi_m_ty) = bi_exports.next().unwrap(); assert_eq!(name, "m"); let ComponentItem::Module(bi_m_ty) = bi_m_ty else { panic!("`bi.m` import item of wrong type") }; assert_eq!(bi_m_ty.imports(linker.engine()).len(), 0); assert_eq!(bi_m_ty.exports(linker.engine()).len(), 0); let mut exports = component_ty.exports(linker.engine()); assert_eq!(exports.len(), 11); let (name, run_ty) = exports.next().unwrap(); assert_eq!(name, "run"); let ComponentItem::ComponentFunc(run_ty) = run_ty else { panic!("`run` export item of wrong type") }; assert_eq!(run_ty.params().len(), 0); let mut run_results = run_ty.results(); assert_eq!(run_results.len(), 1); assert_eq!(run_results.next().unwrap(), types::Type::U32); let (name, i_ty) = exports.next().unwrap(); assert_eq!(name, "i"); let ComponentItem::ComponentInstance(i_ty) = i_ty else { panic!("`i` export item of wrong type") }; let mut i_ty_exports = i_ty.exports(linker.engine()); assert_eq!(i_ty_exports.len(), 1); let (name, i_i_ty) = i_ty_exports.next().unwrap(); assert_eq!(name, "i"); let ComponentItem::ComponentInstance(i_i_ty) = i_i_ty else { panic!("`i.i` import item of wrong type") }; let mut i_i_ty_exports = i_i_ty.exports(linker.engine()); assert_eq!(i_i_ty_exports.len(), 1); let (name, i_i_m_ty) = i_i_ty_exports.next().unwrap(); assert_eq!(name, "m"); let ComponentItem::Module(i_i_m_ty) = i_i_m_ty else { panic!("`i.i.m` import item of wrong type") }; assert_eq!(i_i_m_ty.imports(linker.engine()).len(), 0); assert_eq!(i_i_m_ty.exports(linker.engine()).len(), 0); let (name, r_ty) = exports.next().unwrap(); assert_eq!(name, "r"); let ComponentItem::ComponentInstance(r_ty) = r_ty else { panic!("`r` export item of wrong type") }; assert_eq!(r_ty.exports(linker.engine()).len(), 0); let (name, r2_ty) = exports.next().unwrap(); assert_eq!(name, "r2"); let ComponentItem::ComponentInstance(r2_ty) = r2_ty else { panic!("`r2` export item of wrong type") }; let mut r2_exports = r2_ty.exports(linker.engine()); assert_eq!(r2_exports.len(), 1); let (name, r2_m_ty) = r2_exports.next().unwrap(); assert_eq!(name, "m"); let ComponentItem::Module(r2_m_ty) = r2_m_ty else { panic!("`r2.m` export item of wrong type") }; assert_eq!(r2_m_ty.imports(linker.engine()).len(), 0); assert_eq!(r2_m_ty.exports(linker.engine()).len(), 0); let (name, b_ty) = exports.next().unwrap(); assert_eq!(name, "b"); let ComponentItem::Type(b_ty) = b_ty else { panic!("`b` export item of wrong type") }; assert_eq!(b_ty.unwrap_enum().names().collect::>(), ["a", "b"]); let (name, c_ty) = exports.next().unwrap(); assert_eq!(name, "c"); let ComponentItem::Type(c_ty) = c_ty else { panic!("`c` export item of wrong type") }; let mut fields = c_ty.unwrap_record().fields(); { let Field { name, ty } = fields.next().unwrap(); assert_eq!(name, "D"); assert_eq!(ty, types::Type::Bool); let Field { name, ty } = fields.next().unwrap(); assert_eq!(name, "E"); assert_eq!(ty, types::Type::U32); } let (name, f_ty) = exports.next().unwrap(); assert_eq!(name, "f"); let ComponentItem::Type(f_ty) = f_ty else { panic!("`f` export item of wrong type") }; assert_eq!( f_ty.unwrap_flags().names().collect::>(), ["G", "H", "I"] ); let (name, m_ty) = exports.next().unwrap(); assert_eq!(name, "m"); let ComponentItem::Type(m_ty) = m_ty else { panic!("`m` export item of wrong type") }; { let mut fields = m_ty.unwrap_record().fields(); let Field { name, ty } = fields.next().unwrap(); assert_eq!(name, "N"); assert_eq!(ty, types::Type::Bool); let Field { name, ty } = fields.next().unwrap(); assert_eq!(name, "O"); assert_eq!(ty, types::Type::U32); } let (name, j_ty) = exports.next().unwrap(); assert_eq!(name, "j"); let ComponentItem::Type(j_ty) = j_ty else { panic!("`j` export item of wrong type") }; let mut cases = j_ty.unwrap_variant().cases(); { let Case { name, ty } = cases.next().unwrap(); assert_eq!(name, "K"); assert_eq!(ty, Some(types::Type::U32)); let Case { name, ty } = cases.next().unwrap(); assert_eq!(name, "L"); assert_eq!(ty, Some(types::Type::Float64)); let Case { name, ty } = cases.next().unwrap(); assert_eq!(name, "M"); assert_eq!(ty, Some(m_ty)); } let (name, foo_ty) = exports.next().unwrap(); assert_eq!(name, "foo"); let ComponentItem::Type(foo_ty) = foo_ty else { panic!("`foo` export item of wrong type") }; { let mut fields = foo_ty.unwrap_record().fields(); assert_eq!(fields.len(), 17); let Field { name, ty } = fields.next().unwrap(); assert_eq!(name, "A"); assert_eq!(ty, types::Type::U32); let Field { name, ty } = fields.next().unwrap(); assert_eq!(name, "B"); assert_eq!(ty, b_ty); let Field { name, ty } = fields.next().unwrap(); assert_eq!(name, "C"); assert_eq!(ty, c_ty); let Field { name, ty } = fields.next().unwrap(); assert_eq!(name, "F"); let ty = ty.unwrap_list(); assert_eq!(ty.ty(), f_ty); let Field { name, ty } = fields.next().unwrap(); assert_eq!(name, "J"); assert_eq!(ty, j_ty); let Field { name, ty } = fields.next().unwrap(); assert_eq!(name, "P"); assert_eq!(ty, types::Type::S8); let Field { name, ty } = fields.next().unwrap(); assert_eq!(name, "Q"); assert_eq!(ty, types::Type::S16); let Field { name, ty } = fields.next().unwrap(); assert_eq!(name, "R"); assert_eq!(ty, types::Type::S32); let Field { name, ty } = fields.next().unwrap(); assert_eq!(name, "S"); assert_eq!(ty, types::Type::S64); let Field { name, ty } = fields.next().unwrap(); assert_eq!(name, "T"); assert_eq!(ty, types::Type::Float32); let Field { name, ty } = fields.next().unwrap(); assert_eq!(name, "U"); assert_eq!(ty, types::Type::Float64); let Field { name, ty } = fields.next().unwrap(); assert_eq!(name, "V"); assert_eq!(ty, types::Type::String); let Field { name, ty } = fields.next().unwrap(); assert_eq!(name, "W"); assert_eq!(ty, types::Type::Char); let Field { name, ty } = fields.next().unwrap(); assert_eq!(name, "Y"); assert_eq!( ty.unwrap_tuple().types().collect::>(), [types::Type::U32, types::Type::U32] ); let Field { name, ty } = fields.next().unwrap(); assert_eq!(name, "AA"); assert_eq!(ty.unwrap_option().ty(), types::Type::U32); let Field { name, ty } = fields.next().unwrap(); assert_eq!(name, "BB"); let ty = ty.unwrap_result(); assert_eq!(ty.ok(), Some(types::Type::String)); assert_eq!(ty.err(), Some(types::Type::String)); let Field { name, ty } = fields.next().unwrap(); assert_eq!(name, "CC"); assert_eq!(*ty.unwrap_own(), res_ty); } let (name, fn_ty) = exports.next().unwrap(); assert_eq!(name, "fn"); let ComponentItem::ComponentFunc(fn_ty) = fn_ty else { panic!("`fn` export item of wrong type") }; let mut params = fn_ty.params(); assert_eq!(params.len(), 1); let (name, param) = params.next().unwrap(); assert_eq!(name, "x"); assert_eq!(param.unwrap_option().ty(), foo_ty); let mut results = fn_ty.results(); assert_eq!(results.len(), 1); assert_eq!( results .next() .unwrap() .unwrap_option() .ty() .unwrap_tuple() .types() .collect::>(), [types::Type::U32, types::Type::U32] ); Ok(()) } #[test] fn flags_beyond_end() -> Result<()> { let engine = super::engine(); let component = Component::new( &engine, r#" (component (type $f' (flags "a" "b")) (export $f "f" (type $f')) (core module $m (func (export "r") (param i32) (result i32) local.get 0) ) (core instance $i (instantiate $m)) (func (export "run") (param "x" u32) (result $f) (canon lift (core func $i "r"))) ) "#, )?; let mut store = Store::new(&engine, ()); let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let run = instance.get_func(&mut store, "run").unwrap(); let mut output = [Val::Bool(false)]; run.call(&mut store, &[Val::U32(0)], &mut output)?; assert_eq!(output[0], Val::Flags(vec![])); let mut output = [Val::Bool(false)]; run.call(&mut store, &[Val::U32(1)], &mut output)?; assert_eq!(output[0], Val::Flags(vec!["a".to_string()])); let mut output = [Val::Bool(false)]; run.call(&mut store, &[Val::U32(2)], &mut output)?; assert_eq!(output[0], Val::Flags(vec!["b".to_string()])); let mut output = [Val::Bool(false)]; run.call(&mut store, &[Val::U32(3)], &mut output)?; assert_eq!( output[0], Val::Flags(vec!["a".to_string(), "b".to_string()]) ); let mut output = [Val::Bool(false)]; run.call(&mut store, &[Val::U32(4)], &mut output)?; assert_eq!(output[0], Val::Flags(vec![])); run.call(&mut store, &[Val::U32(5)], &mut output)?; assert_eq!(output[0], Val::Flags(vec!["a".to_string()])); wasmtime::component::flags! { F { #[component(name = "a")] const A; #[component(name = "b")] const B; } } let run = instance.get_typed_func::<(u32,), (F,)>(&mut store, "run")?; assert_eq!(run.call(&mut store, (0,))?, (F::empty(),)); assert_eq!(run.call(&mut store, (1,))?, (F::A,)); assert_eq!(run.call(&mut store, (2,))?, (F::B,)); assert_eq!(run.call(&mut store, (3,))?, (F::A | F::B,)); assert_eq!(run.call(&mut store, (4,))?, (F::empty(),)); assert_eq!(run.call(&mut store, (5,))?, (F::A,)); Ok(()) }