diff --git a/Cargo.lock b/Cargo.lock index 35b6d9f..d18ee7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -764,6 +764,7 @@ name = "uniffi-bindgen-cs-fixtures" version = "0.1.0" dependencies = [ "global-methods-class-name", + "uniffi-cs-custom-types-builtin", "uniffi-cs-disposable-fixture", "uniffi-example-arithmetic", "uniffi-example-callbacks", @@ -779,6 +780,17 @@ dependencies = [ "uniffi-fixture-time", ] +[[package]] +name = "uniffi-cs-custom-types-builtin" +version = "1.0.0" +dependencies = [ + "once_cell", + "paste", + "thiserror", + "uniffi", + "uniffi_macros", +] + [[package]] name = "uniffi-cs-disposable-fixture" version = "1.0.0" diff --git a/bindgen/src/gen_cs/mod.rs b/bindgen/src/gen_cs/mod.rs index 045d07b..a121cbe 100644 --- a/bindgen/src/gen_cs/mod.rs +++ b/bindgen/src/gen_cs/mod.rs @@ -340,6 +340,30 @@ pub mod filters { Ok(codetype.type_label(oracle())) } + pub fn type_name_custom(typ: &Type) -> Result { + // Lowercasing numeric types introduces a problem. In C# custom types are + // implemented with `using` directive, and the `using` directive expects + // and identifier on the right side of `=`. Lowercase numeric types are + // not identifiers, but rather reserved keywords. So its not possible to + // define a type alias using a lowercase numeric type as the underlying + // type. To use numeric types as the underlying type, the uppercase + // numeric system type counterparts must be used. + match typ { + Type::Boolean => Ok("Boolean".to_string()), + Type::Int8 => Ok("SByte".to_string()), + Type::Int16 => Ok("Int16".to_string()), + Type::Int32 => Ok("Int32".to_string()), + Type::Int64 => Ok("Int64".to_string()), + Type::UInt8 => Ok("Byte".to_string()), + Type::UInt16 => Ok("UInt16".to_string()), + Type::UInt32 => Ok("UInt32".to_string()), + Type::UInt64 => Ok("UInt64".to_string()), + Type::Float32 => Ok("Single".to_string()), + Type::Float64 => Ok("Double".to_string()), + _ => type_name(typ), + } + } + pub fn canonical_name(codetype: &impl CodeType) -> Result { Ok(codetype.canonical_name(oracle())) } diff --git a/bindgen/src/gen_cs/primitives.rs b/bindgen/src/gen_cs/primitives.rs index 05c70ac..e1296dc 100644 --- a/bindgen/src/gen_cs/primitives.rs +++ b/bindgen/src/gen_cs/primitives.rs @@ -73,15 +73,15 @@ macro_rules! impl_code_type_for_primitive { }; } -impl_code_type_for_primitive!(BooleanCodeType, "Boolean", "Boolean"); +impl_code_type_for_primitive!(BooleanCodeType, "bool", "Boolean"); impl_code_type_for_primitive!(StringCodeType, "String", "String"); -impl_code_type_for_primitive!(Int8CodeType, "SByte", "SByte"); -impl_code_type_for_primitive!(Int16CodeType, "Int16", "Short"); -impl_code_type_for_primitive!(Int32CodeType, "Int32", "Int"); -impl_code_type_for_primitive!(Int64CodeType, "Int64", "Long"); -impl_code_type_for_primitive!(UInt8CodeType, "Byte", "Byte"); -impl_code_type_for_primitive!(UInt16CodeType, "UInt16", "UShort"); -impl_code_type_for_primitive!(UInt32CodeType, "UInt32", "UInt"); -impl_code_type_for_primitive!(UInt64CodeType, "UInt64", "ULong"); -impl_code_type_for_primitive!(Float32CodeType, "Single", "Float"); -impl_code_type_for_primitive!(Float64CodeType, "Double", "Double"); +impl_code_type_for_primitive!(Int8CodeType, "sbyte", "Int8"); +impl_code_type_for_primitive!(Int16CodeType, "short", "Int16"); +impl_code_type_for_primitive!(Int32CodeType, "int", "Int32"); +impl_code_type_for_primitive!(Int64CodeType, "long", "Int64"); +impl_code_type_for_primitive!(UInt8CodeType, "byte", "UInt8"); +impl_code_type_for_primitive!(UInt16CodeType, "ushort", "UInt16"); +impl_code_type_for_primitive!(UInt32CodeType, "uint", "UInt32"); +impl_code_type_for_primitive!(UInt64CodeType, "ulong", "UInt64"); +impl_code_type_for_primitive!(Float32CodeType, "float", "Float"); +impl_code_type_for_primitive!(Float64CodeType, "double", "Double"); diff --git a/bindgen/templates/CustomTypeTemplate.cs b/bindgen/templates/CustomTypeTemplate.cs index a06c165..190cb52 100644 --- a/bindgen/templates/CustomTypeTemplate.cs +++ b/bindgen/templates/CustomTypeTemplate.cs @@ -10,7 +10,7 @@ * is needed because the UDL type name is used in function/method signatures. * It's also what we have an external type that references a custom type. */ -{{- self.add_type_alias(name, builtin|type_name) }} +{{- self.add_type_alias(name, builtin|type_name_custom) }} {{- self.add_type_alias(ffi_converter_name, builtin|ffi_converter_name) }} {%- when Some with (config) %} diff --git a/bindgen/templates/Float32Helper.cs b/bindgen/templates/Float32Helper.cs index 07e99b4..c85e32d 100644 --- a/bindgen/templates/Float32Helper.cs +++ b/bindgen/templates/Float32Helper.cs @@ -2,8 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#} -class FfiConverterFloat: FfiConverter { - public static FfiConverterFloat INSTANCE = new FfiConverterFloat(); +class {{ ffi_converter_name }}: FfiConverter { + public static {{ ffi_converter_name }} INSTANCE = new {{ ffi_converter_name }}(); public override float Lift(float value) { return value; diff --git a/bindgen/templates/Float64Helper.cs b/bindgen/templates/Float64Helper.cs index fdd48a6..9f7cb9d 100644 --- a/bindgen/templates/Float64Helper.cs +++ b/bindgen/templates/Float64Helper.cs @@ -2,8 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#} -class FfiConverterDouble: FfiConverter { - public static FfiConverterDouble INSTANCE = new FfiConverterDouble(); +class {{ ffi_converter_name }}: FfiConverter { + public static {{ ffi_converter_name }} INSTANCE = new {{ ffi_converter_name }}(); public override double Lift(double value) { return value; diff --git a/bindgen/templates/Int16Helper.cs b/bindgen/templates/Int16Helper.cs index a8a3c2e..d718ef1 100644 --- a/bindgen/templates/Int16Helper.cs +++ b/bindgen/templates/Int16Helper.cs @@ -2,8 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#} -class FfiConverterShort: FfiConverter { - public static FfiConverterShort INSTANCE = new FfiConverterShort(); +class {{ ffi_converter_name }}: FfiConverter { + public static {{ ffi_converter_name }} INSTANCE = new {{ ffi_converter_name }}(); public override short Lift(short value) { return value; diff --git a/bindgen/templates/Int32Helper.cs b/bindgen/templates/Int32Helper.cs index 72e8f42..3e68523 100644 --- a/bindgen/templates/Int32Helper.cs +++ b/bindgen/templates/Int32Helper.cs @@ -2,8 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#} -class FfiConverterInt: FfiConverter { - public static FfiConverterInt INSTANCE = new FfiConverterInt(); +class {{ ffi_converter_name }}: FfiConverter { + public static {{ ffi_converter_name }} INSTANCE = new {{ ffi_converter_name }}(); public override int Lift(int value) { return value; diff --git a/bindgen/templates/Int64Helper.cs b/bindgen/templates/Int64Helper.cs index 3f21c55..5faf217 100644 --- a/bindgen/templates/Int64Helper.cs +++ b/bindgen/templates/Int64Helper.cs @@ -2,8 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#} -class FfiConverterLong: FfiConverter { - public static FfiConverterLong INSTANCE = new FfiConverterLong(); +class {{ ffi_converter_name }}: FfiConverter { + public static {{ ffi_converter_name }} INSTANCE = new {{ ffi_converter_name }}(); public override long Lift(long value) { return value; diff --git a/bindgen/templates/Int8Helper.cs b/bindgen/templates/Int8Helper.cs index 2d0223f..14e4cd1 100644 --- a/bindgen/templates/Int8Helper.cs +++ b/bindgen/templates/Int8Helper.cs @@ -2,8 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#} -class FfiConverterSByte: FfiConverter { - public static FfiConverterSByte INSTANCE = new FfiConverterSByte(); +class {{ ffi_converter_name }}: FfiConverter { + public static {{ ffi_converter_name }} INSTANCE = new {{ ffi_converter_name }}(); public override sbyte Lift(sbyte value) { return value; diff --git a/bindgen/templates/UInt16Helper.cs b/bindgen/templates/UInt16Helper.cs index 20c0057..9a78ece 100644 --- a/bindgen/templates/UInt16Helper.cs +++ b/bindgen/templates/UInt16Helper.cs @@ -2,8 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#} -class FfiConverterUShort: FfiConverter { - public static FfiConverterUShort INSTANCE = new FfiConverterUShort(); +class {{ ffi_converter_name }}: FfiConverter { + public static {{ ffi_converter_name }} INSTANCE = new {{ ffi_converter_name }}(); public override ushort Lift(ushort value) { return value; diff --git a/bindgen/templates/UInt32Helper.cs b/bindgen/templates/UInt32Helper.cs index e92f7b1..f31da57 100644 --- a/bindgen/templates/UInt32Helper.cs +++ b/bindgen/templates/UInt32Helper.cs @@ -2,8 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#} -class FfiConverterUInt: FfiConverter { - public static FfiConverterUInt INSTANCE = new FfiConverterUInt(); +class {{ ffi_converter_name }}: FfiConverter { + public static {{ ffi_converter_name }} INSTANCE = new {{ ffi_converter_name }}(); public override uint Lift(uint value) { return value; diff --git a/bindgen/templates/UInt64Helper.cs b/bindgen/templates/UInt64Helper.cs index deaf178..54c6a95 100644 --- a/bindgen/templates/UInt64Helper.cs +++ b/bindgen/templates/UInt64Helper.cs @@ -2,8 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#} -class FfiConverterULong: FfiConverter { - public static FfiConverterULong INSTANCE = new FfiConverterULong(); +class {{ ffi_converter_name }}: FfiConverter { + public static {{ ffi_converter_name }} INSTANCE = new {{ ffi_converter_name }}(); public override ulong Lift(ulong value) { return value; diff --git a/bindgen/templates/UInt8Helper.cs b/bindgen/templates/UInt8Helper.cs index 7b483fd..728bd17 100644 --- a/bindgen/templates/UInt8Helper.cs +++ b/bindgen/templates/UInt8Helper.cs @@ -2,8 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#} -class FfiConverterByte: FfiConverter { - public static FfiConverterByte INSTANCE = new FfiConverterByte(); +class {{ ffi_converter_name }}: FfiConverter { + public static {{ ffi_converter_name }} INSTANCE = new {{ ffi_converter_name }}(); public override byte Lift(byte value) { return value; diff --git a/dotnet-tests/UniffiCS.binding_tests/TestCustomTypesBuiltin.cs b/dotnet-tests/UniffiCS.binding_tests/TestCustomTypesBuiltin.cs new file mode 100644 index 0000000..a500959 --- /dev/null +++ b/dotnet-tests/UniffiCS.binding_tests/TestCustomTypesBuiltin.cs @@ -0,0 +1,32 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +using System; +using uniffi.custom_types_builtin; + +public class TestCustomTypesBuiltin { + [Fact] + public void CustomTypesWork() { + var demo = CustomTypesBuiltinMethods.GetCustomTypesBuiltin(); + AssertDemo(demo); + + demo = CustomTypesBuiltinMethods.ReturnCustomTypesBuiltin(demo); + AssertDemo(demo); + } + + void AssertDemo(CustomTypesBuiltin demo) { + Assert.Equal("Hello, world!", demo.@string); + Assert.True(demo.boolean); + Assert.Equal(SByte.MaxValue, demo.int8); + Assert.Equal(Int16.MaxValue, demo.int16); + Assert.Equal(Int32.MaxValue, demo.int32); + Assert.Equal(Int64.MaxValue, demo.int64); + Assert.Equal(Byte.MaxValue, demo.uint8); + Assert.Equal(UInt16.MaxValue, demo.uint16); + Assert.Equal(UInt32.MaxValue, demo.uint32); + Assert.Equal(UInt64.MaxValue, demo.uint64); + Assert.Equal(Single.MaxValue, demo.@float); + Assert.Equal(Double.MaxValue, demo.@double); + } +} diff --git a/fixtures/Cargo.toml b/fixtures/Cargo.toml index 74888b1..8f510f8 100644 --- a/fixtures/Cargo.toml +++ b/fixtures/Cargo.toml @@ -10,6 +10,7 @@ crate-type = ["cdylib", "lib"] [dependencies] global-methods-class-name = { path = "global-methods-class-name" } +uniffi-cs-custom-types-builtin = { path = "custom-types-builtin" } uniffi-cs-disposable-fixture = { path = "disposable" } uniffi-example-arithmetic = { path = "../3rd-party/uniffi-rs/examples/arithmetic" } uniffi-example-callbacks = { path = "../3rd-party/uniffi-rs/examples/callbacks" } diff --git a/fixtures/custom-types-builtin/Cargo.toml b/fixtures/custom-types-builtin/Cargo.toml new file mode 100644 index 0000000..eeb5cdd --- /dev/null +++ b/fixtures/custom-types-builtin/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "uniffi-cs-custom-types-builtin" +version = "1.0.0" +edition = "2021" +publish = false + +[lib] +crate-type = ["lib", "cdylib"] +name = "uniffi_cs_custom_types_builtin" + +[dependencies] +once_cell = "1.12" +paste = "1.0" +thiserror = "1.0" +uniffi = {path = "../../3rd-party/uniffi-rs/uniffi", features=["build"]} +uniffi_macros = {path = "../../3rd-party/uniffi-rs/uniffi_macros"} + +[build-dependencies] +uniffi = {path = "../../3rd-party/uniffi-rs/uniffi", features=["bindgen-tests"]} diff --git a/fixtures/custom-types-builtin/build.rs b/fixtures/custom-types-builtin/build.rs new file mode 100644 index 0000000..daa763e --- /dev/null +++ b/fixtures/custom-types-builtin/build.rs @@ -0,0 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +fn main() { + uniffi::generate_scaffolding("./src/custom-types-builtin.udl").unwrap(); +} 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..f3e17d2 --- /dev/null +++ b/fixtures/custom-types-builtin/src/custom-types-builtin.udl @@ -0,0 +1,71 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Ensure that builtin types can be used as the underlying type in custom types. + +[Custom] +typedef string MyString; + +// using sequence or record as the underlying type produces broken code + +// [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; + // Array array; + // Table table; + Boolean boolean; + Int8 int8; + Int16 int16; + Int32 int32; + Int64 int64; + UInt8 uint8; + UInt16 uint16; + UInt32 uint32; + UInt64 uint64; + Float float; + Double double; +}; + +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..0cceb86 --- /dev/null +++ b/fixtures/custom-types-builtin/src/lib.rs @@ -0,0 +1,78 @@ +use paste::paste; + +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!(Array, 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, + // array: Array, + // table: Table, + boolean: Boolean, + int8: Int8, + int16: Int16, + int32: Int32, + int64: Int64, + uint8: UInt8, + uint16: UInt16, + uint32: UInt32, + uint64: UInt64, + float: Float, + double: Double, +} + +pub fn get_custom_types_builtin() -> CustomTypesBuiltin { + return CustomTypesBuiltin { + string: MyString("Hello, world!".to_string()), + // array: Array(vec!["Hello, world!".to_string()]), + // 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), + float: Float(f32::MAX), + double: Double(f64::MAX), + }; +} + +pub fn return_custom_types_builtin(custom_types: CustomTypesBuiltin) -> CustomTypesBuiltin { + custom_types +} + +include!(concat!(env!("OUT_DIR"), "/custom-types-builtin.uniffi.rs")); diff --git a/fixtures/src/lib.rs b/fixtures/src/lib.rs index 5467b07..371cf2c 100644 --- a/fixtures/src/lib.rs +++ b/fixtures/src/lib.rs @@ -18,5 +18,6 @@ mod uniffi_fixtures { uniffi_fixture_docstring::uniffi_reexport_scaffolding!(); global_methods_class_name::uniffi_reexport_scaffolding!(); + uniffi_cs_custom_types_builtin::uniffi_reexport_scaffolding!(); uniffi_cs_disposable::uniffi_reexport_scaffolding!(); } diff --git a/test_bindings.sh b/test_bindings.sh index dbc2bc6..c0559ae 100755 --- a/test_bindings.sh +++ b/test_bindings.sh @@ -25,6 +25,7 @@ bindings 3rd-party/uniffi-rs/fixtures/coverall/src/coverall.udl bindings 3rd-party/uniffi-rs/fixtures/docstring/src/docstring.udl bindings 3rd-party/uniffi-rs/fixtures/external-types/lib/src/external-types-lib.udl bindings 3rd-party/uniffi-rs/fixtures/uniffi-fixture-time/src/chronological.udl +bindings fixtures/custom-types-builtin/src/custom-types-builtin.udl bindings fixtures/disposable/src/disposable.udl CONFIG="fixtures/global-methods-class-name/uniffi.toml" \