diff --git a/crates/wasm-compose/src/encoding.rs b/crates/wasm-compose/src/encoding.rs index 6782a99fd2..890aa50a9f 100644 --- a/crates/wasm-compose/src/encoding.rs +++ b/crates/wasm-compose/src/encoding.rs @@ -650,6 +650,7 @@ impl<'a> TypeEncoder<'a> { ComponentDefinedType::Record(r) => self.record(state, r), ComponentDefinedType::Variant(v) => self.variant(state, v), ComponentDefinedType::List(ty) => self.list(state, *ty), + ComponentDefinedType::Map(key, value) => self.map(state, *key, *value), ComponentDefinedType::FixedSizeList(ty, elements) => { self.fixed_size_list(state, *ty, *elements) } @@ -713,6 +714,19 @@ impl<'a> TypeEncoder<'a> { index } + fn map( + &self, + state: &mut TypeState<'a>, + key: ct::ComponentValType, + value: ct::ComponentValType, + ) -> u32 { + let key = self.component_val_type(state, key); + let value = self.component_val_type(state, value); + let index = state.cur.encodable.type_count(); + state.cur.encodable.ty().defined_type().map(key, value); + index + } + fn fixed_size_list( &self, state: &mut TypeState<'a>, @@ -1253,6 +1267,10 @@ impl DependencyRegistrar<'_, '_> { ComponentDefinedType::List(t) | ComponentDefinedType::FixedSizeList(t, _) | ComponentDefinedType::Option(t) => self.val_type(*t), + ComponentDefinedType::Map(k, v) => { + self.val_type(*k); + self.val_type(*v); + } ComponentDefinedType::Own(r) | ComponentDefinedType::Borrow(r) => { self.ty(ComponentAnyTypeId::Resource(*r)) } diff --git a/crates/wasm-encoder/src/component/types.rs b/crates/wasm-encoder/src/component/types.rs index 980d7701ab..76eb73f260 100644 --- a/crates/wasm-encoder/src/component/types.rs +++ b/crates/wasm-encoder/src/component/types.rs @@ -593,6 +593,13 @@ impl ComponentDefinedTypeEncoder<'_> { ty.into().encode(self.0); } + /// Define a map type. + pub fn map(self, key: impl Into, value: impl Into) { + self.0.push(0x63); + key.into().encode(self.0); + value.into().encode(self.0); + } + /// Define a fixed size list type. pub fn fixed_size_list(self, ty: impl Into, elements: u32) { self.0.push(0x67); diff --git a/crates/wasm-encoder/src/reencode/component.rs b/crates/wasm-encoder/src/reencode/component.rs index 55990698ec..dfc68f8f7a 100644 --- a/crates/wasm-encoder/src/reencode/component.rs +++ b/crates/wasm-encoder/src/reencode/component.rs @@ -768,6 +768,12 @@ pub mod component_utils { wasmparser::ComponentDefinedType::List(t) => { defined.list(reencoder.component_val_type(t)); } + wasmparser::ComponentDefinedType::Map(k, v) => { + defined.map( + reencoder.component_val_type(k), + reencoder.component_val_type(v), + ); + } wasmparser::ComponentDefinedType::FixedSizeList(t, elements) => { defined.fixed_size_list(reencoder.component_val_type(t), elements); } diff --git a/crates/wasmparser/src/readers/component/types.rs b/crates/wasmparser/src/readers/component/types.rs index 5a9dfb24bc..33ecb0a5df 100644 --- a/crates/wasmparser/src/readers/component/types.rs +++ b/crates/wasmparser/src/readers/component/types.rs @@ -440,6 +440,8 @@ pub enum ComponentDefinedType<'a> { Variant(Box<[VariantCase<'a>]>), /// The type is a list of the given value type. List(ComponentValType), + /// The type is a map of the given key and value types. + Map(ComponentValType, ComponentValType), /// The type is a fixed size list of the given value type. FixedSizeList(ComponentValType, u32), /// The type is a tuple of the given value types. @@ -481,6 +483,7 @@ impl<'a> ComponentDefinedType<'a> { .collect::>()?, ), 0x70 => ComponentDefinedType::List(reader.read()?), + 0x63 => ComponentDefinedType::Map(reader.read()?, reader.read()?), 0x6f => ComponentDefinedType::Tuple( reader .read_iter(MAX_WASM_TUPLE_TYPES, "tuple types")? diff --git a/crates/wasmparser/src/validator/component.rs b/crates/wasmparser/src/validator/component.rs index 55b622d8a9..2e5fe1a1a0 100644 --- a/crates/wasmparser/src/validator/component.rs +++ b/crates/wasmparser/src/validator/component.rs @@ -951,6 +951,9 @@ impl ComponentState { ComponentDefinedType::List(ty) | ComponentDefinedType::FixedSizeList(ty, _) | ComponentDefinedType::Option(ty) => types.type_named_valtype(ty, set), + ComponentDefinedType::Map(k, v) => { + types.type_named_valtype(k, set) && types.type_named_valtype(v, set) + } // The resource referred to by own/borrow must be named. ComponentDefinedType::Own(id) | ComponentDefinedType::Borrow(id) => { @@ -3928,6 +3931,10 @@ impl ComponentState { crate::ComponentDefinedType::List(ty) => Ok(ComponentDefinedType::List( self.create_component_val_type(ty, offset)?, )), + crate::ComponentDefinedType::Map(key, value) => Ok(ComponentDefinedType::Map( + self.create_component_val_type(key, offset)?, + self.create_component_val_type(value, offset)?, + )), crate::ComponentDefinedType::FixedSizeList(ty, elements) => { if !self.features.cm_fixed_size_list() { bail!( diff --git a/crates/wasmparser/src/validator/component_types.rs b/crates/wasmparser/src/validator/component_types.rs index 64ddf1ccae..ba40fcc7e2 100644 --- a/crates/wasmparser/src/validator/component_types.rs +++ b/crates/wasmparser/src/validator/component_types.rs @@ -1451,6 +1451,8 @@ pub enum ComponentDefinedType { Variant(VariantType), /// The type is a list. List(ComponentValType), + /// The type is a map. + Map(ComponentValType, ComponentValType), /// The type is a fixed size list. FixedSizeList(ComponentValType, u32), /// The type is a tuple. @@ -1494,6 +1496,11 @@ impl TypeData for ComponentDefinedType { Self::Variant(v) => v.info, Self::Tuple(t) => t.info, Self::List(ty) | Self::FixedSizeList(ty, _) | Self::Option(ty) => ty.info(types), + Self::Map(k, v) => { + let mut info = k.info(types); + info.combine(v.info(types), 0).unwrap(); + info + } Self::Result { ok, err } => { let default = TypeInfo::new(); let mut info = ok.map(|ty| ty.type_info(types)).unwrap_or(default); @@ -1514,7 +1521,7 @@ impl ComponentDefinedType { .cases .values() .any(|case| case.ty.map(|ty| ty.contains_ptr(types)).unwrap_or(false)), - Self::List(_) => true, + Self::List(_) | Self::Map(_, _) => true, Self::Tuple(t) => t.types.iter().any(|ty| ty.contains_ptr(types)), Self::Flags(_) | Self::Enum(_) @@ -1542,7 +1549,7 @@ impl ComponentDefinedType { types, lowered_types, ), - Self::List(_) => { + Self::List(_) | Self::Map(_, _) => { lowered_types.try_push(ValType::I32) && lowered_types.try_push(ValType::I32) } Self::FixedSizeList(ty, length) => { @@ -1622,6 +1629,7 @@ impl ComponentDefinedType { ComponentDefinedType::Flags(_) => "flags", ComponentDefinedType::Option(_) => "option", ComponentDefinedType::List(_) => "list", + ComponentDefinedType::Map(_, _) => "map", ComponentDefinedType::FixedSizeList(_, _) => "fixed size list", ComponentDefinedType::Result { .. } => "result", ComponentDefinedType::Own(_) => "own", @@ -1646,7 +1654,9 @@ impl ComponentDefinedType { ComponentDefinedType::Variant(ty) => ty.lower_gc(types, abi, options, offset, core), - ComponentDefinedType::List(ty) | ComponentDefinedType::FixedSizeList(ty, _) => { + ComponentDefinedType::List(ty) + | ComponentDefinedType::Map(ty, _) + | ComponentDefinedType::FixedSizeList(ty, _) => { let id = match core.as_concrete_ref() { Some(id) => id, None => bail!( @@ -2498,6 +2508,10 @@ impl TypeAlloc { | ComponentDefinedType::Option(ty) => { self.free_variables_valtype(ty, set); } + ComponentDefinedType::Map(k, v) => { + self.free_variables_valtype(k, set); + self.free_variables_valtype(v, set); + } ComponentDefinedType::Result { ok, err } => { if let Some(ok) = ok { self.free_variables_valtype(ok, set); @@ -2640,6 +2654,9 @@ impl TypeAlloc { ComponentDefinedType::List(ty) | ComponentDefinedType::FixedSizeList(ty, _) | ComponentDefinedType::Option(ty) => self.type_named_valtype(ty, set), + ComponentDefinedType::Map(k, v) => { + self.type_named_valtype(k, set) && self.type_named_valtype(v, set) + } // own/borrow themselves don't have to be named, but the resource // they refer to must be named. @@ -2832,6 +2849,10 @@ where | ComponentDefinedType::Option(ty) => { any_changed |= self.remap_valtype(ty, map); } + ComponentDefinedType::Map(k, v) => { + any_changed |= self.remap_valtype(k, map); + any_changed |= self.remap_valtype(v, map); + } ComponentDefinedType::Result { ok, err } => { if let Some(ok) = ok { any_changed |= self.remap_valtype(ok, map); @@ -3724,6 +3745,13 @@ impl<'a> SubtypeCx<'a> { (Variant(_), b) => bail!(offset, "expected {}, found variant", b.desc()), (List(a), List(b)) | (Option(a), Option(b)) => self.component_val_type(a, b, offset), (List(_), b) => bail!(offset, "expected {}, found list", b.desc()), + (Map(ak, av), Map(bk, bv)) => { + self.component_val_type(ak, bk, offset) + .with_context(|| "type mismatch in map key")?; + self.component_val_type(av, bv, offset) + .with_context(|| "type mismatch in map value") + } + (Map(_, _), b) => bail!(offset, "expected {}, found map", b.desc()), (FixedSizeList(a, asize), FixedSizeList(b, bsize)) => { if asize != bsize { bail!(offset, "expected fixed size {bsize}, found size {asize}") diff --git a/crates/wasmprinter/src/component.rs b/crates/wasmprinter/src/component.rs index 5ee525f5c3..90788fcb52 100644 --- a/crates/wasmprinter/src/component.rs +++ b/crates/wasmprinter/src/component.rs @@ -169,6 +169,20 @@ impl Printer<'_, '_> { Ok(()) } + pub(crate) fn print_map_type( + &mut self, + state: &State, + key_ty: &ComponentValType, + value_ty: &ComponentValType, + ) -> Result<()> { + self.start_group("map ")?; + self.print_component_val_type(state, key_ty)?; + self.result.write_str(" ")?; + self.print_component_val_type(state, value_ty)?; + self.end_group()?; + Ok(()) + } + pub(crate) fn print_fixed_size_list_type( &mut self, state: &State, @@ -278,6 +292,7 @@ impl Printer<'_, '_> { ComponentDefinedType::Record(fields) => self.print_record_type(state, fields)?, ComponentDefinedType::Variant(cases) => self.print_variant_type(state, cases)?, ComponentDefinedType::List(ty) => self.print_list_type(state, ty)?, + ComponentDefinedType::Map(key, value) => self.print_map_type(state, key, value)?, ComponentDefinedType::FixedSizeList(ty, elements) => { self.print_fixed_size_list_type(state, ty, *elements)? } diff --git a/crates/wit-component/src/encoding.rs b/crates/wit-component/src/encoding.rs index 8ec301cda0..3d121297f0 100644 --- a/crates/wit-component/src/encoding.rs +++ b/crates/wit-component/src/encoding.rs @@ -324,6 +324,9 @@ impl TypeContents { } TypeDefKind::Enum(_) => Self::empty(), TypeDefKind::List(t) => Self::for_type(resolve, t) | Self::LIST, + TypeDefKind::Map(k, v) => { + Self::for_type(resolve, k) | Self::for_type(resolve, v) | Self::LIST + } TypeDefKind::FixedSizeList(t, _elements) => Self::for_type(resolve, t), TypeDefKind::Type(t) => Self::for_type(resolve, t), TypeDefKind::Future(_) => Self::empty(), diff --git a/crates/wit-component/src/encoding/types.rs b/crates/wit-component/src/encoding/types.rs index 202febbefa..ed49003703 100644 --- a/crates/wit-component/src/encoding/types.rs +++ b/crates/wit-component/src/encoding/types.rs @@ -185,6 +185,13 @@ pub trait ValtypeEncoder<'a> { encoder.list(ty); ComponentValType::Type(index) } + TypeDefKind::Map(key_ty, value_ty) => { + let key = self.encode_valtype(resolve, key_ty)?; + let value = self.encode_valtype(resolve, value_ty)?; + let (index, encoder) = self.defined_type(); + encoder.map(key, value); + ComponentValType::Type(index) + } TypeDefKind::FixedSizeList(ty, elements) => { let ty = self.encode_valtype(resolve, ty)?; let (index, encoder) = self.defined_type(); diff --git a/crates/wit-component/src/printing.rs b/crates/wit-component/src/printing.rs index c120023f7a..c7c1b46781 100644 --- a/crates/wit-component/src/printing.rs +++ b/crates/wit-component/src/printing.rs @@ -606,6 +606,14 @@ impl WitPrinter { self.print_type_name(resolve, ty)?; self.output.generic_args_end(); } + TypeDefKind::Map(key_ty, value_ty) => { + self.output.ty("map", TypeKind::BuiltIn); + self.output.generic_args_start(); + self.print_type_name(resolve, key_ty)?; + self.output.str(", "); + self.print_type_name(resolve, value_ty)?; + self.output.generic_args_end(); + } TypeDefKind::FixedSizeList(ty, size) => { self.output.ty("list", TypeKind::BuiltIn); self.output.generic_args_start(); @@ -787,6 +795,9 @@ impl WitPrinter { TypeDefKind::List(inner) => { self.declare_list(resolve, ty.name.as_deref(), inner)? } + TypeDefKind::Map(key, value) => { + self.declare_map(resolve, ty.name.as_deref(), key, value)? + } TypeDefKind::FixedSizeList(inner, size) => { self.declare_fixed_size_list(resolve, ty.name.as_deref(), inner, *size)? } @@ -1003,6 +1014,31 @@ impl WitPrinter { Ok(()) } + fn declare_map( + &mut self, + resolve: &Resolve, + name: Option<&str>, + key_ty: &Type, + value_ty: &Type, + ) -> Result<()> { + if let Some(name) = name { + self.output.keyword("type"); + self.output.str(" "); + self.print_name_type(name, TypeKind::Map); + self.output.str(" = "); + self.output.ty("map", TypeKind::BuiltIn); + self.output.str("<"); + self.print_type_name(resolve, key_ty)?; + self.output.str(", "); + self.print_type_name(resolve, value_ty)?; + self.output.str(">"); + self.output.semicolon(); + return Ok(()); + } + + Ok(()) + } + fn declare_fixed_size_list( &mut self, resolve: &Resolve, @@ -1343,6 +1379,8 @@ pub enum TypeKind { InterfacePath, /// A list type name. List, + /// A map type name. + Map, /// A namespace declaration. NamespaceDeclaration, /// A namespace when printing a path, for example in `use`. diff --git a/crates/wit-component/tests/interfaces/maps.wat b/crates/wit-component/tests/interfaces/maps.wat new file mode 100644 index 0000000000..91e2f862aa --- /dev/null +++ b/crates/wit-component/tests/interfaces/maps.wat @@ -0,0 +1,186 @@ +(component + (type (;0;) + (component + (type (;0;) + (instance + (type (;0;) (map bool string)) + (export (;1;) "bool-map" (type (eq 0))) + (type (;2;) (map u8 string)) + (export (;3;) "u8-map" (type (eq 2))) + (type (;4;) (map u16 string)) + (export (;5;) "u16-map" (type (eq 4))) + (type (;6;) (map u32 string)) + (export (;7;) "u32-map" (type (eq 6))) + (type (;8;) (map u64 string)) + (export (;9;) "u64-map" (type (eq 8))) + (type (;10;) (map s8 string)) + (export (;11;) "s8-map" (type (eq 10))) + (type (;12;) (map s16 string)) + (export (;13;) "s16-map" (type (eq 12))) + (type (;14;) (map s32 string)) + (export (;15;) "s32-map" (type (eq 14))) + (type (;16;) (map s64 string)) + (export (;17;) "s64-map" (type (eq 16))) + (type (;18;) (map char string)) + (export (;19;) "char-map" (type (eq 18))) + (type (;20;) (map string u32)) + (export (;21;) "string-map" (type (eq 20))) + (type (;22;) (map string bool)) + (export (;23;) "string-to-bool" (type (eq 22))) + (type (;24;) (list u8)) + (type (;25;) (map string 24)) + (export (;26;) "string-to-list" (type (eq 25))) + (type (;27;) (option u32)) + (type (;28;) (map string 27)) + (export (;29;) "string-to-option" (type (eq 28))) + (type (;30;) (result u32 (error string))) + (type (;31;) (map string 30)) + (export (;32;) "string-to-result" (type (eq 31))) + (type (;33;) (tuple u32 string)) + (type (;34;) (map string 33)) + (export (;35;) "string-to-tuple" (type (eq 34))) + (type (;36;) (map string u32)) + (type (;37;) (map string 36)) + (export (;38;) "map-of-maps" (type (eq 37))) + (type (;39;) (list 36)) + (export (;40;) "list-of-maps" (type (eq 39))) + (type (;41;) (option 36)) + (export (;42;) "option-of-map" (type (eq 41))) + (type (;43;) (func (param "x" 36))) + (export (;0;) "map-param" (func (type 43))) + (type (;44;) (func (result 36))) + (export (;1;) "map-result" (func (type 44))) + (type (;45;) (map u32 string)) + (type (;46;) (func (param "x" 45) (result 45))) + (export (;2;) "map-roundtrip" (func (type 46))) + ) + ) + (export (;0;) "foo:maps-test/maps-interface" (instance (type 0))) + ) + ) + (export (;1;) "maps-interface" (type 0)) + (type (;2;) + (component + (type (;0;) + (component + (type (;0;) + (instance + (type (;0;) (map bool string)) + (export (;1;) "bool-map" (type (eq 0))) + (type (;2;) (map u8 string)) + (export (;3;) "u8-map" (type (eq 2))) + (type (;4;) (map u16 string)) + (export (;5;) "u16-map" (type (eq 4))) + (type (;6;) (map u32 string)) + (export (;7;) "u32-map" (type (eq 6))) + (type (;8;) (map u64 string)) + (export (;9;) "u64-map" (type (eq 8))) + (type (;10;) (map s8 string)) + (export (;11;) "s8-map" (type (eq 10))) + (type (;12;) (map s16 string)) + (export (;13;) "s16-map" (type (eq 12))) + (type (;14;) (map s32 string)) + (export (;15;) "s32-map" (type (eq 14))) + (type (;16;) (map s64 string)) + (export (;17;) "s64-map" (type (eq 16))) + (type (;18;) (map char string)) + (export (;19;) "char-map" (type (eq 18))) + (type (;20;) (map string u32)) + (export (;21;) "string-map" (type (eq 20))) + (type (;22;) (map string bool)) + (export (;23;) "string-to-bool" (type (eq 22))) + (type (;24;) (list u8)) + (type (;25;) (map string 24)) + (export (;26;) "string-to-list" (type (eq 25))) + (type (;27;) (option u32)) + (type (;28;) (map string 27)) + (export (;29;) "string-to-option" (type (eq 28))) + (type (;30;) (result u32 (error string))) + (type (;31;) (map string 30)) + (export (;32;) "string-to-result" (type (eq 31))) + (type (;33;) (tuple u32 string)) + (type (;34;) (map string 33)) + (export (;35;) "string-to-tuple" (type (eq 34))) + (type (;36;) (map string u32)) + (type (;37;) (map string 36)) + (export (;38;) "map-of-maps" (type (eq 37))) + (type (;39;) (list 36)) + (export (;40;) "list-of-maps" (type (eq 39))) + (type (;41;) (option 36)) + (export (;42;) "option-of-map" (type (eq 41))) + (type (;43;) (func (param "x" 36))) + (export (;0;) "map-param" (func (type 43))) + (type (;44;) (func (result 36))) + (export (;1;) "map-result" (func (type 44))) + (type (;45;) (map u32 string)) + (type (;46;) (func (param "x" 45) (result 45))) + (export (;2;) "map-roundtrip" (func (type 46))) + ) + ) + (import "foo:maps-test/maps-interface" (instance (;0;) (type 0))) + (type (;1;) + (instance + (type (;0;) (map bool string)) + (export (;1;) "bool-map" (type (eq 0))) + (type (;2;) (map u8 string)) + (export (;3;) "u8-map" (type (eq 2))) + (type (;4;) (map u16 string)) + (export (;5;) "u16-map" (type (eq 4))) + (type (;6;) (map u32 string)) + (export (;7;) "u32-map" (type (eq 6))) + (type (;8;) (map u64 string)) + (export (;9;) "u64-map" (type (eq 8))) + (type (;10;) (map s8 string)) + (export (;11;) "s8-map" (type (eq 10))) + (type (;12;) (map s16 string)) + (export (;13;) "s16-map" (type (eq 12))) + (type (;14;) (map s32 string)) + (export (;15;) "s32-map" (type (eq 14))) + (type (;16;) (map s64 string)) + (export (;17;) "s64-map" (type (eq 16))) + (type (;18;) (map char string)) + (export (;19;) "char-map" (type (eq 18))) + (type (;20;) (map string u32)) + (export (;21;) "string-map" (type (eq 20))) + (type (;22;) (map string bool)) + (export (;23;) "string-to-bool" (type (eq 22))) + (type (;24;) (list u8)) + (type (;25;) (map string 24)) + (export (;26;) "string-to-list" (type (eq 25))) + (type (;27;) (option u32)) + (type (;28;) (map string 27)) + (export (;29;) "string-to-option" (type (eq 28))) + (type (;30;) (result u32 (error string))) + (type (;31;) (map string 30)) + (export (;32;) "string-to-result" (type (eq 31))) + (type (;33;) (tuple u32 string)) + (type (;34;) (map string 33)) + (export (;35;) "string-to-tuple" (type (eq 34))) + (type (;36;) (map string u32)) + (type (;37;) (map string 36)) + (export (;38;) "map-of-maps" (type (eq 37))) + (type (;39;) (list 36)) + (export (;40;) "list-of-maps" (type (eq 39))) + (type (;41;) (option 36)) + (export (;42;) "option-of-map" (type (eq 41))) + (type (;43;) (func (param "x" 36))) + (export (;0;) "map-param" (func (type 43))) + (type (;44;) (func (result 36))) + (export (;1;) "map-result" (func (type 44))) + (type (;45;) (map u32 string)) + (type (;46;) (func (param "x" 45) (result 45))) + (export (;2;) "map-roundtrip" (func (type 46))) + ) + ) + (export (;1;) "foo:maps-test/maps-interface" (instance (type 1))) + ) + ) + (export (;0;) "foo:maps-test/maps-test-world" (component (type 0))) + ) + ) + (export (;3;) "maps-test-world" (type 2)) + (@custom "package-docs" "\01{\22interfaces\22:{\22maps-interface\22:{\22funcs\22:{\22map-param\22:{\22docs\22:\22Functions\22}},\22types\22:{\22bool-map\22:{\22docs\22:\22Test all primitive key types\22},\22string-to-bool\22:{\22docs\22:\22Test all value types\22},\22map-of-maps\22:{\22docs\22:\22Nested structures\22}}}}}") + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) +) diff --git a/crates/wit-component/tests/interfaces/maps.wit b/crates/wit-component/tests/interfaces/maps.wit new file mode 100644 index 0000000000..93bc64a92d --- /dev/null +++ b/crates/wit-component/tests/interfaces/maps.wit @@ -0,0 +1,38 @@ +package foo:maps-test; + +interface maps-interface { + // Test all primitive key types + type bool-map = map; + type u8-map = map; + type u16-map = map; + type u32-map = map; + type u64-map = map; + type s8-map = map; + type s16-map = map; + type s32-map = map; + type s64-map = map; + type char-map = map; + type string-map = map; + + // Test all value types + type string-to-bool = map; + type string-to-list = map>; + type string-to-option = map>; + type string-to-result = map>; + type string-to-tuple = map>; + + // Nested structures + type map-of-maps = map>; + type list-of-maps = list>; + type option-of-map = option>; + + // Functions + map-param: func(x: map); + map-result: func() -> map; + map-roundtrip: func(x: map) -> map; +} + +world maps-test-world { + import maps-interface; + export maps-interface; +} diff --git a/crates/wit-component/tests/interfaces/maps.wit.print b/crates/wit-component/tests/interfaces/maps.wit.print new file mode 100644 index 0000000000..f9235085bc --- /dev/null +++ b/crates/wit-component/tests/interfaces/maps.wit.print @@ -0,0 +1,57 @@ +package foo:maps-test; + +interface maps-interface { + /// Test all primitive key types + type bool-map = map; + + type u8-map = map; + + type u16-map = map; + + type u32-map = map; + + type u64-map = map; + + type s8-map = map; + + type s16-map = map; + + type s32-map = map; + + type s64-map = map; + + type char-map = map; + + type string-map = map; + + /// Test all value types + type string-to-bool = map; + + type string-to-list = map>; + + type string-to-option = map>; + + type string-to-result = map>; + + type string-to-tuple = map>; + + /// Nested structures + type map-of-maps = map>; + + type list-of-maps = list>; + + type option-of-map = option>; + + /// Functions + map-param: func(x: map); + + map-result: func() -> map; + + map-roundtrip: func(x: map) -> map; +} + +world maps-test-world { + import maps-interface; + + export maps-interface; +} diff --git a/crates/wit-dylib/ffi/src/ffi.rs b/crates/wit-dylib/ffi/src/ffi.rs index d911c33e9b..c987206622 100644 --- a/crates/wit-dylib/ffi/src/ffi.rs +++ b/crates/wit-dylib/ffi/src/ffi.rs @@ -28,6 +28,7 @@ pub const WIT_TYPE_FIXED_SIZE_LIST: u32 = 24; pub const WIT_TYPE_FUTURE: u32 = 25; pub const WIT_TYPE_STREAM: u32 = 26; pub const WIT_TYPE_ALIAS: u32 = 27; +pub const WIT_TYPE_MAP: u32 = 28; pub const WIT_TYPE_EMPTY: u32 = 255; pub const WIT_V0: u32 = 0; pub type wit_type_t = u32; @@ -159,6 +160,15 @@ pub struct wit_list { pub type wit_list_t = wit_list; #[repr(C)] #[derive(Debug, Copy, Clone)] +pub struct wit_map { + pub interface: *const ::std::os::raw::c_char, + pub name: *const ::std::os::raw::c_char, + pub key_ty: wit_type_t, + pub value_ty: wit_type_t, +} +pub type wit_map_t = wit_map; +#[repr(C)] +#[derive(Debug, Copy, Clone)] pub struct wit_fixed_size_list { pub interface: *const ::std::os::raw::c_char, pub name: *const ::std::os::raw::c_char, @@ -214,6 +224,8 @@ pub struct wit { pub results: *const wit_result_t, pub num_lists: usize, pub lists: *const wit_list_t, + pub num_maps: usize, + pub maps: *const wit_map_t, pub num_fixed_size_lists: usize, pub fixed_size_lists: *const wit_fixed_size_list_t, pub num_futures: usize, diff --git a/crates/wit-dylib/ffi/src/lib.rs b/crates/wit-dylib/ffi/src/lib.rs index 132e0841f6..e9873e01f2 100644 --- a/crates/wit-dylib/ffi/src/lib.rs +++ b/crates/wit-dylib/ffi/src/lib.rs @@ -350,6 +350,30 @@ macro_rules! export { unsafe { <$name as $crate::RawInterpreter>::raw_list_append(cx, ty) } } + #[no_mangle] + pub unsafe extern "C" fn wit_dylib_pop_map( + cx: *mut u8, + ty: usize, + ptr: &mut *const u8, + ) -> usize { + unsafe { <$name as $crate::RawInterpreter>::raw_pop_map(cx, ty, ptr) } + } + + #[no_mangle] + pub unsafe extern "C" fn wit_dylib_push_map( + cx: *mut u8, + ty: usize, + ptr: *mut u8, + len: usize, + ) -> u32 { + unsafe { <$name as $crate::RawInterpreter>::raw_push_map(cx, ty, ptr, len) } + } + + #[no_mangle] + pub unsafe extern "C" fn wit_dylib_map_append(cx: *mut u8, ty: usize) { + unsafe { <$name as $crate::RawInterpreter>::raw_map_append(cx, ty) } + } + fn main() { unreachable!(); } @@ -427,6 +451,12 @@ pub trait Call { fn pop_iter_next(&mut self, ty: List); fn pop_iter(&mut self, ty: List); + unsafe fn maybe_pop_map(&mut self, ty: Map) -> Option<(*const u8, usize)> { + let _ = ty; + None + } + fn pop_map(&mut self, ty: Map) -> usize; + fn push_bool(&mut self, val: bool); fn push_char(&mut self, val: char); fn push_u8(&mut self, val: u8); @@ -457,6 +487,13 @@ pub trait Call { } fn push_list(&mut self, ty: List, capacity: usize); fn list_append(&mut self, ty: List); + + unsafe fn push_raw_map(&mut self, ty: Map, ptr: *mut u8, len: usize) -> bool { + let _ = (ty, ptr, len); + false + } + fn push_map(&mut self, ty: Map, capacity: usize); + fn map_append(&mut self, ty: Map); } static mut WIT_T: *const ffi::wit_t = ptr::null_mut(); @@ -936,6 +973,48 @@ pub trait RawInterpreter: Interpreter { Self::cx_mut(cx).list_append(wit.list(ty)) } } + + unsafe fn raw_pop_map(cx: *mut u8, ty: usize, retptr: &mut *const u8) -> usize { + debug_println!("pop_map({cx:?}, {ty})"); + unsafe { + let wit = Wit::from_raw(WIT_T); + let cx = Self::cx_mut(cx); + let ty = wit.map(ty); + match cx.maybe_pop_map(ty) { + Some((ptr, len)) => { + *retptr = ptr; + len + } + None => { + *retptr = ptr::null(); + cx.pop_map(ty) + } + } + } + } + + unsafe fn raw_push_map(cx: *mut u8, ty: usize, map: *mut u8, len: usize) -> u32 { + debug_println!("push_map({cx:?}, {ty}, {map:?}, {len})"); + unsafe { + let wit = Wit::from_raw(WIT_T); + let cx = Self::cx_mut(cx); + let ty = wit.map(ty); + if cx.push_raw_map(ty, map, len) { + 1 + } else { + cx.push_map(ty, len); + 0 + } + } + } + + unsafe fn raw_map_append(cx: *mut u8, ty: usize) { + debug_println!("map_append({cx:?}, {ty})"); + unsafe { + let wit = Wit::from_raw(WIT_T); + Self::cx_mut(cx).map_append(wit.map(ty)) + } + } } impl RawInterpreter for T {} diff --git a/crates/wit-dylib/ffi/src/types.rs b/crates/wit-dylib/ffi/src/types.rs index 7e42c52170..1cf1148cb6 100644 --- a/crates/wit-dylib/ffi/src/types.rs +++ b/crates/wit-dylib/ffi/src/types.rs @@ -225,6 +225,21 @@ impl Wit { self.raw_lists().iter().map(|e| List { wit: *self, ptr: e }) } + fn raw_maps(&self) -> &'static [ffi::wit_map_t] { + unsafe { slice(self.ptr.maps, self.ptr.num_maps) } + } + + pub fn map(&self, index: usize) -> Map { + Map { + wit: *self, + ptr: &self.raw_maps()[index], + } + } + + pub fn iter_maps(&self) -> impl ExactSizeIterator + Clone + '_ { + self.raw_maps().iter().map(|e| Map { wit: *self, ptr: e }) + } + fn raw_fixed_size_lists(&self) -> &'static [ffi::wit_fixed_size_list_t] { unsafe { slice(self.ptr.fixed_size_lists, self.ptr.num_fixed_size_lists) } } @@ -837,6 +852,43 @@ impl fmt::Debug for List { } } +#[derive(Copy, Clone)] +pub struct Map { + wit: Wit, + ptr: &'static ffi::wit_map_t, +} + +impl_extra_traits!(Map); + +impl Map { + pub fn interface(&self) -> Option<&'static str> { + unsafe { opt_str(self.ptr.interface) } + } + + pub fn name(&self) -> Option<&'static str> { + unsafe { opt_str(self.ptr.name) } + } + + pub fn key_ty(&self) -> Type { + Type::from_raw(self.wit, self.ptr.key_ty) + } + + pub fn value_ty(&self) -> Type { + Type::from_raw(self.wit, self.ptr.value_ty) + } +} + +impl fmt::Debug for Map { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Map") + .field("interface", &self.interface()) + .field("name", &self.name()) + .field("key_ty", &self.key_ty()) + .field("value_ty", &self.value_ty()) + .finish() + } +} + #[derive(Copy, Clone)] pub struct FixedSizeList { wit: Wit, diff --git a/crates/wit-dylib/src/bindgen.rs b/crates/wit-dylib/src/bindgen.rs index d19d310b5e..8377206ca0 100644 --- a/crates/wit-dylib/src/bindgen.rs +++ b/crates/wit-dylib/src/bindgen.rs @@ -69,6 +69,7 @@ intrinsics! { pop_list : [ValType::I32; 3] -> [ValType::I32] = "wit_dylib_pop_list", pop_iter_next : [ValType::I32; 2] -> [] = "wit_dylib_pop_iter_next", pop_iter : [ValType::I32; 2] -> [] = "wit_dylib_pop_iter", + pop_map : [ValType::I32; 3] -> [ValType::I32] = "wit_dylib_pop_map", push_bool : [ValType::I32; 2] -> [] = "wit_dylib_push_bool", push_char : [ValType::I32; 2] -> [] = "wit_dylib_push_char", @@ -96,6 +97,8 @@ intrinsics! { push_result : [ValType::I32; 3] -> [] = "wit_dylib_push_result", push_list : [ValType::I32; 4] -> [ValType::I32] = "wit_dylib_push_list", list_append : [ValType::I32; 2] -> [] = "wit_dylib_list_append", + push_map : [ValType::I32; 4] -> [ValType::I32] = "wit_dylib_push_map", + map_append : [ValType::I32; 2] -> [] = "wit_dylib_map_append", } pub fn import( @@ -928,6 +931,137 @@ impl<'a> FunctionCompiler<'a> { // that's returned directly (e.g. `list`) but if it's NULL // then the list is allocated manually with `cabi_realloc` and then // lowered element-by-element. + TypeDefKind::Map(key_ty, value_ty) => { + let metadata::Type::Map(map_index) = ty else { + unreachable!() + }; + let pop_map = self.adapter.intrinsics().pop_map; + let pop_iter_next = self.adapter.intrinsics().pop_iter_next; + let pop_iter = self.adapter.intrinsics().pop_iter; + let cabi_realloc = self.adapter.intrinsics().cabi_realloc; + let dealloc_bytes = self.adapter.intrinsics().dealloc_bytes; + + // Calculate size of tuple + let key_size = self.adapter.sizes.size(key_ty).size_wasm32(); + let key_align = self.adapter.sizes.align(key_ty).align_wasm32(); + let value_size = self.adapter.sizes.size(value_ty).size_wasm32(); + let value_align = self.adapter.sizes.align(value_ty).align_wasm32(); + + // Tuple alignment is max of element alignments + let tuple_align = key_align.max(value_align); + // Tuple size is key_size + padding + value_size + let key_end = key_size; + let value_offset = align_up(key_end, value_align); + let tuple_size = value_offset + value_size; + + let (dst_ptr, dst_len) = dest.split_ptr_len(); + + // Load the map ptr/len into locals + self.local_get_ctx(); + self.ins().i32_const(map_index.try_into().unwrap()); + self.local_get_stack_temp_addr(); + self.ins().call(pop_map); + let m_len = self.local_set_new_tmp(ValType::I32); + self.local_get_stack_temp_addr(); + self.ins().i32_load(MemArg { + memory_index: 0, + offset: 0, + align: 2, + }); + let m_ptr = self.local_set_new_tmp(ValType::I32); + + // If the pointer is null then the interpreter doesn't support + // the same canonical ABI view of this map so a loop is + // required to lower element-by-element. + self.ins().local_get(m_ptr.idx); + self.ins().i32_eqz(); + self.ins().if_(BlockType::Empty); + { + self.ins().i32_const(0); + let m_index = self.local_set_new_tmp(ValType::I32); + + self.ins().i32_const(0); + self.ins().i32_const(0); + self.ins().i32_const(tuple_align.try_into().unwrap()); + self.ins().local_get(m_len.idx); + self.ins().i32_const(tuple_size.try_into().unwrap()); + self.ins().i32_mul(); + let m_byte_size = self.local_tee_new_tmp(ValType::I32); + self.ins().call(cabi_realloc); + let m_elem_addr = self.local_tee_new_tmp(ValType::I32); + self.ins().local_set(m_ptr.idx); + + // Loop over each element of the map. + self.ins().loop_(BlockType::Empty); + { + // Entry loop condition, `m_index != m_len` + self.ins().local_get(m_index.idx); + self.ins().local_get(m_len.idx); + self.ins().i32_ne(); + self.ins().if_(BlockType::Empty); + { + // Get the `m_index`th element from map (puts key then value on stack) + self.local_get_ctx(); + self.ins().i32_const(map_index.try_into().unwrap()); + self.ins().call(pop_iter_next); + + // Lower the key + self.lower( + *key_ty, + &AbiLoc::Memory(Memory { + addr: TempLocal::new(m_elem_addr.idx, ValType::I32), + offset: 0, + }), + ); + + // Lower the value + self.lower( + *value_ty, + &AbiLoc::Memory(Memory { + addr: TempLocal::new(m_elem_addr.idx, ValType::I32), + offset: value_offset.try_into().unwrap(), + }), + ); + + // Increment the `m_index` counter + self.ins().local_get(m_index.idx); + self.ins().i32_const(1); + self.ins().i32_add(); + self.ins().local_set(m_index.idx); + + // Increment the `m_elem_addr` counter + self.ins().local_get(m_elem_addr.idx); + self.ins().i32_const(tuple_size.try_into().unwrap()); + self.ins().i32_add(); + self.ins().local_set(m_elem_addr.idx); + + // Continue the loop. + self.ins().br(1); + } + self.ins().end(); + } + self.ins().end(); + + self.local_get_ctx(); + self.ins().i32_const(map_index.try_into().unwrap()); + self.ins().call(pop_iter); + + self.local_get_ctx(); + self.ins().local_get(m_ptr.idx); + self.ins().local_get(m_byte_size.idx); + self.ins().i32_const(tuple_align.try_into().unwrap()); + self.ins().i32_const(tuple_size.try_into().unwrap()); + self.ins().call(dealloc_bytes); + } + self.ins().end(); + + // Store the ptr/len + self.ins().local_get(m_ptr.idx); + self.store_scalar_from_top_of_stack(&dst_ptr, ValType::I32, 2); + self.ins().local_get(m_len.idx); + self.store_scalar_from_top_of_stack(&dst_len, ValType::I32, 2); + } + TypeDefKind::List(t) => { let metadata::Type::List(list_index) = ty else { unreachable!() @@ -1447,6 +1581,121 @@ impl<'a> FunctionCompiler<'a> { ); } + TypeDefKind::Map(key_ty, value_ty) => { + let metadata::Type::Map(map_index) = ty else { + unreachable!() + }; + + // Calculate size of tuple + let key_size = self.adapter.sizes.size(key_ty).size_wasm32(); + let key_align = self.adapter.sizes.align(key_ty).align_wasm32(); + let value_size = self.adapter.sizes.size(value_ty).size_wasm32(); + let value_align = self.adapter.sizes.align(value_ty).align_wasm32(); + + // Tuple alignment is max of element alignments + let tuple_align = key_align.max(value_align); + // Tuple size is key_size + padding + value_size + let key_end = key_size; + let value_offset = align_up(key_end, value_align); + let tuple_size = value_offset + value_size; + + let map_index = i32::try_from(map_index).unwrap(); + let push_map = i.push_map; + let map_append = i.map_append; + let dealloc_bytes = i.dealloc_bytes; + let (src_ptr, src_len) = src.split_ptr_len(); + + // Give the interpreter to lift the map exactly as-is and take + // ownership of the allocation. + self.local_get_ctx(); + self.ins().i32_const(map_index); + self.load_abi_loc(&src_ptr, ValType::I32, 2); + let m_ptr = self.local_tee_new_tmp(ValType::I32); + self.load_abi_loc(&src_len, ValType::I32, 2); + let m_len = self.local_tee_new_tmp(ValType::I32); + self.ins().call(push_map); + + // If the interpreter returned 0 then the map needs to be + // lifted manually element-by-element. + self.ins().i32_eqz(); + self.ins().if_(BlockType::Empty); + { + // Prep deallocation of the map after the loop by saving + // off the pointer/byte size. + self.ins().local_get(m_len.idx); + self.ins().i32_const(tuple_size.try_into().unwrap()); + self.ins().i32_mul(); + let m_byte_size = self.local_set_new_tmp(ValType::I32); + self.ins().local_get(m_ptr.idx); + let m_ptr_to_free = self.local_set_new_tmp(ValType::I32); + + // Using `m_len` as the loop counter, element-by-element + // lift from the map and push into the map. + self.ins().loop_(BlockType::Empty); + { + self.ins().local_get(m_len.idx); + self.ins().if_(BlockType::Empty); + { + // Lift the key from memory + let key_src = AbiLoc::Memory(Memory { + addr: TempLocal::new(m_ptr.idx, m_ptr.ty), + offset: 0, + }); + self.lift(&key_src, *key_ty); + + // Lift the value from memory + let value_src = AbiLoc::Memory(Memory { + addr: TempLocal::new(m_ptr.idx, m_ptr.ty), + offset: value_offset.try_into().unwrap(), + }); + self.lift(&value_src, *value_ty); + + // Push the lifted key-value pair onto the map. + self.local_get_ctx(); + self.ins().i32_const(map_index); + self.ins().call(map_append); + + // decrement the length counter + self.ins().local_get(m_len.idx); + self.ins().i32_const(1); + self.ins().i32_sub(); + self.ins().local_set(m_len.idx); + + // increment the pointer + self.ins().local_get(m_ptr.idx); + self.ins().i32_const(tuple_size.try_into().unwrap()); + self.ins().i32_add(); + self.ins().local_set(m_ptr.idx); + + self.ins().br(1); + } + self.ins().end(); + } + self.ins().end(); + + // The canonical ABI representation of this map is no + // longer needed, so discard it. + self.ins().local_get(m_byte_size.idx); + self.ins().if_(BlockType::Empty); + { + self.local_get_ctx(); + self.ins().local_get(m_ptr_to_free.idx); + self.ins().local_get(m_byte_size.idx); + self.ins().i32_const(tuple_align.try_into().unwrap()); + self.ins().i32_const(0); + self.ins().call(dealloc_bytes); + } + self.ins().end(); + + self.free_temp_local(m_byte_size); + self.free_temp_local(m_ptr_to_free); + } + self.ins().end(); + + self.free_temp_local(m_ptr); + self.free_temp_local(m_len); + } + TypeDefKind::List(t) => { let metadata::Type::List(list_index) = ty else { unreachable!() diff --git a/crates/wit-dylib/src/lib.rs b/crates/wit-dylib/src/lib.rs index 62288165ac..5c4e7e5a2d 100644 --- a/crates/wit-dylib/src/lib.rs +++ b/crates/wit-dylib/src/lib.rs @@ -571,6 +571,16 @@ impl Adapter { }); metadata::Type::List(index) } + TypeDefKind::Map(key_ty, value_ty) => { + let index = self.metadata.maps.len(); + self.metadata.maps.push(metadata::Map { + interface, + name, + key_ty: self.lookup_ty(key_ty), + value_ty: self.lookup_ty(value_ty), + }); + metadata::Type::Map(index) + } TypeDefKind::FixedSizeList(t, len) => { let index = self.metadata.fixed_size_lists.len(); self.metadata diff --git a/crates/wit-dylib/src/metadata.rs b/crates/wit-dylib/src/metadata.rs index 1d031ad272..fa8d06dff2 100644 --- a/crates/wit-dylib/src/metadata.rs +++ b/crates/wit-dylib/src/metadata.rs @@ -16,6 +16,7 @@ pub struct Metadata { pub options: Vec, pub results: Vec, pub lists: Vec, + pub maps: Vec, pub fixed_size_lists: Vec, pub futures: Vec, pub streams: Vec, @@ -91,6 +92,13 @@ pub struct List { pub ty: Type, } +pub struct Map { + pub interface: Option, + pub name: Option, + pub key_ty: Type, + pub value_ty: Type, +} + pub struct FixedSizeList { pub interface: Option, pub name: Option, @@ -142,6 +150,7 @@ pub enum Type { Option(usize), Result(usize), List(usize), + Map(usize), FixedSizeList(usize), Future(usize), Stream(usize), @@ -197,6 +206,7 @@ impl Metadata { let options = encoder.encode_list(&self.options, Encoder::encode_options); let results = encoder.encode_list(&self.results, Encoder::encode_results); let lists = encoder.encode_list(&self.lists, Encoder::encode_lists); + let maps = encoder.encode_list(&self.maps, Encoder::encode_maps); let fixed_size_lists = encoder.encode_list(&self.fixed_size_lists, Encoder::encode_fixed_size_lists); let futures = encoder.encode_list(&self.futures, Encoder::encode_futures); @@ -217,6 +227,7 @@ impl Metadata { options, results, lists, + maps, fixed_size_lists, futures, streams, @@ -460,6 +471,21 @@ impl Encoder { } } + fn encode_maps(&mut self, maps: &[Map]) { + for map in maps { + let Map { + interface, + name, + key_ty, + value_ty, + } = map; + self.opt_string_ptr(interface.as_deref()); + self.opt_string_ptr(name.as_deref()); + self.ty(key_ty); + self.ty(value_ty); + } + } + fn encode_fixed_size_lists(&mut self, lists: &[FixedSizeList]) { for list in lists { let FixedSizeList { @@ -636,6 +662,7 @@ impl Encoder { Type::Future(i) => index(25, i), Type::Stream(i) => index(26, i), Type::Alias(i) => index(27, i), + Type::Map(i) => index(28, i), }; self.put_u32(val); } diff --git a/crates/wit-dylib/test-programs/src/lib.rs b/crates/wit-dylib/test-programs/src/lib.rs index 33096277dc..d029f4895e 100644 --- a/crates/wit-dylib/test-programs/src/lib.rs +++ b/crates/wit-dylib/test-programs/src/lib.rs @@ -116,6 +116,8 @@ impl Drop for Cx<'_> { enum CowIter<'a> { Borrowed(std::slice::Iter<'a, Val>), Owned(std::vec::IntoIter), + BorrowedMap(std::slice::Iter<'a, (Val, Val)>), + OwnedMap(std::vec::IntoIter<(Val, Val)>), } impl<'a> Iterator for CowIter<'a> { @@ -125,6 +127,25 @@ impl<'a> Iterator for CowIter<'a> { match self { CowIter::Borrowed(i) => Some(Cow::Borrowed(i.next()?)), CowIter::Owned(i) => Some(Cow::Owned(i.next()?)), + CowIter::BorrowedMap(_) | CowIter::OwnedMap(_) => { + panic!("use next_map_entry for map iterators") + } + } + } +} + +impl<'a> CowIter<'a> { + fn next_map_entry(&mut self) -> Option<(Cow<'a, Val>, Cow<'a, Val>)> { + match self { + CowIter::BorrowedMap(i) => { + let (k, v) = i.next()?; + Some((Cow::Borrowed(k), Cow::Borrowed(v))) + } + CowIter::OwnedMap(i) => { + let (k, v) = i.next()?; + Some((Cow::Owned(k), Cow::Owned(v))) + } + _ => panic!("next_map_entry called on non-map iterator"), } } } @@ -570,8 +591,18 @@ impl Call for Cx<'_> { } fn pop_iter_next(&mut self, _ty: List) { - let value = self.iterators.last_mut().unwrap().next().unwrap(); - self.stack.push(value); + let iter = self.iterators.last_mut().unwrap(); + match iter { + CowIter::BorrowedMap(_) | CowIter::OwnedMap(_) => { + let (key, value) = iter.next_map_entry().unwrap(); + self.stack.push(key); + self.stack.push(value); + } + _ => { + let value = iter.next().unwrap(); + self.stack.push(value); + } + } } fn pop_iter(&mut self, _ty: List) { @@ -599,6 +630,34 @@ impl Call for Cx<'_> { _ => invalid(), } } + + fn pop_map(&mut self, _ty: Map) -> usize { + match self.always_pop() { + Cow::Borrowed(Val::GenericMap(m)) => { + self.iterators.push(CowIter::BorrowedMap(m.iter())); + m.len() + } + Cow::Owned(Val::GenericMap(m)) => { + let ret = m.len(); + self.iterators.push(CowIter::OwnedMap(m.into_iter())); + ret + } + _ => invalid(), + } + } + + fn push_map(&mut self, _ty: Map, capacity: usize) { + self.push_own(Val::GenericMap(Vec::with_capacity(capacity))); + } + + fn map_append(&mut self, _ty: Map) { + let value = self.always_pop().into_owned(); + let key = self.always_pop().into_owned(); + match self.stack.last_mut() { + Some(Cow::Owned(Val::GenericMap(map))) => map.push((key, value)), + _ => invalid(), + } + } } #[derive(Clone, Debug, PartialEq)] @@ -629,6 +688,7 @@ pub enum Val { Variant(u32, Option>), GenericList(Vec), ByteList(Vec), + GenericMap(Vec<(Val, Val)>), } #[derive(Clone, Debug)] diff --git a/crates/wit-dylib/wit_dylib.h b/crates/wit-dylib/wit_dylib.h index 247681ecf0..76903ec3d8 100644 --- a/crates/wit-dylib/wit_dylib.h +++ b/crates/wit-dylib/wit_dylib.h @@ -50,6 +50,7 @@ typedef uint32_t wit_type_t; #define WIT_TYPE_FUTURE 25 #define WIT_TYPE_STREAM 26 #define WIT_TYPE_ALIAS 27 +#define WIT_TYPE_MAP 28 #define WIT_TYPE_EMPTY 0xff typedef void(*wit_import_fn_t)(void* cx); @@ -191,6 +192,13 @@ typedef struct wit_list { wit_type_t ty; } wit_list_t; +typedef struct wit_map { + const char *interface; + const char *name; + wit_type_t key_ty; + wit_type_t value_ty; +} wit_map_t; + typedef struct wit_fixed_size_list { const char *interface; const char *name; @@ -243,6 +251,8 @@ typedef struct wit { const wit_result_t *results; size_t num_lists; const wit_list_t *lists; + size_t num_maps; + const wit_map_t *maps; size_t num_fixed_size_lists; const wit_fixed_size_list_t *fixed_size_lists; size_t num_futures; @@ -353,6 +363,11 @@ void wit_dylib_push_variant(void *cx, size_t ty, uint32_t discr); // it onto the list which is then at the top of the stack. bool wit_dylib_push_list(void *cx, size_t ty, uint8_t *bytes, size_t len); void wit_dylib_list_append(void *cx, size_t ty); +// Map functions work similarly to list functions. If `wit_dylib_push_map` returns +// false then key-value pairs need to be pushed one-by-one. The `wit_dylib_map_append` +// function expects the top two stack entries to be value (top) and key (second from top). +bool wit_dylib_push_map(void *cx, size_t ty, uint8_t *bytes, size_t len); +void wit_dylib_map_append(void *cx, size_t ty); uint8_t wit_dylib_pop_u8(void *cx); uint16_t wit_dylib_pop_u16(void *cx); @@ -413,5 +428,10 @@ void wit_dylib_pop_tuple(void *cx, size_t ty); size_t wit_dylib_pop_list(void *cx, size_t ty, void **ptr); void wit_dylib_pop_iter_next(void *cx, size_t ty); void wit_dylib_pop_iter(void *cx, size_t ty); +// Map popping works similarly to list popping. Returns the number of key-value pairs. +// If `ptr` is set to non-NULL, the data is in canonical ABI format (as tuple array). +// If `ptr` is set to NULL, then an iterator is pushed to the stack and +// `wit_dylib_pop_iter_next` should be called to extract each key-value pair. +size_t wit_dylib_pop_map(void *cx, size_t ty, void **ptr); #endif diff --git a/crates/wit-encoder/src/from_parser.rs b/crates/wit-encoder/src/from_parser.rs index f11fa8e040..84d8b62c7c 100644 --- a/crates/wit-encoder/src/from_parser.rs +++ b/crates/wit-encoder/src/from_parser.rs @@ -243,6 +243,10 @@ impl<'a> Converter<'a> { let output = Type::list(self.convert_type(ty)); TypeDefKind::Type(output) } + wit_parser::TypeDefKind::Map(key, value) => { + let output = Type::map(self.convert_type(key), self.convert_type(value)); + TypeDefKind::Type(output) + } wit_parser::TypeDefKind::FixedSizeList(ty, size) => { let output = Type::fixed_size_list(self.convert_type(ty), *size); TypeDefKind::Type(output) @@ -308,6 +312,9 @@ impl<'a> Converter<'a> { wit_parser::TypeDefKind::List(type_) => { Type::list(self.convert_type(type_)) } + wit_parser::TypeDefKind::Map(key, value) => { + Type::map(self.convert_type(key), self.convert_type(value)) + } wit_parser::TypeDefKind::FixedSizeList(type_, size) => { Type::fixed_size_list(self.convert_type(type_), *size) } diff --git a/crates/wit-encoder/src/ty.rs b/crates/wit-encoder/src/ty.rs index 18aa2786d1..f0fdb7d56c 100644 --- a/crates/wit-encoder/src/ty.rs +++ b/crates/wit-encoder/src/ty.rs @@ -26,6 +26,7 @@ pub enum Type { Option(Box), Result(Box), List(Box), + Map(Box, Box), FixedSizeList(Box, u32), Tuple(Tuple), Future(Option>), @@ -59,6 +60,9 @@ impl Type { pub fn list(type_: Type) -> Self { Type::List(Box::new(type_)) } + pub fn map(key: Type, value: Type) -> Self { + Type::Map(Box::new(key), Box::new(value)) + } pub fn fixed_size_list(type_: Type, size: u32) -> Self { Type::FixedSizeList(Box::new(type_), size) } @@ -120,6 +124,9 @@ impl Display for Type { Type::List(type_) => { write!(f, "list<{type_}>") } + Type::Map(key, value) => { + write!(f, "map<{key}, {value}>") + } Type::FixedSizeList(type_, size) => { write!(f, "list<{type_}, {size}>") } diff --git a/crates/wit-parser/src/abi.rs b/crates/wit-parser/src/abi.rs index 3cba9eba95..147cad3f7d 100644 --- a/crates/wit-parser/src/abi.rs +++ b/crates/wit-parser/src/abi.rs @@ -335,6 +335,10 @@ impl Resolve { result.push(WasmType::Pointer) && result.push(WasmType::Length) } + TypeDefKind::Map(_, _) => { + result.push(WasmType::Pointer) && result.push(WasmType::Length) + } + TypeDefKind::FixedSizeList(ty, size) => { self.push_flat_list((0..*size).map(|_| ty), result) } diff --git a/crates/wit-parser/src/ast.rs b/crates/wit-parser/src/ast.rs index fba94d0e3e..b59805c2f0 100644 --- a/crates/wit-parser/src/ast.rs +++ b/crates/wit-parser/src/ast.rs @@ -754,6 +754,7 @@ enum Type<'a> { String(Span), Name(Id<'a>), List(List<'a>), + Map(Map<'a>), FixedSizeList(FixedSizeList<'a>), Handle(Handle<'a>), Resource(Resource<'a>), @@ -912,6 +913,12 @@ struct List<'a> { ty: Box>, } +struct Map<'a> { + span: Span, + key: Box>, + value: Box>, +} + struct FixedSizeList<'a> { span: Span, ty: Box>, @@ -1406,6 +1413,20 @@ impl<'a> Type<'a> { } } + // map + Some((span, Token::Map)) => { + tokens.expect(Token::LessThan)?; + let key = Type::parse(tokens)?; + tokens.expect(Token::Comma)?; + let value = Type::parse(tokens)?; + tokens.expect(Token::GreaterThan)?; + Ok(Type::Map(Map { + span, + key: Box::new(key), + value: Box::new(value), + })) + } + // option Some((span, Token::Option_)) => { tokens.expect(Token::LessThan)?; @@ -1516,6 +1537,7 @@ impl<'a> Type<'a> { | Type::ErrorContext(span) => *span, Type::Name(id) => id.span, Type::List(l) => l.span, + Type::Map(m) => m.span, Type::FixedSizeList(l) => l.span, Type::Handle(h) => h.span(), Type::Resource(r) => r.span, diff --git a/crates/wit-parser/src/ast/lex.rs b/crates/wit-parser/src/ast/lex.rs index 36f4baaccf..ac6793fab0 100644 --- a/crates/wit-parser/src/ast/lex.rs +++ b/crates/wit-parser/src/ast/lex.rs @@ -80,6 +80,7 @@ pub enum Token { Stream, ErrorContext, List, + Map, Underscore, As, From_, @@ -301,6 +302,7 @@ impl<'a> Tokenizer<'a> { "stream" => Stream, "error-context" => ErrorContext, "list" => List, + "map" => Map, "_" => Underscore, "as" => As, "from" => From_, @@ -556,6 +558,7 @@ impl Token { Stream => "keyword `stream`", ErrorContext => "keyword `error-context`", List => "keyword `list`", + Map => "keyword `map`", Underscore => "keyword `_`", Id => "an identifier", ExplicitId => "an '%' identifier", diff --git a/crates/wit-parser/src/ast/resolve.rs b/crates/wit-parser/src/ast/resolve.rs index 761668e44f..9f495035b1 100644 --- a/crates/wit-parser/src/ast/resolve.rs +++ b/crates/wit-parser/src/ast/resolve.rs @@ -92,6 +92,7 @@ enum Key { Tuple(Vec), Enum(Vec), List(Type), + Map(Type, Type), FixedSizeList(Type, u32), Option(Type), Result(Option, Option), @@ -1186,6 +1187,11 @@ impl<'a> Resolver<'a> { let ty = self.resolve_type(&list.ty, stability)?; TypeDefKind::List(ty) } + ast::Type::Map(map) => { + let key_ty = self.resolve_type(&map.key, stability)?; + let value_ty = self.resolve_type(&map.value, stability)?; + TypeDefKind::Map(key_ty, value_ty) + } ast::Type::FixedSizeList(list) => { let ty = self.resolve_type(&list.ty, stability)?; TypeDefKind::FixedSizeList(ty, list.size) @@ -1373,6 +1379,9 @@ impl<'a> Resolver<'a> { TypeDefKind::List(ty) | TypeDefKind::FixedSizeList(ty, _) | TypeDefKind::Option(ty) => find_in_type(types, *ty), + TypeDefKind::Map(k, v) => { + find_in_type(types, *k).or_else(|| find_in_type(types, *v)) + } TypeDefKind::Future(ty) | TypeDefKind::Stream(ty) => { ty.as_ref().and_then(|ty| find_in_type(types, *ty)) } @@ -1464,6 +1473,7 @@ impl<'a> Resolver<'a> { Key::Enum(r.cases.iter().map(|f| f.name.clone()).collect::>()) } TypeDefKind::List(ty) => Key::List(*ty), + TypeDefKind::Map(k, v) => Key::Map(*k, *v), TypeDefKind::FixedSizeList(ty, size) => Key::FixedSizeList(*ty, *size), TypeDefKind::Option(t) => Key::Option(*t), TypeDefKind::Result(r) => Key::Result(r.ok, r.err), @@ -1753,6 +1763,10 @@ fn collect_deps<'a>(ty: &ast::Type<'a>, deps: &mut Vec>) { ast::Type::Option(ast::Option_ { ty, .. }) | ast::Type::List(ast::List { ty, .. }) | ast::Type::FixedSizeList(ast::FixedSizeList { ty, .. }) => collect_deps(ty, deps), + ast::Type::Map(ast::Map { key, value, .. }) => { + collect_deps(key, deps); + collect_deps(value, deps); + } ast::Type::Result(r) => { if let Some(ty) = &r.ok { collect_deps(ty, deps); diff --git a/crates/wit-parser/src/decoding.rs b/crates/wit-parser/src/decoding.rs index 6c58ee1e43..f89059c46b 100644 --- a/crates/wit-parser/src/decoding.rs +++ b/crates/wit-parser/src/decoding.rs @@ -1254,6 +1254,7 @@ impl WitPackageDecoder<'_> { match &kind { TypeDefKind::Type(_) | TypeDefKind::List(_) + | TypeDefKind::Map(_, _) | TypeDefKind::FixedSizeList(..) | TypeDefKind::Tuple(_) | TypeDefKind::Option(_) @@ -1295,6 +1296,12 @@ impl WitPackageDecoder<'_> { Ok(TypeDefKind::List(t)) } + ComponentDefinedType::Map(k, v) => { + let k = self.convert_valtype(k)?; + let v = self.convert_valtype(v)?; + Ok(TypeDefKind::Map(k, v)) + } + ComponentDefinedType::FixedSizeList(t, size) => { let t = self.convert_valtype(t)?; Ok(TypeDefKind::FixedSizeList(t, *size)) @@ -1593,6 +1600,16 @@ impl Registrar<'_> { self.valtype(t, ty) } + ComponentDefinedType::Map(k, v) => { + let (key_ty, value_ty) = match &self.resolve.types[id].kind { + TypeDefKind::Map(k, v) => (k, v), + TypeDefKind::Type(Type::Id(_)) => return Ok(()), + _ => bail!("expected a map"), + }; + self.valtype(k, key_ty)?; + self.valtype(v, value_ty) + } + ComponentDefinedType::FixedSizeList(t, elements) => { let ty = match &self.resolve.types[id].kind { TypeDefKind::FixedSizeList(r, elements2) if elements2 == elements => r, diff --git a/crates/wit-parser/src/lib.rs b/crates/wit-parser/src/lib.rs index 7274758491..74800834b1 100644 --- a/crates/wit-parser/src/lib.rs +++ b/crates/wit-parser/src/lib.rs @@ -625,6 +625,7 @@ pub enum TypeDefKind { Option(Type), Result(Result_), List(Type), + Map(Type, Type), FixedSizeList(Type, u32), Future(Option), Stream(Option), @@ -654,6 +655,7 @@ impl TypeDefKind { TypeDefKind::Option(_) => "option", TypeDefKind::Result(_) => "result", TypeDefKind::List(_) => "list", + TypeDefKind::Map(_, _) => "map", TypeDefKind::FixedSizeList(..) => "fixed size list", TypeDefKind::Future(_) => "future", TypeDefKind::Stream(_) => "stream", @@ -1273,6 +1275,10 @@ fn find_futures_and_streams(resolve: &Resolve, ty: Type, results: &mut Vec { find_futures_and_streams(resolve, *ty, results); } + TypeDefKind::Map(k, v) => { + find_futures_and_streams(resolve, *k, results); + find_futures_and_streams(resolve, *v, results); + } TypeDefKind::Result(r) => { if let Some(ty) = r.ok { find_futures_and_streams(resolve, ty, results); diff --git a/crates/wit-parser/src/live.rs b/crates/wit-parser/src/live.rs index bc436faac2..6e21cbd379 100644 --- a/crates/wit-parser/src/live.rs +++ b/crates/wit-parser/src/live.rs @@ -140,6 +140,10 @@ pub trait TypeIdVisitor { | TypeDefKind::Option(t) | TypeDefKind::Future(Some(t)) | TypeDefKind::Stream(Some(t)) => self.visit_type(resolve, t), + TypeDefKind::Map(k, v) => { + self.visit_type(resolve, k); + self.visit_type(resolve, v); + } TypeDefKind::Handle(handle) => match handle { crate::Handle::Own(ty) => self.visit_type_id(resolve, *ty), crate::Handle::Borrow(ty) => self.visit_type_id(resolve, *ty), diff --git a/crates/wit-parser/src/resolve.rs b/crates/wit-parser/src/resolve.rs index f77a5c6341..e2c17e7ea3 100644 --- a/crates/wit-parser/src/resolve.rs +++ b/crates/wit-parser/src/resolve.rs @@ -572,6 +572,7 @@ package {name} is defined in two different locations:\n\ Type::Id(id) => match &self.types[*id].kind { TypeDefKind::List(_) + | TypeDefKind::Map(_, _) | TypeDefKind::Variant(_) | TypeDefKind::Enum(_) | TypeDefKind::Option(_) @@ -3299,6 +3300,10 @@ impl Remap { Option(t) | List(t, ..) | FixedSizeList(t, ..) | Future(Some(t)) | Stream(Some(t)) => { self.update_ty(resolve, t, span)? } + Map(k, v) => { + self.update_ty(resolve, k, span)?; + self.update_ty(resolve, v, span)?; + } Result(r) => { if let Some(ty) = &mut r.ok { self.update_ty(resolve, ty, span)?; @@ -3747,6 +3752,9 @@ impl Remap { | TypeDefKind::Future(Some(ty)) | TypeDefKind::Stream(Some(ty)) | TypeDefKind::Option(ty) => self.type_has_borrow(resolve, ty), + TypeDefKind::Map(k, v) => { + self.type_has_borrow(resolve, k) || self.type_has_borrow(resolve, v) + } TypeDefKind::Result(r) => [&r.ok, &r.err] .iter() .filter_map(|t| t.as_ref()) diff --git a/crates/wit-parser/src/resolve/clone.rs b/crates/wit-parser/src/resolve/clone.rs index 785938054d..0a8008788d 100644 --- a/crates/wit-parser/src/resolve/clone.rs +++ b/crates/wit-parser/src/resolve/clone.rs @@ -118,6 +118,10 @@ impl<'a> Cloner<'a> { | TypeDefKind::FixedSizeList(ty, ..) => { self.ty(ty); } + TypeDefKind::Map(k, v) => { + self.ty(k); + self.ty(v); + } TypeDefKind::Tuple(list) => { for ty in list.types.iter_mut() { self.ty(ty); diff --git a/crates/wit-parser/src/sizealign.rs b/crates/wit-parser/src/sizealign.rs index a9b96dc008..a060f99ea5 100644 --- a/crates/wit-parser/src/sizealign.rs +++ b/crates/wit-parser/src/sizealign.rs @@ -274,6 +274,9 @@ impl SizeAlign { TypeDefKind::List(_) => { ElementInfo::new(ArchitectureSize::new(0, 2), Alignment::Pointer) } + TypeDefKind::Map(_, _) => { + ElementInfo::new(ArchitectureSize::new(0, 2), Alignment::Pointer) + } TypeDefKind::Record(r) => self.record(r.fields.iter().map(|f| &f.ty)), TypeDefKind::Tuple(t) => self.record(t.types.iter()), TypeDefKind::Flags(f) => match f.repr() { diff --git a/crates/wit-parser/tests/ui/maps.wit b/crates/wit-parser/tests/ui/maps.wit new file mode 100644 index 0000000000..deac260d00 --- /dev/null +++ b/crates/wit-parser/tests/ui/maps.wit @@ -0,0 +1,34 @@ +package foo:maps; + +interface maps { + // Basic maps + type string-to-u32 = map; + type u32-to-string = map; + + // Nested maps + type nested-map = map>; + + // Maps with complex types + record person { + name: string, + age: u32, + } + type person-map = map; + + // Maps with tuples, lists, options + type complex-map = map>>; + + // Functions using maps + get-value: func(m: map, key: string) -> option; + set-value: func(m: map, key: string, value: u32) -> map; + merge-maps: func(a: map, b: map) -> map; + + // Map with various key types + type int-key-map = map; + type char-key-map = map; +} + +world maps-world { + import maps; + export maps; +} diff --git a/crates/wit-parser/tests/ui/maps.wit.json b/crates/wit-parser/tests/ui/maps.wit.json new file mode 100644 index 0000000000..33f5065f12 --- /dev/null +++ b/crates/wit-parser/tests/ui/maps.wit.json @@ -0,0 +1,255 @@ +{ + "worlds": [ + { + "name": "maps-world", + "imports": { + "interface-0": { + "interface": { + "id": 0 + } + } + }, + "exports": { + "interface-0": { + "interface": { + "id": 0 + } + } + }, + "package": 0 + } + ], + "interfaces": [ + { + "name": "maps", + "types": { + "string-to-u32": 0, + "u32-to-string": 1, + "nested-map": 3, + "person": 4, + "person-map": 5, + "complex-map": 8, + "int-key-map": 9, + "char-key-map": 10 + }, + "functions": { + "get-value": { + "name": "get-value", + "kind": "freestanding", + "params": [ + { + "name": "m", + "type": 2 + }, + { + "name": "key", + "type": "string" + } + ], + "result": 11, + "docs": { + "contents": "Functions using maps" + } + }, + "set-value": { + "name": "set-value", + "kind": "freestanding", + "params": [ + { + "name": "m", + "type": 2 + }, + { + "name": "key", + "type": "string" + }, + { + "name": "value", + "type": "u32" + } + ], + "result": 2 + }, + "merge-maps": { + "name": "merge-maps", + "kind": "freestanding", + "params": [ + { + "name": "a", + "type": 2 + }, + { + "name": "b", + "type": 2 + } + ], + "result": 2 + } + }, + "package": 0 + } + ], + "types": [ + { + "name": "string-to-u32", + "kind": { + "map": [ + "string", + "u32" + ] + }, + "owner": { + "interface": 0 + }, + "docs": { + "contents": "Basic maps" + } + }, + { + "name": "u32-to-string", + "kind": { + "map": [ + "u32", + "string" + ] + }, + "owner": { + "interface": 0 + } + }, + { + "name": null, + "kind": { + "map": [ + "string", + "u32" + ] + }, + "owner": null + }, + { + "name": "nested-map", + "kind": { + "map": [ + "string", + 2 + ] + }, + "owner": { + "interface": 0 + }, + "docs": { + "contents": "Nested maps" + } + }, + { + "name": "person", + "kind": { + "record": { + "fields": [ + { + "name": "name", + "type": "string" + }, + { + "name": "age", + "type": "u32" + } + ] + } + }, + "owner": { + "interface": 0 + }, + "docs": { + "contents": "Maps with complex types" + } + }, + { + "name": "person-map", + "kind": { + "map": [ + "string", + 4 + ] + }, + "owner": { + "interface": 0 + } + }, + { + "name": null, + "kind": { + "list": "u32" + }, + "owner": null + }, + { + "name": null, + "kind": { + "option": 6 + }, + "owner": null + }, + { + "name": "complex-map", + "kind": { + "map": [ + "string", + 7 + ] + }, + "owner": { + "interface": 0 + }, + "docs": { + "contents": "Maps with tuples, lists, options" + } + }, + { + "name": "int-key-map", + "kind": { + "map": [ + "u32", + "string" + ] + }, + "owner": { + "interface": 0 + }, + "docs": { + "contents": "Map with various key types" + } + }, + { + "name": "char-key-map", + "kind": { + "map": [ + "char", + "string" + ] + }, + "owner": { + "interface": 0 + } + }, + { + "name": null, + "kind": { + "option": "u32" + }, + "owner": null + } + ], + "packages": [ + { + "name": "foo:maps", + "interfaces": { + "maps": 0 + }, + "worlds": { + "maps-world": 0 + } + } + ] +} \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/map-invalid-key.wit b/crates/wit-parser/tests/ui/parse-fail/map-invalid-key.wit new file mode 100644 index 0000000000..75a5f899cc --- /dev/null +++ b/crates/wit-parser/tests/ui/parse-fail/map-invalid-key.wit @@ -0,0 +1,7 @@ +// Map keys must be primitive types only +package foo:invalid-map-key; + +interface test { + // This should fail - tuple is not a valid map key type + type invalid = map, u32>; +} diff --git a/crates/wit-parser/tests/ui/parse-fail/map-invalid-key.wit.result b/crates/wit-parser/tests/ui/parse-fail/map-invalid-key.wit.result new file mode 100644 index 0000000000..4d8b09c5d2 --- /dev/null +++ b/crates/wit-parser/tests/ui/parse-fail/map-invalid-key.wit.result @@ -0,0 +1,5 @@ +invalid map key type: map keys must be bool, u8, u16, u32, u64, s8, s16, s32, s64, char, or string + --> tests/ui/parse-fail/map-invalid-key.wit:6:18 + | + 6 | type invalid = map, u32>; + | ^-- \ No newline at end of file diff --git a/tests/cli/wit-dylib-smoke.wit.stdout b/tests/cli/wit-dylib-smoke.wit.stdout index 02b9c82a93..785770e77f 100644 --- a/tests/cli/wit-dylib-smoke.wit.stdout +++ b/tests/cli/wit-dylib-smoke.wit.stdout @@ -1,6 +1,6 @@ (module (@dylink.0 - (mem-info (memory 208 2) (table 1 0)) + (mem-info (memory 216 2) (table 1 0)) ) (type (;0;) (func (param i32))) (type (;1;) (func (param i32 i32 i32 i32) (result i32))) @@ -53,33 +53,36 @@ (import "env" "wit_dylib_pop_list" (func $wit_dylib_pop_list (;33;) (type 9))) (import "env" "wit_dylib_pop_iter_next" (func $wit_dylib_pop_iter_next (;34;) (type 4))) (import "env" "wit_dylib_pop_iter" (func $wit_dylib_pop_iter (;35;) (type 4))) - (import "env" "wit_dylib_push_bool" (func $wit_dylib_push_bool (;36;) (type 4))) - (import "env" "wit_dylib_push_char" (func $wit_dylib_push_char (;37;) (type 4))) - (import "env" "wit_dylib_push_u8" (func $wit_dylib_push_u8 (;38;) (type 4))) - (import "env" "wit_dylib_push_s8" (func $wit_dylib_push_s8 (;39;) (type 4))) - (import "env" "wit_dylib_push_u16" (func $wit_dylib_push_u16 (;40;) (type 4))) - (import "env" "wit_dylib_push_s16" (func $wit_dylib_push_s16 (;41;) (type 4))) - (import "env" "wit_dylib_push_u32" (func $wit_dylib_push_u32 (;42;) (type 4))) - (import "env" "wit_dylib_push_s32" (func $wit_dylib_push_s32 (;43;) (type 4))) - (import "env" "wit_dylib_push_u64" (func $wit_dylib_push_u64 (;44;) (type 10))) - (import "env" "wit_dylib_push_s64" (func $wit_dylib_push_s64 (;45;) (type 10))) - (import "env" "wit_dylib_push_f32" (func $wit_dylib_push_f32 (;46;) (type 11))) - (import "env" "wit_dylib_push_f64" (func $wit_dylib_push_f64 (;47;) (type 12))) - (import "env" "wit_dylib_push_string" (func $wit_dylib_push_string (;48;) (type 13))) - (import "env" "wit_dylib_push_record" (func $wit_dylib_push_record (;49;) (type 4))) - (import "env" "wit_dylib_push_tuple" (func $wit_dylib_push_tuple (;50;) (type 4))) - (import "env" "wit_dylib_push_flags" (func $wit_dylib_push_flags (;51;) (type 13))) - (import "env" "wit_dylib_push_enum" (func $wit_dylib_push_enum (;52;) (type 13))) - (import "env" "wit_dylib_push_borrow" (func $wit_dylib_push_borrow (;53;) (type 13))) - (import "env" "wit_dylib_push_own" (func $wit_dylib_push_own (;54;) (type 13))) - (import "env" "wit_dylib_push_future" (func $wit_dylib_push_future (;55;) (type 13))) - (import "env" "wit_dylib_push_stream" (func $wit_dylib_push_stream (;56;) (type 13))) - (import "env" "wit_dylib_push_variant" (func $wit_dylib_push_variant (;57;) (type 13))) - (import "env" "wit_dylib_push_option" (func $wit_dylib_push_option (;58;) (type 13))) - (import "env" "wit_dylib_push_result" (func $wit_dylib_push_result (;59;) (type 13))) - (import "env" "wit_dylib_push_list" (func $wit_dylib_push_list (;60;) (type 1))) - (import "env" "wit_dylib_list_append" (func $wit_dylib_list_append (;61;) (type 4))) - (import "$root" "x" (func $x (;62;) (type 14))) + (import "env" "wit_dylib_pop_map" (func $wit_dylib_pop_map (;36;) (type 9))) + (import "env" "wit_dylib_push_bool" (func $wit_dylib_push_bool (;37;) (type 4))) + (import "env" "wit_dylib_push_char" (func $wit_dylib_push_char (;38;) (type 4))) + (import "env" "wit_dylib_push_u8" (func $wit_dylib_push_u8 (;39;) (type 4))) + (import "env" "wit_dylib_push_s8" (func $wit_dylib_push_s8 (;40;) (type 4))) + (import "env" "wit_dylib_push_u16" (func $wit_dylib_push_u16 (;41;) (type 4))) + (import "env" "wit_dylib_push_s16" (func $wit_dylib_push_s16 (;42;) (type 4))) + (import "env" "wit_dylib_push_u32" (func $wit_dylib_push_u32 (;43;) (type 4))) + (import "env" "wit_dylib_push_s32" (func $wit_dylib_push_s32 (;44;) (type 4))) + (import "env" "wit_dylib_push_u64" (func $wit_dylib_push_u64 (;45;) (type 10))) + (import "env" "wit_dylib_push_s64" (func $wit_dylib_push_s64 (;46;) (type 10))) + (import "env" "wit_dylib_push_f32" (func $wit_dylib_push_f32 (;47;) (type 11))) + (import "env" "wit_dylib_push_f64" (func $wit_dylib_push_f64 (;48;) (type 12))) + (import "env" "wit_dylib_push_string" (func $wit_dylib_push_string (;49;) (type 13))) + (import "env" "wit_dylib_push_record" (func $wit_dylib_push_record (;50;) (type 4))) + (import "env" "wit_dylib_push_tuple" (func $wit_dylib_push_tuple (;51;) (type 4))) + (import "env" "wit_dylib_push_flags" (func $wit_dylib_push_flags (;52;) (type 13))) + (import "env" "wit_dylib_push_enum" (func $wit_dylib_push_enum (;53;) (type 13))) + (import "env" "wit_dylib_push_borrow" (func $wit_dylib_push_borrow (;54;) (type 13))) + (import "env" "wit_dylib_push_own" (func $wit_dylib_push_own (;55;) (type 13))) + (import "env" "wit_dylib_push_future" (func $wit_dylib_push_future (;56;) (type 13))) + (import "env" "wit_dylib_push_stream" (func $wit_dylib_push_stream (;57;) (type 13))) + (import "env" "wit_dylib_push_variant" (func $wit_dylib_push_variant (;58;) (type 13))) + (import "env" "wit_dylib_push_option" (func $wit_dylib_push_option (;59;) (type 13))) + (import "env" "wit_dylib_push_result" (func $wit_dylib_push_result (;60;) (type 13))) + (import "env" "wit_dylib_push_list" (func $wit_dylib_push_list (;61;) (type 1))) + (import "env" "wit_dylib_list_append" (func $wit_dylib_list_append (;62;) (type 4))) + (import "env" "wit_dylib_push_map" (func $wit_dylib_push_map (;63;) (type 1))) + (import "env" "wit_dylib_map_append" (func $wit_dylib_map_append (;64;) (type 4))) + (import "$root" "x" (func $x (;65;) (type 14))) (import "env" "__table_base" (global $__table_base (;0;) i32)) (import "env" "__memory_base" (global $__memory_base (;1;) i32)) (import "env" "__stack_pointer" (global $__stack_pointer (;2;) (mut i32))) @@ -91,7 +94,7 @@ (export "__wasm_apply_data_relocs" (func $__wasm_apply_data_relocs)) (export "__wasm_call_ctors" (func $__wasm_call_ctors)) (elem (;0;) (table 0) (global.get $__table_base) func $"adapter x") - (func $"adapter x" (;63;) (type 0) (param i32) + (func $"adapter x" (;66;) (type 0) (param i32) (local i32) global.get $__stack_pointer i32.const 16 @@ -104,7 +107,7 @@ i32.add global.set $__stack_pointer ) - (func $y (;64;) (type 14) + (func $y (;67;) (type 14) (local i32 i32 i32) global.get $__stack_pointer i32.const 16 @@ -126,7 +129,7 @@ local.get 1 i32.store ) - (func $cabi_post_y (;65;) (type 14) + (func $cabi_post_y (;68;) (type 14) (local i32 i32) global.get $__stack_pointer local.set 0 @@ -147,10 +150,10 @@ i32.add global.set $__stack_pointer ) - (func $__wasm_apply_data_relocs (;66;) (type 14) + (func $__wasm_apply_data_relocs (;69;) (type 14) global.get $__memory_base global.get $__memory_base - i32.const 204 + i32.const 212 i32.add i32.store offset=4 global.get $__memory_base @@ -158,7 +161,7 @@ i32.store offset=8 global.get $__memory_base global.get $__memory_base - i32.const 206 + i32.const 214 i32.add i32.store offset=48 global.get $__memory_base @@ -167,11 +170,11 @@ i32.add i32.store offset=96 ) - (func $__wasm_call_ctors (;67;) (type 14) + (func $__wasm_call_ctors (;70;) (type 14) i32.const 88 global.get $__memory_base i32.add call $wit_dylib_initialize ) - (data (;0;) (global.get $__memory_base) "/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/ff/ff/ff/ff/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/ff/ff/ff/ff/00/00/00/00/00/00/00/00/00/00/00/00/02/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00x/00y/00") + (data (;0;) (global.get $__memory_base) "/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/ff/ff/ff/ff/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/ff/ff/ff/ff/00/00/00/00/00/00/00/00/00/00/00/00/02/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00/00x/00y/00") )