diff --git a/crates/cpp/helper-types/wit.h b/crates/cpp/helper-types/wit.h index c84f181c9..8a79db32c 100644 --- a/crates/cpp/helper-types/wit.h +++ b/crates/cpp/helper-types/wit.h @@ -95,6 +95,18 @@ class string { memcpy(addr, v.data(), v.size()); return string(addr, v.size()); } + char* begin() { + return (char*)data_; + } + char* end() { + return (char*)data_ + length; + } + char const* begin() const { + return (char const*)data_; + } + char const* end() const { + return (char const*)data_ + length; + } }; /// A vector in linear memory, freed unconditionally using free @@ -113,7 +125,7 @@ template class vector { vector &operator=(vector const &) = delete; vector &operator=(vector &&b) { if (data_ && length>0) { - free(const_cast(data_)); + free(data_); } data_ = b.data_; length = b.length; diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 027064f6d..9ba821db6 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2,7 +2,7 @@ use anyhow::bail; use heck::{ToPascalCase, ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase}; use std::{ collections::{HashMap, HashSet}, - fmt::{Display, Write as FmtWrite}, + fmt::{self, Display, Write as FmtWrite}, io::{Read, Write}, path::PathBuf, process::{Command, Stdio}, @@ -15,9 +15,10 @@ use wit_bindgen_core::{ uwrite, uwriteln, wit_parser::{ Alignment, ArchitectureSize, Docs, Function, FunctionKind, Handle, Int, InterfaceId, - Resolve, SizeAlign, Stability, Type, TypeDefKind, TypeId, TypeOwner, WorldId, WorldKey, + Resolve, SizeAlign, Stability, Type, TypeDef, TypeDefKind, TypeId, TypeOwner, WorldId, + WorldKey, }, - Files, InterfaceGenerator, Source, WorldGenerator, + Files, InterfaceGenerator, Source, Types, WorldGenerator, }; // mod wamr; @@ -28,8 +29,6 @@ pub const RESOURCE_EXPORT_BASE_CLASS_NAME: &str = "ResourceExportBase"; pub const RESOURCE_TABLE_NAME: &str = "ResourceTable"; pub const OWNED_CLASS_NAME: &str = "Owned"; pub const POINTER_SIZE_EXPRESSION: &str = "sizeof(void*)"; -// these types are always defined in the non-exports namespace -const NOT_IN_EXPORTED_NAMESPACE: bool = false; type CppType = String; @@ -41,16 +40,6 @@ enum Flavor { BorrowedArgument, } -impl Flavor { - fn is_guest_export(&self) -> bool { - match self { - Flavor::Argument(var) => matches!(var, AbiVariant::GuestExport), - Flavor::Result(var) => matches!(var, AbiVariant::GuestExport), - Flavor::InStruct | Flavor::BorrowedArgument => false, - } - } -} - #[derive(Default)] struct HighlevelSignature { /// this is a constructor or destructor without a written type @@ -113,65 +102,13 @@ struct Cpp { imported_interfaces: HashSet, user_class_files: HashMap, defined_types: HashSet<(Vec, String)>, + types: Types, // needed for symmetric disambiguation interface_prefixes: HashMap<(Direction, WorldKey), String>, import_prefix: Option, } -#[derive(Default, Debug, Clone, Copy)] -pub enum Ownership { - /// Generated types will be composed entirely of owning fields, regardless - /// of whether they are used as parameters to imports or not. - #[default] - Owning, - - /// Generated types used as parameters to imports will be "deeply - /// borrowing", i.e. contain references rather than owned values when - /// applicable. - Borrowing { - /// Whether or not to generate "duplicate" type definitions for a single - /// WIT type if necessary, for example if it's used as both an import - /// and an export, or if it's used both as a parameter to an import and - /// a return value from an import. - duplicate_if_necessary: bool, - }, -} - -impl FromStr for Ownership { - type Err = String; - - fn from_str(s: &str) -> Result { - match s { - "owning" => Ok(Self::Owning), - "borrowing" => Ok(Self::Borrowing { - duplicate_if_necessary: false, - }), - "borrowing-duplicate-if-necessary" => Ok(Self::Borrowing { - duplicate_if_necessary: true, - }), - _ => Err(format!( - "unrecognized ownership: `{s}`; \ - expected `owning`, `borrowing`, or `borrowing-duplicate-if-necessary`" - )), - } - } -} - -impl core::fmt::Display for Ownership { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - f.write_str(match self { - Ownership::Owning => "owning", - Ownership::Borrowing { - duplicate_if_necessary: false, - } => "borrowing", - Ownership::Borrowing { - duplicate_if_necessary: true, - } => "borrowing-duplicate-if-necessary", - }) - } -} - #[derive(Default, Debug, Clone)] #[cfg_attr(feature = "clap", derive(clap::Args))] pub struct Opts { @@ -196,22 +133,6 @@ pub struct Opts { #[cfg_attr(feature = "clap", arg(long))] pub internal_prefix: Option, - /// Whether to generate owning or borrowing type definitions. - /// - /// Valid values include: - /// - /// - `owning`: Generated types will be composed entirely of owning fields, - /// regardless of whether they are used as parameters to imports or not. - /// - /// - `borrowing`: Generated types used as parameters to imports will be - /// "deeply borrowing", i.e. contain references rather than owned values - /// when applicable. - /// - /// - `borrowing-duplicate-if-necessary`: As above, but generating distinct - /// types for borrowing and owning, if necessary. - #[cfg_attr(feature = "clap", arg(long, default_value_t = Ownership::Owning))] - pub ownership: Ownership, - /// Set API style to symmetric or asymmetric #[cfg_attr( feature = "clap", @@ -223,6 +144,24 @@ pub struct Opts { )] pub api_style: APIStyle, + /// Whether to generate owning or borrowing type definitions for `record` arguments to imported functions. + /// + /// Valid values include: + /// + /// - `owning`: Generated types will be composed entirely of owning fields, + /// regardless of whether they are used as parameters to imports or not. + /// + /// - `coarse-borrowing`: Generated types used as parameters to imports will be + /// "deeply borrowing", i.e. contain references rather than owned values, + /// so long as they don't contain resources, in which case they will be + /// owning. + /// + /// - `fine-borrowing": Generated types used as parameters to imports will be + /// "deeply borrowing", i.e. contain references rather than owned values + /// for all fields that are not resources, which will be owning. + #[cfg_attr(feature = "clap", arg(long, default_value_t = Ownership::Owning))] + pub ownership: Ownership, + /// Where to place output files #[cfg_attr(feature = "clap", arg(skip))] out_dir: Option, @@ -262,6 +201,50 @@ impl FromStr for APIStyle { } } +#[derive(Default, Debug, Clone, Copy)] +pub enum Ownership { + /// Generated types will be composed entirely of owning fields, regardless + /// of whether they are used as parameters to imports or not. + #[default] + Owning, + + /// Generated types used as parameters to imports will be "deeply + /// borrowing", i.e. contain references rather than owned values when + /// applicable. + CoarseBorrowing, + + /// Generated types used as parameters to imports will be "deeply + /// borrowing", i.e. contain references rather than owned values + /// for all fields that are not resources, which will be owning. + FineBorrowing, +} + +impl FromStr for Ownership { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "owning" => Ok(Self::Owning), + "coarse-borrowing" => Ok(Self::CoarseBorrowing), + "fine-borrowing" => Ok(Self::FineBorrowing), + _ => Err(format!( + "unrecognized ownership: `{s}`; \ + expected `owning`, `coarse-borrowing`, or `fine-borrowing`" + )), + } + } +} + +impl fmt::Display for Ownership { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(match self { + Ownership::Owning => "owning", + Ownership::CoarseBorrowing => "coarse-borrowing", + Ownership::FineBorrowing => "fine-borrowing", + }) + } +} + impl Opts { pub fn build(mut self, out_dir: Option<&PathBuf>) -> Box { let mut r = Cpp::new(); @@ -469,6 +452,7 @@ impl WorldGenerator for Cpp { fn preprocess(&mut self, resolve: &Resolve, world: WorldId) { let name = &resolve.worlds[world].name; self.world = name.to_string(); + self.types.analyze(resolve); self.world_id = Some(world); uwriteln!( self.c_src_head, @@ -723,7 +707,6 @@ impl WorldGenerator for Cpp { if self.dependencies.needs_wit { files.push(&format!("wit.h"), include_bytes!("../helper-types/wit.h")); } - Ok(()) } } @@ -765,15 +748,11 @@ impl SourceWithState { } } for _i in same..self.namespace.len() { - uwrite!(self.src, "}}"); - } - if same != self.namespace.len() { - // finish closing brackets by a newline - uwriteln!(self.src, ""); + uwrite!(self.src, "}}\n"); } self.namespace.truncate(same); for i in target.iter().skip(same) { - uwrite!(self.src, "namespace {} {{", i); + uwrite!(self.src, "namespace {} {{\n", i); self.namespace.push(i.clone()); } } @@ -1129,7 +1108,7 @@ impl CppInterfaceGenerator<'_> { cpp_sig .arguments .iter() - .map(|(arg, _)| arg.clone()) + .map(|(arg, _)| format!("std::move({})", arg)) .collect::>() .join(", ") ); @@ -1405,6 +1384,36 @@ impl CppInterfaceGenerator<'_> { } } + fn scoped_record_name( + &self, + id: TypeId, + from_namespace: &[String], + guest_export: bool, + flavor: Flavor, + ) -> String { + let name = self.scoped_type_name(id, from_namespace, guest_export); + + if let Flavor::Argument(AbiVariant::GuestImport) = flavor { + match self.gen.opts.ownership { + Ownership::Owning => { + format!("{}", name) + } + Ownership::CoarseBorrowing => { + if self.gen.types.get(id).has_own_handle { + format!("{}", name) + } else { + format!("{}Param", name) + } + } + Ownership::FineBorrowing => { + format!("{}Param", name) + } + } + } else { + name + } + } + fn scoped_type_name( &self, id: TypeId, @@ -1459,11 +1468,13 @@ impl CppInterfaceGenerator<'_> { } }, Type::Id(id) => match &self.resolve.types[*id].kind { - TypeDefKind::Record(_r) => { - self.scoped_type_name(*id, from_namespace, NOT_IN_EXPORTED_NAMESPACE) + TypeDefKind::Record(_) => { + let guest_export = self.is_exported_type(&self.resolve.types[*id]); + self.scoped_record_name(*id, from_namespace, guest_export, flavor) } TypeDefKind::Resource => { - self.scoped_type_name(*id, from_namespace, flavor.is_guest_export()) + let guest_export = self.is_exported_type(&self.resolve.types[*id]); + self.scoped_type_name(*id, from_namespace, guest_export) } TypeDefKind::Handle(Handle::Own(id)) => { let mut typename = self.type_name(&Type::Id(*id), from_namespace, flavor); @@ -1484,6 +1495,10 @@ impl CppInterfaceGenerator<'_> { (false, Flavor::BorrowedArgument) => (), (_, _) => todo!(), } + let ty = &self.resolve.types[*id]; + if matches!(flavor, Flavor::InStruct) && self.is_exported_type(ty) { + typename.push_str(&format!("::{OWNED_CLASS_NAME}")) + } typename } TypeDefKind::Handle(Handle::Borrow(id)) => { @@ -1492,7 +1507,9 @@ impl CppInterfaceGenerator<'_> { + ">" } TypeDefKind::Flags(_f) => { - self.scoped_type_name(*id, from_namespace, NOT_IN_EXPORTED_NAMESPACE) + let ty = &self.resolve.types[*id]; + let guest_export = self.is_exported_type(ty); + self.scoped_type_name(*id, from_namespace, guest_export) } TypeDefKind::Tuple(t) => { let types = t.types.iter().fold(String::new(), |mut a, b| { @@ -1505,10 +1522,14 @@ impl CppInterfaceGenerator<'_> { String::from("std::tuple<") + &types + ">" } TypeDefKind::Variant(_v) => { - self.scoped_type_name(*id, from_namespace, NOT_IN_EXPORTED_NAMESPACE) + let ty = &self.resolve.types[*id]; + let guest_export = self.is_exported_type(ty); + self.scoped_type_name(*id, from_namespace, guest_export) } TypeDefKind::Enum(_e) => { - self.scoped_type_name(*id, from_namespace, NOT_IN_EXPORTED_NAMESPACE) + let ty = &self.resolve.types[*id]; + let guest_export = self.is_exported_type(ty); + self.scoped_type_name(*id, from_namespace, guest_export) } TypeDefKind::Option(o) => { self.gen.dependencies.needs_optional = true; @@ -1537,7 +1558,13 @@ impl CppInterfaceGenerator<'_> { || self.gen.opts.api_style == APIStyle::Symmetric => { self.gen.dependencies.needs_span = true; - format!("std::span<{inner} const>") + // If the list has an owning handle, it must support moving, so can't be const + let constness = if self.gen.types.get(*id).has_own_handle { + "" + } else { + " const" + }; + format!("std::span<{inner}{constness}>") } Flavor::Argument(AbiVariant::GuestExport) => { self.gen.dependencies.needs_wit = true; @@ -1607,6 +1634,48 @@ impl CppInterfaceGenerator<'_> { } } } + + fn type_record_param( + &mut self, + id: TypeId, + name: &str, + record: &wit_bindgen_core::wit_parser::Record, + namespc: &[String], + ) { + let (flavor, needs_param_type) = { + match self.gen.opts.ownership { + Ownership::Owning => (Flavor::InStruct, false), + Ownership::CoarseBorrowing => { + if self.gen.types.get(id).has_own_handle { + (Flavor::InStruct, false) + } else { + (Flavor::BorrowedArgument, true) + } + } + Ownership::FineBorrowing => (Flavor::BorrowedArgument, true), + } + }; + + if needs_param_type { + let pascal = format!("{name}-param").to_pascal_case(); + + uwriteln!(self.gen.h_src.src, "struct {pascal} {{"); + for field in record.fields.iter() { + let typename = self.type_name(&field.ty, namespc, flavor); + let fname = field.name.to_snake_case(); + uwriteln!(self.gen.h_src.src, "{typename} {fname};"); + } + uwriteln!(self.gen.h_src.src, "}};"); + } + } + + fn is_exported_type(&self, ty: &TypeDef) -> bool { + if let TypeOwner::Interface(intf) = ty.owner { + !self.gen.imported_interfaces.contains(&intf) + } else { + true + } + } } impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> { @@ -1622,16 +1691,14 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: &wit_bindgen_core::wit_parser::Docs, ) { let ty = &self.resolve.types[id]; - let namespc = namespace( - self.resolve, - &ty.owner, - NOT_IN_EXPORTED_NAMESPACE, - &self.gen.opts, - ); - if self.gen.is_first_definition(&namespc, name) { + let guest_export = self.is_exported_type(ty); + let namespc = namespace(self.resolve, &ty.owner, guest_export, &self.gen.opts); + + if self.gen.is_first_definition(&namespc, &name) { self.gen.h_src.change_namespace(&namespc); Self::docs(&mut self.gen.h_src.src, docs); let pascal = name.to_pascal_case(); + uwriteln!(self.gen.h_src.src, "struct {pascal} {{"); for field in record.fields.iter() { Self::docs(&mut self.gen.h_src.src, &field.docs); @@ -1640,6 +1707,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> uwriteln!(self.gen.h_src.src, "{typename} {fname};"); } uwriteln!(self.gen.h_src.src, "}};"); + self.type_record_param(id, name, record, namespc.as_slice()); } } @@ -1805,12 +1873,8 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: &wit_bindgen_core::wit_parser::Docs, ) { let ty = &self.resolve.types[id]; - let namespc = namespace( - self.resolve, - &ty.owner, - NOT_IN_EXPORTED_NAMESPACE, - &self.gen.opts, - ); + let guest_export = self.is_exported_type(ty); + let namespc = namespace(self.resolve, &ty.owner, guest_export, &self.gen.opts); if self.gen.is_first_definition(&namespc, name) { self.gen.h_src.change_namespace(&namespc); Self::docs(&mut self.gen.h_src.src, docs); @@ -1850,12 +1914,8 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: &wit_bindgen_core::wit_parser::Docs, ) { let ty = &self.resolve.types[id]; - let namespc = namespace( - self.resolve, - &ty.owner, - NOT_IN_EXPORTED_NAMESPACE, - &self.gen.opts, - ); + let guest_export = self.is_exported_type(ty); + let namespc = namespace(self.resolve, &ty.owner, guest_export, &self.gen.opts); if self.gen.is_first_definition(&namespc, name) { self.gen.h_src.change_namespace(&namespc); Self::docs(&mut self.gen.h_src.src, docs); @@ -1912,12 +1972,8 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: &wit_bindgen_core::wit_parser::Docs, ) { let ty = &self.resolve.types[id]; - let namespc = namespace( - self.resolve, - &ty.owner, - NOT_IN_EXPORTED_NAMESPACE, - &self.gen.opts, - ); + let guest_export = self.is_exported_type(ty); + let namespc = namespace(self.resolve, &ty.owner, guest_export, &self.gen.opts); if self.gen.is_first_definition(&namespc, name) { self.gen.h_src.change_namespace(&namespc); let pascal = name.to_pascal_case(); @@ -1944,12 +2000,8 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: &wit_bindgen_core::wit_parser::Docs, ) { let ty = &self.resolve.types[id]; - let namespc = namespace( - self.resolve, - &ty.owner, - NOT_IN_EXPORTED_NAMESPACE, - &self.gen.opts, - ); + let guest_export = self.is_exported_type(ty); + let namespc = namespace(self.resolve, &ty.owner, guest_export, &self.gen.opts); self.gen.h_src.change_namespace(&namespc); let pascal = name.to_pascal_case(); Self::docs(&mut self.gen.h_src.src, docs); @@ -2095,48 +2147,6 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { operands[0] ); } - - fn has_resources2(&self, ty: &Type) -> bool { - match ty { - Type::Bool - | Type::U8 - | Type::U16 - | Type::U32 - | Type::U64 - | Type::S8 - | Type::S16 - | Type::S32 - | Type::S64 - | Type::F32 - | Type::F64 - | Type::Char => false, - Type::String => false, - Type::Id(id) => self.has_resources(id), - Type::ErrorContext => todo!(), - } - } - fn has_resources(&self, id: &TypeId) -> bool { - match &self.gen.resolve.types[*id].kind { - TypeDefKind::Record(_) => todo!(), - TypeDefKind::Resource => true, - TypeDefKind::Handle(_) => true, - TypeDefKind::Flags(_) => false, - TypeDefKind::Tuple(t) => t.types.iter().any(|ty| self.has_resources2(ty)), - TypeDefKind::Variant(_) => todo!(), - TypeDefKind::Enum(_) => false, - TypeDefKind::Option(_) => todo!(), - TypeDefKind::Result(_) => todo!(), - TypeDefKind::List(_) => todo!(), - TypeDefKind::Future(_) => todo!(), - TypeDefKind::Stream(_) => todo!(), - TypeDefKind::Type(ty) => match ty { - Type::Id(id) => self.has_resources(id), - _ => false, - }, - TypeDefKind::FixedSizeList(_, _) => todo!(), - TypeDefKind::Unknown => todo!(), - } - } } fn move_if_necessary(arg: &str) -> String { @@ -2255,7 +2265,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let val = format!("vec{}", tmp); let ptr = format!("ptr{}", tmp); let len = format!("len{}", tmp); - self.push_str(&format!("auto const&{} = {};\n", val, operands[0])); + self.push_str(&format!("auto&& {} = {};\n", val, operands[0])); self.push_str(&format!( "auto {} = ({})({}.data());\n", ptr, @@ -2276,7 +2286,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let val = format!("vec{}", tmp); let ptr = format!("ptr{}", tmp); let len = format!("len{}", tmp); - self.push_str(&format!("auto const&{} = {};\n", val, operands[0])); + self.push_str(&format!("auto&& {} = {};\n", val, operands[0])); self.push_str(&format!( "auto {} = ({})({}.data());\n", ptr, @@ -2292,15 +2302,14 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } results.push(len); } - abi::Instruction::ListLower { - element: _, - realloc, - } => { + abi::Instruction::ListLower { element, realloc } => { let tmp = self.tmp(); + let body = self.blocks.pop().unwrap(); let val = format!("vec{}", tmp); let ptr = format!("ptr{}", tmp); let len = format!("len{}", tmp); - self.push_str(&format!("auto const&{} = {};\n", val, operands[0])); + let size = self.gen.sizes.size(element); + self.push_str(&format!("auto&& {} = {};\n", val, operands[0])); self.push_str(&format!( "auto {} = ({})({}.data());\n", ptr, @@ -2308,6 +2317,14 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { val )); self.push_str(&format!("auto {} = (size_t)({}.size());\n", len, val)); + self.push_str(&format!("for (size_t i = 0; i < {len}; ++i) {{\n")); + self.push_str(&format!( + "auto base = {ptr} + i * {size};\n", + size = size.format(POINTER_SIZE_EXPRESSION) + )); + self.push_str(&format!("auto&& IterElem = {val}[i];\n")); + self.push_str(&format!("{}\n", body.0)); + self.push_str("}\n"); if realloc.is_none() { results.push(ptr); } else { @@ -2382,6 +2399,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { r#"auto {result} = wit::vector<{vtype}>::allocate({len}); "#, )); + if self.gen.gen.opts.api_style == APIStyle::Symmetric && matches!(self.variant, AbiVariant::GuestExport) { @@ -2404,6 +2422,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { // inplace construct uwriteln!(self.src, "{result}.initialize(i, std::move(e{tmp}));"); uwriteln!(self.src, "}}"); + if self.gen.gen.opts.api_style == APIStyle::Symmetric && matches!(self.variant, AbiVariant::GuestExport) { @@ -3127,7 +3146,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { return false; } match ty { - Type::Id(id) => !self.has_resources(id), + Type::Id(id) => !self.gen.gen.types.get(*id).has_resource, _ => true, } } diff --git a/tests/runtime/cpp/param-ownership/cpp/exports-test-ownership-both_list_and_resource-TheResource.h b/tests/runtime/cpp/param-ownership/cpp/exports-test-ownership-both_list_and_resource-TheResource.h new file mode 100644 index 000000000..d6d9073ee --- /dev/null +++ b/tests/runtime/cpp/param-ownership/cpp/exports-test-ownership-both_list_and_resource-TheResource.h @@ -0,0 +1,45 @@ +#pragma once +#include "wit.h" +#include +#include +#include +/* User class definition file, autogenerated once, then user modified + * Updated versions of this file are generated into TheResource.template. + */ +namespace exports { +namespace test { +namespace ownership { +namespace both_list_and_resource { +class TheResource : public wit::ResourceExportBase { + +public: + static void Dtor(both_list_and_resource::TheResource *self) { delete self; } + TheResource(wit::vector the_list) { + the_list_ = std::move(the_list); + } + static Owned New(wit::vector the_list) { + return Owned(new TheResource(std::move(the_list))); + } + wit::vector ToUpper() { + wit::vector result = + wit::vector::allocate(the_list_.size()); + for (size_t i = 0; i < the_list_.size(); ++i) { + auto str = the_list_[i]; + for (char &c : str) { + c = std::toupper(c); + } + result.initialize(i, std::move(str)); + } + return result; + } + static int32_t ResourceNew(both_list_and_resource::TheResource *self); + static TheResource *ResourceRep(int32_t id); + static void ResourceDrop(int32_t id); + +private: + wit::vector the_list_; +}; +} // namespace both_list_and_resource +} // namespace ownership +} // namespace test +} // namespace exports diff --git a/tests/runtime/cpp/param-ownership/runner-owning.cpp b/tests/runtime/cpp/param-ownership/runner-owning.cpp index 82444bf1b..fe6e4a66d 100644 --- a/tests/runtime/cpp/param-ownership/runner-owning.cpp +++ b/tests/runtime/cpp/param-ownership/runner-owning.cpp @@ -19,13 +19,21 @@ int main() { assert(res[1][0].get_view() == "VALUE3"); assert(res[1][1].get_view() == "VALUE4"); - test::ownership::Thing thing; - thing.name = wit::string::from_view("thing"); - thing.value = wit::vector::allocate(2); - thing.value.initialize(0, wit::string::from_view("value1")); - thing.value.initialize(1, wit::string::from_view("value2")); - test::ownership::Bar(thing); - auto result = test::ownership::Baz(thing); + test::ownership::Thing thing1 { + wit::string::from_view("thing"), + wit::vector::allocate(2) + }; + thing1.value.initialize(0, wit::string::from_view("value1")); + thing1.value.initialize(1, wit::string::from_view("value2")); + test::ownership::Bar(std::move(thing1)); + + test::ownership::Thing thing2 { + wit::string::from_view("thing"), + wit::vector::allocate(2) + }; + thing2.value.initialize(0, wit::string::from_view("value1")); + thing2.value.initialize(1, wit::string::from_view("value2")); + auto result = test::ownership::Baz(std::move(thing2)); assert(result.name.get_view() == "THING"); assert(result.value.size() == 2); assert(result.value[0].get_view() == "VALUE1"); diff --git a/tests/runtime/cpp/param-ownership/test.cpp b/tests/runtime/cpp/param-ownership/test.cpp new file mode 100644 index 000000000..250be578c --- /dev/null +++ b/tests/runtime/cpp/param-ownership/test.cpp @@ -0,0 +1,47 @@ +#include "test_cpp.h" +#include +#include +#include +namespace exports::test::ownership { +wit::vector> +Foo(wit::vector> a) { + for (size_t i = 0; i < a.size(); ++i) { + for (size_t j = 0; j < a[i].size(); ++j) { + for (char &c : a[i][j]) { + c = std::toupper(c); + } + } + } + return a; +} + +void Bar(Thing a) { + assert(a.name.get_view() == "thing"); + assert(a.value.size() == 2); + assert(a.value[0].get_view() == "value1"); + assert(a.value[1].get_view() == "value2"); +} +Thing Baz(Thing a) { + for (char &c : a.name) { + c = std::toupper(c); + } + for (size_t i = 0; i < a.value.size(); ++i) { + for (char &c : a.value[i]) { + c = std::toupper(c); + } + } + return a; +} +namespace both_list_and_resource { +void ListAndResource(Thing a) { + auto upper = a.b->ToUpper(); + assert(upper.size() == a.a.size()); + for (size_t i = 0; i < a.a.size(); ++i) { + auto v1 = a.a[i].get_view(); + auto v2 = upper[i].get_view(); + assert(std::equal(v1.begin(), v1.end(), v2.begin(), v2.end(), + [](char c1, char c2) { return std::toupper(c1) == c2; })); + } +} +} // namespace both_list_and_resource +} // namespace exports::test::ownership \ No newline at end of file diff --git a/tests/runtime/cpp/param-ownership/test.wit b/tests/runtime/cpp/param-ownership/test.wit new file mode 100644 index 000000000..0626c768b --- /dev/null +++ b/tests/runtime/cpp/param-ownership/test.wit @@ -0,0 +1,66 @@ +package test:ownership; + +interface both-list-and-resource { + resource the-resource { + constructor(the-list: list); + to-upper: func() -> list; + } + record thing { + a: list, + b: the-resource, + } + + list-and-resource: func(a: thing); +} + +world runner { + import lists: interface { + foo: func(a: list>) -> list>; + } + + import thing-in: interface { + record thing { + name: string, + value: list + } + + bar: func(a: thing); + } + + import thing-in-and-out: interface { + record thing { + name: string, + value: list + } + + baz: func(a: thing) -> thing; + } + + import both-list-and-resource; +} + +world test { + export lists: interface { + foo: func(a: list>) -> list>; + } + + export thing-in: interface { + record thing { + name: string, + value: list + } + + bar: func(a: thing); + } + + export thing-in-and-out: interface { + record thing { + name: string, + value: list + } + + baz: func(a: thing) -> thing; + } + + export both-list-and-resource; +} diff --git a/tests/runtime/lists/test.cpp b/tests/runtime/lists/test.cpp index 0324b652a..c4073bdc9 100644 --- a/tests/runtime/lists/test.cpp +++ b/tests/runtime/lists/test.cpp @@ -33,7 +33,7 @@ static bool equal(std::span const&a, std::vector const& b) { } template static bool equal(wit::vector const&a, std::vector const& b) { - return equal(a.get_view(), std::span(b)); + return equal(a.get_view(), std::span(b)); } template static bool equal(std::tuple const&a, std::tuple const& b) { @@ -82,7 +82,6 @@ void exports::test::lists::to_test::ListParam4(wit::vector> a) { } diff --git a/tests/runtime/records/test.cpp b/tests/runtime/records/test.cpp index 6d69cc193..1f134ee2d 100644 --- a/tests/runtime/records/test.cpp +++ b/tests/runtime/records/test.cpp @@ -1,30 +1,32 @@ #include #include -std::tuple exports::test::records::to_test::MultipleResults() { +namespace test_exports = ::exports::test::records::to_test; + +std::tuple test_exports::MultipleResults() { return std::tuple(4, 5); } -std::tuple exports::test::records::to_test::SwapTuple(std::tuple a) { +std::tuple test_exports::SwapTuple(std::tuple a) { return std::tuple(std::get<1>(a), std::get<0>(a)); } -test::records::to_test::F1 exports::test::records::to_test::RoundtripFlags1(::test::records::to_test::F1 a) { +test_exports::F1 test_exports::RoundtripFlags1(test_exports::F1 a) { return a; } -test::records::to_test::F2 exports::test::records::to_test::RoundtripFlags2(::test::records::to_test::F2 a) { +test_exports::F2 test_exports::RoundtripFlags2(test_exports::F2 a) { return a; } -std::tuple exports::test::records::to_test::RoundtripFlags3(::test::records::to_test::Flag8 a, ::test::records::to_test::Flag16 b, ::test::records::to_test::Flag32 c) { - return std::tuple<::test::records::to_test::Flag8, ::test::records::to_test::Flag16, ::test::records::to_test::Flag32>(a, b, c); +std::tuple test_exports::RoundtripFlags3(test_exports::Flag8 a, test_exports::Flag16 b, test_exports::Flag32 c) { + return std::tuple(a, b, c); } -test::records::to_test::R1 exports::test::records::to_test::RoundtripRecord1(::test::records::to_test::R1 a) { +test_exports::R1 test_exports::RoundtripRecord1(R1 a) { return a; } -std::tuple exports::test::records::to_test::Tuple1(std::tuple a) { +std::tuple test_exports::Tuple1(std::tuple a) { return std::tuple(std::get<0>(a)); } diff --git a/tests/runtime/resource_borrow_in_record/cpp/exports-test-resource_borrow_in_record-to_test-Thing.h b/tests/runtime/resource_borrow_in_record/cpp/exports-test-resource_borrow_in_record-to_test-Thing.h new file mode 100644 index 000000000..fbd735158 --- /dev/null +++ b/tests/runtime/resource_borrow_in_record/cpp/exports-test-resource_borrow_in_record-to_test-Thing.h @@ -0,0 +1,32 @@ +#pragma once +#include "wit.h" +#include +#include +#include +#include +/* User class definition file, autogenerated once, then user modified + * Updated versions of this file are generated into Thing.template. + */ +namespace exports { +namespace test { +namespace resource_borrow_in_record { +namespace to_test { +class Thing : public wit::ResourceExportBase { + +public: + static void Dtor(to_test::Thing *self) { delete self; } + Thing(wit::string s) : contents(s.to_string()) {} + static Owned New(wit::string s) { return Owned(new Thing(std::move(s))); } + wit::string Get() const { return wit::string::from_view(std::string_view(contents)); } + static int32_t ResourceNew(to_test::Thing *self); + static Thing *ResourceRep(int32_t id); + static void ResourceDrop(int32_t id); + +private: + std::string contents; +}; + +} // namespace to_test +} // namespace resource_borrow_in_record +} // namespace test +} // namespace exports diff --git a/tests/runtime/resource_borrow_in_record/runner.cpp b/tests/runtime/resource_borrow_in_record/runner.cpp new file mode 100644 index 000000000..074dc49d2 --- /dev/null +++ b/tests/runtime/resource_borrow_in_record/runner.cpp @@ -0,0 +1,12 @@ +#include + +namespace test_imports = ::test::resource_borrow_in_record::to_test; +#include +int main() { + auto thing1 = test_imports::Thing("Bonjour"); + auto thing2 = test_imports::Thing("mon cher"); + std::cout << thing1.Get().to_string() << ' ' << thing1.get_handle() << std::endl; + std::cout << thing2.Get().to_string() << ' ' << thing2.get_handle() << std::endl; + std::array things {test_imports::Foo{thing1}, test_imports::Foo{thing2}}; + auto result = test_imports::Test(things); +} diff --git a/tests/runtime/resource_borrow_in_record/test.cpp b/tests/runtime/resource_borrow_in_record/test.cpp new file mode 100644 index 000000000..572766076 --- /dev/null +++ b/tests/runtime/resource_borrow_in_record/test.cpp @@ -0,0 +1,12 @@ +#include + +namespace test_exports = ::exports::test::resource_borrow_in_record::to_test; + +wit::vector test_exports::Test(wit::vector list) { + auto result = wit::vector::allocate(list.size()); + for (size_t i = 0; i < list.size(); ++i) { + auto str = wit::string::from_view(std::string_view(list[i].thing.get().Get().to_string() + " test")); + result.initialize(i, test_exports::Thing::New(std::move(str))); + } + return result; +} \ No newline at end of file diff --git a/tests/runtime/results/intermediate.cpp b/tests/runtime/results/intermediate.cpp index 5aca8eaf6..89b46fc36 100644 --- a/tests/runtime/results/intermediate.cpp +++ b/tests/runtime/results/intermediate.cpp @@ -6,51 +6,53 @@ bool equal(T const&a, T const&b) { return a==b; } -std::expected exports::test::results::test::StringError(float a) { - return ::test::results::test::StringError(a); +namespace test_imports = ::test::results::test; +namespace test_exports = ::exports::test::results::test; + +std::expected test_exports::StringError(float a) { + return test_imports::StringError(a); +} + +test_exports::E to_exports_e(test_imports::E e) { + switch (e) { + case test_imports::E::kA: return test_exports::E::kA; + case test_imports::E::kB: return test_exports::E::kB; + case test_imports::E::kC: return test_exports::E::kC; + } } -std::expected exports::test::results::test::EnumError(float a) { - auto result = ::test::results::test::EnumError(a); +std::expected test_exports::EnumError(float a) { + auto result = test_imports::EnumError(a); if (result.has_value()) { return result.value(); } - return std::unexpected(result.error()); - // if (result.error()==::test::results::test::E::kA) { return std::unexpected(::test::results::test::E::kA); } - // if (result.error()==::test::results::test::E::kB) { return std::unexpected(::test::results::test::E::kB); } - // if (result.error()==::test::results::test::E::kC) { return std::unexpected(::test::results::test::E::kC); } + return std::unexpected(to_exports_e(result.error())); } -std::expected exports::test::results::test::RecordError(float a) { - auto result = ::test::results::test::RecordError(a); +std::expected test_exports::RecordError(float a) { + auto result = test_imports::RecordError(a); if (result.has_value()) { return result.value(); } - return std::unexpected(::test::results::test::E2{ result.error().line, result.error().column }); + return std::unexpected(test_exports::E2{ result.error().line, result.error().column }); } -std::expected exports::test::results::test::VariantError(float a) { - auto result = ::test::results::test::VariantError(a); +template +struct overloaded : Fs... { + using Fs::operator()...; +}; +template +overloaded(Fs...) -> overloaded; + +std::expected test_exports::VariantError(float a) { + auto result = test_imports::VariantError(a); if (result.has_value()) { return result.value(); } - return std::unexpected(result.error()); - - // match test_imports::variant_error(a) { - // Ok(b) => Ok(b), - // Err(test_imports::E3::E1(test_imports::E::A)) => { - // Err(test_exports::E3::E1(test_exports::E::A)) - // } - // Err(test_imports::E3::E1(test_imports::E::B)) => { - // Err(test_exports::E3::E1(test_exports::E::B)) - // } - // Err(test_imports::E3::E1(test_imports::E::C)) => { - // Err(test_exports::E3::E1(test_exports::E::C)) - // } - // Err(test_imports::E3::E2(test_imports::E2 { line, column })) => { - // Err(test_exports::E3::E2(test_exports::E2 { line, column })) - // } - // } -} - -std::expected exports::test::results::test::EmptyError(uint32_t a) { - return ::test::results::test::EmptyError(a); -} - -std::expected, wit::string> exports::test::results::test::DoubleError(uint32_t a) { - return ::test::results::test::DoubleError(a); + return std::visit(overloaded{ + [](test_imports::E3::E1 const& e1) { return std::unexpected(test_exports::E3{test_exports::E3::E1{to_exports_e(e1.value)}}); }, + [](test_imports::E3::E2 const& e2) { return std::unexpected(test_exports::E3{test_exports::E3::E2{e2.value.line, e2.value.column}}); } + }, result.error().variants); +} + +std::expected test_exports::EmptyError(uint32_t a) { + return test_imports::EmptyError(a); +} + +std::expected, wit::string> test_exports::DoubleError(uint32_t a) { + return test_imports::DoubleError(a); } diff --git a/tests/runtime/results/leaf.cpp b/tests/runtime/results/leaf.cpp index edc91da94..d37b8fe70 100644 --- a/tests/runtime/results/leaf.cpp +++ b/tests/runtime/results/leaf.cpp @@ -6,7 +6,9 @@ bool equal(T const&a, T const&b) { return a==b; } -std::expected exports::test::results::test::StringError(float a) { +namespace test_exports = ::exports::test::results::test; + +std::expected test_exports::StringError(float a) { if (a==0.0) { return std::unexpected(wit::string::from_view("zero")); } @@ -15,43 +17,43 @@ std::expected exports::test::results::test::StringError(floa } } -std::expected exports::test::results::test::EnumError(float a) { +std::expected test_exports::EnumError(float a) { if (a==0.0) { - return std::unexpected(::test::results::test::E::kA); + return std::unexpected(test_exports::E::kA); } else { return a; } } -std::expected exports::test::results::test::RecordError(float a) { +std::expected test_exports::RecordError(float a) { if (a==0.0) { - return std::unexpected(::test::results::test::E2{420, 0}); + return std::unexpected(test_exports::E2{420, 0}); } else if (a==1.0) { - return std::unexpected(::test::results::test::E2{77, 2}); + return std::unexpected(test_exports::E2{77, 2}); } else { return a; } } -std::expected exports::test::results::test::VariantError(float a) { +std::expected test_exports::VariantError(float a) { if (a==0.0) { - return std::unexpected(::test::results::test::E3{::test::results::test::E3::E2{::test::results::test::E2{420, 0}}}); + return std::unexpected(test_exports::E3{test_exports::E3::E2{test_exports::E2{420, 0}}}); } else if (a==1.0) { - return std::unexpected(::test::results::test::E3{::test::results::test::E3::E1{::test::results::test::E::kB}}); + return std::unexpected(test_exports::E3{test_exports::E3::E1{test_exports::E::kB}}); } else if (a==2.0) { - return std::unexpected(::test::results::test::E3{::test::results::test::E3::E1{::test::results::test::E::kC}}); + return std::unexpected(test_exports::E3{test_exports::E3::E1{test_exports::E::kC}}); } else { return a; } } -std::expected exports::test::results::test::EmptyError(uint32_t a) { +std::expected test_exports::EmptyError(uint32_t a) { if (a==0) { return std::unexpected(wit::Void{}); } @@ -63,7 +65,7 @@ std::expected exports::test::results::test::EmptyError(uint } } -std::expected, wit::string> exports::test::results::test::DoubleError(uint32_t a) { +std::expected, wit::string> test_exports::DoubleError(uint32_t a) { if (a==0) { return std::expected(); } diff --git a/tests/runtime/variants/test.cpp b/tests/runtime/variants/test.cpp index c1eda6b9c..f0d6270a8 100644 --- a/tests/runtime/variants/test.cpp +++ b/tests/runtime/variants/test.cpp @@ -2,7 +2,9 @@ #include #include -std::optional exports::test::variants::to_test::RoundtripOption(std::optional a) { +namespace test_exports = ::exports::test::variants::to_test; + +std::optional test_exports::RoundtripOption(std::optional a) { if (a.has_value()) { return std::optional(a); } else { @@ -10,7 +12,7 @@ std::optional exports::test::variants::to_test::RoundtripOption(std::op } } -std::expected exports::test::variants::to_test::RoundtripResult(std::expected a) { +std::expected test_exports::RoundtripResult(std::expected a) { if (a.has_value()) { return std::expected(double(a.value())); } else { @@ -18,26 +20,26 @@ std::expected exports::test::variants::to_test::RoundtripResult } } -::test::variants::to_test::E1 exports::test::variants::to_test::RoundtripEnum(::test::variants::to_test::E1 a) { +test_exports::E1 test_exports::RoundtripEnum(test_exports::E1 a) { return a; } -bool exports::test::variants::to_test::InvertBool(bool a) { +bool test_exports::InvertBool(bool a) { return !a; } -std::tuple<::test::variants::to_test::C1, ::test::variants::to_test::C2, ::test::variants::to_test::C3, ::test::variants::to_test::C4, ::test::variants::to_test::C5, ::test::variants::to_test::C6> exports::test::variants::to_test::VariantCasts(std::tuple<::test::variants::to_test::C1, ::test::variants::to_test::C2, ::test::variants::to_test::C3, ::test::variants::to_test::C4, ::test::variants::to_test::C5, ::test::variants::to_test::C6> a) { +std::tuple test_exports::VariantCasts(std::tuple a) { return a; } -std::tuple<::test::variants::to_test::Z1, ::test::variants::to_test::Z2, ::test::variants::to_test::Z3, ::test::variants::to_test::Z4> exports::test::variants::to_test::VariantZeros(std::tuple<::test::variants::to_test::Z1, ::test::variants::to_test::Z2, ::test::variants::to_test::Z3, ::test::variants::to_test::Z4> a) { +std::tuple test_exports::VariantZeros(std::tuple a) { return a; } -void exports::test::variants::to_test::VariantTypedefs(std::optional a, bool b, std::expected c) { +void test_exports::VariantTypedefs(std::optional a, bool b, std::expected c) { } -std::tuple, ::test::variants::to_test::MyErrno> exports::test::variants::to_test::VariantEnums(bool a, std::expected b, ::test::variants::to_test::MyErrno c) { - return std::tuple, ::test::variants::to_test::MyErrno>(a, b, c); +std::tuple, test_exports::MyErrno> test_exports::VariantEnums(bool a, std::expected b, test_exports::MyErrno c) { + return std::tuple, test_exports::MyErrno>(a, b, c); }