From 5a2ea0d3a176c261e46c44354081eb5ae7060d4b Mon Sep 17 00:00:00 2001 From: Martynas Gurskas Date: Wed, 13 Dec 2023 15:23:08 +0200 Subject: [PATCH 1/2] Finish custom type implementation Signed-off-by: Martynas Gurskas --- Cargo.lock | 11 +++ bindgen/src/bindings/cpp/gen_cpp/mod.rs | 21 ++++- bindgen/src/bindings/cpp/mod.rs | 2 +- bindgen/src/bindings/cpp/templates/custom.cpp | 38 +++++++++ bindgen/src/bindings/cpp/templates/custom.hpp | 20 +++-- bindgen/src/bindings/cpp/templates/types.cpp | 2 + .../src/bindings/cpp/templates/wrapper.hpp | 16 +++- cpp-tests/CMakeLists.txt | 1 + cpp-tests/tests/custom_types_builtin/main.cpp | 35 ++++++++ cpp-tests/tests/include/custom_string.hpp | 13 +++ fixtures/Cargo.toml | 2 +- fixtures/custom-types-builtin/Cargo.toml | 17 ++++ fixtures/custom-types-builtin/build.rs | 3 + .../src/custom_types_builtin.udl | 71 ++++++++++++++++ fixtures/custom-types-builtin/src/lib.rs | 85 +++++++++++++++++++ fixtures/custom-types-builtin/uniffi.toml | 5 ++ fixtures/src/lib.rs | 2 + 17 files changed, 328 insertions(+), 16 deletions(-) create mode 100644 bindgen/src/bindings/cpp/templates/custom.cpp create mode 100644 cpp-tests/tests/custom_types_builtin/main.cpp create mode 100644 cpp-tests/tests/include/custom_string.hpp create mode 100644 fixtures/custom-types-builtin/Cargo.toml create mode 100644 fixtures/custom-types-builtin/build.rs create mode 100644 fixtures/custom-types-builtin/src/custom_types_builtin.udl create mode 100644 fixtures/custom-types-builtin/src/lib.rs create mode 100644 fixtures/custom-types-builtin/uniffi.toml diff --git a/Cargo.lock b/Cargo.lock index e81d4d3..cb82f58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -870,6 +870,7 @@ dependencies = [ name = "uniffi-bindgen-cpp-fixtures" version = "1.0.0" dependencies = [ + "uniffi-custom-types-builtin", "uniffi-example-arithmetic", "uniffi-example-callbacks", "uniffi-example-custom-types", @@ -885,6 +886,16 @@ dependencies = [ "uniffi-fixture-trait-methods", ] +[[package]] +name = "uniffi-custom-types-builtin" +version = "0.1.0" +dependencies = [ + "once_cell", + "paste", + "thiserror", + "uniffi", +] + [[package]] name = "uniffi-example-arithmetic" version = "0.22.0" diff --git a/bindgen/src/bindings/cpp/gen_cpp/mod.rs b/bindgen/src/bindings/cpp/gen_cpp/mod.rs index 6eca9e1..9bc80ca 100644 --- a/bindgen/src/bindings/cpp/gen_cpp/mod.rs +++ b/bindgen/src/bindings/cpp/gen_cpp/mod.rs @@ -20,12 +20,15 @@ use serde::{Deserialize, Serialize}; use topological_sort::TopologicalSort; use uniffi_bindgen::{ interface::{AsType, Type, UniffiTrait}, - BindingsConfig, ComponentInterface, + BindingsConfig, ComponentInterface, backend::TemplateExpression, }; #[derive(Clone, Deserialize, Serialize, Debug, Default)] struct CustomTypesConfig { imports: Option>, + type_name: Option, + into_custom: TemplateExpression, + from_custom: TemplateExpression, } #[derive(Clone, Deserialize, Serialize, Debug, Default)] @@ -59,6 +62,7 @@ struct InternalTypeRenderer<'a> { #[template(syntax = "cpp", escape = "none", path = "types.cpp")] struct TypeRenderer<'a> { ci: &'a ComponentInterface, + config: &'a Config, } #[derive(Template)] @@ -83,10 +87,20 @@ struct CppWrapperHeader<'a> { impl<'a> CppWrapperHeader<'a> { fn new(ci: &'a ComponentInterface, config: &'a Config) -> Self { + let includes = config.custom_types.values().fold( + BTreeSet::new(), + |mut acc: BTreeSet, custom_type| { + if let Some(imports) = &custom_type.imports { + acc.extend(imports.iter().cloned()); + } + acc + }, + ); + Self { ci, config, - includes: RefCell::new(BTreeSet::new()), + includes: includes.into(), } } @@ -199,6 +213,7 @@ impl<'a> CppWrapperHeader<'a> { } } +#[allow(dead_code)] #[derive(Template)] #[template(syntax = "cpp", escape = "none", path = "wrapper.cpp")] struct CppWrapper<'a> { @@ -214,7 +229,7 @@ impl<'a> CppWrapper<'a> { ci, config, internal_type_helper_code: InternalTypeRenderer { ci }.render().unwrap(), - type_helper_code: TypeRenderer { ci }.render().unwrap(), + type_helper_code: TypeRenderer { ci, config }.render().unwrap(), } } } diff --git a/bindgen/src/bindings/cpp/mod.rs b/bindgen/src/bindings/cpp/mod.rs index 5794087..f4e641b 100644 --- a/bindgen/src/bindings/cpp/mod.rs +++ b/bindgen/src/bindings/cpp/mod.rs @@ -67,7 +67,7 @@ impl BindingGenerator for CppBindingGenerator { Ok(()) } - fn check_library_path(&self, library_path: &Utf8Path, cdylib_name: Option<&str>) -> Result<()> { + fn check_library_path(&self, _library_path: &Utf8Path, cdylib_name: Option<&str>) -> Result<()> { if cdylib_name.is_none() { bail!("A path to a library file is required to generate bindings"); } diff --git a/bindgen/src/bindings/cpp/templates/custom.cpp b/bindgen/src/bindings/cpp/templates/custom.cpp new file mode 100644 index 0000000..fa87b27 --- /dev/null +++ b/bindgen/src/bindings/cpp/templates/custom.cpp @@ -0,0 +1,38 @@ +{%- match config.custom_types.get(name.as_str()) %} +{%- when Some with (type_config) %} +{%- match type_config.type_name %} +{%- when Some with (type_name) %} +{%- let ffi_type_name = builtin|ffi_type|ffi_type_name %} +{{ type_name }} uniffi::{{ ffi_converter_name }}::lift(RustBuffer buff) { + auto builtin_val = {{ builtin|lift_fn }}(buff); + + return {{ type_config.into_custom.render("builtin_val") }}; +} + +RustBuffer uniffi::{{ ffi_converter_name }}::lower(const {{ type_name }} &val) { + auto builtin_val = {{ type_config.from_custom.render("val") }}; + + return {{ builtin|lower_fn }}(builtin_val); +} + +{{ type_name }} uniffi::{{ ffi_converter_name }}::read(uniffi::RustStream &stream) { + auto builtin_val = {{ builtin|read_fn }}(stream); + + return {{ type_config.into_custom.render("builtin_val") }}; +} + +void uniffi::{{ ffi_converter_name }}::write(uniffi::RustStream &stream, const {{ type_name }} &val) { + auto builtin_val = {{ type_config.from_custom.render("val") }}; + + {{ builtin|write_fn }}(stream, builtin_val); +} + +int32_t uniffi::{{ ffi_converter_name }}::allocation_size(const {{ type_name }} &val) { + auto builtin_val = {{ type_config.from_custom.render("val") }}; + + return {{ builtin|allocation_size_fn }}(builtin_val); +} +{%- else %} +{%- endmatch %} +{%- else %} +{%- endmatch %} \ No newline at end of file diff --git a/bindgen/src/bindings/cpp/templates/custom.hpp b/bindgen/src/bindings/cpp/templates/custom.hpp index c9123c9..ec23745 100644 --- a/bindgen/src/bindings/cpp/templates/custom.hpp +++ b/bindgen/src/bindings/cpp/templates/custom.hpp @@ -1,12 +1,16 @@ {%- match config.custom_types.get(name.as_str()) %} {%- when Some with (type_config) %} -{%- match type_config.imports %} -{%- when Some with (includes) %} -{%- for include in includes %} -{{ self.add_include(include) }} -{%- endfor %} +{%- match type_config.type_name %} +{%- when Some with (type_name) %} +struct {{ typ|ffi_converter_name }} { + static {{ name }} lift(RustBuffer); + static RustBuffer lower(const {{ name }} &); + static {{ name }} read(uniffi::RustStream &); + static void write(RustStream &, const {{ name }} &); + static int32_t allocation_size(const {{ name }} &); +}; {%- else %} {%- endmatch %} -{%- when None %} -typedef {{ builtin|type_name }} {{ name }}; -{%- endmatch %} +{%- else -%} +typedef struct {{ builtin|ffi_converter_name }} {{ typ|ffi_converter_name }}; +{%- endmatch %} \ No newline at end of file diff --git a/bindgen/src/bindings/cpp/templates/types.cpp b/bindgen/src/bindings/cpp/templates/types.cpp index 2328890..4e6d803 100644 --- a/bindgen/src/bindings/cpp/templates/types.cpp +++ b/bindgen/src/bindings/cpp/templates/types.cpp @@ -34,6 +34,8 @@ {% include "duration_helper.cpp" %} {%- when Type::External { module_path, name, namespace, kind, tagged } %} {% include "ext_typ_tmpl.cpp" %} +{%- when Type::Custom { module_path, name, builtin } %} +{%- include "custom.cpp" %} {%- else %} {%- endmatch %} {%- endfor %} \ No newline at end of file diff --git a/bindgen/src/bindings/cpp/templates/wrapper.hpp b/bindgen/src/bindings/cpp/templates/wrapper.hpp index d81bb01..d18d7a4 100644 --- a/bindgen/src/bindings/cpp/templates/wrapper.hpp +++ b/bindgen/src/bindings/cpp/templates/wrapper.hpp @@ -18,6 +18,7 @@ #include <{{ include }}> {%- endfor %} + {%~ let namespace = ci.namespace() %} #include "{{ namespace }}_scaffolding.hpp" @@ -42,6 +43,17 @@ struct {{ name }}; struct {{ name }}; {%- when Type::CallbackInterface { module_path, name } %} struct {{ name }}; +{%- when Type::Custom { module_path, name, builtin } %} +{%- match config.custom_types.get(name.as_str()) %} +{%- when None %} +typedef {{ builtin|type_name }} {{ name }}; +{%- when Some with (type_config) %} +{%- match type_config.type_name %} +{%- when Some with (type_name) %} +typedef {{ type_name }} {{ name }}; +{%- else %} +{%- endmatch %} +{%- endmatch %} {%- else %} {%- endmatch %} {%- endfor %} @@ -56,8 +68,6 @@ struct {{ name }}; {%- else %} {% include "enum.hpp" %} {%- endif %} -{%- when Type::Custom { module_path, name, builtin } %} -{%- include "custom.hpp" %} {%- when Type::Record { module_path, name } %} {% include "rec.hpp" %} {%- when Type::CallbackInterface { module_path, name } %} @@ -213,7 +223,7 @@ template struct HandleMap { {%- when Type::CallbackInterface { module_path, name } %} {% include "callback_conv.hpp" %} {%- when Type::Custom { module_path, name, builtin } %} -typedef struct {{ builtin|ffi_converter_name }} {{ typ|ffi_converter_name }}; +{% include "custom.hpp" %} {%- else %} {%- endmatch %} {%- endfor %} diff --git a/cpp-tests/CMakeLists.txt b/cpp-tests/CMakeLists.txt index 1e939f1..afb8ae0 100644 --- a/cpp-tests/CMakeLists.txt +++ b/cpp-tests/CMakeLists.txt @@ -38,6 +38,7 @@ test_case(traits) test_case(coverall) test_case(uniffi_docstring) test_case(trait_methods) +test_case(custom_types_builtin) add_custom_target(libs ALL BYPRODUCTS ${BINDING_FILES} diff --git a/cpp-tests/tests/custom_types_builtin/main.cpp b/cpp-tests/tests/custom_types_builtin/main.cpp new file mode 100644 index 0000000..3069e42 --- /dev/null +++ b/cpp-tests/tests/custom_types_builtin/main.cpp @@ -0,0 +1,35 @@ +#include + +#include + +void assert_custom_type(custom_types_builtin::CustomTypesBuiltin& type) { + auto table = std::unordered_map{{"hello", "world"}}; + auto bytes = std::vector{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + ASSERT_EQ(type.string, "Hello, world!"); + ASSERT_EQ(type.custom_string.string, "Custom string"); + ASSERT_EQ(type.array, std::vector{"Hello, world!"}); + ASSERT_EQ(type.bytes, bytes); + ASSERT_EQ(type.table, table); + ASSERT_EQ(type.boolean, true); + ASSERT_EQ(type.int8, std::numeric_limits::max()); + ASSERT_EQ(type.int16, std::numeric_limits::max()); + ASSERT_EQ(type.int32, std::numeric_limits::max()); + ASSERT_EQ(type.int64, std::numeric_limits::max()); + ASSERT_EQ(type.uint8, std::numeric_limits::max()); + ASSERT_EQ(type.uint16, std::numeric_limits::max()); + ASSERT_EQ(type.uint32, std::numeric_limits::max()); + ASSERT_EQ(type.uint64, std::numeric_limits::max()); + ASSERT_EQ(type.flt, std::numeric_limits::max()); + ASSERT_EQ(type.dbl, std::numeric_limits::max()); +} + +int main() { + auto custom_type = custom_types_builtin::get_custom_types_builtin(); + assert_custom_type(custom_type); + + custom_type = custom_types_builtin::return_custom_types_builtin(custom_type); + assert_custom_type(custom_type); + + return 0; +} \ No newline at end of file diff --git a/cpp-tests/tests/include/custom_string.hpp b/cpp-tests/tests/include/custom_string.hpp new file mode 100644 index 0000000..520d332 --- /dev/null +++ b/cpp-tests/tests/include/custom_string.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +struct CustomString { + std::string string; + + CustomString(const std::string &string): string(string) { } + + std::string to_string() const { + return this->string; + } +}; \ No newline at end of file diff --git a/fixtures/Cargo.toml b/fixtures/Cargo.toml index a7cd2e3..df5c87c 100644 --- a/fixtures/Cargo.toml +++ b/fixtures/Cargo.toml @@ -20,4 +20,4 @@ uniffi-fixture-coverall = { git = "https://github.com/NordSecurity/uniffi-rs.git uniffi-fixture-docstring = { git = "https://github.com/NordSecurity/uniffi-rs.git", branch = "nordsec.0.25.0" } uniffi-fixture-time = { git = "https://github.com/NordSecurity/uniffi-rs.git", branch = "nordsec.0.25.0" } uniffi-fixture-trait-methods = { git = "https://github.com/NordSecurity/uniffi-rs.git", branch = "nordsec.0.25.0" } - +uniffi-custom-types-builtin = { path = "custom-types-builtin" } \ No newline at end of file diff --git a/fixtures/custom-types-builtin/Cargo.toml b/fixtures/custom-types-builtin/Cargo.toml new file mode 100644 index 0000000..36fd5e3 --- /dev/null +++ b/fixtures/custom-types-builtin/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "uniffi-custom-types-builtin" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["lib", "cdylib"] +name = "uniffi_cpp_custom_types_builtin" + +[dependencies] +uniffi = { git = "https://github.com/NordSecurity/uniffi-rs.git", branch = "nordsec.0.25.0" } +once_cell = "1.12" +paste = "1.0" +thiserror = "1.0" + +[build-dependencies] +uniffi = { git = "https://github.com/NordSecurity/uniffi-rs.git", branch = "nordsec.0.25.0", features = ["build"] } \ No newline at end of file diff --git a/fixtures/custom-types-builtin/build.rs b/fixtures/custom-types-builtin/build.rs new file mode 100644 index 0000000..d5082d6 --- /dev/null +++ b/fixtures/custom-types-builtin/build.rs @@ -0,0 +1,3 @@ +fn main() { + uniffi::generate_scaffolding("src/custom_types_builtin.udl").unwrap(); +} \ No newline at end of file diff --git a/fixtures/custom-types-builtin/src/custom_types_builtin.udl b/fixtures/custom-types-builtin/src/custom_types_builtin.udl new file mode 100644 index 0000000..91402b1 --- /dev/null +++ b/fixtures/custom-types-builtin/src/custom_types_builtin.udl @@ -0,0 +1,71 @@ +[Custom] +typedef string MyString; + +[Custom] +typedef string CustomString; + +[Custom] +typedef bytes Bytes; + +[Custom] +typedef sequence Array; + +[Custom] +typedef record Table; + +[Custom] +typedef boolean Boolean; + +[Custom] +typedef i8 Int8; + +[Custom] +typedef i16 Int16; + +[Custom] +typedef i32 Int32; + +[Custom] +typedef i64 Int64; + +[Custom] +typedef u8 UInt8; + +[Custom] +typedef u16 UInt16; + +[Custom] +typedef u32 UInt32; + +[Custom] +typedef u64 UInt64; + +[Custom] +typedef float Float; + +[Custom] +typedef double Double; + +dictionary CustomTypesBuiltin { + MyString string; + CustomString custom_string; + Array array; + Bytes bytes; + Table table; + Boolean boolean; + Int8 int8; + Int16 int16; + Int32 int32; + Int64 int64; + UInt8 uint8; + UInt16 uint16; + UInt32 uint32; + UInt64 uint64; + Float flt; + Double dbl; +}; + +namespace custom_types_builtin { + CustomTypesBuiltin get_custom_types_builtin(); + CustomTypesBuiltin return_custom_types_builtin(CustomTypesBuiltin custom_types); +}; diff --git a/fixtures/custom-types-builtin/src/lib.rs b/fixtures/custom-types-builtin/src/lib.rs new file mode 100644 index 0000000..81310d2 --- /dev/null +++ b/fixtures/custom-types-builtin/src/lib.rs @@ -0,0 +1,85 @@ +use paste::paste; +use std::collections::HashMap; + +macro_rules! define_custom_builtin_type { + ($custom:ty, $underlying:ty) => { + paste! { + pub struct $custom(pub $underlying); + + impl UniffiCustomTypeConverter for $custom { + type Builtin = $underlying; + + fn into_custom(val: Self::Builtin) -> uniffi::Result { + Ok($custom(val)) + } + + fn from_custom(obj: Self) -> Self::Builtin { + obj.0 + } + } + } + }; +} + +define_custom_builtin_type!(MyString, String); +define_custom_builtin_type!(CustomString, String); +define_custom_builtin_type!(Array, Vec); +define_custom_builtin_type!(Bytes, Vec); +define_custom_builtin_type!(Table, HashMap); +define_custom_builtin_type!(Boolean, bool); +define_custom_builtin_type!(Int8, i8); +define_custom_builtin_type!(Int16, i16); +define_custom_builtin_type!(Int32, i32); +define_custom_builtin_type!(Int64, i64); +define_custom_builtin_type!(UInt8, u8); +define_custom_builtin_type!(UInt16, u16); +define_custom_builtin_type!(UInt32, u32); +define_custom_builtin_type!(UInt64, u64); +define_custom_builtin_type!(Float, f32); +define_custom_builtin_type!(Double, f64); + +pub struct CustomTypesBuiltin { + string: MyString, + custom_string: CustomString, + array: Array, + bytes: Bytes, + table: Table, + boolean: Boolean, + int8: Int8, + int16: Int16, + int32: Int32, + int64: Int64, + uint8: UInt8, + uint16: UInt16, + uint32: UInt32, + uint64: UInt64, + flt: Float, + dbl: Double, +} + +pub fn get_custom_types_builtin() -> CustomTypesBuiltin { + return CustomTypesBuiltin { + string: MyString("Hello, world!".to_string()), + custom_string: CustomString("Custom string".to_string()), + array: Array(vec!["Hello, world!".to_string()]), + bytes: Bytes(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), + table: Table(HashMap::from([("hello".to_string(), "world".to_string())])), + boolean: Boolean(true), + int8: Int8(i8::MAX), + int16: Int16(i16::MAX), + int32: Int32(i32::MAX), + int64: Int64(i64::MAX), + uint8: UInt8(u8::MAX), + uint16: UInt16(u16::MAX), + uint32: UInt32(u32::MAX), + uint64: UInt64(u64::MAX), + flt: Float(f32::MAX), + dbl: Double(f64::MAX), + }; +} + +pub fn return_custom_types_builtin(custom_types: CustomTypesBuiltin) -> CustomTypesBuiltin { + custom_types +} + +uniffi::include_scaffolding!("custom_types_builtin"); diff --git a/fixtures/custom-types-builtin/uniffi.toml b/fixtures/custom-types-builtin/uniffi.toml new file mode 100644 index 0000000..72dafa7 --- /dev/null +++ b/fixtures/custom-types-builtin/uniffi.toml @@ -0,0 +1,5 @@ +[bindings.cpp.custom_types.CustomString] +imports = ["custom_string.hpp"] +type_name = "CustomString" +into_custom = "CustomString({})" +from_custom = "{}.to_string()" \ No newline at end of file diff --git a/fixtures/src/lib.rs b/fixtures/src/lib.rs index 98e448b..95cac83 100644 --- a/fixtures/src/lib.rs +++ b/fixtures/src/lib.rs @@ -11,4 +11,6 @@ mod uniffi_fixtures { uniffi_trait_methods::uniffi_reexport_scaffolding!(); uniffi_coverall::uniffi_reexport_scaffolding!(); uniffi_fixture_docstring::uniffi_reexport_scaffolding!(); + + uniffi_cpp_custom_types_builtin::uniffi_reexport_scaffolding!(); } From 9f2460fb8b35653c64aa4c32b3726f6d46d31e08 Mon Sep 17 00:00:00 2001 From: Martynas Gurskas Date: Wed, 13 Dec 2023 16:29:25 +0200 Subject: [PATCH 2/2] Remove unused add include function Signed-off-by: Martynas Gurskas --- bindgen/src/bindings/cpp/gen_cpp/mod.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/bindgen/src/bindings/cpp/gen_cpp/mod.rs b/bindgen/src/bindings/cpp/gen_cpp/mod.rs index 9bc80ca..523ac3e 100644 --- a/bindgen/src/bindings/cpp/gen_cpp/mod.rs +++ b/bindgen/src/bindings/cpp/gen_cpp/mod.rs @@ -202,12 +202,6 @@ impl<'a> CppWrapperHeader<'a> { sorted.into_iter().chain(rest) } - pub(crate) fn add_include(&self, include: &str) -> &str { - self.includes.borrow_mut().insert(include.to_string()); - - "" - } - pub(crate) fn includes(&self) -> Vec { self.includes.borrow().iter().cloned().collect() }