diff --git a/feature_tests/c/include/OpaqueMutexedString.h b/feature_tests/c/include/OpaqueMutexedString.h index ee0c1039c..ffc9783b0 100644 --- a/feature_tests/c/include/OpaqueMutexedString.h +++ b/feature_tests/c/include/OpaqueMutexedString.h @@ -32,6 +32,8 @@ DiplomatStringView OpaqueMutexedString_dummy_str(const OpaqueMutexedString* self Utf16Wrap* OpaqueMutexedString_wrapper(const OpaqueMutexedString* self); +uint16_t OpaqueMutexedString_to_unsigned_from_unsigned(const OpaqueMutexedString* self, uint16_t input); + void OpaqueMutexedString_destroy(OpaqueMutexedString* self); diff --git a/feature_tests/c/include/TesterTrait.d.h b/feature_tests/c/include/TesterTrait.d.h index b0aaa02d2..6c6295982 100644 --- a/feature_tests/c/include/TesterTrait.d.h +++ b/feature_tests/c/include/TesterTrait.d.h @@ -15,7 +15,7 @@ typedef struct TesterTrait_VTable { void (*destructor)(const void*); size_t SIZE; size_t ALIGNMENT; - int32_t (*run_test_trait_fn_callback)(void*, int32_t); + uint32_t (*run_test_trait_fn_callback)(void*, uint32_t); void (*run_test_void_trait_fn_callback)(void*); int32_t (*run_test_struct_trait_fn_callback)(void*, TraitTestingStruct); } TesterTrait_VTable; diff --git a/feature_tests/cpp/include/OpaqueMutexedString.d.hpp b/feature_tests/cpp/include/OpaqueMutexedString.d.hpp index 5e9f0c3bf..62543e2d1 100644 --- a/feature_tests/cpp/include/OpaqueMutexedString.d.hpp +++ b/feature_tests/cpp/include/OpaqueMutexedString.d.hpp @@ -38,6 +38,8 @@ class OpaqueMutexedString { inline std::unique_ptr wrapper() const; + inline uint16_t to_unsigned_from_unsigned(uint16_t input) const; + inline const diplomat::capi::OpaqueMutexedString* AsFFI() const; inline diplomat::capi::OpaqueMutexedString* AsFFI(); inline static const OpaqueMutexedString* FromFFI(const diplomat::capi::OpaqueMutexedString* ptr); diff --git a/feature_tests/cpp/include/OpaqueMutexedString.hpp b/feature_tests/cpp/include/OpaqueMutexedString.hpp index 531b03c1c..2cca3b895 100644 --- a/feature_tests/cpp/include/OpaqueMutexedString.hpp +++ b/feature_tests/cpp/include/OpaqueMutexedString.hpp @@ -33,6 +33,8 @@ namespace capi { diplomat::capi::Utf16Wrap* OpaqueMutexedString_wrapper(const diplomat::capi::OpaqueMutexedString* self); + uint16_t OpaqueMutexedString_to_unsigned_from_unsigned(const diplomat::capi::OpaqueMutexedString* self, uint16_t input); + void OpaqueMutexedString_destroy(OpaqueMutexedString* self); @@ -82,6 +84,12 @@ inline std::unique_ptr OpaqueMutexedString::wrapper() const { return std::unique_ptr(Utf16Wrap::FromFFI(result)); } +inline uint16_t OpaqueMutexedString::to_unsigned_from_unsigned(uint16_t input) const { + auto result = diplomat::capi::OpaqueMutexedString_to_unsigned_from_unsigned(this->AsFFI(), + input); + return result; +} + inline const diplomat::capi::OpaqueMutexedString* OpaqueMutexedString::AsFFI() const { return reinterpret_cast(this); } diff --git a/feature_tests/dart/lib/src/OpaqueMutexedString.g.dart b/feature_tests/dart/lib/src/OpaqueMutexedString.g.dart index 859335ed8..1ba390c62 100644 --- a/feature_tests/dart/lib/src/OpaqueMutexedString.g.dart +++ b/feature_tests/dart/lib/src/OpaqueMutexedString.g.dart @@ -67,6 +67,11 @@ final class OpaqueMutexedString implements ffi.Finalizable { final result = _OpaqueMutexedString_wrapper(_ffi); return Utf16Wrap._fromFfi(result, []); } + + int toUnsignedFromUnsigned(int input) { + final result = _OpaqueMutexedString_to_unsigned_from_unsigned(_ffi, input); + return result; + } } @meta.RecordUse() @@ -113,3 +118,8 @@ external _SliceUtf8 _OpaqueMutexedString_dummy_str(ffi.Pointer self) @ffi.Native Function(ffi.Pointer)>(isLeaf: true, symbol: 'OpaqueMutexedString_wrapper') // ignore: non_constant_identifier_names external ffi.Pointer _OpaqueMutexedString_wrapper(ffi.Pointer self); + +@meta.RecordUse() +@ffi.Native, ffi.Uint16)>(isLeaf: true, symbol: 'OpaqueMutexedString_to_unsigned_from_unsigned') +// ignore: non_constant_identifier_names +external int _OpaqueMutexedString_to_unsigned_from_unsigned(ffi.Pointer self, int input); diff --git a/feature_tests/js/api/OpaqueMutexedString.d.ts b/feature_tests/js/api/OpaqueMutexedString.d.ts index fa9bf3093..d1989901c 100644 --- a/feature_tests/js/api/OpaqueMutexedString.d.ts +++ b/feature_tests/js/api/OpaqueMutexedString.d.ts @@ -22,4 +22,6 @@ export class OpaqueMutexedString { dummyStr(): string; wrapper(): Utf16Wrap; + + toUnsignedFromUnsigned(input: number): number; } \ No newline at end of file diff --git a/feature_tests/js/api/OpaqueMutexedString.mjs b/feature_tests/js/api/OpaqueMutexedString.mjs index 69abbaa8a..16a0ea5ec 100644 --- a/feature_tests/js/api/OpaqueMutexedString.mjs +++ b/feature_tests/js/api/OpaqueMutexedString.mjs @@ -126,4 +126,14 @@ export class OpaqueMutexedString { finally {} } + + toUnsignedFromUnsigned(input) { + const result = wasm.OpaqueMutexedString_to_unsigned_from_unsigned(this.ffiValue, input); + + try { + return result; + } + + finally {} + } } \ No newline at end of file diff --git a/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/OpaqueMutexedString.kt b/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/OpaqueMutexedString.kt index dc2158d81..4717eea8c 100644 --- a/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/OpaqueMutexedString.kt +++ b/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/OpaqueMutexedString.kt @@ -16,6 +16,7 @@ internal interface OpaqueMutexedStringLib: Library { fun OpaqueMutexedString_get_len_and_add(handle: Pointer, other: Long): Long fun OpaqueMutexedString_dummy_str(handle: Pointer): Slice fun OpaqueMutexedString_wrapper(handle: Pointer): Pointer + fun OpaqueMutexedString_to_unsigned_from_unsigned(handle: Pointer, input: Short): Short } class OpaqueMutexedString internal constructor ( @@ -100,5 +101,11 @@ class OpaqueMutexedString internal constructor ( CLEANER.register(returnOpaque, Utf16Wrap.Utf16WrapCleaner(handle, Utf16Wrap.lib)); return returnOpaque } + + fun toUnsignedFromUnsigned(input: UShort): UShort { + + val returnVal = lib.OpaqueMutexedString_to_unsigned_from_unsigned(handle, input.toShort()); + return (returnVal.toUShort()) + } } diff --git a/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/TesterTrait.kt b/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/TesterTrait.kt index eb66bee4f..03ab28289 100644 --- a/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/TesterTrait.kt +++ b/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/TesterTrait.kt @@ -7,14 +7,14 @@ import com.sun.jna.Pointer import com.sun.jna.Structure interface TesterTrait { - fun testTraitFn(x: Int): Int; + fun testTraitFn(x: UInt): UInt; fun testVoidTraitFn(): Unit; fun testStructTraitFn(s: TraitTestingStruct): Int; } internal interface Runner_DiplomatTraitMethod_TesterTrait_testTraitFn: Callback { - fun invoke(ignored: Pointer?, x: Int ): Int + fun invoke(ignored: Pointer?, x: UInt ): UInt } internal interface Runner_DiplomatTraitMethod_TesterTrait_testVoidTraitFn: Callback { fun invoke(ignored: Pointer?): Unit @@ -40,7 +40,7 @@ internal class DiplomatTrait_TesterTrait_VTable_Native: Structure(), Structure.B @JvmField internal var run_testTraitFn_callback: Runner_DiplomatTraitMethod_TesterTrait_testTraitFn = object : Runner_DiplomatTraitMethod_TesterTrait_testTraitFn { - override fun invoke(ignored: Pointer?, x: Int ): Int { + override fun invoke(ignored: Pointer?, x: UInt ): UInt { throw Exception("ERROR NOT IMPLEMENTED") } } @@ -90,7 +90,7 @@ internal class DiplomatTrait_TesterTrait_Wrapper internal constructor ( val testTraitFn: Runner_DiplomatTraitMethod_TesterTrait_testTraitFn = object : Runner_DiplomatTraitMethod_TesterTrait_testTraitFn { - override fun invoke(ignored: Pointer?, x: Int ): Int { + override fun invoke(ignored: Pointer?, x: UInt ): UInt { return trt_obj.testTraitFn(x); } } diff --git a/feature_tests/src/structs.rs b/feature_tests/src/structs.rs index 3ad66e1df..8e84ee9e8 100644 --- a/feature_tests/src/structs.rs +++ b/feature_tests/src/structs.rs @@ -142,6 +142,10 @@ pub mod ffi { .collect(); Box::new(Utf16Wrap(chars)) } + + pub fn to_unsigned_from_unsigned(&self, input: u16) -> u16 { + input + } } impl Utf16Wrap { diff --git a/feature_tests/src/traits.rs b/feature_tests/src/traits.rs index 87690fe43..41f10338e 100644 --- a/feature_tests/src/traits.rs +++ b/feature_tests/src/traits.rs @@ -6,7 +6,7 @@ mod ffi { y: i32, } pub trait TesterTrait { - fn test_trait_fn(&self, x: i32) -> i32; + fn test_trait_fn(&self, x: u32) -> u32; fn test_void_trait_fn(&self); fn test_struct_trait_fn(&self, s: TraitTestingStruct) -> i32; } @@ -18,7 +18,7 @@ mod ffi { impl TraitWrapper { pub fn test_with_trait(t: impl TesterTrait, x: i32) -> i32 { t.test_void_trait_fn(); - t.test_trait_fn(x) + t.test_trait_fn(x.try_into().unwrap()).try_into().unwrap() } pub fn test_trait_with_struct(t: impl TesterTrait) -> i32 { diff --git a/tool/src/kotlin/formatter.rs b/tool/src/kotlin/formatter.rs index 58fdb3d6d..6767659a9 100644 --- a/tool/src/kotlin/formatter.rs +++ b/tool/src/kotlin/formatter.rs @@ -40,7 +40,6 @@ impl<'tcx> KotlinFormatter<'tcx> { pub fn fmt_primitive_to_native_conversion(&self, name: &str, prim: PrimitiveType) -> String { match prim { - PrimitiveType::Bool => format!("{name}.toByte()"), PrimitiveType::Int(IntType::U8) => format!("{name}.toByte()"), PrimitiveType::Int(IntType::U16) => format!("{name}.toShort()"), PrimitiveType::Int(IntType::U32) => format!("{name}.toInt()"), @@ -70,21 +69,55 @@ impl<'tcx> KotlinFormatter<'tcx> { "Array" } - pub fn fmt_primitive_as_ffi(&self, prim: PrimitiveType) -> &'static str { + pub fn fmt_primitive_as_ffi( + &self, + prim: PrimitiveType, + support_unsigned: bool, + ) -> &'static str { match prim { - PrimitiveType::Bool => "Byte", + PrimitiveType::Bool => "Boolean", PrimitiveType::Char => "Int", PrimitiveType::Int(IntType::I8) => "Byte", PrimitiveType::Int(IntType::I16) => "Short", PrimitiveType::Int(IntType::I32) => "Int", PrimitiveType::Int(IntType::I64) => "Long", - PrimitiveType::Int(IntType::U8) => "UByte", - PrimitiveType::Int(IntType::U16) => "UShort", - PrimitiveType::Int(IntType::U32) => "UInt", - PrimitiveType::Int(IntType::U64) => "ULong", + PrimitiveType::Int(IntType::U8) => { + if support_unsigned { + "UByte" + } else { + "Byte" + } + } + PrimitiveType::Int(IntType::U16) => { + if support_unsigned { + "UShort" + } else { + "Short" + } + } + PrimitiveType::Int(IntType::U32) => { + if support_unsigned { + "UInt" + } else { + "Int" + } + } + PrimitiveType::Int(IntType::U64) => { + if support_unsigned { + "ULong" + } else { + "Long" + } + } PrimitiveType::Byte => "Byte", PrimitiveType::IntSize(IntSizeType::Isize) => "Long", - PrimitiveType::IntSize(IntSizeType::Usize) => "Long", + PrimitiveType::IntSize(IntSizeType::Usize) => { + if support_unsigned { + "ULong" + } else { + "Long" + } + } PrimitiveType::Float(FloatType::F32) => "Float", PrimitiveType::Float(FloatType::F64) => "Double", PrimitiveType::Int128(_) => panic!("i128 not supported in Kotlin"), @@ -342,7 +375,7 @@ impl<'tcx> KotlinFormatter<'tcx> { PrimitiveType::Int(IntType::U32) => "Int", PrimitiveType::Int(IntType::U64) => "Long", PrimitiveType::IntSize(_) => "Long", - prim => self.fmt_primitive_as_ffi(prim), + prim => self.fmt_primitive_as_ffi(prim, false), } } diff --git a/tool/src/kotlin/mod.rs b/tool/src/kotlin/mod.rs index e1d57bb09..8c182a5c1 100644 --- a/tool/src/kotlin/mod.rs +++ b/tool/src/kotlin/mod.rs @@ -597,11 +597,11 @@ return string{return_type_modifier}"# _ => todo!(), }, Slice::Primitive(Some(_), prim_ty) => { - let prim_ty = self.formatter.fmt_primitive_as_ffi(*prim_ty); + let prim_ty = self.formatter.fmt_primitive_as_kt(*prim_ty); format!(" return PrimitiveArrayTools.get{prim_ty}Array({val_name}){return_type_modifier}") } Slice::Primitive(None, prim_ty) => { - let prim_ty = self.formatter.fmt_primitive_as_ffi(*prim_ty); + let prim_ty = self.formatter.fmt_primitive_as_kt(*prim_ty); let prim_ty_array = format!("{prim_ty}Array"); Self::boxed_slice_return(prim_ty_array.as_str(), val_name, return_type_modifier) } @@ -1106,7 +1106,7 @@ returnVal.option() ?: return null .unzip(); let (native_output_type, return_modification) = match **output { Some(ref ty) => ( - self.gen_native_type_name(ty, None).into(), + self.gen_native_type_name(ty, None, false).into(), match ty { Type::Enum(..) => ".toNative()", Type::Struct(..) => ".nativeStruct", @@ -1286,7 +1286,7 @@ returnVal.option() ?: return null param_decls.push(format!( "{param_name}: {}", - self.gen_native_type_name(¶m.ty, additional_name.clone()), + self.gen_native_type_name(¶m.ty, additional_name.clone(), false), )); } if let ReturnType::Infallible(SuccessType::Write) @@ -1603,7 +1603,7 @@ returnVal.option() ?: return null }); let (native_output_type, return_modification) = match *method.output { Some(ref ty) => ( - self.gen_native_type_name(ty, None).into(), + self.gen_native_type_name(ty, None, true).into(), match ty { Type::Enum(..) => ".toNative()", Type::Struct(..) => ".nativeStruct", @@ -1827,9 +1827,18 @@ returnVal.option() ?: return null &self, ty: &Type

, additional_name: Option, + // flag to represent whether the API this type is a part of has support for unsigned types. + // Non-trait methods do not, because they are called through the JNA library built in Java + // which doesn't support unsigned types. + // The true fix is to use JNA `IntegerType` to represent unsigned ints: + // TODO: https://github.com/rust-diplomat/diplomat/issues/748 + support_unsigned: bool, ) -> Cow<'cx, str> { match *ty { - Type::Primitive(prim) => self.formatter.fmt_primitive_as_ffi(prim).into(), + Type::Primitive(prim) => self + .formatter + .fmt_primitive_as_ffi(prim, support_unsigned) + .into(), Type::Opaque(ref op) => { let optional = if op.is_optional() { "?" } else { "" }; format!("Pointer{optional}").into()