diff --git a/crates/bindgen-core/src/lib.rs b/crates/bindgen-core/src/lib.rs index 02e1bb307..8ad5ae2d6 100644 --- a/crates/bindgen-core/src/lib.rs +++ b/crates/bindgen-core/src/lib.rs @@ -564,6 +564,7 @@ mod tests { pub trait WorldGenerator { fn generate(&mut self, name: &str, interfaces: &ComponentInterfaces, files: &mut Files) { + self.preprocess(name); for (name, import) in interfaces.imports.iter() { self.import(name, import, files); } @@ -576,6 +577,10 @@ pub trait WorldGenerator { self.finish(name, interfaces, files); } + fn preprocess(&mut self, name: &str) { + drop(name); + } + fn import(&mut self, name: &str, iface: &Interface, files: &mut Files); fn export(&mut self, name: &str, iface: &Interface, files: &mut Files); fn export_default(&mut self, name: &str, iface: &Interface, files: &mut Files); diff --git a/crates/gen-guest-c/Cargo.toml b/crates/gen-guest-c/Cargo.toml index cca39e174..66e55f052 100644 --- a/crates/gen-guest-c/Cargo.toml +++ b/crates/gen-guest-c/Cargo.toml @@ -17,4 +17,4 @@ heck = { workspace = true } clap = { workspace = true, optional = true } [dev-dependencies] -test-helpers = { path = '../test-helpers', default-features = false } +test-helpers = { path = '../test-helpers', default-features = false, features = ['macros'] } diff --git a/crates/gen-guest-c/src/component_type_object.rs b/crates/gen-guest-c/src/component_type_object.rs index 6cab4bfcd..c36f21c82 100644 --- a/crates/gen-guest-c/src/component_type_object.rs +++ b/crates/gen-guest-c/src/component_type_object.rs @@ -3,21 +3,14 @@ use heck::ToSnakeCase; use wasm_encoder::{ CodeSection, CustomSection, Encode, Function, FunctionSection, Module, TypeSection, }; -use wit_bindgen_core::{wit_parser::Interface, Direction}; -use wit_component::ComponentEncoder; +use wit_component::{ComponentEncoder, ComponentInterfaces}; -pub fn linking_symbol(iface: &Interface, direction: Direction) -> String { - format!( - "__component_type_object_force_link_{}_{}", - iface.name.to_snake_case(), - match direction { - Direction::Import => "import", - Direction::Export => "export", - } - ) +pub fn linking_symbol(name: &str) -> String { + let snake = name.to_snake_case(); + format!("__component_type_object_force_link_{snake}") } -pub fn object(iface: &Interface, direction: Direction) -> Result> { +pub fn object(name: &str, interfaces: &ComponentInterfaces) -> Result> { let mut module = Module::new(); // Build a module with one function that's a "dummy function" @@ -31,32 +24,28 @@ pub fn object(iface: &Interface, direction: Direction) -> Result> { code.function(&Function::new([])); module.section(&code); - let mut encoder = ComponentEncoder::default(); - encoder = match direction { - Direction::Import => encoder.imports([iface.clone()])?, - Direction::Export => encoder.interface(iface.clone())?, - }; + let mut encoder = ComponentEncoder::default() + .imports(interfaces.imports.values().cloned())? + .exports(interfaces.exports.values().cloned())?; + + if let Some(default) = &interfaces.default { + encoder = encoder.interface(default.clone())?; + } + let data = encoder .types_only(true) .encode() - .with_context(|| format!("translating interface {} to component type", iface.name))?; + .with_context(|| format!("translating {name} to component type"))?; // The custom section name here must start with "component-type" but // otherwise is attempted to be unique here to ensure that this doesn't get // concatenated to other custom sections by LLD by accident since LLD will // concatenate custom sections of the same name. - let name = format!( - "component-type:{}:{}", - match direction { - Direction::Import => "import", - Direction::Export => "export", - }, - iface.name - ); + let section_name = format!("component-type:{name}",); // Add our custom section module.section(&CustomSection { - name: &name, + name: §ion_name, data: data.as_slice(), }); @@ -69,7 +58,7 @@ pub fn object(iface: &Interface, direction: Direction) -> Result> { subsection.push(0x00); // SYMTAB_FUNCTION 0u32.encode(&mut subsection); // flags 0u32.encode(&mut subsection); // index - linking_symbol(iface, direction).encode(&mut subsection); // name + linking_symbol(name).encode(&mut subsection); // name data.push(0x08); // `WASM_SYMBOL_TABLE` subsection.encode(&mut data); diff --git a/crates/gen-guest-c/src/lib.rs b/crates/gen-guest-c/src/lib.rs index d0cbbea8e..9668182e6 100644 --- a/crates/gen-guest-c/src/lib.rs +++ b/crates/gen-guest-c/src/lib.rs @@ -7,45 +7,21 @@ use std::mem; use wit_bindgen_core::wit_parser::abi::{ AbiVariant, Bindgen, Bitcast, Instruction, LiftLower, WasmType, }; -use wit_bindgen_core::{uwrite, uwriteln, wit_parser::*, Direction, Files, Generator, Ns}; -use wit_component::StringEncoding; +use wit_bindgen_core::{ + uwrite, uwriteln, wit_parser::*, Files, InterfaceGenerator as _, Ns, WorldGenerator, +}; +use wit_component::{ComponentInterfaces, StringEncoding}; #[derive(Default)] -pub struct C { +struct C { src: Source, - in_import: bool, opts: Opts, includes: Vec, - funcs: HashMap>, return_pointer_area_size: usize, return_pointer_area_align: usize, - sizes: SizeAlign, names: Ns, - - // The set of types that are considered public (aka need to be in the - // header file) which are anonymous and we're effectively monomorphizing. - // This is discovered lazily when printing type names. - public_anonymous_types: BTreeSet, - - // This is similar to `public_anonymous_types` where it's discovered - // lazily, but the set here are for private types only used in the - // implementation of functions. These types go in the implementation file, - // not the header file. - private_anonymous_types: BTreeSet, - - // Type definitions for the given `TypeId`. This is printed topologically - // at the end. - types: HashMap, - - has_exports: bool, - has_imports: bool, needs_string: bool, - - direction: Direction, -} - -struct Func { - src: Source, + world: String, } #[derive(Default, Debug, Clone)] @@ -60,10 +36,10 @@ pub struct Opts { } impl Opts { - pub fn build(&self) -> C { - let mut r = C::new(); + pub fn build(&self) -> Box { + let mut r = C::default(); r.opts = self.clone(); - r + Box::new(r) } } @@ -90,738 +66,533 @@ enum Scalar { Type(Type), } -impl C { - pub fn new() -> C { - C::default() +impl WorldGenerator for C { + fn preprocess(&mut self, name: &str) { + self.world = name.to_string(); } - fn abi_variant(dir: Direction) -> AbiVariant { - // This generator uses the obvious direction to ABI variant mapping. - match dir { - Direction::Export => AbiVariant::GuestExport, - Direction::Import => AbiVariant::GuestImport, + fn import(&mut self, name: &str, iface: &Interface, _files: &mut Files) { + let mut gen = self.interface(iface, true); + gen.types(); + + for (i, func) in iface.functions.iter().enumerate() { + if i == 0 { + uwriteln!(gen.src.h_fns, "\n// Imported Functions from `{name}`"); + } + gen.import(func); } + + gen.finish(); + + gen.gen.src.append(&gen.src); } - fn include(&mut self, incl: &str) { - if !self.includes.iter().any(|i| i == incl) { - self.includes.push(format!("#include {}", incl)); + fn export(&mut self, name: &str, iface: &Interface, _files: &mut Files) { + let mut gen = self.interface(iface, false); + gen.types(); + + for (i, func) in iface.functions.iter().enumerate() { + if i == 0 { + uwriteln!(gen.src.h_fns, "\n// Exported Functions from `{name}`"); + } + gen.export(func, false); } + + gen.finish(); + + gen.gen.src.append(&gen.src); } - fn classify_ret(&mut self, iface: &Interface, func: &Function) -> Return { - let mut ret = Return { - return_multiple: false, - scalar: None, - retptrs: Vec::new(), - }; - match func.results.len() { - 0 => ret.scalar = Some(Scalar::Void), - 1 => { - let ty = func.results.iter_types().next().unwrap(); - ret.return_single(iface, ty, ty); - } - _ => { - ret.return_multiple = true; - ret.retptrs.extend(func.results.iter_types().cloned()); + fn export_default(&mut self, name: &str, iface: &Interface, _files: &mut Files) { + let mut gen = self.interface(iface, false); + gen.types(); + + for (i, func) in iface.functions.iter().enumerate() { + if i == 0 { + uwriteln!(gen.src.h_fns, "\n// Exported Functions from `{name}`"); } + gen.export(func, true); } - return ret; + + gen.finish(); + + gen.gen.src.append(&gen.src); } - fn print_sig(&mut self, iface: &Interface, func: &Function) -> CSig { - let name = format!( - "{}_{}", - iface.name.to_snake_case(), - func.name.to_snake_case() + fn finish(&mut self, name: &str, interfaces: &ComponentInterfaces, files: &mut Files) { + let linking_symbol = component_type_object::linking_symbol(name); + self.include(""); + let snake = name.to_snake_case(); + self.include(&format!("\"{snake}.h\"")); + uwrite!( + self.src.c_adapters, + " + extern void {linking_symbol}(void); + void {linking_symbol}_public_use_in_this_compilation_unit(void) {{ + {linking_symbol}(); + }} + ", ); - self.names.insert(&name).expect("duplicate symbols"); - let start = self.src.h_fns.len(); + self.print_intrinsics(); - let ret = self.classify_ret(iface, func); - match &ret.scalar { - None | Some(Scalar::Void) => self.src.h_fns("void"), - Some(Scalar::OptionBool(_id)) => self.src.h_fns("bool"), - Some(Scalar::ResultEnum { err, .. }) => { - self.print_ty(SourceType::HFns, iface, &Type::Id(*err)) - } - Some(Scalar::Type(ty)) => self.print_ty(SourceType::HFns, iface, ty), + if self.needs_string { + self.include(""); + let (strlen, size) = match self.opts.string_encoding { + StringEncoding::UTF8 => (format!("strlen(s)"), 1), + StringEncoding::UTF16 => { + self.include(""); + uwrite!( + self.src.h_helpers, + " + size_t {snake}_string_len(const char16_t* s); + ", + ); + uwrite!( + self.src.c_helpers, + " + size_t {snake}_string_len(const char16_t* s) {{ + char16_t* c = (char16_t*)s; + for (; *c; ++c); + return c-s; + }} + ", + ); + (format!("{snake}_string_len(s)"), 2) + } + StringEncoding::CompactUTF16 => unimplemented!(), + }; + let ty = self.char_type(); + uwrite!( + self.src.h_helpers, + " + void {snake}_string_set({snake}_string_t *ret, const {ty} *s); + void {snake}_string_dup({snake}_string_t *ret, const {ty} *s); + void {snake}_string_free({snake}_string_t *ret);\ + ", + ); + uwrite!( + self.src.c_helpers, + " + void {snake}_string_set({snake}_string_t *ret, const {ty} *s) {{ + ret->ptr = ({ty}*) s; + ret->len = {strlen}; + }} + + void {snake}_string_dup({snake}_string_t *ret, const {ty} *s) {{ + ret->len = {strlen}; + ret->ptr = cabi_realloc(NULL, 0, {size}, ret->len * {size}); + memcpy(ret->ptr, s, ret->len * {size}); + }} + + void {snake}_string_free({snake}_string_t *ret) {{ + if (ret->len > 0) {{ + free(ret->ptr); + }} + ret->ptr = NULL; + ret->len = 0; + }} + ", + ); } - self.src.h_fns(" "); - self.src.h_fns(&name); - self.src.h_fns("("); - let mut params = Vec::new(); - for (i, (name, ty)) in func.params.iter().enumerate() { - if i > 0 { - self.src.h_fns(", "); - } - self.print_ty(SourceType::HFns, iface, ty); - self.src.h_fns(" "); - let pointer = self.is_arg_by_pointer(iface, ty); - if pointer { - self.src.h_fns("*"); - } - let name = name.to_snake_case(); - self.src.h_fns(&name); - params.push((pointer, name)); + + let mut h_str = format!( + "#ifndef __BINDINGS_{0}_H\n\ + #define __BINDINGS_{0}_H\n\ + #ifdef __cplusplus\n\ + extern \"C\" {{\n\ + #endif\n\n", + name.to_shouty_snake_case(), + ); + self.include(""); + self.include(""); + + for include in self.includes.iter() { + uwriteln!(h_str, "#include {include}"); } - let mut retptrs = Vec::new(); - let single_ret = ret.retptrs.len() == 1; - for (i, ty) in ret.retptrs.iter().enumerate() { - if i > 0 || func.params.len() > 0 { - self.src.h_fns(", "); - } - self.print_ty(SourceType::HFns, iface, ty); - self.src.h_fns(" *"); - let name: String = if single_ret { - "ret".into() - } else { - format!("ret{}", i) - }; - self.src.h_fns(&name); - retptrs.push(name); + h_str.push_str("\n"); + + let mut c_str = format!("#include \"{snake}.h\"\n"); + if c_str.len() > 0 { + c_str.push_str("\n"); } - if func.params.len() == 0 && ret.retptrs.len() == 0 { - self.src.h_fns("void"); + c_str.push_str(&self.src.c_fns); + + if self.needs_string { + uwrite!( + h_str, + " + typedef struct {{ + {ty} *ptr; + size_t len; + }} {snake}_string_t; + ", + ty = self.char_type(), + ); + } + if self.src.h_defs.len() > 0 { + h_str.push_str(&self.src.h_defs); } - self.src.h_fns(")"); - let sig = self.src.h_fns[start..].to_string(); - self.src.h_fns(";\n"); + h_str.push_str(&self.src.h_fns); - CSig { - sig, - name, - params, - ret, - retptrs, + if !self.opts.no_helpers && self.src.h_helpers.len() > 0 { + h_str.push_str("\n// Helper Functions\n"); + h_str.push_str(&self.src.h_helpers); + h_str.push_str("\n"); + } + + if !self.opts.no_helpers && self.src.c_helpers.len() > 0 { + c_str.push_str("\n// Helper Functions\n"); + c_str.push_str(self.src.c_helpers.as_mut_string()); + } + + c_str.push_str("\n// Component Adapters\n"); + + // Declare a statically-allocated return area, if needed. We only do + // this for export bindings, because import bindings allocate their + // return-area on the stack. + if self.return_pointer_area_size > 0 { + uwrite!( + c_str, + " + __attribute__((aligned({}))) + static uint8_t RET_AREA[{}]; + ", + self.return_pointer_area_align, + self.return_pointer_area_size, + ); } + c_str.push_str(&self.src.c_adapters); + + h_str.push_str("\n#ifdef __cplusplus\n}\n#endif\n#endif\n"); + + files.push(&format!("{snake}.c"), c_str.as_bytes()); + files.push(&format!("{snake}.h"), h_str.as_bytes()); + files.push( + &format!("{snake}_component_type.o",), + component_type_object::object(name, interfaces) + .unwrap() + .as_slice(), + ); } +} - fn is_arg_by_pointer(&self, iface: &Interface, ty: &Type) -> bool { - match ty { - Type::Id(id) => match &iface.types[*id].kind { - TypeDefKind::Type(t) => self.is_arg_by_pointer(iface, t), - TypeDefKind::Variant(_) => true, - TypeDefKind::Union(_) => true, - TypeDefKind::Option(_) => true, - TypeDefKind::Result(_) => true, - TypeDefKind::Enum(_) => false, - TypeDefKind::Flags(_) => false, - TypeDefKind::Tuple(_) | TypeDefKind::Record(_) | TypeDefKind::List(_) => true, - TypeDefKind::Future(_) => todo!("is_arg_by_pointer for future"), - TypeDefKind::Stream(_) => todo!("is_arg_by_pointer for stream"), - }, - Type::String => true, - _ => false, +impl C { + fn interface<'a>( + &'a mut self, + iface: &'a Interface, + in_import: bool, + ) -> InterfaceGenerator<'a> { + let mut sizes = SizeAlign::default(); + sizes.fill(iface); + InterfaceGenerator { + src: Source::default(), + gen: self, + iface, + sizes, + public_anonymous_types: Default::default(), + private_anonymous_types: Default::default(), + types: Default::default(), + in_import, } } - fn char_type(&self) -> String { + fn include(&mut self, s: &str) { + self.includes.push(s.to_string()); + } + + fn char_type(&self) -> &'static str { match self.opts.string_encoding { - StringEncoding::UTF8 => String::from("char"), - StringEncoding::UTF16 => String::from("char16_t"), + StringEncoding::UTF8 => "char", + StringEncoding::UTF16 => "char16_t", StringEncoding::CompactUTF16 => panic!("Compact UTF16 unsupported"), } } +} - fn type_string(&mut self, iface: &Interface, ty: &Type) -> String { - // Getting a type string happens during codegen, and by default means - // that this is a private type that's being generated. This means we - // want to keep track of new anonymous types that are *only* mentioned - // in methods like this, so we can place those types in the C file - // instead of the header interface file. - let prev = mem::take(&mut self.src.h_defs); - let prev_public = mem::take(&mut self.public_anonymous_types); - let prev_private = mem::take(&mut self.private_anonymous_types); +struct InterfaceGenerator<'a> { + src: Source, + in_import: bool, + gen: &'a mut C, + iface: &'a Interface, + sizes: SizeAlign, - // Print the type, which will collect into the fields that we replaced - // above. - self.print_ty(SourceType::HDefs, iface, ty); + // The set of types that are considered public (aka need to be in the + // header file) which are anonymous and we're effectively monomorphizing. + // This is discovered lazily when printing type names. + public_anonymous_types: BTreeSet, - // Reset our public/private sets back to what they were beforehand. - // Note that `print_ty` always adds to the public set, so we're - // inverting the meaning here by interpreting those as new private - // types. - let new_private = mem::replace(&mut self.public_anonymous_types, prev_public); - assert!(self.private_anonymous_types.is_empty()); - self.private_anonymous_types = prev_private; + // This is similar to `public_anonymous_types` where it's discovered + // lazily, but the set here are for private types only used in the + // implementation of functions. These types go in the implementation file, + // not the header file. + private_anonymous_types: BTreeSet, - // For all new private types found while we printed this type, if the - // type isn't already public then it's a new private type. - for id in new_private { - if !self.public_anonymous_types.contains(&id) { - self.private_anonymous_types.insert(id); - } - } + // Type definitions for the given `TypeId`. This is printed topologically + // at the end. + types: HashMap, +} - mem::replace(&mut self.src.h_defs, prev).into() +impl C { + fn print_intrinsics(&mut self) { + // Note that these intrinsics are declared as `weak` so they can be + // overridden from some other symbol. + self.src.c_fns( + " + __attribute__((weak, export_name(\"cabi_realloc\"))) + void *cabi_realloc(void *ptr, size_t orig_size, size_t org_align, size_t new_size) { + void *ret = realloc(ptr, new_size); + if (!ret) abort(); + return ret; + } + ", + ); } +} - fn print_ty(&mut self, stype: SourceType, iface: &Interface, ty: &Type) { - match ty { - Type::Bool => self.src.print(stype, "bool"), - Type::Char => self.src.print(stype, "uint32_t"), // TODO: better type? - Type::U8 => self.src.print(stype, "uint8_t"), - Type::S8 => self.src.print(stype, "int8_t"), - Type::U16 => self.src.print(stype, "uint16_t"), - Type::S16 => self.src.print(stype, "int16_t"), - Type::U32 => self.src.print(stype, "uint32_t"), - Type::S32 => self.src.print(stype, "int32_t"), - Type::U64 => self.src.print(stype, "uint64_t"), - Type::S64 => self.src.print(stype, "int64_t"), - Type::Float32 => self.src.print(stype, "float"), - Type::Float64 => self.src.print(stype, "double"), +impl Return { + fn return_single(&mut self, iface: &Interface, ty: &Type, orig_ty: &Type) { + let id = match ty { + Type::Id(id) => *id, Type::String => { - self.print_namespace(stype, iface); - self.src.print(stype, "string_t"); - self.needs_string = true; + self.retptrs.push(*orig_ty); + return; } - Type::Id(id) => { - let ty = &iface.types[*id]; - match &ty.name { - Some(name) => { - self.print_namespace(stype, iface); - self.src.print(stype, &name.to_snake_case()); - self.src.print(stype, "_t"); - } - None => match &ty.kind { - TypeDefKind::Type(t) => self.print_ty(stype, iface, t), - _ => { - self.public_anonymous_types.insert(*id); - self.private_anonymous_types.remove(id); - self.print_namespace(stype, iface); - self.print_ty_name(stype, iface, &Type::Id(*id)); - self.src.print(stype, "_t"); - } - }, - } + _ => { + self.scalar = Some(Scalar::Type(*orig_ty)); + return; } - } - } + }; + match &iface.types[id].kind { + TypeDefKind::Type(t) => return self.return_single(iface, t, orig_ty), - fn print_ty_name(&mut self, stype: SourceType, iface: &Interface, ty: &Type) { - match ty { - Type::Bool => self.src.print(stype, "bool"), - Type::Char => self.src.print(stype, "char32"), - Type::U8 => self.src.print(stype, "u8"), - Type::S8 => self.src.print(stype, "s8"), - Type::U16 => self.src.print(stype, "u16"), - Type::S16 => self.src.print(stype, "s16"), - Type::U32 => self.src.print(stype, "u32"), - Type::S32 => self.src.print(stype, "s32"), - Type::U64 => self.src.print(stype, "u64"), - Type::S64 => self.src.print(stype, "s64"), - Type::Float32 => self.src.print(stype, "float32"), - Type::Float64 => self.src.print(stype, "float64"), - Type::String => self.src.print(stype, "string"), - Type::Id(id) => { - let ty = &iface.types[*id]; - if let Some(name) = &ty.name { - return self.src.print(stype, &name.to_snake_case()); - } - match &ty.kind { - TypeDefKind::Type(t) => self.print_ty_name(stype, iface, t), - TypeDefKind::Record(_) - | TypeDefKind::Flags(_) - | TypeDefKind::Enum(_) - | TypeDefKind::Variant(_) - | TypeDefKind::Union(_) => { - unimplemented!() - } - TypeDefKind::Tuple(t) => { - self.src.print(stype, "tuple"); - self.src.print(stype, &t.types.len().to_string()); - for ty in t.types.iter() { - self.src.print(stype, "_"); - self.print_ty_name(stype, iface, ty); + // Flags are returned as their bare values, and enums are scalars + TypeDefKind::Flags(_) | TypeDefKind::Enum(_) => { + self.scalar = Some(Scalar::Type(*orig_ty)); + return; + } + + // Unpack optional returns where a boolean discriminant is + // returned and then the actual type returned is returned + // through a return pointer. + TypeDefKind::Option(ty) => { + self.scalar = Some(Scalar::OptionBool(*ty)); + self.retptrs.push(*ty); + return; + } + + // Unpack `result` returns where `E` looks like an enum + // so we can return that in the scalar return and have `T` get + // returned through the normal returns. + TypeDefKind::Result(r) => { + if let Some(Type::Id(err)) = r.err { + if let TypeDefKind::Enum(enum_) = &iface.types[err].kind { + self.scalar = Some(Scalar::ResultEnum { + err, + max_err: enum_.cases.len(), + }); + if let Some(ok) = r.ok { + self.retptrs.push(ok); } - } - TypeDefKind::Option(ty) => { - self.src.print(stype, "option_"); - self.print_ty_name(stype, iface, ty); - } - TypeDefKind::Result(r) => { - self.src.print(stype, "result_"); - self.print_optional_ty_name(stype, iface, r.ok.as_ref()); - self.src.print(stype, "_"); - self.print_optional_ty_name(stype, iface, r.err.as_ref()); - } - TypeDefKind::List(t) => { - self.src.print(stype, "list_"); - self.print_ty_name(stype, iface, t); - } - TypeDefKind::Future(t) => { - self.src.print(stype, "future_"); - self.print_optional_ty_name(stype, iface, t.as_ref()); - } - TypeDefKind::Stream(s) => { - self.src.print(stype, "stream_"); - self.print_optional_ty_name(stype, iface, s.element.as_ref()); - self.src.print(stype, "_"); - self.print_optional_ty_name(stype, iface, s.end.as_ref()); + return; } } + + // fall through to the return pointer case } + + // These types are always returned indirectly. + TypeDefKind::Tuple(_) + | TypeDefKind::Record(_) + | TypeDefKind::List(_) + | TypeDefKind::Variant(_) + | TypeDefKind::Union(_) => {} + + TypeDefKind::Future(_) => todo!("return_single for future"), + TypeDefKind::Stream(_) => todo!("return_single for stream"), } + + self.retptrs.push(*orig_ty); } +} - fn print_optional_ty_name(&mut self, stype: SourceType, iface: &Interface, ty: Option<&Type>) { - match ty { - Some(ty) => self.print_ty_name(stype, iface, ty), - None => self.src.print(stype, "void"), - } +impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { + fn iface(&self) -> &'a Interface { + self.iface } - fn print_anonymous_type(&mut self, iface: &Interface, ty: TypeId) { + fn type_record(&mut self, id: TypeId, name: &str, record: &Record, docs: &Docs) { let prev = mem::take(&mut self.src.h_defs); - self.src.h_defs("\ntypedef "); - let kind = &iface.types[ty].kind; - match kind { - TypeDefKind::Type(_) - | TypeDefKind::Flags(_) - | TypeDefKind::Record(_) - | TypeDefKind::Enum(_) - | TypeDefKind::Variant(_) - | TypeDefKind::Union(_) => { - unreachable!() - } - TypeDefKind::Tuple(t) => { - self.src.h_defs("struct {\n"); - for (i, t) in t.types.iter().enumerate() { - self.print_ty(SourceType::HDefs, iface, t); - uwriteln!(self.src.h_defs, " f{i};"); - } - self.src.h_defs("}"); - } - TypeDefKind::Option(t) => { - self.src.h_defs("struct {\n"); - self.src.h_defs("bool is_some;\n"); - if !self.is_empty_type(iface, t) { - self.print_ty(SourceType::HDefs, iface, t); - self.src.h_defs(" val;\n"); - } - self.src.h_defs("}"); - } - TypeDefKind::Result(r) => { - self.src.h_defs( - "struct { - bool is_err; - union { - ", - ); - if let Some(ok) = self.get_nonempty_type(iface, r.ok.as_ref()) { - self.print_ty(SourceType::HDefs, iface, ok); - self.src.h_defs(" ok;\n"); - } - if let Some(err) = self.get_nonempty_type(iface, r.err.as_ref()) { - self.print_ty(SourceType::HDefs, iface, err); - self.src.h_defs(" err;\n"); - } - self.src.h_defs("} val;\n"); - self.src.h_defs("}"); - } - TypeDefKind::List(t) => { - self.src.h_defs("struct {\n"); - self.print_ty(SourceType::HDefs, iface, t); - self.src.h_defs(" *ptr;\n"); - self.src.h_defs("size_t len;\n"); - self.src.h_defs("}"); - } - TypeDefKind::Future(_) => todo!("print_anonymous_type for future"), - TypeDefKind::Stream(_) => todo!("print_anonymous_type for stream"), + self.docs(docs); + self.src.h_defs("\ntypedef struct {\n"); + for field in record.fields.iter() { + self.print_ty(SourceType::HDefs, &field.ty); + self.src.h_defs(" "); + self.src.h_defs(&field.name.to_snake_case()); + self.src.h_defs(";\n"); } - self.src.h_defs(" "); - self.print_namespace(SourceType::HDefs, iface); - self.print_ty_name(SourceType::HDefs, iface, &Type::Id(ty)); - self.src.h_defs("_t;\n"); - let type_source = mem::replace(&mut self.src.h_defs, prev); - self.types.insert(ty, type_source); - } + self.src.h_defs("} "); + self.print_typedef_target(name); - fn is_empty_type(&self, iface: &Interface, ty: &Type) -> bool { - let id = match ty { - Type::Id(id) => *id, - _ => return false, - }; - match &iface.types[id].kind { - TypeDefKind::Type(t) => self.is_empty_type(iface, t), - TypeDefKind::Record(r) => r.fields.is_empty(), - TypeDefKind::Tuple(t) => t.types.is_empty(), - _ => false, - } + self.types + .insert(id, mem::replace(&mut self.src.h_defs, prev)); } - fn get_nonempty_type<'o>(&self, iface: &Interface, ty: Option<&'o Type>) -> Option<&'o Type> { - match ty { - Some(ty) => { - if self.is_empty_type(iface, ty) { - None - } else { - Some(ty) - } - } - None => None, + fn type_tuple(&mut self, id: TypeId, name: &str, tuple: &Tuple, docs: &Docs) { + let prev = mem::take(&mut self.src.h_defs); + self.docs(docs); + self.src.h_defs("\ntypedef struct {\n"); + for (i, ty) in tuple.types.iter().enumerate() { + self.print_ty(SourceType::HDefs, ty); + uwriteln!(self.src.h_defs, " f{i};"); } - } + self.src.h_defs("} "); + self.print_typedef_target(name); - fn print_intrinsics(&mut self) { - // Note that these intrinsics are declared as `weak` so they can be - // overridden from some other symbol. - self.src.c_fns( - " - __attribute__((weak, export_name(\"cabi_realloc\"))) - void *cabi_realloc(void *ptr, size_t orig_size, size_t org_align, size_t new_size) { - void *ret = realloc(ptr, new_size); - if (!ret) abort(); - return ret; - } - ", - ); + self.types + .insert(id, mem::replace(&mut self.src.h_defs, prev)); } - fn print_namespace(&mut self, stype: SourceType, iface: &Interface) { - self.src.print(stype, &iface.name.to_snake_case()); - self.src.print(stype, "_"); - } + fn type_flags(&mut self, id: TypeId, name: &str, flags: &Flags, docs: &Docs) { + let prev = mem::take(&mut self.src.h_defs); + self.docs(docs); + self.src.h_defs("\ntypedef "); + let repr = flags_repr(flags); + self.src.h_defs(int_repr(repr)); + self.src.h_defs(" "); + self.print_typedef_target(name); - fn print_dtor(&mut self, iface: &Interface, id: TypeId) { - let ty = Type::Id(id); - if !self.owns_anything(iface, &ty) { - return; + if flags.flags.len() > 0 { + self.src.h_defs("\n"); + } + for (i, flag) in flags.flags.iter().enumerate() { + uwriteln!( + self.src.h_defs, + "#define {}_{}_{} (1 << {})", + self.iface.name.to_shouty_snake_case(), + name.to_shouty_snake_case(), + flag.name.to_shouty_snake_case(), + i, + ); } - let pos = self.src.h_helpers.len(); - self.src.h_helpers("\nvoid "); - self.print_namespace(SourceType::HHelpers, iface); - self.print_ty_name(SourceType::HHelpers, iface, &ty); - self.src.h_helpers("_free("); - self.print_namespace(SourceType::HHelpers, iface); - self.print_ty_name(SourceType::HHelpers, iface, &ty); - self.src.h_helpers("_t *ptr)"); - - self.src.c_helpers(&self.src.h_helpers[pos..].to_string()); - self.src.h_helpers(";"); - self.src.c_helpers(" {\n"); - match &iface.types[id].kind { - TypeDefKind::Type(t) => self.free(iface, t, "ptr"), - TypeDefKind::Flags(_) => {} - TypeDefKind::Enum(_) => {} + self.types + .insert(id, mem::replace(&mut self.src.h_defs, prev)); + } - TypeDefKind::Record(r) => { - for field in r.fields.iter() { - if !self.owns_anything(iface, &field.ty) { - continue; - } - self.free( - iface, - &field.ty, - &format!("&ptr->{}", field.name.to_snake_case()), - ); - } + fn type_variant(&mut self, id: TypeId, name: &str, variant: &Variant, docs: &Docs) { + let prev = mem::take(&mut self.src.h_defs); + self.docs(docs); + self.src.h_defs("\ntypedef struct {\n"); + self.src.h_defs(int_repr(variant.tag())); + self.src.h_defs(" tag;\n"); + self.src.h_defs("union {\n"); + for case in variant.cases.iter() { + if let Some(ty) = self.get_nonempty_type(case.ty.as_ref()) { + self.print_ty(SourceType::HDefs, ty); + self.src.h_defs(" "); + self.src.h_defs(&case.name.to_snake_case()); + self.src.h_defs(";\n"); } - - TypeDefKind::Tuple(t) => { - for (i, ty) in t.types.iter().enumerate() { - if !self.owns_anything(iface, ty) { - continue; - } - self.free(iface, ty, &format!("&ptr->f{i}")); - } - } - - TypeDefKind::List(t) => { - if self.owns_anything(iface, t) { - self.src - .c_helpers("for (size_t i = 0; i < ptr->len; i++) {\n"); - self.free(iface, t, "&ptr->ptr[i]"); - self.src.c_helpers("}\n"); - } - uwriteln!(self.src.c_helpers, "if (ptr->len > 0) {{"); - uwriteln!(self.src.c_helpers, "free(ptr->ptr);"); - uwriteln!(self.src.c_helpers, "}}"); - } - - TypeDefKind::Variant(v) => { - self.src.c_helpers("switch ((int32_t) ptr->tag) {\n"); - for (i, case) in v.cases.iter().enumerate() { - if let Some(ty) = &case.ty { - if !self.owns_anything(iface, ty) { - continue; - } - uwriteln!(self.src.c_helpers, "case {}: {{", i); - let expr = format!("&ptr->val.{}", case.name.to_snake_case()); - if let Some(ty) = &case.ty { - self.free(iface, ty, &expr); - } - self.src.c_helpers("break;\n"); - self.src.c_helpers("}\n"); - } - } - self.src.c_helpers("}\n"); - } - - TypeDefKind::Union(u) => { - self.src.c_helpers("switch ((int32_t) ptr->tag) {\n"); - for (i, case) in u.cases.iter().enumerate() { - if !self.owns_anything(iface, &case.ty) { - continue; - } - uwriteln!(self.src.c_helpers, "case {i}: {{"); - let expr = format!("&ptr->val.f{i}"); - self.free(iface, &case.ty, &expr); - self.src.c_helpers("break;\n"); - self.src.c_helpers("}\n"); - } - self.src.c_helpers("}\n"); - } - - TypeDefKind::Option(t) => { - self.src.c_helpers("if (ptr->is_some) {\n"); - self.free(iface, t, "&ptr->val"); - self.src.c_helpers("}\n"); - } - - TypeDefKind::Result(r) => { - self.src.c_helpers("if (!ptr->is_err) {\n"); - if let Some(ok) = &r.ok { - if self.owns_anything(iface, ok) { - self.free(iface, ok, "&ptr->val.ok"); - } - } - if let Some(err) = &r.err { - if self.owns_anything(iface, err) { - self.src.c_helpers("} else {\n"); - self.free(iface, err, "&ptr->val.err"); - } - } - self.src.c_helpers("}\n"); - } - TypeDefKind::Future(_) => todo!("print_dtor for future"), - TypeDefKind::Stream(_) => todo!("print_dtor for stream"), } - self.src.c_helpers("}\n"); - } + self.src.h_defs("} val;\n"); + self.src.h_defs("} "); + self.print_typedef_target(name); - fn owns_anything(&self, iface: &Interface, ty: &Type) -> bool { - let id = match ty { - Type::Id(id) => *id, - Type::String => return true, - _ => return false, - }; - match &iface.types[id].kind { - TypeDefKind::Type(t) => self.owns_anything(iface, t), - TypeDefKind::Record(r) => r.fields.iter().any(|t| self.owns_anything(iface, &t.ty)), - TypeDefKind::Tuple(t) => t.types.iter().any(|t| self.owns_anything(iface, t)), - TypeDefKind::Flags(_) => false, - TypeDefKind::Enum(_) => false, - TypeDefKind::List(_) => true, - TypeDefKind::Variant(v) => v - .cases - .iter() - .any(|c| self.optional_owns_anything(iface, c.ty.as_ref())), - TypeDefKind::Union(v) => v - .cases - .iter() - .any(|case| self.owns_anything(iface, &case.ty)), - TypeDefKind::Option(t) => self.owns_anything(iface, t), - TypeDefKind::Result(r) => { - self.optional_owns_anything(iface, r.ok.as_ref()) - || self.optional_owns_anything(iface, r.err.as_ref()) - } - TypeDefKind::Future(_) => todo!("owns_anything for future"), - TypeDefKind::Stream(_) => todo!("owns_anything for stream"), + if variant.cases.len() > 0 { + self.src.h_defs("\n"); } - } - - fn optional_owns_anything(&self, iface: &Interface, ty: Option<&Type>) -> bool { - match ty { - Some(ty) => self.owns_anything(iface, ty), - None => false, + for (i, case) in variant.cases.iter().enumerate() { + uwriteln!( + self.src.h_defs, + "#define {}_{}_{} {}", + self.iface.name.to_shouty_snake_case(), + name.to_shouty_snake_case(), + case.name.to_shouty_snake_case(), + i, + ); } - } - - fn free(&mut self, iface: &Interface, ty: &Type, expr: &str) { - let prev = mem::take(&mut self.src.h_helpers); - self.print_namespace(SourceType::HHelpers, iface); - self.print_ty_name(SourceType::HHelpers, iface, ty); - let name = mem::replace(&mut self.src.h_helpers, prev); - - self.src.c_helpers(&name); - self.src.c_helpers("_free("); - self.src.c_helpers(expr); - self.src.c_helpers(");\n"); - } - fn docs(&mut self, docs: &Docs) { - let docs = match &docs.contents { - Some(docs) => docs, - None => return, - }; - for line in docs.trim().lines() { - self.src.h_defs("// "); - self.src.h_defs(line); - self.src.h_defs("\n"); - } + self.types + .insert(id, mem::replace(&mut self.src.h_defs, prev)); } -} - -impl Return { - fn return_single(&mut self, iface: &Interface, ty: &Type, orig_ty: &Type) { - let id = match ty { - Type::Id(id) => *id, - Type::String => { - self.retptrs.push(*orig_ty); - return; - } - _ => { - self.scalar = Some(Scalar::Type(*orig_ty)); - return; - } - }; - match &iface.types[id].kind { - TypeDefKind::Type(t) => return self.return_single(iface, t, orig_ty), - - // Flags are returned as their bare values, and enums are scalars - TypeDefKind::Flags(_) | TypeDefKind::Enum(_) => { - self.scalar = Some(Scalar::Type(*orig_ty)); - return; - } - - // Unpack optional returns where a boolean discriminant is - // returned and then the actual type returned is returned - // through a return pointer. - TypeDefKind::Option(ty) => { - self.scalar = Some(Scalar::OptionBool(*ty)); - self.retptrs.push(*ty); - return; - } - - // Unpack `result` returns where `E` looks like an enum - // so we can return that in the scalar return and have `T` get - // returned through the normal returns. - TypeDefKind::Result(r) => { - if let Some(Type::Id(err)) = r.err { - if let TypeDefKind::Enum(enum_) = &iface.types[err].kind { - self.scalar = Some(Scalar::ResultEnum { - err, - max_err: enum_.cases.len(), - }); - if let Some(ok) = r.ok { - self.retptrs.push(ok); - } - return; - } - } - - // fall through to the return pointer case - } - // These types are always returned indirectly. - TypeDefKind::Tuple(_) - | TypeDefKind::Record(_) - | TypeDefKind::List(_) - | TypeDefKind::Variant(_) - | TypeDefKind::Union(_) => {} - - TypeDefKind::Future(_) => todo!("return_single for future"), - TypeDefKind::Stream(_) => todo!("return_single for stream"), + fn type_union(&mut self, id: TypeId, name: &str, union: &Union, docs: &Docs) { + let prev = mem::take(&mut self.src.h_defs); + self.docs(docs); + self.src.h_defs("\ntypedef struct {\n"); + self.src.h_defs(int_repr(union.tag())); + self.src.h_defs(" tag;\n"); + self.src.h_defs("union {\n"); + for (i, case) in union.cases.iter().enumerate() { + self.print_ty(SourceType::HDefs, &case.ty); + uwriteln!(self.src.h_defs, " f{i};"); } + self.src.h_defs("} val;\n"); + self.src.h_defs("} "); + self.print_typedef_target(name); - self.retptrs.push(*orig_ty); - } -} - -impl Generator for C { - fn preprocess_one(&mut self, iface: &Interface, dir: Direction) { - self.direction = dir; - let variant = Self::abi_variant(dir); - self.sizes.fill(iface); - self.in_import = variant == AbiVariant::GuestImport; + self.types + .insert(id, mem::replace(&mut self.src.h_defs, prev)); } - fn type_record( - &mut self, - iface: &Interface, - id: TypeId, - name: &str, - record: &Record, - docs: &Docs, - ) { + fn type_option(&mut self, id: TypeId, name: &str, payload: &Type, docs: &Docs) { let prev = mem::take(&mut self.src.h_defs); self.docs(docs); - self.names.insert(&name.to_snake_case()).unwrap(); self.src.h_defs("\ntypedef struct {\n"); - for field in record.fields.iter() { - self.print_ty(SourceType::HDefs, iface, &field.ty); - self.src.h_defs(" "); - self.src.h_defs(&field.name.to_snake_case()); - self.src.h_defs(";\n"); + self.src.h_defs("bool is_some;\n"); + if !self.is_empty_type(payload) { + self.print_ty(SourceType::HDefs, payload); + self.src.h_defs(" val;\n"); } self.src.h_defs("} "); - self.print_namespace(SourceType::HDefs, iface); - self.src.h_defs(&name.to_snake_case()); - self.src.h_defs("_t;\n"); + self.print_typedef_target(name); self.types .insert(id, mem::replace(&mut self.src.h_defs, prev)); } - fn type_tuple( - &mut self, - iface: &Interface, - id: TypeId, - name: &str, - tuple: &Tuple, - docs: &Docs, - ) { + fn type_result(&mut self, id: TypeId, name: &str, result: &Result_, docs: &Docs) { let prev = mem::take(&mut self.src.h_defs); self.docs(docs); - self.names.insert(&name.to_snake_case()).unwrap(); self.src.h_defs("\ntypedef struct {\n"); - for (i, ty) in tuple.types.iter().enumerate() { - self.print_ty(SourceType::HDefs, iface, ty); - uwriteln!(self.src.h_defs, " f{i};"); + self.src.h_defs("bool is_err;\n"); + self.src.h_defs("union {\n"); + if let Some(ok) = self.get_nonempty_type(result.ok.as_ref()) { + self.print_ty(SourceType::HDefs, ok); + self.src.h_defs(" ok;\n"); + } + if let Some(err) = self.get_nonempty_type(result.err.as_ref()) { + self.print_ty(SourceType::HDefs, err); + self.src.h_defs(" err;\n"); } + self.src.h_defs("} val;\n"); self.src.h_defs("} "); - self.print_namespace(SourceType::HDefs, iface); - self.src.h_defs(&name.to_snake_case()); - self.src.h_defs("_t;\n"); + self.print_typedef_target(name); self.types .insert(id, mem::replace(&mut self.src.h_defs, prev)); } - fn type_flags( - &mut self, - iface: &Interface, - id: TypeId, - name: &str, - flags: &Flags, - docs: &Docs, - ) { + fn type_enum(&mut self, id: TypeId, name: &str, enum_: &Enum, docs: &Docs) { let prev = mem::take(&mut self.src.h_defs); self.docs(docs); - self.names.insert(&name.to_snake_case()).unwrap(); self.src.h_defs("\ntypedef "); - let repr = flags_repr(flags); - self.src.h_defs(int_repr(repr)); + self.src.h_defs(int_repr(enum_.tag())); self.src.h_defs(" "); - self.print_namespace(SourceType::HDefs, iface); - self.src.h_defs(&name.to_snake_case()); - self.src.h_defs("_t;\n"); + self.print_typedef_target(name); - if flags.flags.len() > 0 { + if enum_.cases.len() > 0 { self.src.h_defs("\n"); } - for (i, flag) in flags.flags.iter().enumerate() { + for (i, case) in enum_.cases.iter().enumerate() { uwriteln!( self.src.h_defs, - "#define {}_{}_{} (1 << {})", - iface.name.to_shouty_snake_case(), + "#define {}_{}_{} {}", + self.iface.name.to_shouty_snake_case(), name.to_shouty_snake_case(), - flag.name.to_shouty_snake_case(), + case.name.to_shouty_snake_case(), i, ); } @@ -830,209 +601,39 @@ impl Generator for C { .insert(id, mem::replace(&mut self.src.h_defs, prev)); } - fn type_variant( - &mut self, - iface: &Interface, - id: TypeId, - name: &str, - variant: &Variant, - docs: &Docs, - ) { + fn type_alias(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs) { let prev = mem::take(&mut self.src.h_defs); self.docs(docs); - self.names.insert(&name.to_snake_case()).unwrap(); - self.src.h_defs("\ntypedef struct {\n"); - self.src.h_defs(int_repr(variant.tag())); - self.src.h_defs(" tag;\n"); - self.src.h_defs("union {\n"); - for case in variant.cases.iter() { - if let Some(ty) = self.get_nonempty_type(iface, case.ty.as_ref()) { - self.print_ty(SourceType::HDefs, iface, ty); - self.src.h_defs(" "); - self.src.h_defs(&case.name.to_snake_case()); - self.src.h_defs(";\n"); - } - } - self.src.h_defs("} val;\n"); - self.src.h_defs("} "); - self.print_namespace(SourceType::HDefs, iface); - self.src.h_defs(&name.to_snake_case()); - self.src.h_defs("_t;\n"); + self.src.h_defs("\ntypedef "); + self.print_ty(SourceType::HDefs, ty); + self.src.h_defs(" "); + self.print_typedef_target(name); + self.types + .insert(id, mem::replace(&mut self.src.h_defs, prev)); + } - if variant.cases.len() > 0 { - self.src.h_defs("\n"); - } - for (i, case) in variant.cases.iter().enumerate() { - uwriteln!( - self.src.h_defs, - "#define {}_{}_{} {}", - iface.name.to_shouty_snake_case(), - name.to_shouty_snake_case(), - case.name.to_shouty_snake_case(), - i, - ); - } - - self.types - .insert(id, mem::replace(&mut self.src.h_defs, prev)); - } - - fn type_union( - &mut self, - iface: &Interface, - id: TypeId, - name: &str, - union: &Union, - docs: &Docs, - ) { + fn type_list(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs) { let prev = mem::take(&mut self.src.h_defs); self.docs(docs); - self.names.insert(&name.to_snake_case()).unwrap(); self.src.h_defs("\ntypedef struct {\n"); - self.src.h_defs(int_repr(union.tag())); - self.src.h_defs(" tag;\n"); - self.src.h_defs("union {\n"); - for (i, case) in union.cases.iter().enumerate() { - self.print_ty(SourceType::HDefs, iface, &case.ty); - uwriteln!(self.src.h_defs, " f{i};"); - } - self.src.h_defs("} val;\n"); - self.src.h_defs("} "); - self.print_namespace(SourceType::HDefs, iface); - self.src.h_defs(&name.to_snake_case()); - self.src.h_defs("_t;\n"); - - self.types - .insert(id, mem::replace(&mut self.src.h_defs, prev)); - } - - fn type_option( - &mut self, - iface: &Interface, - id: TypeId, - name: &str, - payload: &Type, - docs: &Docs, - ) { - let prev = mem::take(&mut self.src.h_defs); - self.docs(docs); - self.names.insert(&name.to_snake_case()).unwrap(); - self.src.h_defs("\ntypedef struct {\n"); - self.src.h_defs("bool is_some;\n"); - if !self.is_empty_type(iface, payload) { - self.print_ty(SourceType::HDefs, iface, payload); - self.src.h_defs(" val;\n"); - } - self.src.h_defs("} "); - self.print_namespace(SourceType::HDefs, iface); - self.src.h_defs(&name.to_snake_case()); - self.src.h_defs("_t;\n"); - - self.types - .insert(id, mem::replace(&mut self.src.h_defs, prev)); - } - - fn type_result( - &mut self, - iface: &Interface, - id: TypeId, - name: &str, - result: &Result_, - docs: &Docs, - ) { - let prev = mem::take(&mut self.src.h_defs); - self.docs(docs); - self.names.insert(&name.to_snake_case()).unwrap(); - self.src.h_defs("\ntypedef struct {\n"); - self.src.h_defs("bool is_err;\n"); - self.src.h_defs("union {\n"); - if let Some(ok) = self.get_nonempty_type(iface, result.ok.as_ref()) { - self.print_ty(SourceType::HDefs, iface, ok); - self.src.h_defs(" ok;\n"); - } - if let Some(err) = self.get_nonempty_type(iface, result.err.as_ref()) { - self.print_ty(SourceType::HDefs, iface, err); - self.src.h_defs(" err;\n"); - } - self.src.h_defs("} val;\n"); - self.src.h_defs("} "); - self.print_namespace(SourceType::HDefs, iface); - self.src.h_defs(&name.to_snake_case()); - self.src.h_defs("_t;\n"); - - self.types - .insert(id, mem::replace(&mut self.src.h_defs, prev)); - } - - fn type_enum(&mut self, iface: &Interface, id: TypeId, name: &str, enum_: &Enum, docs: &Docs) { - let prev = mem::take(&mut self.src.h_defs); - self.docs(docs); - self.names.insert(&name.to_snake_case()).unwrap(); - self.src.h_defs("\ntypedef "); - self.src.h_defs(int_repr(enum_.tag())); - self.src.h_defs(" "); - self.print_namespace(SourceType::HDefs, iface); - self.src.h_defs(&name.to_snake_case()); - self.src.h_defs("_t;\n"); - - if enum_.cases.len() > 0 { - self.src.h_defs("\n"); - } - for (i, case) in enum_.cases.iter().enumerate() { - uwriteln!( - self.src.h_defs, - "#define {}_{}_{} {}", - iface.name.to_shouty_snake_case(), - name.to_shouty_snake_case(), - case.name.to_shouty_snake_case(), - i, - ); - } - - self.types - .insert(id, mem::replace(&mut self.src.h_defs, prev)); - } - - fn type_alias(&mut self, iface: &Interface, id: TypeId, name: &str, ty: &Type, docs: &Docs) { - let prev = mem::take(&mut self.src.h_defs); - self.docs(docs); - self.src.h_defs("\ntypedef "); - self.print_ty(SourceType::HDefs, iface, ty); - self.src.h_defs(" "); - self.print_namespace(SourceType::HDefs, iface); - self.src.h_defs(&name.to_snake_case()); - self.src.h_defs("_t;\n"); - self.types - .insert(id, mem::replace(&mut self.src.h_defs, prev)); - } - - fn type_list(&mut self, iface: &Interface, id: TypeId, name: &str, ty: &Type, docs: &Docs) { - let prev = mem::take(&mut self.src.h_defs); - self.docs(docs); - self.src.h_defs("\ntypedef struct {\n"); - self.print_ty(SourceType::HDefs, iface, ty); + self.print_ty(SourceType::HDefs, ty); self.src.h_defs(" *ptr;\n"); self.src.h_defs("size_t len;\n"); self.src.h_defs("} "); - self.print_namespace(SourceType::HDefs, iface); - self.src.h_defs(&name.to_snake_case()); - self.src.h_defs("_t;\n"); + self.print_typedef_target(name); self.types .insert(id, mem::replace(&mut self.src.h_defs, prev)); } - fn type_builtin(&mut self, iface: &Interface, _id: TypeId, name: &str, ty: &Type, docs: &Docs) { - drop((iface, _id, name, ty, docs)); + fn type_builtin(&mut self, _id: TypeId, name: &str, ty: &Type, docs: &Docs) { + drop((_id, name, ty, docs)); } +} - fn import(&mut self, iface: &Interface, func: &Function) { - let prev = mem::take(&mut self.src); - let sig = iface.wasm_signature(AbiVariant::GuestImport, func); +impl InterfaceGenerator<'_> { + fn import(&mut self, func: &Function) { + let sig = self.iface.wasm_signature(AbiVariant::GuestImport, func); - if !self.has_imports { - self.has_imports = true; - self.src.h_fns("\n// Imported Functions\n"); - } self.src.h_fns("\n"); // In the private C file, print a function declaration which is the @@ -1041,12 +642,12 @@ impl Generator for C { uwriteln!( self.src.h_fns, "__attribute__((import_module(\"{}\"), import_name(\"{}\")))", - iface.name, + self.iface.name, func.name ); - let import_name = self.names.tmp(&format!( + let import_name = self.gen.names.tmp(&format!( "__wasm_import_{}_{}", - iface.name.to_snake_case(), + self.iface.name.to_snake_case(), func.name.to_snake_case() )); match sig.results.len() { @@ -1070,7 +671,7 @@ impl Generator for C { // Print the public facing signature into the header, and since that's // what we are defining also print it into the C file. - let c_sig = self.print_sig(iface, func); + let c_sig = self.print_sig(func); self.src.c_adapters("\n"); self.src.c_adapters(&c_sig.sig); self.src.c_adapters(" {\n"); @@ -1088,7 +689,7 @@ impl Generator for C { for ptr in f.sig.retptrs.iter() { f.locals.insert(ptr).unwrap(); } - iface.call( + f.gen.iface.call( AbiVariant::GuestImport, LiftLower::LowerArgsLiftResults, func, @@ -1099,31 +700,18 @@ impl Generator for C { self.src.c_adapters(&String::from(src)); self.src.c_adapters("}\n"); - - let src = mem::replace(&mut self.src, prev); - self.funcs - .entry(iface.name.to_string()) - .or_insert(Vec::new()) - .push(Func { src }); } - fn export(&mut self, iface: &Interface, func: &Function) { - let prev = mem::take(&mut self.src); - - if !self.has_exports { - self.has_exports = true; - self.src.h_fns("\n// Exported Functions\n\n"); - } - - let sig = iface.wasm_signature(AbiVariant::GuestExport, func); + fn export(&mut self, func: &Function, is_default: bool) { + let sig = self.iface.wasm_signature(AbiVariant::GuestExport, func); // Currently the C generator always emits default exports // This needs to change once the generator works from a world - let export_name = iface.core_export_name(true, func); + let export_name = self.iface.core_export_name(is_default, func); // Print the actual header for this function into the header file, and // it's what we'll be calling. - let c_sig = self.print_sig(iface, func); + let c_sig = self.print_sig(func); // Generate, in the C source file, the raw wasm signature that has the // canonical ABI. @@ -1131,9 +719,9 @@ impl Generator for C { self.src.c_adapters, "__attribute__((export_name(\"{export_name}\")))" ); - let import_name = self.names.tmp(&format!( + let import_name = self.gen.names.tmp(&format!( "__wasm_export_{}_{}", - iface.name.to_snake_case(), + self.iface.name.to_snake_case(), func.name.to_snake_case() )); @@ -1160,7 +748,7 @@ impl Generator for C { f.gen.src.c_adapters(") {\n"); // Perform all lifting/lowering and append it to our src. - iface.call( + f.gen.iface.call( AbiVariant::GuestExport, LiftLower::LiftArgsLowerResults, func, @@ -1170,7 +758,7 @@ impl Generator for C { self.src.c_adapters(&src); self.src.c_adapters("}\n"); - if iface.guest_export_needs_post_return(func) { + if self.iface.guest_export_needs_post_return(func) { uwriteln!( self.src.c_fns, "__attribute__((export_name(\"cabi_post_{export_name}\")))" @@ -1195,242 +783,612 @@ impl Generator for C { c_sig.params.push((false, name.clone())); params.push(name); } - self.src.c_fns.push_str(") {\n"); - - let mut f = FunctionBindgen::new(self, c_sig, &import_name); - f.params = params; - iface.post_return(func, &mut f); - let FunctionBindgen { src, .. } = f; - self.src.c_fns(&src); - self.src.c_fns("}\n"); + self.src.c_fns.push_str(") {\n"); + + let mut f = FunctionBindgen::new(self, c_sig, &import_name); + f.params = params; + f.gen.iface.post_return(func, &mut f); + let FunctionBindgen { src, .. } = f; + self.src.c_fns(&src); + self.src.c_fns("}\n"); + } + } + + fn finish(&mut self) { + // Continuously generate anonymous types while we continue to find more + // + // First we take care of the public set of anonymous types. This will + // iteratively print them and also remove any references from the + // private set if we happen to also reference them. + while !self.public_anonymous_types.is_empty() { + for ty in mem::take(&mut self.public_anonymous_types) { + self.print_anonymous_type(ty); + } + } + + // Next we take care of private types. To do this we have basically the + // same loop as above, after we switch the sets. We record, however, + // all private types in a local set here to later determine if the type + // needs to be in the C file or the H file. + // + // Note though that we don't re-print a type (and consider it private) + // if we already printed it above as part of the public set. + let mut private_types = HashSet::new(); + self.public_anonymous_types = mem::take(&mut self.private_anonymous_types); + while !self.public_anonymous_types.is_empty() { + for ty in mem::take(&mut self.public_anonymous_types) { + if self.types.contains_key(&ty) { + continue; + } + private_types.insert(ty); + self.print_anonymous_type(ty); + } + } + + for id in self.iface.topological_types() { + if let Some(ty) = self.types.get(&id) { + if private_types.contains(&id) { + self.src.h_defs(ty); + } else { + self.src.h_defs(ty); + self.print_dtor(id); + } + } + } + } + + fn print_sig(&mut self, func: &Function) -> CSig { + let name = format!( + "{}_{}", + self.iface.name.to_snake_case(), + func.name.to_snake_case() + ); + self.gen.names.insert(&name).expect("duplicate symbols"); + + let start = self.src.h_fns.len(); + + let ret = self.classify_ret(func); + match &ret.scalar { + None | Some(Scalar::Void) => self.src.h_fns("void"), + Some(Scalar::OptionBool(_id)) => self.src.h_fns("bool"), + Some(Scalar::ResultEnum { err, .. }) => { + self.print_ty(SourceType::HFns, &Type::Id(*err)) + } + Some(Scalar::Type(ty)) => self.print_ty(SourceType::HFns, ty), + } + self.src.h_fns(" "); + self.src.h_fns(&name); + self.src.h_fns("("); + let mut params = Vec::new(); + for (i, (name, ty)) in func.params.iter().enumerate() { + if i > 0 { + self.src.h_fns(", "); + } + self.print_ty(SourceType::HFns, ty); + self.src.h_fns(" "); + let pointer = self.is_arg_by_pointer(ty); + if pointer { + self.src.h_fns("*"); + } + let name = name.to_snake_case(); + self.src.h_fns(&name); + params.push((pointer, name)); + } + let mut retptrs = Vec::new(); + let single_ret = ret.retptrs.len() == 1; + for (i, ty) in ret.retptrs.iter().enumerate() { + if i > 0 || func.params.len() > 0 { + self.src.h_fns(", "); + } + self.print_ty(SourceType::HFns, ty); + self.src.h_fns(" *"); + let name: String = if single_ret { + "ret".into() + } else { + format!("ret{}", i) + }; + self.src.h_fns(&name); + retptrs.push(name); + } + if func.params.len() == 0 && ret.retptrs.len() == 0 { + self.src.h_fns("void"); + } + self.src.h_fns(")"); + + let sig = self.src.h_fns[start..].to_string(); + self.src.h_fns(";\n"); + + CSig { + sig, + name, + params, + ret, + retptrs, + } + } + + fn classify_ret(&mut self, func: &Function) -> Return { + let mut ret = Return { + return_multiple: false, + scalar: None, + retptrs: Vec::new(), + }; + match func.results.len() { + 0 => ret.scalar = Some(Scalar::Void), + 1 => { + let ty = func.results.iter_types().next().unwrap(); + ret.return_single(self.iface, ty, ty); + } + _ => { + ret.return_multiple = true; + ret.retptrs.extend(func.results.iter_types().cloned()); + } + } + return ret; + } + + fn is_arg_by_pointer(&self, ty: &Type) -> bool { + match ty { + Type::Id(id) => match &self.iface.types[*id].kind { + TypeDefKind::Type(t) => self.is_arg_by_pointer(t), + TypeDefKind::Variant(_) => true, + TypeDefKind::Union(_) => true, + TypeDefKind::Option(_) => true, + TypeDefKind::Result(_) => true, + TypeDefKind::Enum(_) => false, + TypeDefKind::Flags(_) => false, + TypeDefKind::Tuple(_) | TypeDefKind::Record(_) | TypeDefKind::List(_) => true, + TypeDefKind::Future(_) => todo!("is_arg_by_pointer for future"), + TypeDefKind::Stream(_) => todo!("is_arg_by_pointer for stream"), + }, + Type::String => true, + _ => false, + } + } + + fn print_typedef_target(&mut self, name: &str) { + let iface_snake = self.iface.name.to_snake_case(); + let snake = name.to_snake_case(); + self.print_namespace(SourceType::HDefs); + self.src.h_defs(&snake); + self.src.h_defs("_t;\n"); + self.gen + .names + .insert(&format!("{iface_snake}_{snake}_t")) + .unwrap(); + } + + fn print_namespace(&mut self, stype: SourceType) { + self.src.print(stype, &self.iface.name.to_snake_case()); + self.src.print(stype, "_"); + } + + fn print_ty(&mut self, stype: SourceType, ty: &Type) { + match ty { + Type::Bool => self.src.print(stype, "bool"), + Type::Char => self.src.print(stype, "uint32_t"), // TODO: better type? + Type::U8 => self.src.print(stype, "uint8_t"), + Type::S8 => self.src.print(stype, "int8_t"), + Type::U16 => self.src.print(stype, "uint16_t"), + Type::S16 => self.src.print(stype, "int16_t"), + Type::U32 => self.src.print(stype, "uint32_t"), + Type::S32 => self.src.print(stype, "int32_t"), + Type::U64 => self.src.print(stype, "uint64_t"), + Type::S64 => self.src.print(stype, "int64_t"), + Type::Float32 => self.src.print(stype, "float"), + Type::Float64 => self.src.print(stype, "double"), + Type::String => { + self.src.print(stype, &self.gen.world.to_snake_case()); + self.src.print(stype, "_"); + self.src.print(stype, "string_t"); + self.gen.needs_string = true; + } + Type::Id(id) => { + let ty = &self.iface.types[*id]; + match &ty.name { + Some(name) => { + self.print_namespace(stype); + self.src.print(stype, &name.to_snake_case()); + self.src.print(stype, "_t"); + } + None => match &ty.kind { + TypeDefKind::Type(t) => self.print_ty(stype, t), + _ => { + self.public_anonymous_types.insert(*id); + self.private_anonymous_types.remove(id); + self.print_namespace(stype); + self.print_ty_name(stype, &Type::Id(*id)); + self.src.print(stype, "_t"); + } + }, + } + } + } + } + + fn print_ty_name(&mut self, stype: SourceType, ty: &Type) { + match ty { + Type::Bool => self.src.print(stype, "bool"), + Type::Char => self.src.print(stype, "char32"), + Type::U8 => self.src.print(stype, "u8"), + Type::S8 => self.src.print(stype, "s8"), + Type::U16 => self.src.print(stype, "u16"), + Type::S16 => self.src.print(stype, "s16"), + Type::U32 => self.src.print(stype, "u32"), + Type::S32 => self.src.print(stype, "s32"), + Type::U64 => self.src.print(stype, "u64"), + Type::S64 => self.src.print(stype, "s64"), + Type::Float32 => self.src.print(stype, "float32"), + Type::Float64 => self.src.print(stype, "float64"), + Type::String => self.src.print(stype, "string"), + Type::Id(id) => { + let ty = &self.iface.types[*id]; + if let Some(name) = &ty.name { + return self.src.print(stype, &name.to_snake_case()); + } + match &ty.kind { + TypeDefKind::Type(t) => self.print_ty_name(stype, t), + TypeDefKind::Record(_) + | TypeDefKind::Flags(_) + | TypeDefKind::Enum(_) + | TypeDefKind::Variant(_) + | TypeDefKind::Union(_) => { + unimplemented!() + } + TypeDefKind::Tuple(t) => { + self.src.print(stype, "tuple"); + self.src.print(stype, &t.types.len().to_string()); + for ty in t.types.iter() { + self.src.print(stype, "_"); + self.print_ty_name(stype, ty); + } + } + TypeDefKind::Option(ty) => { + self.src.print(stype, "option_"); + self.print_ty_name(stype, ty); + } + TypeDefKind::Result(r) => { + self.src.print(stype, "result_"); + self.print_optional_ty_name(stype, r.ok.as_ref()); + self.src.print(stype, "_"); + self.print_optional_ty_name(stype, r.err.as_ref()); + } + TypeDefKind::List(t) => { + self.src.print(stype, "list_"); + self.print_ty_name(stype, t); + } + TypeDefKind::Future(t) => { + self.src.print(stype, "future_"); + self.print_optional_ty_name(stype, t.as_ref()); + } + TypeDefKind::Stream(s) => { + self.src.print(stype, "stream_"); + self.print_optional_ty_name(stype, s.element.as_ref()); + self.src.print(stype, "_"); + self.print_optional_ty_name(stype, s.end.as_ref()); + } + } + } + } + } + + fn print_optional_ty_name(&mut self, stype: SourceType, ty: Option<&Type>) { + match ty { + Some(ty) => self.print_ty_name(stype, ty), + None => self.src.print(stype, "void"), + } + } + + fn docs(&mut self, docs: &Docs) { + let docs = match &docs.contents { + Some(docs) => docs, + None => return, + }; + for line in docs.trim().lines() { + self.src.h_defs("// "); + self.src.h_defs(line); + self.src.h_defs("\n"); + } + } + + fn is_empty_type(&self, ty: &Type) -> bool { + let id = match ty { + Type::Id(id) => *id, + _ => return false, + }; + match &self.iface.types[id].kind { + TypeDefKind::Type(t) => self.is_empty_type(t), + TypeDefKind::Record(r) => r.fields.is_empty(), + TypeDefKind::Tuple(t) => t.types.is_empty(), + _ => false, + } + } + + fn get_nonempty_type<'o>(&self, ty: Option<&'o Type>) -> Option<&'o Type> { + match ty { + Some(ty) => { + if self.is_empty_type(ty) { + None + } else { + Some(ty) + } + } + None => None, + } + } + + fn type_string(&mut self, ty: &Type) -> String { + // Getting a type string happens during codegen, and by default means + // that this is a private type that's being generated. This means we + // want to keep track of new anonymous types that are *only* mentioned + // in methods like this, so we can place those types in the C file + // instead of the header interface file. + let prev = mem::take(&mut self.src.h_defs); + let prev_public = mem::take(&mut self.public_anonymous_types); + let prev_private = mem::take(&mut self.private_anonymous_types); + + // Print the type, which will collect into the fields that we replaced + // above. + self.print_ty(SourceType::HDefs, ty); + + // Reset our public/private sets back to what they were beforehand. + // Note that `print_ty` always adds to the public set, so we're + // inverting the meaning here by interpreting those as new private + // types. + let new_private = mem::replace(&mut self.public_anonymous_types, prev_public); + assert!(self.private_anonymous_types.is_empty()); + self.private_anonymous_types = prev_private; + + // For all new private types found while we printed this type, if the + // type isn't already public then it's a new private type. + for id in new_private { + if !self.public_anonymous_types.contains(&id) { + self.private_anonymous_types.insert(id); + } + } + + mem::replace(&mut self.src.h_defs, prev).into() + } + + fn print_anonymous_type(&mut self, ty: TypeId) { + let prev = mem::take(&mut self.src.h_defs); + self.src.h_defs("\ntypedef "); + let kind = &self.iface.types[ty].kind; + match kind { + TypeDefKind::Type(_) + | TypeDefKind::Flags(_) + | TypeDefKind::Record(_) + | TypeDefKind::Enum(_) + | TypeDefKind::Variant(_) + | TypeDefKind::Union(_) => { + unreachable!() + } + TypeDefKind::Tuple(t) => { + self.src.h_defs("struct {\n"); + for (i, t) in t.types.iter().enumerate() { + self.print_ty(SourceType::HDefs, t); + uwriteln!(self.src.h_defs, " f{i};"); + } + self.src.h_defs("}"); + } + TypeDefKind::Option(t) => { + self.src.h_defs("struct {\n"); + self.src.h_defs("bool is_some;\n"); + if !self.is_empty_type(t) { + self.print_ty(SourceType::HDefs, t); + self.src.h_defs(" val;\n"); + } + self.src.h_defs("}"); + } + TypeDefKind::Result(r) => { + self.src.h_defs( + "struct { + bool is_err; + union { + ", + ); + if let Some(ok) = self.get_nonempty_type(r.ok.as_ref()) { + self.print_ty(SourceType::HDefs, ok); + self.src.h_defs(" ok;\n"); + } + if let Some(err) = self.get_nonempty_type(r.err.as_ref()) { + self.print_ty(SourceType::HDefs, err); + self.src.h_defs(" err;\n"); + } + self.src.h_defs("} val;\n"); + self.src.h_defs("}"); + } + TypeDefKind::List(t) => { + self.src.h_defs("struct {\n"); + self.print_ty(SourceType::HDefs, t); + self.src.h_defs(" *ptr;\n"); + self.src.h_defs("size_t len;\n"); + self.src.h_defs("}"); + } + TypeDefKind::Future(_) => todo!("print_anonymous_type for future"), + TypeDefKind::Stream(_) => todo!("print_anonymous_type for stream"), } - - let src = mem::replace(&mut self.src, prev); - self.funcs - .entry(iface.name.to_string()) - .or_insert(Vec::new()) - .push(Func { src }); + self.src.h_defs(" "); + self.print_namespace(SourceType::HDefs); + self.print_ty_name(SourceType::HDefs, &Type::Id(ty)); + self.src.h_defs("_t;\n"); + let type_source = mem::replace(&mut self.src.h_defs, prev); + self.types.insert(ty, type_source); } - fn finish_one(&mut self, iface: &Interface, files: &mut Files) { - let linking_symbol = component_type_object::linking_symbol(iface, self.direction); - self.include("".into()); - uwrite!( - self.src.c_adapters, - " - extern void {linking_symbol}(void); - void {linking_symbol}_public_use_in_this_compilation_unit(void) {{ - {linking_symbol}(); - }} - ", - ); + fn print_dtor(&mut self, id: TypeId) { + let ty = Type::Id(id); + if !self.owns_anything(&ty) { + return; + } + let pos = self.src.h_helpers.len(); + self.src.h_helpers("\nvoid "); + self.print_namespace(SourceType::HHelpers); + self.print_ty_name(SourceType::HHelpers, &ty); + self.src.h_helpers("_free("); + self.print_namespace(SourceType::HHelpers); + self.print_ty_name(SourceType::HHelpers, &ty); + self.src.h_helpers("_t *ptr)"); - self.print_intrinsics(); + self.src.c_helpers(&self.src.h_helpers[pos..].to_string()); + self.src.h_helpers(";"); + self.src.c_helpers(" {\n"); + match &self.iface.types[id].kind { + TypeDefKind::Type(t) => self.free(t, "ptr"), - // Continuously generate anonymous types while we continue to find more - // - // First we take care of the public set of anonymous types. This will - // iteratively print them and also remove any references from the - // private set if we happen to also reference them. - while !self.public_anonymous_types.is_empty() { - for ty in mem::take(&mut self.public_anonymous_types) { - self.print_anonymous_type(iface, ty); - } - } + TypeDefKind::Flags(_) => {} + TypeDefKind::Enum(_) => {} - // Next we take care of private types. To do this we have basically the - // same loop as above, after we switch the sets. We record, however, - // all private types in a local set here to later determine if the type - // needs to be in the C file or the H file. - // - // Note though that we don't re-print a type (and consider it private) - // if we already printed it above as part of the public set. - let mut private_types = HashSet::new(); - self.public_anonymous_types = mem::take(&mut self.private_anonymous_types); - while !self.public_anonymous_types.is_empty() { - for ty in mem::take(&mut self.public_anonymous_types) { - if self.types.contains_key(&ty) { - continue; + TypeDefKind::Record(r) => { + for field in r.fields.iter() { + if !self.owns_anything(&field.ty) { + continue; + } + self.free(&field.ty, &format!("&ptr->{}", field.name.to_snake_case())); } - private_types.insert(ty); - self.print_anonymous_type(iface, ty); } - } - if self.needs_string { - self.include(""); - if self.opts.string_encoding == StringEncoding::UTF16 { - self.include(""); - } - if self.opts.string_encoding == StringEncoding::CompactUTF16 { - panic!("Compact UTF16 is unsupported"); + TypeDefKind::Tuple(t) => { + for (i, ty) in t.types.iter().enumerate() { + if !self.owns_anything(ty) { + continue; + } + self.free(ty, &format!("&ptr->f{i}")); + } } - uwrite!( - self.src.h_defs, - " - typedef struct {{ - {0} *ptr; - size_t len; - }} {1}_string_t; - ", - self.char_type(), - iface.name.to_snake_case(), - ); - // Perhaps the string helpers should just take an explicit length? - uwrite!( - self.src.h_helpers, - " - void {0}_string_set({0}_string_t *ret, const {1} *s); - void {0}_string_dup({0}_string_t *ret, const {1} *s); - void {0}_string_free({0}_string_t *ret);\ - ", - iface.name.to_snake_case(), - self.char_type(), - ); - let (str_len_s, alignment) = if self.opts.string_encoding == StringEncoding::UTF16 { - uwrite!( - self.src.h_helpers, - " - size_t {0}_string_len(const char16_t* s); - ", - iface.name.to_snake_case(), - ); - uwrite!( - self.src.c_helpers, - " - size_t {0}_string_len(const char16_t* s) {{ - char16_t* c = (char16_t*)s; - for (; *c; ++c); - return c-s; - }} - ", - iface.name.to_snake_case(), - ); - (format!("{}_string_len(s)", iface.name.to_snake_case()), "2") - } else { - (String::from("strlen(s)"), "1") - }; - uwrite!( - self.src.c_helpers, - " - void {0}_string_set({0}_string_t *ret, const {1} *s) {{ - ret->ptr = ({1}*) s; - ret->len = {2}; - }} - - void {0}_string_dup({0}_string_t *ret, const {1} *s) {{ - ret->len = {2}; - ret->ptr = cabi_realloc(NULL, 0, {3}, ret->len); - memcpy(ret->ptr, s, ret->len); - }} + TypeDefKind::List(t) => { + if self.owns_anything(t) { + self.src + .c_helpers("for (size_t i = 0; i < ptr->len; i++) {\n"); + self.free(t, "&ptr->ptr[i]"); + self.src.c_helpers("}\n"); + } + uwriteln!(self.src.c_helpers, "if (ptr->len > 0) {{"); + uwriteln!(self.src.c_helpers, "free(ptr->ptr);"); + uwriteln!(self.src.c_helpers, "}}"); + } - void {0}_string_free({0}_string_t *ret) {{ - if (ret->len > 0) {{ - free(ret->ptr); - }} - ret->ptr = NULL; - ret->len = 0; - }} - ", - iface.name.to_snake_case(), - self.char_type(), - str_len_s, - alignment, - ); - } + TypeDefKind::Variant(v) => { + self.src.c_helpers("switch ((int32_t) ptr->tag) {\n"); + for (i, case) in v.cases.iter().enumerate() { + if let Some(ty) = &case.ty { + if !self.owns_anything(ty) { + continue; + } + uwriteln!(self.src.c_helpers, "case {}: {{", i); + let expr = format!("&ptr->val.{}", case.name.to_snake_case()); + if let Some(ty) = &case.ty { + self.free(ty, &expr); + } + self.src.c_helpers("break;\n"); + self.src.c_helpers("}\n"); + } + } + self.src.c_helpers("}\n"); + } - // Afterwards print all types. Note that this print must be in a - // topological order, so we - for id in iface.topological_types() { - if let Some(ty) = self.types.get(&id) { - if private_types.contains(&id) { - self.src.h_defs(ty); - } else { - self.src.h_defs(ty); - self.print_dtor(iface, id); + TypeDefKind::Union(u) => { + self.src.c_helpers("switch ((int32_t) ptr->tag) {\n"); + for (i, case) in u.cases.iter().enumerate() { + if !self.owns_anything(&case.ty) { + continue; + } + uwriteln!(self.src.c_helpers, "case {i}: {{"); + let expr = format!("&ptr->val.f{i}"); + self.free(&case.ty, &expr); + self.src.c_helpers("break;\n"); + self.src.c_helpers("}\n"); } + self.src.c_helpers("}\n"); } - } - // Declare a statically-allocated return area, if needed. We only do - // this for export bindings, because import bindings allocate their - // return-area on the stack. - if !self.in_import && self.return_pointer_area_size > 0 { - uwrite!( - self.src.c_adapters, - " - __attribute__((aligned({}))) - static uint8_t RET_AREA[{}]; - ", - self.return_pointer_area_align, - self.return_pointer_area_size, - ); - } + TypeDefKind::Option(t) => { + self.src.c_helpers("if (ptr->is_some) {\n"); + self.free(t, "&ptr->val"); + self.src.c_helpers("}\n"); + } - for (_module, funcs) in mem::take(&mut self.funcs) { - for func in funcs { - self.src.append(&func.src); + TypeDefKind::Result(r) => { + self.src.c_helpers("if (!ptr->is_err) {\n"); + if let Some(ok) = &r.ok { + if self.owns_anything(ok) { + self.free(ok, "&ptr->val.ok"); + } + } + if let Some(err) = &r.err { + if self.owns_anything(err) { + self.src.c_helpers("} else {\n"); + self.free(err, "&ptr->val.err"); + } + } + self.src.c_helpers("}\n"); } + TypeDefKind::Future(_) => todo!("print_dtor for future"), + TypeDefKind::Stream(_) => todo!("print_dtor for stream"), } + self.src.c_helpers("}\n"); + } - self.include(""); - self.include(""); - - let mut h_str = format!("#ifndef __BINDINGS_{}_H\n#define __BINDINGS_{}_H\n#ifdef __cplusplus\nextern \"C\" {{\n#endif\n", - iface.name.to_shouty_snake_case(), iface.name.to_shouty_snake_case()); - h_str.push_str(&self.includes.join("\n")); - h_str.push_str("\n"); - - let mut c_str = format!("#include \"{}.h\"\n", iface.name.to_kebab_case()); - c_str.push_str(&self.src.c_fns); - - if self.src.h_defs.len() > 0 { - h_str.push_str(&self.src.h_defs); + fn owns_anything(&self, ty: &Type) -> bool { + let id = match ty { + Type::Id(id) => *id, + Type::String => return true, + _ => return false, + }; + match &self.iface.types[id].kind { + TypeDefKind::Type(t) => self.owns_anything(t), + TypeDefKind::Record(r) => r.fields.iter().any(|t| self.owns_anything(&t.ty)), + TypeDefKind::Tuple(t) => t.types.iter().any(|t| self.owns_anything(t)), + TypeDefKind::Flags(_) => false, + TypeDefKind::Enum(_) => false, + TypeDefKind::List(_) => true, + TypeDefKind::Variant(v) => v + .cases + .iter() + .any(|c| self.optional_owns_anything(c.ty.as_ref())), + TypeDefKind::Union(v) => v.cases.iter().any(|case| self.owns_anything(&case.ty)), + TypeDefKind::Option(t) => self.owns_anything(t), + TypeDefKind::Result(r) => { + self.optional_owns_anything(r.ok.as_ref()) + || self.optional_owns_anything(r.err.as_ref()) + } + TypeDefKind::Future(_) => todo!("owns_anything for future"), + TypeDefKind::Stream(_) => todo!("owns_anything for stream"), } + } - h_str.push_str(&self.src.h_fns); - - if !self.opts.no_helpers && self.src.h_helpers.len() > 0 { - h_str.push_str("\n// Helper Functions\n"); - h_str.push_str(&self.src.h_helpers); - h_str.push_str("\n"); + fn optional_owns_anything(&self, ty: Option<&Type>) -> bool { + match ty { + Some(ty) => self.owns_anything(ty), + None => false, } + } - if !self.opts.no_helpers && self.src.c_helpers.len() > 0 { - c_str.push_str("\n// Helper Functions\n"); - c_str.push_str(self.src.c_helpers.as_mut_string()); + fn free(&mut self, ty: &Type, expr: &str) { + let prev = mem::take(&mut self.src.h_helpers); + match ty { + Type::String => { + self.src.h_helpers(&self.gen.world.to_snake_case()); + self.src.h_helpers("_"); + } + _ => { + self.print_namespace(SourceType::HHelpers); + } } + self.print_ty_name(SourceType::HHelpers, ty); + let name = mem::replace(&mut self.src.h_helpers, prev); - c_str.push_str("\n// Component Adapters\n"); - c_str.push_str(&self.src.c_adapters); - - h_str.push_str("\n#ifdef __cplusplus\n}\n#endif\n#endif\n"); - - files.push( - &format!("{}.c", iface.name.to_kebab_case()), - c_str.as_bytes(), - ); - files.push( - &format!("{}.h", iface.name.to_kebab_case()), - h_str.as_bytes(), - ); - files.push( - &format!("{}_component_type.o", iface.name.to_kebab_case()), - component_type_object::object(iface, self.direction) - .unwrap() - .as_slice(), - ); - - // reset sources for next write ops - self.src = Default::default(); + self.src.c_helpers(&name); + self.src.c_helpers("_free("); + self.src.c_helpers(expr); + self.src.c_helpers(");\n"); } } -struct FunctionBindgen<'a> { - gen: &'a mut C, +struct FunctionBindgen<'a, 'b> { + gen: &'a mut InterfaceGenerator<'b>, locals: Ns, - // tmp: usize, src: wit_bindgen_core::Source, sig: CSig, func_to_call: &'a str, @@ -1441,8 +1399,12 @@ struct FunctionBindgen<'a> { wasm_return: Option, } -impl<'a> FunctionBindgen<'a> { - fn new(gen: &'a mut C, sig: CSig, func_to_call: &'a str) -> FunctionBindgen<'a> { +impl<'a, 'b> FunctionBindgen<'a, 'b> { + fn new( + gen: &'a mut InterfaceGenerator<'b>, + sig: CSig, + func_to_call: &'a str, + ) -> FunctionBindgen<'a, 'b> { FunctionBindgen { gen, sig, @@ -1493,7 +1455,7 @@ impl<'a> FunctionBindgen<'a> { } } -impl Bindgen for FunctionBindgen<'_> { +impl Bindgen for FunctionBindgen<'_, '_> { type Operand = String; fn sizes(&self) -> &SizeAlign { @@ -1512,8 +1474,6 @@ impl Bindgen for FunctionBindgen<'_> { } fn return_pointer(&mut self, _iface: &Interface, size: usize, align: usize) -> String { - self.gen.return_pointer_area_size = self.gen.return_pointer_area_size.max(size); - self.gen.return_pointer_area_align = self.gen.return_pointer_area_align.max(align); let ptr = self.locals.tmp("ptr"); if self.gen.in_import { @@ -1531,6 +1491,9 @@ impl Bindgen for FunctionBindgen<'_> { ); uwriteln!(self.src, "int32_t {} = (int32_t) &ret_area;", ptr); } else { + self.gen.gen.return_pointer_area_size = self.gen.gen.return_pointer_area_size.max(size); + self.gen.gen.return_pointer_area_align = + self.gen.gen.return_pointer_area_align.max(align); // Declare a statically-allocated return area. uwriteln!(self.src, "int32_t {} = (int32_t) &RET_AREA;", ptr); } @@ -1544,7 +1507,7 @@ impl Bindgen for FunctionBindgen<'_> { fn emit( &mut self, - iface: &Interface, + _iface: &Interface, inst: &Instruction<'_>, operands: &mut Vec, results: &mut Vec, @@ -1642,7 +1605,7 @@ impl Bindgen for FunctionBindgen<'_> { } } Instruction::RecordLift { ty, .. } => { - let name = self.gen.type_string(iface, &Type::Id(*ty)); + let name = self.gen.type_string(&Type::Id(*ty)); let mut result = format!("({}) {{\n", name); for op in operands { uwriteln!(result, "{},", op); @@ -1658,7 +1621,7 @@ impl Bindgen for FunctionBindgen<'_> { } } Instruction::TupleLift { ty, .. } => { - let name = self.gen.type_string(iface, &Type::Id(*ty)); + let name = self.gen.type_string(&Type::Id(*ty)); let mut result = format!("({}) {{\n", name); for op in operands { uwriteln!(result, "{},", op); @@ -1673,7 +1636,7 @@ impl Bindgen for FunctionBindgen<'_> { results.push(operands.pop().unwrap()); } Int::U64 => { - let name = self.gen.type_string(iface, &Type::Id(*ty)); + let name = self.gen.type_string(&Type::Id(*ty)); let tmp = self.locals.tmp("flags"); uwriteln!(self.src, "{name} {tmp} = {};", operands[0]); results.push(format!("{tmp} & 0xffffffff")); @@ -1686,7 +1649,7 @@ impl Bindgen for FunctionBindgen<'_> { results.push(operands.pop().unwrap()); } Int::U64 => { - let name = self.gen.type_string(iface, &Type::Id(*ty)); + let name = self.gen.type_string(&Type::Id(*ty)); let op0 = &operands[0]; let op1 = &operands[1]; results.push(format!("(({name}) ({op0})) | ((({name}) ({op1})) << 32)")); @@ -1731,8 +1694,8 @@ impl Bindgen for FunctionBindgen<'_> { variant.cases.iter().zip(blocks).zip(payloads).enumerate() { uwriteln!(self.src, "case {}: {{", i); - if let Some(ty) = self.gen.get_nonempty_type(iface, case.ty.as_ref()) { - let ty = self.gen.type_string(iface, ty); + if let Some(ty) = self.gen.get_nonempty_type(case.ty.as_ref()) { + let ty = self.gen.type_string(ty); uwrite!( self.src, "const {} *{} = &({}).val", @@ -1760,7 +1723,7 @@ impl Bindgen for FunctionBindgen<'_> { .drain(self.blocks.len() - variant.cases.len()..) .collect::>(); - let ty = self.gen.type_string(iface, &Type::Id(*ty)); + let ty = self.gen.type_string(&Type::Id(*ty)); let result = self.locals.tmp("variant"); uwriteln!(self.src, "{} {};", ty, result); uwriteln!(self.src, "{}.tag = {};", result, operands[0]); @@ -1772,7 +1735,7 @@ impl Bindgen for FunctionBindgen<'_> { self.src.push_str(&block); assert!(block_results.len() == (case.ty.is_some() as usize)); - if let Some(_) = self.gen.get_nonempty_type(iface, case.ty.as_ref()) { + if let Some(_) = self.gen.get_nonempty_type(case.ty.as_ref()) { let mut dst = format!("{}.val", result); dst.push_str("."); dst.push_str(&case.name.to_snake_case()); @@ -1813,8 +1776,8 @@ impl Bindgen for FunctionBindgen<'_> { union.cases.iter().zip(blocks).zip(payloads).enumerate() { uwriteln!(self.src, "case {i}: {{"); - if !self.gen.is_empty_type(iface, &case.ty) { - let ty = self.gen.type_string(iface, &case.ty); + if !self.gen.is_empty_type(&case.ty) { + let ty = self.gen.type_string(&case.ty); uwriteln!(self.src, "const {ty} *{payload} = &({op0}).val.f{i};"); } self.src.push_str(&block); @@ -1833,7 +1796,7 @@ impl Bindgen for FunctionBindgen<'_> { .drain(self.blocks.len() - union.cases.len()..) .collect::>(); - let ty = self.gen.type_string(iface, &Type::Id(*ty)); + let ty = self.gen.type_string(&Type::Id(*ty)); let result = self.locals.tmp("unionres"); uwriteln!(self.src, "{} {};", ty, result); uwriteln!(self.src, "{}.tag = {};", result, operands[0]); @@ -1877,8 +1840,8 @@ impl Bindgen for FunctionBindgen<'_> { } let op0 = &operands[0]; - let ty = self.gen.type_string(iface, payload); - let bind_some = if self.gen.is_empty_type(iface, payload) { + let ty = self.gen.type_string(payload); + let bind_some = if self.gen.is_empty_type(payload) { String::new() } else { format!("const {ty} *{some_payload} = &({op0}).val;") @@ -1902,11 +1865,11 @@ impl Bindgen for FunctionBindgen<'_> { assert!(some_results.len() == 1); let some_result = &some_results[0]; - let ty = self.gen.type_string(iface, &Type::Id(*ty)); + let ty = self.gen.type_string(&Type::Id(*ty)); let result = self.locals.tmp("option"); uwriteln!(self.src, "{ty} {result};"); let op0 = &operands[0]; - let set_some = if self.gen.is_empty_type(iface, payload) { + let set_some = if self.gen.is_empty_type(payload) { String::new() } else { format!("{result}.val = {some_result};\n") @@ -1960,20 +1923,18 @@ impl Bindgen for FunctionBindgen<'_> { } let op0 = &operands[0]; - let bind_ok = - if let Some(ok) = self.gen.get_nonempty_type(iface, result.ok.as_ref()) { - let ok_ty = self.gen.type_string(iface, ok); - format!("const {ok_ty} *{ok_payload} = &({op0}).val.ok;\n") - } else { - String::new() - }; - let bind_err = - if let Some(err) = self.gen.get_nonempty_type(iface, result.err.as_ref()) { - let err_ty = self.gen.type_string(iface, err); - format!("const {err_ty} *{err_payload} = &({op0}).val.err;\n") - } else { - String::new() - }; + let bind_ok = if let Some(ok) = self.gen.get_nonempty_type(result.ok.as_ref()) { + let ok_ty = self.gen.type_string(ok); + format!("const {ok_ty} *{ok_payload} = &({op0}).val.ok;") + } else { + String::new() + }; + let bind_err = if let Some(err) = self.gen.get_nonempty_type(result.err.as_ref()) { + let err_ty = self.gen.type_string(err); + format!("const {err_ty} *{err_payload} = &({op0}).val.err;") + } else { + String::new() + }; uwrite!( self.src, "\ @@ -1995,22 +1956,20 @@ impl Bindgen for FunctionBindgen<'_> { assert!(ok_results.len() == (result.ok.is_some() as usize)); let result_tmp = self.locals.tmp("result"); - let set_ok = if let Some(_) = self.gen.get_nonempty_type(iface, result.ok.as_ref()) - { + let set_ok = if let Some(_) = self.gen.get_nonempty_type(result.ok.as_ref()) { let ok_result = &ok_results[0]; format!("{result_tmp}.val.ok = {ok_result};") } else { String::new() }; - let set_err = - if let Some(_) = self.gen.get_nonempty_type(iface, result.err.as_ref()) { - let err_result = &err_results[0]; - format!("{result_tmp}.val.err = {err_result};") - } else { - String::new() - }; + let set_err = if let Some(_) = self.gen.get_nonempty_type(result.err.as_ref()) { + let err_result = &err_results[0]; + format!("{result_tmp}.val.err = {err_result};") + } else { + String::new() + }; - let ty = self.gen.type_string(iface, &Type::Id(*ty)); + let ty = self.gen.type_string(&Type::Id(*ty)); uwriteln!(self.src, "{ty} {result_tmp};"); let op0 = &operands[0]; uwrite!( @@ -2041,19 +2000,19 @@ impl Bindgen for FunctionBindgen<'_> { results.push(format!("(int32_t) ({}).len", operands[0])); } Instruction::ListCanonLift { element, ty, .. } => { - let list_name = self.gen.type_string(iface, &Type::Id(*ty)); - let elem_name = self.gen.type_string(iface, element); + let list_name = self.gen.type_string(&Type::Id(*ty)); + let elem_name = self.gen.type_string(element); results.push(format!( "({}) {{ ({}*)({}), (size_t)({}) }}", list_name, elem_name, operands[0], operands[1] )); } Instruction::StringLift { .. } => { - let list_name = self.gen.type_string(iface, &Type::String); + let list_name = self.gen.type_string(&Type::String); results.push(format!( "({}) {{ ({}*)({}), (size_t)({}) }}", list_name, - self.gen.char_type(), + self.gen.gen.char_type(), operands[0], operands[1] )); @@ -2067,8 +2026,8 @@ impl Bindgen for FunctionBindgen<'_> { Instruction::ListLift { element, ty, .. } => { let _body = self.blocks.pop().unwrap(); - let list_name = self.gen.type_string(iface, &Type::Id(*ty)); - let elem_name = self.gen.type_string(iface, element); + let list_name = self.gen.type_string(&Type::Id(*ty)); + let elem_name = self.gen.type_string(element); results.push(format!( "({}) {{ ({}*)({}), (size_t)({}) }}", list_name, elem_name, operands[0], operands[1] @@ -2108,7 +2067,7 @@ impl Bindgen for FunctionBindgen<'_> { } if *byref { let name = self.locals.tmp("arg"); - let ty = self.gen.type_string(iface, &func.params[i].1); + let ty = self.gen.type_string(&func.params[i].1); uwriteln!(self.src, "{} {} = {};", ty, name, op); args.push_str("&"); args.push_str(&name); @@ -2121,7 +2080,7 @@ impl Bindgen for FunctionBindgen<'_> { let mut retptrs = Vec::new(); for ty in self.sig.ret.retptrs.iter() { let name = self.locals.tmp("ret"); - let ty = self.gen.type_string(iface, ty); + let ty = self.gen.type_string(ty); uwriteln!(self.src, "{} {};", ty, name); if args.len() > 0 { args.push_str(", "); @@ -2140,7 +2099,7 @@ impl Bindgen for FunctionBindgen<'_> { let ret = self.locals.tmp("ret"); let ty = self .gen - .type_string(iface, func.results.iter_types().next().unwrap()); + .type_string(func.results.iter_types().next().unwrap()); uwriteln!(self.src, "{} {} = {}({});", ty, ret, self.sig.name, args); results.push(ret); } @@ -2152,12 +2111,12 @@ impl Bindgen for FunctionBindgen<'_> { } args.push_str("&"); args.push_str(&val); - let payload_ty = self.gen.type_string(iface, ty); + let payload_ty = self.gen.type_string(ty); uwriteln!(self.src, "{} {};", payload_ty, val); uwriteln!(self.src, "bool {} = {}({});", ret, self.sig.name, args); let option_ty = self .gen - .type_string(iface, func.results.iter_types().next().unwrap()); + .type_string(func.results.iter_types().next().unwrap()); let option_ret = self.locals.tmp("ret"); uwrite!( self.src, @@ -2183,11 +2142,11 @@ impl Bindgen for FunctionBindgen<'_> { } args.push_str("&"); args.push_str(&val); - let ty = self.gen.type_string(iface, ty); + let ty = self.gen.type_string(ty); uwriteln!(self.src, "{} {};", ty, val); ok_names.push(val); } - let err_ty = self.gen.type_string(iface, &Type::Id(*err)); + let err_ty = self.gen.type_string(&Type::Id(*err)); uwriteln!( self.src, "{} {} = {}({});", @@ -2198,7 +2157,7 @@ impl Bindgen for FunctionBindgen<'_> { ); let result_ty = self .gen - .type_string(iface, func.results.iter_types().next().unwrap()); + .type_string(func.results.iter_types().next().unwrap()); let result_ret = self.locals.tmp("ret"); uwrite!( self.src, diff --git a/crates/gen-guest-c/tests/codegen.rs b/crates/gen-guest-c/tests/codegen.rs index c9f187c56..ffd0fe826 100644 --- a/crates/gen-guest-c/tests/codegen.rs +++ b/crates/gen-guest-c/tests/codegen.rs @@ -1,3 +1,4 @@ +use heck::*; use std::env; use std::path::{Path, PathBuf}; use std::process::Command; @@ -6,16 +7,15 @@ macro_rules! gen_test { ($name:ident $test:tt $dir:ident) => { #[test] fn $name() { - test_helpers::run_codegen_test( + test_helpers::run_world_codegen_test( "guest-c", - std::path::Path::new($test) - .file_stem() - .unwrap() - .to_str() - .unwrap(), - include_str!($test), + $test.as_ref(), test_helpers::Direction::$dir, - wit_bindgen_gen_guest_c::Opts::default().build(), + |name, interfaces, files| { + wit_bindgen_gen_guest_c::Opts::default() + .build() + .generate(name, interfaces, files) + }, super::verify, ) } @@ -40,7 +40,7 @@ fn verify(dir: &Path, name: &str) { let path = PathBuf::from(env::var_os("WASI_SDK_PATH").unwrap()); let mut cmd = Command::new(path.join("bin/clang")); cmd.arg("--sysroot").arg(path.join("share/wasi-sysroot")); - cmd.arg(dir.join(format!("{}.c", name))); + cmd.arg(dir.join(format!("{}.c", name.to_snake_case()))); cmd.arg("-I").arg(dir); cmd.arg("-Wall") .arg("-Wextra") diff --git a/crates/gen-host-js/src/lib.rs b/crates/gen-host-js/src/lib.rs index 5e150a6d9..51f636873 100644 --- a/crates/gen-host-js/src/lib.rs +++ b/crates/gen-host-js/src/lib.rs @@ -490,7 +490,7 @@ impl Js { Intrinsic::Utf16Encode => self.src.js(" function utf16Encode (str, realloc, memory) { - const len = str.length, ptr = realloc(0, 0, 2, len), out = new Uint16Array(memory.buffer, ptr, len); + const len = str.length, ptr = realloc(0, 0, 2, len * 2), out = new Uint16Array(memory.buffer, ptr, len); let i = 0; if (isLE) { while (i < len) out[i] = str.charCodeAt(i++); diff --git a/crates/test-helpers/macros/build.rs b/crates/test-helpers/macros/build.rs index c2393bd78..bdc48b9ef 100644 --- a/crates/test-helpers/macros/build.rs +++ b/crates/test-helpers/macros/build.rs @@ -3,8 +3,7 @@ use std::fs; use std::path::PathBuf; use std::process::Command; use wit_bindgen_core::{wit_parser::Interface, Direction, Generator}; -use wit_component::ComponentEncoder; -use wit_component::StringEncoding; +use wit_component::{ComponentEncoder, ComponentInterfaces, StringEncoding}; fn guest_c( wasms: &mut Vec<(String, String, String, String)>, @@ -27,18 +26,19 @@ fn guest_c( let import = Interface::parse_file(&test_dir.join("imports.wit")).unwrap(); let export = Interface::parse_file(&test_dir.join("exports.wit")).unwrap(); + let interfaces = ComponentInterfaces { + imports: [(import.name.clone(), import)].into_iter().collect(), + exports: Default::default(), + default: Some(export), + }; + let name = test_dir.file_name().unwrap().to_str().unwrap(); + let snake = name.replace("-", "_"); let mut files = Default::default(); - // TODO: should combine this into one - let mut opts = wit_bindgen_gen_guest_c::Opts::default(); - if utf_16 { - opts.string_encoding = StringEncoding::UTF16; - } - opts.build().generate_all(&[import], &[], &mut files); let mut opts = wit_bindgen_gen_guest_c::Opts::default(); if utf_16 { opts.string_encoding = StringEncoding::UTF16; } - opts.build().generate_all(&[], &[export], &mut files); + opts.build().generate(&name, &interfaces, &mut files); let out_dir = out_dir.join(format!( "c{}-{}", @@ -60,10 +60,8 @@ fn guest_c( let out_wasm = out_dir.join(format!("c{}.wasm", utf16_suffix)); cmd.arg("--sysroot").arg(path.join("share/wasi-sysroot")); cmd.arg(c_impl) - .arg(out_dir.join("imports.c")) - .arg(out_dir.join("imports_component_type.o")) - .arg(out_dir.join("exports.c")) - .arg(out_dir.join("exports_component_type.o")) + .arg(out_dir.join(format!("{snake}.c"))) + .arg(out_dir.join(format!("{snake}_component_type.o"))) .arg("-I") .arg(&out_dir) .arg("-Wall") @@ -89,8 +87,6 @@ fn guest_c( panic!("failed to compile"); } - let stem = test_dir.file_stem().unwrap().to_str().unwrap().to_string(); - // Translate the canonical ABI module into a component. let module = fs::read(&out_wasm).expect("failed to read wasm file"); let mut encoder = ComponentEncoder::default(); @@ -113,7 +109,7 @@ fn guest_c( wasms.push(( format!("c{}", utf16_suffix), - stem, + name.to_string(), out_wasm.to_str().unwrap().to_string(), component_path.to_str().unwrap().to_string(), )); diff --git a/crates/test-helpers/src/lib.rs b/crates/test-helpers/src/lib.rs index fefd0c84f..94ac7cbdb 100644 --- a/crates/test-helpers/src/lib.rs +++ b/crates/test-helpers/src/lib.rs @@ -5,6 +5,7 @@ use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; use wit_bindgen_core::{Files, Generator}; +use wit_component::ComponentInterfaces; use wit_parser::abi::{AbiVariant, WasmType}; use wit_parser::{Function, Interface}; @@ -96,6 +97,47 @@ stderr --- ); } +pub fn run_world_codegen_test( + gen_name: &str, + wit_path: &Path, + dir: Direction, + generate: fn(&str, &ComponentInterfaces, &mut Files), + verify: fn(&Path, &str), +) { + let iface = Interface::parse_file(wit_path).unwrap(); + let mut interfaces = ComponentInterfaces::default(); + + match dir { + Direction::Import => { + interfaces.imports.insert(iface.name.clone(), iface); + } + Direction::Export => { + interfaces.default = Some(iface); + } + } + + let name = wit_path.file_stem().and_then(|s| s.to_str()).unwrap(); + + let gen_name = format!( + "{gen_name}-{}", + match dir { + Direction::Import => "import", + Direction::Export => "export", + } + ); + let dir = test_directory("codegen", &gen_name, name); + + let mut files = Default::default(); + generate(name, &interfaces, &mut files); + for (file, contents) in files.iter() { + let dst = dir.join(file); + std::fs::create_dir_all(dst.parent().unwrap()).unwrap(); + std::fs::write(&dst, contents).unwrap(); + } + + verify(&dir, name); +} + pub fn run_component_codegen_test( gen_name: &str, wit_path: &Path, diff --git a/crates/wit-bindgen-demo/src/lib.rs b/crates/wit-bindgen-demo/src/lib.rs index 7131b471a..f9dc64594 100644 --- a/crates/wit-bindgen-demo/src/lib.rs +++ b/crates/wit-bindgen-demo/src/lib.rs @@ -121,10 +121,7 @@ fn render(lang: demo::Lang, wit: &str, files: &mut Files, options: &demo::Option wit_bindgen_gen_host_wasmtime_py::Opts::default().build(), files, )?, - demo::Lang::C => gen_world_legacy( - Box::new(wit_bindgen_gen_guest_c::Opts::default().build()), - files, - ), + demo::Lang::C => gen_world(wit_bindgen_gen_guest_c::Opts::default().build(), files), demo::Lang::Markdown => gen_world(wit_bindgen_gen_markdown::Opts::default().build(), files), demo::Lang::Js => gen_component(wit_bindgen_gen_host_js::Opts::default().build(), files)?, } diff --git a/src/bin/wit-bindgen.rs b/src/bin/wit-bindgen.rs index 990ecab10..5f0e9b3f5 100644 --- a/src/bin/wit-bindgen.rs +++ b/src/bin/wit-bindgen.rs @@ -83,7 +83,7 @@ enum GuestGenerator { #[clap(flatten)] common: Common, #[clap(flatten)] - world: LegacyWorld, + world: World, }, /// Generates bindings for TeaVM-based Java guest modules. TeavmJava { @@ -217,7 +217,7 @@ fn main() -> Result<()> { gen_world(opts.build(), world, &mut files)?; } Category::Guest(GuestGenerator::C { opts, world, .. }) => { - gen_legacy_world(Box::new(opts.build()), world, &mut files)?; + gen_world(opts.build(), world, &mut files)?; } Category::Guest(GuestGenerator::TeavmJava { opts, world, .. }) => { gen_legacy_world(Box::new(opts.build()), world, &mut files)?; diff --git a/tests/runtime/flavorful/wasm.c b/tests/runtime/flavorful/wasm.c index 0ea2c62e1..6ca147219 100644 --- a/tests/runtime/flavorful/wasm.c +++ b/tests/runtime/flavorful/wasm.c @@ -1,13 +1,12 @@ #include -#include -#include +#include #include #include void exports_test_imports() { { imports_list_in_record1_t a; - imports_string_set(&a.a, "list_in_record1"); + flavorful_string_set(&a.a, "list_in_record1"); imports_f_list_in_record1(&a); imports_list_in_record2_t b; @@ -18,7 +17,7 @@ void exports_test_imports() { { imports_list_in_record3_t a, b; - imports_string_set(&a.a, "list_in_record3 input"); + flavorful_string_set(&a.a, "list_in_record3 input"); imports_f_list_in_record3(&a, &b); assert(memcmp(b.a.ptr, "list_in_record3 output", b.a.len) == 0); imports_list_in_record3_free(&b); @@ -26,7 +25,7 @@ void exports_test_imports() { { imports_list_in_record4_t a, b; - imports_string_set(&a.a, "input4"); + flavorful_string_set(&a.a, "input4"); imports_f_list_in_record4(&a, &b); assert(memcmp(b.a.ptr, "result4", b.a.len) == 0); imports_list_in_record4_free(&b); @@ -37,38 +36,38 @@ void exports_test_imports() { imports_list_in_variant1_v2_t b; imports_list_in_variant1_v3_t c; a.is_some = true; - imports_string_set(&a.val, "foo"); + flavorful_string_set(&a.val, "foo"); b.is_err = true; - imports_string_set(&b.val.err, "bar"); + flavorful_string_set(&b.val.err, "bar"); c.tag = 0; - imports_string_set(&c.val.f0, "baz"); + flavorful_string_set(&c.val.f0, "baz"); imports_f_list_in_variant1(&a, &b, &c); } { - imports_string_t a; + flavorful_string_t a; assert(imports_f_list_in_variant2(&a)); assert(memcmp(a.ptr, "list_in_variant2", a.len) == 0); - imports_string_free(&a); + flavorful_string_free(&a); } { imports_list_in_variant3_t a; a.is_some = true; - imports_string_set(&a.val, "input3"); - imports_string_t b; + flavorful_string_set(&a.val, "input3"); + flavorful_string_t b; assert(imports_f_list_in_variant3(&a, &b)); assert(memcmp(b.ptr, "output3", b.len) == 0); - imports_string_free(&b); + flavorful_string_free(&b); } assert(imports_errno_result() == IMPORTS_MY_ERRNO_B); { - imports_string_t a; - imports_string_set(&a, "typedef1"); - imports_string_t b_str; - imports_string_set(&b_str, "typedef2"); + flavorful_string_t a; + flavorful_string_set(&a, "typedef1"); + flavorful_string_t b_str; + flavorful_string_set(&b_str, "typedef2"); imports_list_typedef3_t b; b.ptr = &b_str; b.len = 1; @@ -133,19 +132,19 @@ void exports_f_list_in_record1(exports_list_in_record1_t *a) { } void exports_f_list_in_record2(exports_list_in_record2_t *ret0) { - exports_string_dup(&ret0->a, "list_in_record2"); + flavorful_string_dup(&ret0->a, "list_in_record2"); } void exports_f_list_in_record3(exports_list_in_record3_t *a, exports_list_in_record3_t *ret0) { assert(memcmp(a->a.ptr, "list_in_record3 input", a->a.len) == 0); exports_list_in_record3_free(a); - exports_string_dup(&ret0->a, "list_in_record3 output"); + flavorful_string_dup(&ret0->a, "list_in_record3 output"); } void exports_f_list_in_record4(exports_list_in_alias_t *a, exports_list_in_alias_t *ret0) { assert(memcmp(a->a.ptr, "input4", a->a.len) == 0); exports_list_in_alias_free(a); - exports_string_dup(&ret0->a, "result4"); + flavorful_string_dup(&ret0->a, "result4"); } void exports_f_list_in_variant1(exports_list_in_variant1_v1_t *a, exports_list_in_variant1_v2_t *b, exports_list_in_variant1_v3_t *c) { @@ -162,16 +161,16 @@ void exports_f_list_in_variant1(exports_list_in_variant1_v1_t *a, exports_list_i exports_list_in_variant1_v3_free(c); } -bool exports_f_list_in_variant2(exports_string_t *ret0) { - exports_string_dup(ret0, "list_in_variant2"); +bool exports_f_list_in_variant2(flavorful_string_t *ret0) { + flavorful_string_dup(ret0, "list_in_variant2"); return true; } -bool exports_f_list_in_variant3(exports_list_in_variant3_t *a, exports_string_t *ret0) { +bool exports_f_list_in_variant3(exports_list_in_variant3_t *a, flavorful_string_t *ret0) { assert(a->is_some); assert(memcmp(a->val.ptr, "input3", a->val.len) == 0); exports_list_in_variant3_free(a); - exports_string_dup(ret0, "output3"); + flavorful_string_dup(ret0, "output3"); return true; } @@ -191,7 +190,7 @@ void exports_list_typedefs(exports_list_typedef_t *a, exports_list_typedef3_t *c ret0->len = 8; memcpy(ret0->ptr, "typedef3", 8); - ret1->ptr = malloc(sizeof(exports_string_t)); + ret1->ptr = malloc(sizeof(flavorful_string_t)); ret1->len = 1; - exports_string_dup(&ret1->ptr[0], "typedef4"); + flavorful_string_dup(&ret1->ptr[0], "typedef4"); } diff --git a/tests/runtime/lists/wasm.c b/tests/runtime/lists/wasm.c index 77cd6e5f9..e45729f40 100644 --- a/tests/runtime/lists/wasm.c +++ b/tests/runtime/lists/wasm.c @@ -1,7 +1,6 @@ #include -#include +#include #include -#include #include #include #include @@ -22,8 +21,8 @@ void exports_test_imports() { } { - imports_string_t a; - imports_string_set(&a, ""); + lists_string_t a; + lists_string_set(&a, ""); imports_empty_string_param(&a); } @@ -34,7 +33,7 @@ void exports_test_imports() { } { - imports_string_t a; + lists_string_t a; imports_empty_string_result(&a); assert(a.len == 0); } @@ -48,16 +47,16 @@ void exports_test_imports() { } { - imports_string_t a; - imports_string_set(&a, "foo"); + lists_string_t a; + lists_string_set(&a, "foo"); imports_list_param2(&a); } { - imports_string_t list[3]; - imports_string_set(&list[0], "foo"); - imports_string_set(&list[1], "bar"); - imports_string_set(&list[2], "baz"); + lists_string_t list[3]; + lists_string_set(&list[0], "foo"); + lists_string_set(&list[1], "bar"); + lists_string_set(&list[2], "baz"); imports_list_string_t a; a.ptr = list; a.len = 3; @@ -65,11 +64,11 @@ void exports_test_imports() { } { - imports_string_t list1[2]; - imports_string_t list2[1]; - imports_string_set(&list1[0], "foo"); - imports_string_set(&list1[1], "bar"); - imports_string_set(&list2[0], "baz"); + lists_string_t list1[2]; + lists_string_t list2[1]; + lists_string_set(&list1[0], "foo"); + lists_string_set(&list1[1], "bar"); + lists_string_set(&list2[0], "baz"); imports_list_list_string_t a; a.ptr[0].len = 2; a.ptr[0].ptr = list1; @@ -88,11 +87,11 @@ void exports_test_imports() { } { - imports_string_t a; + lists_string_t a; imports_list_result2(&a); assert(a.len == 6); assert(memcmp(a.ptr, "hello!", 6) == 0); - imports_string_free(&a); + lists_string_free(&a); } { @@ -131,29 +130,29 @@ void exports_test_imports() { } { - imports_string_t a, b; - imports_string_set(&a, "x"); + lists_string_t a, b; + lists_string_set(&a, "x"); imports_string_roundtrip(&a, &b); assert(b.len == a.len); assert(memcmp(b.ptr, a.ptr, a.len) == 0); - imports_string_free(&b); + lists_string_free(&b); - imports_string_set(&a, ""); + lists_string_set(&a, ""); imports_string_roundtrip(&a, &b); assert(b.len == a.len); - imports_string_free(&b); + lists_string_free(&b); - imports_string_set(&a, "hello"); + lists_string_set(&a, "hello"); imports_string_roundtrip(&a, &b); assert(b.len == a.len); assert(memcmp(b.ptr, a.ptr, a.len) == 0); - imports_string_free(&b); + lists_string_free(&b); - imports_string_set(&a, "hello ⚑ world"); + lists_string_set(&a, "hello ⚑ world"); imports_string_roundtrip(&a, &b); assert(b.len == a.len); assert(memcmp(b.ptr, a.ptr, a.len) == 0); - imports_string_free(&b); + lists_string_free(&b); } { @@ -233,7 +232,7 @@ void exports_empty_list_param(exports_list_u8_t *a) { assert(a->len == 0); } -void exports_empty_string_param(exports_string_t *a) { +void exports_empty_string_param(lists_string_t *a) { assert(a->len == 0); } @@ -242,7 +241,7 @@ void exports_empty_list_result(exports_list_u8_t *ret0) { ret0->len = 0; } -void exports_empty_string_result(exports_string_t *ret0) { +void exports_empty_string_result(lists_string_t *ret0) { ret0->ptr = 0; ret0->len = 0; } @@ -256,12 +255,12 @@ void exports_list_param(exports_list_u8_t *a) { exports_list_u8_free(a); } -void exports_list_param2(exports_string_t *a) { +void exports_list_param2(lists_string_t *a) { assert(a->len == 3); assert(a->ptr[0] == 'f'); assert(a->ptr[1] == 'o'); assert(a->ptr[2] == 'o'); - exports_string_free(a); + lists_string_free(a); } void exports_list_param3(exports_list_string_t *a) { @@ -317,22 +316,22 @@ void exports_list_result(exports_list_u8_t *ret0) { ret0->ptr[4] = 5; } -void exports_list_result2(exports_string_t *ret0) { - exports_string_dup(ret0, "hello!"); +void exports_list_result2(lists_string_t *ret0) { + lists_string_dup(ret0, "hello!"); } void exports_list_result3(exports_list_string_t *ret0) { ret0->len = 2; - ret0->ptr = malloc(2 * sizeof(exports_string_t)); + ret0->ptr = malloc(2 * sizeof(lists_string_t)); - exports_string_dup(&ret0->ptr[0], "hello,"); - exports_string_dup(&ret0->ptr[1], "world!"); + lists_string_dup(&ret0->ptr[0], "hello,"); + lists_string_dup(&ret0->ptr[1], "world!"); } void exports_list_roundtrip(exports_list_u8_t *a, exports_list_u8_t *ret0) { *ret0 = *a; } -void exports_string_roundtrip(exports_string_t *a, exports_string_t *ret0) { +void exports_string_roundtrip(lists_string_t *a, lists_string_t *ret0) { *ret0 = *a; } diff --git a/tests/runtime/many_arguments/wasm.c b/tests/runtime/many_arguments/wasm.c index 09960fc0a..2ab49a836 100644 --- a/tests/runtime/many_arguments/wasm.c +++ b/tests/runtime/many_arguments/wasm.c @@ -1,6 +1,5 @@ #include -#include -#include +#include #include #include diff --git a/tests/runtime/numbers/wasm.c b/tests/runtime/numbers/wasm.c index e373aff5a..79517deb9 100644 --- a/tests/runtime/numbers/wasm.c +++ b/tests/runtime/numbers/wasm.c @@ -1,8 +1,7 @@ #include -#include -#include #include #include +#include uint8_t exports_roundtrip_u8(uint8_t a) { return a; diff --git a/tests/runtime/records/wasm.c b/tests/runtime/records/wasm.c index 8f5e28961..e9ef8b032 100644 --- a/tests/runtime/records/wasm.c +++ b/tests/runtime/records/wasm.c @@ -1,6 +1,5 @@ #include -#include -#include +#include void exports_test_imports() { { diff --git a/tests/runtime/smoke/wasm.c b/tests/runtime/smoke/wasm.c index 0ca7b9e3a..4c1a2f18c 100644 --- a/tests/runtime/smoke/wasm.c +++ b/tests/runtime/smoke/wasm.c @@ -1,5 +1,4 @@ -#include -#include +#include #include void exports_thunk() { diff --git a/tests/runtime/strings/exports.wit b/tests/runtime/strings/exports.wit index 440a6c820..4d3083323 100644 --- a/tests/runtime/strings/exports.wit +++ b/tests/runtime/strings/exports.wit @@ -1,4 +1,3 @@ test-imports: func() -f1: func(s: string) -f2: func() -> string +roundtrip: func(s: string) -> string diff --git a/tests/runtime/strings/host.ts b/tests/runtime/strings/host.ts index bc3b9d8c8..922ebd680 100644 --- a/tests/runtime/strings/host.ts +++ b/tests/runtime/strings/host.ts @@ -8,19 +8,18 @@ async function run() { const wasm = await instantiate(loadWasm, { testwasi, imports: { - f1 (s: string) { + takeBasic(s: string) { assert.strictEqual(s, 'latin utf16'); }, - f2 () { + returnUnicode() { return '🚀🚀🚀 𠈄𓀀'; } } }); wasm.testImports(); - assert.strictEqual(wasm.f2(), '🚀🚀🚀 𠈄𓀀'); - wasm.f1('str'); - assert.strictEqual(wasm.f2(), 'str'); + assert.strictEqual(wasm.roundtrip('str'), 'str'); + assert.strictEqual(wasm.roundtrip('🚀🚀🚀 𠈄𓀀'), '🚀🚀🚀 𠈄𓀀'); } await run() diff --git a/tests/runtime/strings/imports.wit b/tests/runtime/strings/imports.wit index be77ad337..b586d5189 100644 --- a/tests/runtime/strings/imports.wit +++ b/tests/runtime/strings/imports.wit @@ -1,2 +1,2 @@ -f1: func(s: string) -f2: func() -> string +take-basic: func(s: string) +return-unicode: func() -> string diff --git a/tests/runtime/strings/wasm_utf16.c b/tests/runtime/strings/wasm_utf16.c index b2d52aa98..8c50764a4 100644 --- a/tests/runtime/strings/wasm_utf16.c +++ b/tests/runtime/strings/wasm_utf16.c @@ -1,40 +1,34 @@ #include -#include -#include +#include #include #include #include -char16_t BASIC_STRING[] = u"latin utf16"; -// 🚀 = 0xD83D 0xDE80 -// 𠈄 = 0xD840 0xDE04 -// 𓀀 = 0xD80C 0xDC00 -char16_t UNICODE_STRING[] = { 0xD83D, 0xDE80, 0xD83D, 0xDE80, 0xD83D, 0xDE80, ' ', 0xD840, 0xDE04, 0xD80C, 0xDC00 }; char16_t STR_BUFFER[500]; -void assert_str(imports_string_t* str, char16_t* expected, size_t expected_len) { +void assert_str(strings_string_t* str, char16_t* expected) { + size_t expected_len = 0; + while (expected[expected_len]) + expected_len++; assert(str->len == expected_len); assert(memcmp(str->ptr, expected, expected_len * 2) == 0); } void exports_test_imports() { - imports_string_t str1; - imports_string_set(&str1, BASIC_STRING); - assert_str(&str1, &BASIC_STRING[0], 11); - imports_f1(&str1); - imports_string_t str2; - imports_f2(&str2); - memcpy(&STR_BUFFER[0], str2.ptr, str2.len * 2); - STR_BUFFER[str2.len] = '\0'; - assert_str(&str2, &UNICODE_STRING[0], 11); -} + strings_string_t str1; + strings_string_set(&str1, u"latin utf16"); + imports_take_basic(&str1); -void exports_f1(exports_string_t *str) { - assert(str->len > 0); - memcpy(&STR_BUFFER[0], str->ptr, str->len * 2); - STR_BUFFER[str->len] = '\0'; + strings_string_t str2; + imports_return_unicode(&str2); + assert_str(&str2, u"🚀🚀🚀 𠈄𓀀"); + strings_string_free(&str2); } -void exports_f2(exports_string_t *ret) { - exports_string_set(ret, STR_BUFFER); +void exports_roundtrip(strings_string_t *str, strings_string_t *ret) { + assert(str->len > 0); + ret->len = str->len; + ret->ptr = malloc(ret->len * 2); + memcpy(ret->ptr, str->ptr, 2 * ret->len); + strings_string_free(str); } diff --git a/tests/runtime/variants/wasm.c b/tests/runtime/variants/wasm.c index 533b6e7bb..3b8e3aff5 100644 --- a/tests/runtime/variants/wasm.c +++ b/tests/runtime/variants/wasm.c @@ -1,6 +1,5 @@ #include -#include -#include +#include void exports_test_imports() { {