diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index 96345c210..15ada323f 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -1088,7 +1088,7 @@ fn is_prim_type_id(resolve: &Resolve, id: TypeId) -> bool { | TypeDefKind::Stream(_) | TypeDefKind::Unknown => false, TypeDefKind::FixedLengthList(..) => todo!(), - TypeDefKind::Map(..) => todo!(), + TypeDefKind::Map(key, value) => is_prim_type(resolve, key) && is_prim_type(resolve, value), } } @@ -1174,7 +1174,12 @@ pub fn push_ty_name(resolve: &Resolve, ty: &Type, src: &mut String) { } TypeDefKind::Unknown => unreachable!(), TypeDefKind::FixedLengthList(..) => todo!(), - TypeDefKind::Map(..) => todo!(), + TypeDefKind::Map(key, value) => { + src.push_str("map_"); + push_ty_name(resolve, key, src); + src.push_str("_"); + push_ty_name(resolve, value, src); + } } } } @@ -1383,12 +1388,12 @@ impl Return { TypeDefKind::Tuple(_) | TypeDefKind::Record(_) | TypeDefKind::List(_) + | TypeDefKind::Map(..) | TypeDefKind::Variant(_) => {} TypeDefKind::Resource => todo!("return_single for resource"), TypeDefKind::Unknown => unreachable!(), TypeDefKind::FixedLengthList(..) => todo!(), - TypeDefKind::Map(..) => todo!(), } self.retptrs.push(*orig_ty); @@ -1746,8 +1751,15 @@ void __wasm_export_{ns}_{snake}_dtor({ns}_{snake}_t* arg) {{ todo!("named fixed-length list types are not yet supported in the C backend") } - fn type_map(&mut self, _id: TypeId, _name: &str, _key: &Type, _value: &Type, _docs: &Docs) { - todo!("map types are not yet supported in the C backend") + fn type_map(&mut self, id: TypeId, _name: &str, key: &Type, value: &Type, docs: &Docs) { + self.src.h_defs("\n"); + self.docs(docs, SourceType::HDefs); + self.print_map_entry_typedef(id, key, value); + self.start_typedef_struct(id); + self.print_map_entry_typename(id); + self.src.h_defs(" *ptr;\n"); + self.src.h_defs("size_t len;\n"); + self.finish_typedef_struct(id); } fn type_future(&mut self, id: TypeId, _name: &str, _ty: &Option, docs: &Docs) { @@ -1872,8 +1884,16 @@ impl<'a> wit_bindgen_core::AnonymousTypeGenerator<'a> for InterfaceGenerator<'a> todo!("print_anonymous_type for fixed length list"); } - fn anonymous_type_map(&mut self, _id: TypeId, _key: &Type, _value: &Type, _docs: &Docs) { - todo!("anonymous map types are not yet supported in the C backend"); + fn anonymous_type_map(&mut self, id: TypeId, key: &Type, value: &Type, _docs: &Docs) { + self.print_map_entry_typedef(id, key, value); + self.src.h_defs("\ntypedef "); + self.src.h_defs("struct {\n"); + self.print_map_entry_typename(id); + self.src.h_defs(" *ptr;\n"); + self.src.h_defs("size_t len;\n"); + self.src.h_defs("}"); + self.src.h_defs(" "); + self.print_typedef_target(id); } } @@ -2053,7 +2073,19 @@ impl InterfaceGenerator<'_> { } TypeDefKind::Unknown => unreachable!(), TypeDefKind::FixedLengthList(..) => todo!(), - TypeDefKind::Map(..) => todo!(), + TypeDefKind::Map(key, value) => { + let entry_name = self.map_entry_typename(id); + self.src.c_helpers("size_t map_len = ptr->len;\n"); + uwriteln!(self.src.c_helpers, "if (map_len > 0) {{"); + uwriteln!(self.src.c_helpers, "{entry_name} *map_ptr = ptr->ptr;"); + self.src + .c_helpers("for (size_t i = 0; i < map_len; i++) {\n"); + self.free(key, "&map_ptr[i].key"); + self.free(value, "&map_ptr[i].value"); + self.src.c_helpers("}\n"); + uwriteln!(self.src.c_helpers, "free(map_ptr);"); + uwriteln!(self.src.c_helpers, "}}"); + } } if c_helpers_body_start == self.src.c_helpers.len() { self.src.c_helpers.as_mut_string().truncate(c_helpers_start); @@ -2662,6 +2694,31 @@ void {name}_return({return_ty}) {{ self.print_typedef_target(id); } + /// Returns the typedef name of the synthetic entry struct used as the + /// element type of a `map`'s flat-pair representation. + fn map_entry_typename(&self, id: TypeId) -> String { + let map_name = &self.r#gen.type_names[&id]; + let prefix = map_name.strip_suffix("_t").unwrap_or(map_name.as_str()); + format!("{prefix}_entry_t") + } + + fn print_map_entry_typename(&mut self, id: TypeId) { + let entry_name = self.map_entry_typename(id); + self.src.h_defs(&entry_name); + } + + /// Emits the `typedef struct { K key; V value; } _entry_t;` companion + /// declaration that precedes the map struct itself. + fn print_map_entry_typedef(&mut self, id: TypeId, key: &Type, value: &Type) { + self.src.h_defs("\ntypedef struct {\n"); + self.print_ty(SourceType::HDefs, key); + self.src.h_defs(" key;\n"); + self.print_ty(SourceType::HDefs, value); + self.src.h_defs(" value;\n} "); + self.print_map_entry_typename(id); + self.src.h_defs(";\n"); + } + fn owner_namespace(&self, id: TypeId) -> String { owner_namespace( self.interface, @@ -2747,7 +2804,9 @@ void {name}_return({return_ty}) {{ TypeDefKind::Unknown => false, TypeDefKind::FixedLengthList(..) => todo!(), - TypeDefKind::Map(..) => todo!(), + TypeDefKind::Map(key, value) => { + self.contains_droppable_borrow(key) || self.contains_droppable_borrow(value) + } } } else { false @@ -3629,7 +3688,35 @@ impl Bindgen for FunctionBindgen<'_, '_> { list_name, elem_name, operands[0], operands[1] )); } + + // Maps share the canonical "flat array of entries" layout with the + // C struct emitted by `type_map`/`anonymous_type_map`, so the body + // block is discarded and we splice `.ptr` / `.len` through directly + // (mirroring how `ListLower`/`ListLift` work above). + Instruction::MapLower { .. } => { + let _body = self.blocks.pop().unwrap(); + results.push(format!("(uint8_t *) ({}).ptr", operands[0])); + results.push(format!("({}).len", operands[0])); + } + + Instruction::MapLift { ty, .. } => { + self.assert_no_droppable_borrows("map", &Type::Id(*ty)); + + let _body = self.blocks.pop().unwrap(); + let map_name = self.r#gen.r#gen.type_name(&Type::Id(*ty)); + let entry_name = { + let prefix = map_name.strip_suffix("_t").unwrap_or(&map_name); + format!("{prefix}_entry_t") + }; + results.push(format!( + "({}) {{ ({}*)({}), ({}) }}", + map_name, entry_name, operands[0], operands[1] + )); + } + Instruction::IterElem { .. } => results.push("e".to_string()), + Instruction::IterMapKey { .. } => results.push("map_key".to_string()), + Instruction::IterMapValue { .. } => results.push("map_value".to_string()), Instruction::IterBasePointer => results.push("base".to_string()), Instruction::CallWasm { sig, .. } => { @@ -3965,6 +4052,29 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!(self.src, "}}"); } + Instruction::GuestDeallocateMap { key, value } => { + let (body, results) = self.blocks.pop().unwrap(); + assert!(results.is_empty()); + let len = self.locals.tmp("len"); + uwriteln!(self.src, "size_t {len} = {};", operands[1]); + uwriteln!(self.src, "if ({len} > 0) {{"); + let ptr = self.locals.tmp("ptr"); + uwriteln!(self.src, "uint8_t *{ptr} = {};", operands[0]); + let i = self.locals.tmp("i"); + uwriteln!(self.src, "for (size_t {i} = 0; {i} < {len}; {i}++) {{"); + let entry = self.r#gen.r#gen.sizes.record([*key, *value]); + uwriteln!( + self.src, + "uint8_t *base = {ptr} + {i} * {};", + entry.size.format(POINTER_SIZE_EXPRESSION) + ); + uwriteln!(self.src, "(void) base;"); + uwrite!(self.src, "{body}"); + uwriteln!(self.src, "}}"); + uwriteln!(self.src, "free({ptr});"); + uwriteln!(self.src, "}}"); + } + Instruction::Flush { amt } => { results.extend(operands.iter().take(*amt).cloned()); } @@ -4111,13 +4221,15 @@ pub fn is_arg_by_pointer(resolve: &Resolve, ty: &Type) -> bool { TypeDefKind::Enum(_) => false, TypeDefKind::Flags(_) => false, TypeDefKind::Handle(_) => false, - TypeDefKind::Tuple(_) | TypeDefKind::Record(_) | TypeDefKind::List(_) => true, + TypeDefKind::Tuple(_) + | TypeDefKind::Record(_) + | TypeDefKind::List(_) + | TypeDefKind::Map(..) => true, TypeDefKind::Future(_) => false, TypeDefKind::Stream(_) => false, TypeDefKind::Resource => todo!("is_arg_by_pointer for resource"), TypeDefKind::Unknown => unreachable!(), TypeDefKind::FixedLengthList(..) => todo!(), - TypeDefKind::Map(..) => todo!(), }, Type::String => true, _ => false, diff --git a/crates/test/src/c.rs b/crates/test/src/c.rs index 7ca7e49b9..9eb3c0495 100644 --- a/crates/test/src/c.rs +++ b/crates/test/src/c.rs @@ -56,6 +56,12 @@ impl LanguageMethods for C { config: &crate::config::WitConfig, _args: &[String], ) -> bool { + // `map.wit` exercises `tuple` where `names-by-id` + // is a named alias for `map`. The C backend's + // world-level "primitive" namespace dedup currently produces an + // import-vs-export name collision for anonymous containers of + // named-prim-content types, which is a pre-existing limitation that + // surfaces here. Keep skipped until that is addressed separately. config.error_context || name.starts_with("named-fixed-length-list.wit") || name.starts_with("map.wit") diff --git a/tests/runtime/map/runner.c b/tests/runtime/map/runner.c new file mode 100644 index 000000000..2c1a05cfc --- /dev/null +++ b/tests/runtime/map/runner.c @@ -0,0 +1,367 @@ +//@ wasmtime-flags = '-Wcomponent-model-map' + +#include +#include +#include +#include + +#include "runner.h" + +static bool string_eq(const runner_string_t *s, const char *lit) { + size_t len = strlen(lit); + return s->len == len && memcmp(s->ptr, lit, len) == 0; +} + +static void test_named_roundtrip(void) { + test_maps_to_test_names_by_id_entry_t entries[2]; + entries[0].key = 1; + runner_string_dup(&entries[0].value, "uno"); + entries[1].key = 2; + runner_string_dup(&entries[1].value, "two"); + test_maps_to_test_names_by_id_t input = {.ptr = entries, .len = 2}; + + test_maps_to_test_ids_by_name_t result; + test_maps_to_test_named_roundtrip(&input, &result); + + assert(result.len == 2); + bool saw_uno = false, saw_two = false; + for (size_t i = 0; i < result.len; i++) { + if (string_eq(&result.ptr[i].key, "uno")) { + assert(result.ptr[i].value == 1); + saw_uno = true; + } else if (string_eq(&result.ptr[i].key, "two")) { + assert(result.ptr[i].value == 2); + saw_two = true; + } else { + assert(0 && "unexpected key"); + } + } + assert(saw_uno && saw_two); + test_maps_to_test_ids_by_name_free(&result); +} + +static void test_bytes_roundtrip(void) { + test_maps_to_test_bytes_by_name_entry_t entries[2]; + runner_string_dup(&entries[0].key, "hello"); + uint8_t world_bytes[] = {'w', 'o', 'r', 'l', 'd'}; + entries[0].value.ptr = (uint8_t *)malloc(sizeof(world_bytes)); + memcpy(entries[0].value.ptr, world_bytes, sizeof(world_bytes)); + entries[0].value.len = sizeof(world_bytes); + + runner_string_dup(&entries[1].key, "bin"); + uint8_t bin_bytes[] = {0, 1, 2}; + entries[1].value.ptr = (uint8_t *)malloc(sizeof(bin_bytes)); + memcpy(entries[1].value.ptr, bin_bytes, sizeof(bin_bytes)); + entries[1].value.len = sizeof(bin_bytes); + + test_maps_to_test_bytes_by_name_t input = {.ptr = entries, .len = 2}; + test_maps_to_test_bytes_by_name_t result; + test_maps_to_test_bytes_roundtrip(&input, &result); + + assert(result.len == 2); + for (size_t i = 0; i < result.len; i++) { + if (string_eq(&result.ptr[i].key, "hello")) { + assert(result.ptr[i].value.len == 5); + assert(memcmp(result.ptr[i].value.ptr, "world", 5) == 0); + } else if (string_eq(&result.ptr[i].key, "bin")) { + assert(result.ptr[i].value.len == 3); + uint8_t expected[] = {0, 1, 2}; + assert(memcmp(result.ptr[i].value.ptr, expected, 3) == 0); + } else { + assert(0 && "unexpected key"); + } + } + test_maps_to_test_bytes_by_name_free(&result); +} + +static void test_empty_roundtrip(void) { + test_maps_to_test_names_by_id_t input = {.ptr = NULL, .len = 0}; + test_maps_to_test_names_by_id_t result; + test_maps_to_test_empty_roundtrip(&input, &result); + assert(result.len == 0); + test_maps_to_test_names_by_id_free(&result); +} + +static void test_option_roundtrip(void) { + runner_map_string_option_u32_entry_t entries[2]; + runner_string_dup(&entries[0].key, "some"); + entries[0].value.is_some = true; + entries[0].value.val = 42; + runner_string_dup(&entries[1].key, "none"); + entries[1].value.is_some = false; + entries[1].value.val = 0; + + runner_map_string_option_u32_t input = {.ptr = entries, .len = 2}; + runner_map_string_option_u32_t result; + test_maps_to_test_option_roundtrip(&input, &result); + + assert(result.len == 2); + bool saw_some = false, saw_none = false; + for (size_t i = 0; i < result.len; i++) { + if (string_eq(&result.ptr[i].key, "some")) { + assert(result.ptr[i].value.is_some); + assert(result.ptr[i].value.val == 42); + saw_some = true; + } else if (string_eq(&result.ptr[i].key, "none")) { + assert(!result.ptr[i].value.is_some); + saw_none = true; + } + } + assert(saw_some && saw_none); + runner_map_string_option_u32_free(&result); +} + +static void test_record_roundtrip(void) { + test_maps_to_test_names_by_id_entry_t values[2]; + values[0].key = 10; + runner_string_dup(&values[0].value, "ten"); + values[1].key = 20; + runner_string_dup(&values[1].value, "twenty"); + + test_maps_to_test_labeled_entry_t input; + runner_string_dup(&input.label, "test-label"); + input.values.ptr = values; + input.values.len = 2; + + test_maps_to_test_labeled_entry_t result; + test_maps_to_test_record_roundtrip(&input, &result); + + assert(string_eq(&result.label, "test-label")); + assert(result.values.len == 2); + for (size_t i = 0; i < result.values.len; i++) { + if (result.values.ptr[i].key == 10) { + assert(string_eq(&result.values.ptr[i].value, "ten")); + } else if (result.values.ptr[i].key == 20) { + assert(string_eq(&result.values.ptr[i].value, "twenty")); + } else { + assert(0 && "unexpected key"); + } + } + test_maps_to_test_labeled_entry_free(&result); +} + +static void test_inline_roundtrip(void) { + runner_map_u32_string_entry_t entries[2]; + entries[0].key = 1; + runner_string_dup(&entries[0].value, "one"); + entries[1].key = 2; + runner_string_dup(&entries[1].value, "two"); + + runner_map_u32_string_t input = {.ptr = entries, .len = 2}; + runner_map_string_u32_t result; + test_maps_to_test_inline_roundtrip(&input, &result); + + assert(result.len == 2); + bool saw_one = false, saw_two = false; + for (size_t i = 0; i < result.len; i++) { + if (string_eq(&result.ptr[i].key, "one")) { + assert(result.ptr[i].value == 1); + saw_one = true; + } else if (string_eq(&result.ptr[i].key, "two")) { + assert(result.ptr[i].value == 2); + saw_two = true; + } + } + assert(saw_one && saw_two); + runner_map_string_u32_free(&result); +} + +static void test_large_roundtrip(void) { + size_t n = 100; + test_maps_to_test_names_by_id_entry_t *entries = + (test_maps_to_test_names_by_id_entry_t *)malloc( + n * sizeof(test_maps_to_test_names_by_id_entry_t)); + for (size_t i = 0; i < n; i++) { + entries[i].key = (uint32_t)i; + char buf[32]; + int len = snprintf(buf, sizeof(buf), "value-%zu", i); + runner_string_dup_n(&entries[i].value, buf, (size_t)len); + } + test_maps_to_test_names_by_id_t input = {.ptr = entries, .len = n}; + test_maps_to_test_names_by_id_t result; + test_maps_to_test_large_roundtrip(&input, &result); + assert(result.len == n); + + // Free locally-allocated input contents (keys are u32, values are strings). + for (size_t i = 0; i < n; i++) { + runner_string_free(&entries[i].value); + } + free(entries); + + test_maps_to_test_names_by_id_free(&result); +} + +static void test_multi_param_roundtrip(void) { + test_maps_to_test_names_by_id_entry_t names_entries[2]; + names_entries[0].key = 1; + runner_string_dup(&names_entries[0].value, "one"); + names_entries[1].key = 2; + runner_string_dup(&names_entries[1].value, "two"); + test_maps_to_test_names_by_id_t names = {.ptr = names_entries, .len = 2}; + + test_maps_to_test_bytes_by_name_entry_t bytes_entries[1]; + runner_string_dup(&bytes_entries[0].key, "key"); + bytes_entries[0].value.ptr = (uint8_t *)malloc(1); + bytes_entries[0].value.ptr[0] = 42; + bytes_entries[0].value.len = 1; + test_maps_to_test_bytes_by_name_t bytes = {.ptr = bytes_entries, .len = 1}; + + runner_tuple2_ids_by_name_bytes_by_name_t result; + test_maps_to_test_multi_param_roundtrip(&names, &bytes, &result); + + assert(result.f0.len == 2); + assert(result.f1.len == 1); + assert(string_eq(&result.f1.ptr[0].key, "key")); + assert(result.f1.ptr[0].value.len == 1); + assert(result.f1.ptr[0].value.ptr[0] == 42); + runner_tuple2_ids_by_name_bytes_by_name_free(&result); +} + +static void test_nested_roundtrip(void) { + runner_map_u32_string_entry_t inner_a[2]; + inner_a[0].key = 1; + runner_string_dup(&inner_a[0].value, "one"); + inner_a[1].key = 2; + runner_string_dup(&inner_a[1].value, "two"); + + runner_map_u32_string_entry_t inner_b[1]; + inner_b[0].key = 10; + runner_string_dup(&inner_b[0].value, "ten"); + + runner_map_string_map_u32_string_entry_t outer[2]; + runner_string_dup(&outer[0].key, "group-a"); + outer[0].value.ptr = inner_a; + outer[0].value.len = 2; + runner_string_dup(&outer[1].key, "group-b"); + outer[1].value.ptr = inner_b; + outer[1].value.len = 1; + + runner_map_string_map_u32_string_t input = {.ptr = outer, .len = 2}; + runner_map_string_map_u32_string_t result; + test_maps_to_test_nested_roundtrip(&input, &result); + + assert(result.len == 2); + for (size_t i = 0; i < result.len; i++) { + if (string_eq(&result.ptr[i].key, "group-a")) { + assert(result.ptr[i].value.len == 2); + } else if (string_eq(&result.ptr[i].key, "group-b")) { + assert(result.ptr[i].value.len == 1); + } else { + assert(0 && "unexpected outer key"); + } + } + runner_map_string_map_u32_string_free(&result); +} + +static void test_variant_roundtrip(void) { + test_maps_to_test_names_by_id_entry_t entries[1]; + entries[0].key = 1; + runner_string_dup(&entries[0].value, "one"); + + test_maps_to_test_map_or_string_t input; + input.tag = TEST_MAPS_TO_TEST_MAP_OR_STRING_AS_MAP; + input.val.as_map.ptr = entries; + input.val.as_map.len = 1; + + test_maps_to_test_map_or_string_t result; + test_maps_to_test_variant_roundtrip(&input, &result); + + assert(result.tag == TEST_MAPS_TO_TEST_MAP_OR_STRING_AS_MAP); + assert(result.val.as_map.len == 1); + assert(result.val.as_map.ptr[0].key == 1); + assert(string_eq(&result.val.as_map.ptr[0].value, "one")); + test_maps_to_test_map_or_string_free(&result); + + test_maps_to_test_map_or_string_t input2; + input2.tag = TEST_MAPS_TO_TEST_MAP_OR_STRING_AS_STRING; + runner_string_dup(&input2.val.as_string, "hello"); + + test_maps_to_test_map_or_string_t result2; + test_maps_to_test_variant_roundtrip(&input2, &result2); + + assert(result2.tag == TEST_MAPS_TO_TEST_MAP_OR_STRING_AS_STRING); + assert(string_eq(&result2.val.as_string, "hello")); + test_maps_to_test_map_or_string_free(&result2); +} + +static void test_result_roundtrip(void) { + test_maps_to_test_names_by_id_entry_t entries[1]; + entries[0].key = 5; + runner_string_dup(&entries[0].value, "five"); + + test_maps_to_test_result_names_by_id_string_t ok_input; + ok_input.is_err = false; + ok_input.val.ok.ptr = entries; + ok_input.val.ok.len = 1; + + test_maps_to_test_names_by_id_t ok_ret; + runner_string_t err_ret; + bool is_ok = test_maps_to_test_result_roundtrip(&ok_input, &ok_ret, &err_ret); + assert(is_ok); + assert(ok_ret.len == 1); + assert(ok_ret.ptr[0].key == 5); + assert(string_eq(&ok_ret.ptr[0].value, "five")); + test_maps_to_test_names_by_id_free(&ok_ret); + + test_maps_to_test_result_names_by_id_string_t err_input; + err_input.is_err = true; + runner_string_dup(&err_input.val.err, "bad input"); + + test_maps_to_test_names_by_id_t ok_ret2; + runner_string_t err_ret2; + is_ok = test_maps_to_test_result_roundtrip(&err_input, &ok_ret2, &err_ret2); + assert(!is_ok); + assert(string_eq(&err_ret2, "bad input")); + runner_string_free(&err_ret2); +} + +static void test_tuple_roundtrip(void) { + test_maps_to_test_names_by_id_entry_t entries[1]; + entries[0].key = 7; + runner_string_dup(&entries[0].value, "seven"); + + runner_tuple2_names_by_id_u64_t input; + input.f0.ptr = entries; + input.f0.len = 1; + input.f1 = 42; + + runner_tuple2_names_by_id_u64_t result; + test_maps_to_test_tuple_roundtrip(&input, &result); + + assert(result.f0.len == 1); + assert(result.f0.ptr[0].key == 7); + assert(string_eq(&result.f0.ptr[0].value, "seven")); + assert(result.f1 == 42); + runner_tuple2_names_by_id_u64_free(&result); +} + +static void test_single_entry_roundtrip(void) { + test_maps_to_test_names_by_id_entry_t entries[1]; + entries[0].key = 99; + runner_string_dup(&entries[0].value, "ninety-nine"); + + test_maps_to_test_names_by_id_t input = {.ptr = entries, .len = 1}; + test_maps_to_test_names_by_id_t result; + test_maps_to_test_single_entry_roundtrip(&input, &result); + + assert(result.len == 1); + assert(result.ptr[0].key == 99); + assert(string_eq(&result.ptr[0].value, "ninety-nine")); + test_maps_to_test_names_by_id_free(&result); +} + +void exports_runner_run(void) { + test_named_roundtrip(); + test_bytes_roundtrip(); + test_empty_roundtrip(); + test_option_roundtrip(); + test_record_roundtrip(); + test_inline_roundtrip(); + test_large_roundtrip(); + test_multi_param_roundtrip(); + test_nested_roundtrip(); + test_variant_roundtrip(); + test_result_roundtrip(); + test_tuple_roundtrip(); + test_single_entry_roundtrip(); +} diff --git a/tests/runtime/map/test.c b/tests/runtime/map/test.c new file mode 100644 index 000000000..e2781876a --- /dev/null +++ b/tests/runtime/map/test.c @@ -0,0 +1,184 @@ +//@ wasmtime-flags = '-Wcomponent-model-map' + +#include +#include +#include + +#include "test.h" + +static bool string_eq(const test_string_t *s, const char *lit) { + size_t len = strlen(lit); + return s->len == len && memcmp(s->ptr, lit, len) == 0; +} + +void exports_test_maps_to_test_named_roundtrip( + exports_test_maps_to_test_names_by_id_t *a, + exports_test_maps_to_test_ids_by_name_t *ret) { + assert(a->len == 2); + for (size_t i = 0; i < a->len; i++) { + if (a->ptr[i].key == 1) { + assert(string_eq(&a->ptr[i].value, "uno")); + } else if (a->ptr[i].key == 2) { + assert(string_eq(&a->ptr[i].value, "two")); + } else { + assert(0 && "unexpected key"); + } + } + + ret->len = a->len; + ret->ptr = (exports_test_maps_to_test_ids_by_name_entry_t *)malloc( + a->len * sizeof(exports_test_maps_to_test_ids_by_name_entry_t)); + for (size_t i = 0; i < a->len; i++) { + ret->ptr[i].key = a->ptr[i].value; + ret->ptr[i].value = a->ptr[i].key; + } + free(a->ptr); +} + +void exports_test_maps_to_test_bytes_roundtrip( + exports_test_maps_to_test_bytes_by_name_t *a, + exports_test_maps_to_test_bytes_by_name_t *ret) { + assert(a->len == 2); + for (size_t i = 0; i < a->len; i++) { + if (string_eq(&a->ptr[i].key, "hello")) { + assert(a->ptr[i].value.len == 5); + assert(memcmp(a->ptr[i].value.ptr, "world", 5) == 0); + } else if (string_eq(&a->ptr[i].key, "bin")) { + assert(a->ptr[i].value.len == 3); + uint8_t expected[] = {0, 1, 2}; + assert(memcmp(a->ptr[i].value.ptr, expected, 3) == 0); + } else { + assert(0 && "unexpected key"); + } + } + *ret = *a; +} + +void exports_test_maps_to_test_empty_roundtrip( + exports_test_maps_to_test_names_by_id_t *a, + exports_test_maps_to_test_names_by_id_t *ret) { + assert(a->len == 0); + *ret = *a; +} + +void exports_test_maps_to_test_option_roundtrip(test_map_string_option_u32_t *a, + test_map_string_option_u32_t *ret) { + assert(a->len == 2); + bool saw_some = false, saw_none = false; + for (size_t i = 0; i < a->len; i++) { + if (string_eq(&a->ptr[i].key, "some")) { + assert(a->ptr[i].value.is_some); + assert(a->ptr[i].value.val == 42); + saw_some = true; + } else if (string_eq(&a->ptr[i].key, "none")) { + assert(!a->ptr[i].value.is_some); + saw_none = true; + } + } + assert(saw_some && saw_none); + *ret = *a; +} + +void exports_test_maps_to_test_record_roundtrip( + exports_test_maps_to_test_labeled_entry_t *a, + exports_test_maps_to_test_labeled_entry_t *ret) { + assert(string_eq(&a->label, "test-label")); + assert(a->values.len == 2); + for (size_t i = 0; i < a->values.len; i++) { + if (a->values.ptr[i].key == 10) { + assert(string_eq(&a->values.ptr[i].value, "ten")); + } else if (a->values.ptr[i].key == 20) { + assert(string_eq(&a->values.ptr[i].value, "twenty")); + } else { + assert(0 && "unexpected key"); + } + } + *ret = *a; +} + +void exports_test_maps_to_test_inline_roundtrip(test_map_u32_string_t *a, + test_map_string_u32_t *ret) { + ret->len = a->len; + ret->ptr = (test_map_string_u32_entry_t *)malloc( + a->len * sizeof(test_map_string_u32_entry_t)); + for (size_t i = 0; i < a->len; i++) { + ret->ptr[i].key = a->ptr[i].value; + ret->ptr[i].value = a->ptr[i].key; + } + free(a->ptr); +} + +void exports_test_maps_to_test_large_roundtrip( + exports_test_maps_to_test_names_by_id_t *a, + exports_test_maps_to_test_names_by_id_t *ret) { + assert(a->len == 100); + *ret = *a; +} + +void exports_test_maps_to_test_multi_param_roundtrip( + exports_test_maps_to_test_names_by_id_t *a, + exports_test_maps_to_test_bytes_by_name_t *b, + test_tuple2_ids_by_name_bytes_by_name_t *ret) { + assert(a->len == 2); + assert(b->len == 1); + + ret->f0.len = a->len; + ret->f0.ptr = (exports_test_maps_to_test_ids_by_name_entry_t *)malloc( + a->len * sizeof(exports_test_maps_to_test_ids_by_name_entry_t)); + for (size_t i = 0; i < a->len; i++) { + ret->f0.ptr[i].key = a->ptr[i].value; + ret->f0.ptr[i].value = a->ptr[i].key; + } + free(a->ptr); + + ret->f1 = *b; +} + +void exports_test_maps_to_test_nested_roundtrip( + test_map_string_map_u32_string_t *a, test_map_string_map_u32_string_t *ret) { + assert(a->len == 2); + for (size_t i = 0; i < a->len; i++) { + if (string_eq(&a->ptr[i].key, "group-a")) { + assert(a->ptr[i].value.len == 2); + } else if (string_eq(&a->ptr[i].key, "group-b")) { + assert(a->ptr[i].value.len == 1); + } else { + assert(0 && "unexpected outer key"); + } + } + *ret = *a; +} + +void exports_test_maps_to_test_variant_roundtrip( + exports_test_maps_to_test_map_or_string_t *a, + exports_test_maps_to_test_map_or_string_t *ret) { + *ret = *a; +} + +bool exports_test_maps_to_test_result_roundtrip( + exports_test_maps_to_test_result_names_by_id_string_t *a, + exports_test_maps_to_test_names_by_id_t *ret, test_string_t *err) { + if (a->is_err) { + *err = a->val.err; + return false; + } + *ret = a->val.ok; + return true; +} + +void exports_test_maps_to_test_tuple_roundtrip( + test_tuple2_names_by_id_u64_t *a, + test_tuple2_names_by_id_u64_t *ret) { + assert(a->f0.len == 1); + assert(a->f0.ptr[0].key == 7); + assert(string_eq(&a->f0.ptr[0].value, "seven")); + assert(a->f1 == 42); + *ret = *a; +} + +void exports_test_maps_to_test_single_entry_roundtrip( + exports_test_maps_to_test_names_by_id_t *a, + exports_test_maps_to_test_names_by_id_t *ret) { + assert(a->len == 1); + *ret = *a; +}