From 45513f21cb99065843ed518c0104c8ed6bd7c6cd Mon Sep 17 00:00:00 2001 From: chavic Date: Sun, 24 Aug 2025 18:50:57 +0200 Subject: [PATCH 1/5] feat: added BigInt support --- .../type-limits/test/type_limits_test.dart | 24 ++--- src/gen/oracle.rs | 12 +-- src/gen/primitives/mod.rs | 90 ++++++++++++++++++- src/gen/render/mod.rs | 32 +------ src/gen/types.rs | 13 ++- 5 files changed, 113 insertions(+), 58 deletions(-) diff --git a/fixtures/type-limits/test/type_limits_test.dart b/fixtures/type-limits/test/type_limits_test.dart index e4067db..ddc65d4 100644 --- a/fixtures/type-limits/test/type_limits_test.dart +++ b/fixtures/type-limits/test/type_limits_test.dart @@ -31,8 +31,7 @@ void main() { test('i64 bounds', () { // Test valid lower bound - final minI64 = - -9223372036854775808; // -2^63 (this is within Dart's range) + final minI64 = BigInt.from(-9223372036854775808); // -2^63 expect(takeI64(minI64), minI64); // Test values that would overflow are caught by Dart @@ -67,8 +66,7 @@ void main() { test('i64 bounds', () { // Test valid upper bound - final maxI64 = - 9223372036854775807; // 2^63 - 1 (this is within Dart's range) + final maxI64 = BigInt.from(9223372036854775807); // 2^63 - 1 expect(takeI64(maxI64), maxI64); // Test values that would overflow are caught by Dart @@ -103,10 +101,10 @@ void main() { test('u64 bounds', () { // Test valid lower bound - expect(takeU64(0), 0); + expect(takeU64(BigInt.zero), BigInt.zero); // Test invalid lower bound - expect(() => takeU64(-1), throwsArgumentError); + expect(() => takeU64(BigInt.from(-1)), throwsArgumentError); }); }); @@ -226,12 +224,18 @@ void main() { expect(takeI8(100), 100); expect(takeI16(10000), 10000); expect(takeI32(1000000000), 1000000000); - expect(takeI64(1000000000000000000), 1000000000000000000); + expect( + takeI64(BigInt.from(1000000000000000000)), + BigInt.from(1000000000000000000), + ); expect(takeU8(100), 100); expect(takeU16(10000), 10000); expect(takeU32(1000000000), 1000000000); - expect(takeU64(1000000000000000000), 1000000000000000000); + expect( + takeU64(BigInt.from(1000000000000000000)), + BigInt.from(1000000000000000000), + ); }); test('large invalid numbers', () { @@ -250,11 +254,11 @@ void main() { expect(takeI8(0), 0); expect(takeI16(0), 0); expect(takeI32(0), 0); - expect(takeI64(0), 0); + expect(takeI64(BigInt.zero), BigInt.zero); expect(takeU8(0), 0); expect(takeU16(0), 0); expect(takeU32(0), 0); - expect(takeU64(0), 0); + expect(takeU64(BigInt.zero), BigInt.zero); expect(takeF32(0.0), 0.0); expect(takeF64(0.0), 0.0); }); diff --git a/src/gen/oracle.rs b/src/gen/oracle.rs index bd2d39b..e53f00e 100644 --- a/src/gen/oracle.rs +++ b/src/gen/oracle.rs @@ -221,10 +221,12 @@ impl DartCodeOracle { | Type::Int16 | Type::UInt16 | Type::Int32 - | Type::Int64 - | Type::UInt64 | Type::Float32 | Type::Float64 => inner, + // 64-bit integers need lowering because they use BigInt + Type::Int64 | Type::UInt64 => { + quote!($(ty.as_codetype().ffi_converter_name()).lower($inner)) + } Type::Boolean | Type::Duration | Type::String @@ -261,11 +263,11 @@ impl DartCodeOracle { Type::UInt8 | Type::UInt16 | Type::UInt32 - | Type::UInt64 | Type::Int8 | Type::Int16 - | Type::Int32 - | Type::Int64 => quote!(int), + | Type::Int32 => quote!(int), + // Use BigInt for 64-bit integers to handle values that exceed Dart's safe integer limits + Type::Int64 | Type::UInt64 => quote!(BigInt), Type::Float32 | Type::Float64 => quote!(double), Type::Boolean => quote!(bool), Type::Bytes => quote!(Uint8List), diff --git a/src/gen/primitives/mod.rs b/src/gen/primitives/mod.rs index aff6473..50660a6 100644 --- a/src/gen/primitives/mod.rs +++ b/src/gen/primitives/mod.rs @@ -60,11 +60,11 @@ impl_code_type_for_primitive!(BytesCodeType, "Uint8List", "Uint8List"); impl_code_type_for_primitive!(Int8CodeType, "int", "Int8"); impl_code_type_for_primitive!(Int16CodeType, "int", "Int16"); impl_code_type_for_primitive!(Int32CodeType, "int", "Int32"); -impl_code_type_for_primitive!(Int64CodeType, "int", "Int64"); +impl_code_type_for_primitive!(Int64CodeType, "BigInt", "Int64"); impl_code_type_for_primitive!(UInt8CodeType, "int", "UInt8"); impl_code_type_for_primitive!(UInt16CodeType, "int", "UInt16"); impl_code_type_for_primitive!(UInt32CodeType, "int", "UInt32"); -impl_code_type_for_primitive!(UInt64CodeType, "int", "UInt64"); +impl_code_type_for_primitive!(UInt64CodeType, "BigInt", "UInt64"); impl_code_type_for_primitive!(Float32CodeType, "double", "Double32"); impl_code_type_for_primitive!(Float64CodeType, "double", "Double64"); @@ -72,10 +72,92 @@ impl_renderable_for_primitive!(BytesCodeType, "Uint8List", "Uint8List"); impl_renderable_for_primitive!(Int8CodeType, "int", "Int8", 1); impl_renderable_for_primitive!(Int16CodeType, "int", "Int16", 2); impl_renderable_for_primitive!(Int32CodeType, "int", "Int32", 4); -impl_renderable_for_primitive!(Int64CodeType, "int", "Int64", 8); impl_renderable_for_primitive!(UInt8CodeType, "int", "UInt8", 1); impl_renderable_for_primitive!(UInt16CodeType, "int", "UInt16", 2); impl_renderable_for_primitive!(UInt32CodeType, "int", "UInt32", 4); -impl_renderable_for_primitive!(UInt64CodeType, "int", "UInt64", 8); impl_renderable_for_primitive!(Float32CodeType, "double", "Double32", 4); impl_renderable_for_primitive!(Float64CodeType, "double", "Double64", 8); + +// Custom implementations for 64-bit integer types that use BigInt +impl Renderable for Int64CodeType { + fn render_type_helper(&self, _type_helper: &dyn TypeHelperRenderer) -> dart::Tokens { + let cl_name = &self.ffi_converter_name(); + let type_signature = &self.type_label(); + + quote! { + class $cl_name { + static $type_signature lift(int value) { + return BigInt.from(value); + } + + static LiftRetVal<$type_signature> read(Uint8List buf) { + final value = buf.buffer.asByteData(buf.offsetInBytes).getInt64(0); + return LiftRetVal(BigInt.from(value), 8); + } + + static int lower($type_signature value) { + // Convert BigInt to int, checking bounds + if (value < BigInt.from(-9223372036854775808) || value > BigInt.from(9223372036854775807)) { + throw ArgumentError("Value out of range for i64: " + value.toString()); + } + return value.toInt(); + } + + static int allocationSize([$type_signature? value]) { + return 8; + } + + static int write($type_signature value, Uint8List buf) { + buf.buffer.asByteData(buf.offsetInBytes).setInt64(0, lower(value)); + return 8; + } + } + } + } +} + +impl Renderable for UInt64CodeType { + fn render_type_helper(&self, _type_helper: &dyn TypeHelperRenderer) -> dart::Tokens { + let cl_name = &self.ffi_converter_name(); + let type_signature = &self.type_label(); + + quote! { + class $cl_name { + static $type_signature lift(int value) { + // Convert unsigned int64 to BigInt, handling the unsigned nature + if (value < 0) { + // Handle unsigned interpretation of negative signed value + return BigInt.from(value) + (BigInt.one << 64); + } + return BigInt.from(value); + } + + static LiftRetVal<$type_signature> read(Uint8List buf) { + final value = buf.buffer.asByteData(buf.offsetInBytes).getUint64(0); + return LiftRetVal(BigInt.from(value), 8); + } + + static int lower($type_signature value) { + // Convert BigInt to int, checking bounds for u64 + if (value < BigInt.zero || value > BigInt.parse("18446744073709551615")) { + throw ArgumentError("Value out of range for u64: " + value.toString()); + } + // Handle conversion to signed representation if needed + if (value > BigInt.from(9223372036854775807)) { + return (value - (BigInt.one << 64)).toInt(); + } + return value.toInt(); + } + + static int allocationSize([$type_signature? value]) { + return 8; + } + + static int write($type_signature value, Uint8List buf) { + buf.buffer.asByteData(buf.offsetInBytes).setUint64(0, lower(value)); + return 8; + } + } + } + } +} diff --git a/src/gen/render/mod.rs b/src/gen/render/mod.rs index a29405d..4a7c836 100644 --- a/src/gen/render/mod.rs +++ b/src/gen/render/mod.rs @@ -22,37 +22,7 @@ pub trait Renderable { fn render_type(&self, ty: &Type, type_helper: &dyn TypeHelperRenderer) -> dart::Tokens { use super::oracle::DartCodeOracle; - let type_name = match ty { - Type::UInt8 - | Type::Int8 - | Type::UInt16 - | Type::Int16 - | Type::UInt32 - | Type::Int32 - | Type::UInt64 - | Type::Int64 => quote!(int), - Type::Float32 | Type::Float64 => quote!(double), - Type::String => quote!(String), - Type::Boolean => quote!(bool), - Type::Bytes => quote!(Uint8List), - Type::Object { name, .. } => quote!($name), - Type::Optional { inner_type } => quote!($(&self.render_type(inner_type, type_helper))?), - Type::Sequence { inner_type } => { - quote!(List<$(&self.render_type(inner_type, type_helper))>) - } - Type::Map { - key_type, - value_type, - } => { - quote!(Map<$(&self.render_type(key_type, type_helper)), $(&self.render_type(value_type, type_helper))>) - } - Type::Enum { name, .. } => quote!($(DartCodeOracle::class_name(name))), - Type::Record { name, .. } => quote!($name), - Type::Custom { name, .. } => quote!($name), - Type::Duration => quote!(Duration), - Type::CallbackInterface { name, .. } => quote!($name), - _ => todo!("Type::{:?}", ty), - }; + let type_name = DartCodeOracle::dart_type_label(Some(ty)); if !type_helper.include_once_check(&ty.as_codetype().canonical_name(), ty) { println!("{} Added", &ty.as_codetype().canonical_name()); diff --git a/src/gen/types.rs b/src/gen/types.rs index 558825e..e1a4fae 100644 --- a/src/gen/types.rs +++ b/src/gen/types.rs @@ -445,14 +445,11 @@ impl Renderer<(FunctionDefinition, dart::Tokens)> for TypeHelpersRenderer<'_> { pub fn generate_type(ty: &Type) -> dart::Tokens { match ty { - Type::UInt8 - | Type::UInt32 - | Type::Int8 - | Type::Int16 - | Type::Int64 - | Type::UInt16 - | Type::Int32 - | Type::UInt64 => quote!(int), + Type::UInt8 | Type::UInt32 | Type::Int8 | Type::Int16 | Type::UInt16 | Type::Int32 => { + quote!(int) + } + // Use BigInt for 64-bit integers to handle values that exceed Dart's safe integer limits + Type::Int64 | Type::UInt64 => quote!(BigInt), Type::Float32 | Type::Float64 => quote!(double), Type::String => quote!(String), Type::Bytes => quote!(Uint8List), From 050fda4e2fb458f40c99fd91aa1444e94c436e12 Mon Sep 17 00:00:00 2001 From: chavic Date: Sun, 24 Aug 2025 19:01:22 +0200 Subject: [PATCH 2/5] moving primitives out of marcos and adding type boundaries --- src/gen/oracle.rs | 17 ++-- src/gen/primitives/mod.rs | 206 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 209 insertions(+), 14 deletions(-) diff --git a/src/gen/oracle.rs b/src/gen/oracle.rs index e53f00e..957d30e 100644 --- a/src/gen/oracle.rs +++ b/src/gen/oracle.rs @@ -215,16 +215,17 @@ impl DartCodeOracle { pub fn type_lower_fn(ty: &Type, inner: dart::Tokens) -> dart::Tokens { match ty { - Type::UInt32 - | Type::Int8 - | Type::UInt8 + // Float types don't need validation, just pass through + Type::Float32 | Type::Float64 => inner, + // All integer types now have validation and need lowering + Type::Int8 | Type::Int16 - | Type::UInt16 | Type::Int32 - | Type::Float32 - | Type::Float64 => inner, - // 64-bit integers need lowering because they use BigInt - Type::Int64 | Type::UInt64 => { + | Type::Int64 + | Type::UInt8 + | Type::UInt16 + | Type::UInt32 + | Type::UInt64 => { quote!($(ty.as_codetype().ffi_converter_name()).lower($inner)) } Type::Boolean diff --git a/src/gen/primitives/mod.rs b/src/gen/primitives/mod.rs index 50660a6..ea29a18 100644 --- a/src/gen/primitives/mod.rs +++ b/src/gen/primitives/mod.rs @@ -69,12 +69,7 @@ impl_code_type_for_primitive!(Float32CodeType, "double", "Double32"); impl_code_type_for_primitive!(Float64CodeType, "double", "Double64"); impl_renderable_for_primitive!(BytesCodeType, "Uint8List", "Uint8List"); -impl_renderable_for_primitive!(Int8CodeType, "int", "Int8", 1); -impl_renderable_for_primitive!(Int16CodeType, "int", "Int16", 2); -impl_renderable_for_primitive!(Int32CodeType, "int", "Int32", 4); -impl_renderable_for_primitive!(UInt8CodeType, "int", "UInt8", 1); -impl_renderable_for_primitive!(UInt16CodeType, "int", "UInt16", 2); -impl_renderable_for_primitive!(UInt32CodeType, "int", "UInt32", 4); +// Custom implementations for integer types with bounds checking impl_renderable_for_primitive!(Float32CodeType, "double", "Double32", 4); impl_renderable_for_primitive!(Float64CodeType, "double", "Double64", 8); @@ -161,3 +156,202 @@ impl Renderable for UInt64CodeType { } } } + +// Custom implementations for smaller integer types with bounds checking +impl Renderable for Int8CodeType { + fn render_type_helper(&self, _type_helper: &dyn TypeHelperRenderer) -> dart::Tokens { + let cl_name = &self.ffi_converter_name(); + let type_signature = &self.type_label(); + + quote! { + class $cl_name { + static $type_signature lift($type_signature value) => value; + + static LiftRetVal<$type_signature> read(Uint8List buf) { + return LiftRetVal(buf.buffer.asByteData(buf.offsetInBytes).getInt8(0), 1); + } + + static $type_signature lower($type_signature value) { + if (value < -128 || value > 127) { + throw ArgumentError("Value out of range for i8: " + value.toString()); + } + return value; + } + + static int allocationSize([$type_signature value = 0]) { + return 1; + } + + static int write($type_signature value, Uint8List buf) { + buf.buffer.asByteData(buf.offsetInBytes).setInt8(0, lower(value)); + return 1; + } + } + } + } +} + +impl Renderable for Int16CodeType { + fn render_type_helper(&self, _type_helper: &dyn TypeHelperRenderer) -> dart::Tokens { + let cl_name = &self.ffi_converter_name(); + let type_signature = &self.type_label(); + + quote! { + class $cl_name { + static $type_signature lift($type_signature value) => value; + + static LiftRetVal<$type_signature> read(Uint8List buf) { + return LiftRetVal(buf.buffer.asByteData(buf.offsetInBytes).getInt16(0), 2); + } + + static $type_signature lower($type_signature value) { + if (value < -32768 || value > 32767) { + throw ArgumentError("Value out of range for i16: " + value.toString()); + } + return value; + } + + static int allocationSize([$type_signature value = 0]) { + return 2; + } + + static int write($type_signature value, Uint8List buf) { + buf.buffer.asByteData(buf.offsetInBytes).setInt16(0, lower(value)); + return 2; + } + } + } + } +} + +impl Renderable for Int32CodeType { + fn render_type_helper(&self, _type_helper: &dyn TypeHelperRenderer) -> dart::Tokens { + let cl_name = &self.ffi_converter_name(); + let type_signature = &self.type_label(); + + quote! { + class $cl_name { + static $type_signature lift($type_signature value) => value; + + static LiftRetVal<$type_signature> read(Uint8List buf) { + return LiftRetVal(buf.buffer.asByteData(buf.offsetInBytes).getInt32(0), 4); + } + + static $type_signature lower($type_signature value) { + if (value < -2147483648 || value > 2147483647) { + throw ArgumentError("Value out of range for i32: " + value.toString()); + } + return value; + } + + static int allocationSize([$type_signature value = 0]) { + return 4; + } + + static int write($type_signature value, Uint8List buf) { + buf.buffer.asByteData(buf.offsetInBytes).setInt32(0, lower(value)); + return 4; + } + } + } + } +} + +impl Renderable for UInt8CodeType { + fn render_type_helper(&self, _type_helper: &dyn TypeHelperRenderer) -> dart::Tokens { + let cl_name = &self.ffi_converter_name(); + let type_signature = &self.type_label(); + + quote! { + class $cl_name { + static $type_signature lift($type_signature value) => value; + + static LiftRetVal<$type_signature> read(Uint8List buf) { + return LiftRetVal(buf.buffer.asByteData(buf.offsetInBytes).getUint8(0), 1); + } + + static $type_signature lower($type_signature value) { + if (value < 0 || value > 255) { + throw ArgumentError("Value out of range for u8: " + value.toString()); + } + return value; + } + + static int allocationSize([$type_signature value = 0]) { + return 1; + } + + static int write($type_signature value, Uint8List buf) { + buf.buffer.asByteData(buf.offsetInBytes).setUint8(0, lower(value)); + return 1; + } + } + } + } +} + +impl Renderable for UInt16CodeType { + fn render_type_helper(&self, _type_helper: &dyn TypeHelperRenderer) -> dart::Tokens { + let cl_name = &self.ffi_converter_name(); + let type_signature = &self.type_label(); + + quote! { + class $cl_name { + static $type_signature lift($type_signature value) => value; + + static LiftRetVal<$type_signature> read(Uint8List buf) { + return LiftRetVal(buf.buffer.asByteData(buf.offsetInBytes).getUint16(0), 2); + } + + static $type_signature lower($type_signature value) { + if (value < 0 || value > 65535) { + throw ArgumentError("Value out of range for u16: " + value.toString()); + } + return value; + } + + static int allocationSize([$type_signature value = 0]) { + return 2; + } + + static int write($type_signature value, Uint8List buf) { + buf.buffer.asByteData(buf.offsetInBytes).setUint16(0, lower(value)); + return 2; + } + } + } + } +} + +impl Renderable for UInt32CodeType { + fn render_type_helper(&self, _type_helper: &dyn TypeHelperRenderer) -> dart::Tokens { + let cl_name = &self.ffi_converter_name(); + let type_signature = &self.type_label(); + + quote! { + class $cl_name { + static $type_signature lift($type_signature value) => value; + + static LiftRetVal<$type_signature> read(Uint8List buf) { + return LiftRetVal(buf.buffer.asByteData(buf.offsetInBytes).getUint32(0), 4); + } + + static $type_signature lower($type_signature value) { + if (value < 0 || value > 4294967295) { + throw ArgumentError("Value out of range for u32: " + value.toString()); + } + return value; + } + + static int allocationSize([$type_signature value = 0]) { + return 4; + } + + static int write($type_signature value, Uint8List buf) { + buf.buffer.asByteData(buf.offsetInBytes).setUint32(0, lower(value)); + return 4; + } + } + } + } +} From f278e0f4d3e0f8938a5bdf1e04faff209fc0e0e7 Mon Sep 17 00:00:00 2001 From: chavic Date: Mon, 25 Aug 2025 08:10:23 +0200 Subject: [PATCH 3/5] feat: Refactored int primitives with BigInt support and bounds checking --- .../test/duration_type_test.dart | 14 +- src/gen/compounds.rs | 2 +- src/gen/primitives/macros.rs | 42 ++++ src/gen/primitives/mod.rs | 213 ++---------------- src/gen/render/mod.rs | 2 +- 5 files changed, 66 insertions(+), 207 deletions(-) diff --git a/fixtures/duration_type_test/test/duration_type_test.dart b/fixtures/duration_type_test/test/duration_type_test.dart index 942e4d7..09e8b84 100644 --- a/fixtures/duration_type_test/test/duration_type_test.dart +++ b/fixtures/duration_type_test/test/duration_type_test.dart @@ -3,32 +3,32 @@ import '../duration_type_test.dart'; void main() { test('rust return value seconds check', () { - final duration = makeDuration(5, 0); + final duration = makeDuration(BigInt.from(5), 0); expect(duration.inSeconds, 5); - expect(getSeconds(duration), 5); + expect(getSeconds(duration), BigInt.from(5)); expect(getNanos(duration), 0); }); test('seconds data check from dart', () { final duration = Duration(seconds: 10); - expect(getSeconds(duration), 10); + expect(getSeconds(duration), BigInt.from(10)); expect(getNanos(duration), 0); }); test('check nanos/micros', () { - final duration = makeDuration(0, 3000); + final duration = makeDuration(BigInt.zero, 3000); expect(duration.inSeconds, 0); expect(duration.inMicroseconds, 3); - expect(getSeconds(duration), 0); + expect(getSeconds(duration), BigInt.zero); expect(getNanos(duration), 3000); }); test('check large values', () { - final duration = makeDuration(123456789, 3000000); + final duration = makeDuration(BigInt.from(123456789), 3000000); expect(duration.inSeconds, 123456789); expect(duration.inMicroseconds, 123456789003000); - expect(getSeconds(duration), 123456789); + expect(getSeconds(duration), BigInt.from(123456789)); expect(getNanos(duration), 3000000); }); } diff --git a/src/gen/compounds.rs b/src/gen/compounds.rs index 6276922..283f6b4 100644 --- a/src/gen/compounds.rs +++ b/src/gen/compounds.rs @@ -222,7 +222,7 @@ impl CodeType for MapCodeType { fn canonical_name(&self) -> String { let key = DartCodeOracle::find(self.key()).canonical_name(); let val = DartCodeOracle::find(self.value()).canonical_name(); - format!("Map{}To{}", key, val) + format!("Map{key}To{val}") } } diff --git a/src/gen/primitives/macros.rs b/src/gen/primitives/macros.rs index 22d37fa..af144dd 100644 --- a/src/gen/primitives/macros.rs +++ b/src/gen/primitives/macros.rs @@ -68,6 +68,7 @@ macro_rules! impl_renderable_for_primitive { } } }; + // Variant without bounds checking (for floats, etc.) ($T:ty, $class_name:literal, $canonical_name:literal, $allocation_size:literal) => { impl Renderable for $T { fn render_type_helper(&self, _type_helper: &dyn TypeHelperRenderer) -> dart::Tokens { @@ -111,4 +112,45 @@ macro_rules! impl_renderable_for_primitive { } } }; + // Variant with bounds checking (for integers) + ($T:ty, $class_name:literal, $canonical_name:literal, $allocation_size:literal, $min_value:literal, $max_value:literal, $type_name:literal) => { + impl Renderable for $T { + fn render_type_helper(&self, _type_helper: &dyn TypeHelperRenderer) -> dart::Tokens { + let cl_name = &self.ffi_converter_name(); + let type_signature = &self.type_label(); + let conversion_name = &$canonical_name + .replace("UInt", "Uint") + .replace("Double", "Float"); + + // Pre-compute the error message to avoid string interpolation conflicts + let error_message = format!("\"Value out of range for {}: \" + value.toString()", $type_name); + + quote! { + class $cl_name { + static $type_signature lift($type_signature value) => value; + + static LiftRetVal<$type_signature> read(Uint8List buf) { + return LiftRetVal(buf.buffer.asByteData(buf.offsetInBytes).get$conversion_name(0), $allocation_size); + } + + static $type_signature lower($type_signature value) { + if (value < $min_value || value > $max_value) { + throw ArgumentError($error_message); + } + return value; + } + + static int allocationSize([$type_signature value = 0]) { + return $allocation_size; + } + + static int write($type_signature value, Uint8List buf) { + buf.buffer.asByteData(buf.offsetInBytes).set$conversion_name(0, lower(value)); + return $allocation_size; + } + } + } + } + } + }; } diff --git a/src/gen/primitives/mod.rs b/src/gen/primitives/mod.rs index ea29a18..b6f3fe3 100644 --- a/src/gen/primitives/mod.rs +++ b/src/gen/primitives/mod.rs @@ -157,201 +157,18 @@ impl Renderable for UInt64CodeType { } } -// Custom implementations for smaller integer types with bounds checking -impl Renderable for Int8CodeType { - fn render_type_helper(&self, _type_helper: &dyn TypeHelperRenderer) -> dart::Tokens { - let cl_name = &self.ffi_converter_name(); - let type_signature = &self.type_label(); - - quote! { - class $cl_name { - static $type_signature lift($type_signature value) => value; - - static LiftRetVal<$type_signature> read(Uint8List buf) { - return LiftRetVal(buf.buffer.asByteData(buf.offsetInBytes).getInt8(0), 1); - } - - static $type_signature lower($type_signature value) { - if (value < -128 || value > 127) { - throw ArgumentError("Value out of range for i8: " + value.toString()); - } - return value; - } - - static int allocationSize([$type_signature value = 0]) { - return 1; - } - - static int write($type_signature value, Uint8List buf) { - buf.buffer.asByteData(buf.offsetInBytes).setInt8(0, lower(value)); - return 1; - } - } - } - } -} - -impl Renderable for Int16CodeType { - fn render_type_helper(&self, _type_helper: &dyn TypeHelperRenderer) -> dart::Tokens { - let cl_name = &self.ffi_converter_name(); - let type_signature = &self.type_label(); - - quote! { - class $cl_name { - static $type_signature lift($type_signature value) => value; - - static LiftRetVal<$type_signature> read(Uint8List buf) { - return LiftRetVal(buf.buffer.asByteData(buf.offsetInBytes).getInt16(0), 2); - } - - static $type_signature lower($type_signature value) { - if (value < -32768 || value > 32767) { - throw ArgumentError("Value out of range for i16: " + value.toString()); - } - return value; - } - - static int allocationSize([$type_signature value = 0]) { - return 2; - } - - static int write($type_signature value, Uint8List buf) { - buf.buffer.asByteData(buf.offsetInBytes).setInt16(0, lower(value)); - return 2; - } - } - } - } -} - -impl Renderable for Int32CodeType { - fn render_type_helper(&self, _type_helper: &dyn TypeHelperRenderer) -> dart::Tokens { - let cl_name = &self.ffi_converter_name(); - let type_signature = &self.type_label(); - - quote! { - class $cl_name { - static $type_signature lift($type_signature value) => value; - - static LiftRetVal<$type_signature> read(Uint8List buf) { - return LiftRetVal(buf.buffer.asByteData(buf.offsetInBytes).getInt32(0), 4); - } - - static $type_signature lower($type_signature value) { - if (value < -2147483648 || value > 2147483647) { - throw ArgumentError("Value out of range for i32: " + value.toString()); - } - return value; - } - - static int allocationSize([$type_signature value = 0]) { - return 4; - } - - static int write($type_signature value, Uint8List buf) { - buf.buffer.asByteData(buf.offsetInBytes).setInt32(0, lower(value)); - return 4; - } - } - } - } -} - -impl Renderable for UInt8CodeType { - fn render_type_helper(&self, _type_helper: &dyn TypeHelperRenderer) -> dart::Tokens { - let cl_name = &self.ffi_converter_name(); - let type_signature = &self.type_label(); - - quote! { - class $cl_name { - static $type_signature lift($type_signature value) => value; - - static LiftRetVal<$type_signature> read(Uint8List buf) { - return LiftRetVal(buf.buffer.asByteData(buf.offsetInBytes).getUint8(0), 1); - } - - static $type_signature lower($type_signature value) { - if (value < 0 || value > 255) { - throw ArgumentError("Value out of range for u8: " + value.toString()); - } - return value; - } - - static int allocationSize([$type_signature value = 0]) { - return 1; - } - - static int write($type_signature value, Uint8List buf) { - buf.buffer.asByteData(buf.offsetInBytes).setUint8(0, lower(value)); - return 1; - } - } - } - } -} - -impl Renderable for UInt16CodeType { - fn render_type_helper(&self, _type_helper: &dyn TypeHelperRenderer) -> dart::Tokens { - let cl_name = &self.ffi_converter_name(); - let type_signature = &self.type_label(); - - quote! { - class $cl_name { - static $type_signature lift($type_signature value) => value; - - static LiftRetVal<$type_signature> read(Uint8List buf) { - return LiftRetVal(buf.buffer.asByteData(buf.offsetInBytes).getUint16(0), 2); - } - - static $type_signature lower($type_signature value) { - if (value < 0 || value > 65535) { - throw ArgumentError("Value out of range for u16: " + value.toString()); - } - return value; - } - - static int allocationSize([$type_signature value = 0]) { - return 2; - } - - static int write($type_signature value, Uint8List buf) { - buf.buffer.asByteData(buf.offsetInBytes).setUint16(0, lower(value)); - return 2; - } - } - } - } -} - -impl Renderable for UInt32CodeType { - fn render_type_helper(&self, _type_helper: &dyn TypeHelperRenderer) -> dart::Tokens { - let cl_name = &self.ffi_converter_name(); - let type_signature = &self.type_label(); - - quote! { - class $cl_name { - static $type_signature lift($type_signature value) => value; - - static LiftRetVal<$type_signature> read(Uint8List buf) { - return LiftRetVal(buf.buffer.asByteData(buf.offsetInBytes).getUint32(0), 4); - } - - static $type_signature lower($type_signature value) { - if (value < 0 || value > 4294967295) { - throw ArgumentError("Value out of range for u32: " + value.toString()); - } - return value; - } - - static int allocationSize([$type_signature value = 0]) { - return 4; - } - - static int write($type_signature value, Uint8List buf) { - buf.buffer.asByteData(buf.offsetInBytes).setUint32(0, lower(value)); - return 4; - } - } - } - } -} +// Use the unified macro with bounds checking for integer types +impl_renderable_for_primitive!(Int8CodeType, "int", "Int8", 1, -128, 127, "i8"); +impl_renderable_for_primitive!(Int16CodeType, "int", "Int16", 2, -32768, 32767, "i16"); +impl_renderable_for_primitive!( + Int32CodeType, + "int", + "Int32", + 4, + -2147483648, + 2147483647, + "i32" +); +impl_renderable_for_primitive!(UInt8CodeType, "int", "UInt8", 1, 0, 255, "u8"); +impl_renderable_for_primitive!(UInt16CodeType, "int", "UInt16", 2, 0, 65535, "u16"); +impl_renderable_for_primitive!(UInt32CodeType, "int", "UInt32", 4, 0, 4294967295, "u32"); diff --git a/src/gen/render/mod.rs b/src/gen/render/mod.rs index 4a7c836..b24a29f 100644 --- a/src/gen/render/mod.rs +++ b/src/gen/render/mod.rs @@ -1,6 +1,6 @@ use super::{callback_interface, compounds, custom, enums, primitives, records}; use super::{objects, oracle::AsCodeType}; -use genco::{lang::dart, quote}; +use genco::lang::dart; use uniffi_bindgen::interface::{AsType, Enum, Object, Record, Type}; use uniffi_bindgen::ComponentInterface; From a31acc1a94afb14815049dd238bc93b5b8387bd1 Mon Sep 17 00:00:00 2001 From: chavic Date: Wed, 27 Aug 2025 10:41:58 +0200 Subject: [PATCH 4/5] fix: using bitInt in tests to match expected type --- .../test/simple_arithmetic_test.dart | 12 +++++----- .../streams_ext/test/strems_ext_test.dart | 23 +++++++++++++++++-- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/fixtures/arithmetic/test/simple_arithmetic_test.dart b/fixtures/arithmetic/test/simple_arithmetic_test.dart index 8ca7091..cd8bbd1 100644 --- a/fixtures/arithmetic/test/simple_arithmetic_test.dart +++ b/fixtures/arithmetic/test/simple_arithmetic_test.dart @@ -24,7 +24,7 @@ void main() { expect(addU16(2, 2), 4); }); test('u64', () { - expect(addU64(2, 2), 4); + expect(addU64(BigInt.from(2), BigInt.from(2)), BigInt.from(4)); }); test('i8', () { @@ -37,7 +37,7 @@ void main() { expect(addI32(2, 2), 4); }); test('i64', () { - expect(addI64(2, 2), 4); + expect(addI64(BigInt.from(2), BigInt.from(2)), BigInt.from(4)); }); test('f32', () { expect(addF32(2.0, 2.0), 4.0); @@ -53,7 +53,7 @@ void main() { expect(getBackU16(4), 4); }); test('get back u64', () { - expect(getBackU64(4), 4); + expect(getBackU64(BigInt.from(4)), BigInt.from(4)); }); test('get back i8', () { @@ -67,15 +67,15 @@ void main() { }); test('divide by zero - success case', () { - expect(divideByZero(10, 2), 5); + expect(divideByZero(BigInt.from(10), BigInt.from(2)), BigInt.from(5)); }); test('divide by zero - error case', () { expect( - () => divideByZero(10, 0), throwsA(isA())); + () => divideByZero(BigInt.from(10), BigInt.zero), throwsA(isA())); }); test('divide by zero - specific error type', () { - expect(() => divideByZero(10, 0), throwsA(isA())); + expect(() => divideByZero(BigInt.from(10), BigInt.zero), throwsA(isA())); }); } diff --git a/fixtures/streams_ext/test/strems_ext_test.dart b/fixtures/streams_ext/test/strems_ext_test.dart index e257d24..ce20fb3 100644 --- a/fixtures/streams_ext/test/strems_ext_test.dart +++ b/fixtures/streams_ext/test/strems_ext_test.dart @@ -29,14 +29,33 @@ void main() { test('Fibonacci Stream emits first 10 Fibonacci numbers', () { expect( fibonacciStream().take(10), - emitsInOrder([0, 1, 1, 2, 3, 5, 8, 13, 21, 34, emitsDone]), + emitsInOrder([ + BigInt.from(0), + BigInt.from(1), + BigInt.from(1), + BigInt.from(2), + BigInt.from(3), + BigInt.from(5), + BigInt.from(8), + BigInt.from(13), + BigInt.from(21), + BigInt.from(34), + emitsDone + ]), ); }); test('Async Timer Stream emits incrementing numbers', () { expect( asyncTimerStream().take(5), - emitsInOrder([1, 2, 3, 4, 5, emitsDone]), + emitsInOrder([ + BigInt.from(1), + BigInt.from(2), + BigInt.from(3), + BigInt.from(4), + BigInt.from(5), + emitsDone + ]), ); }, timeout: Timeout(Duration(seconds: 6))); From 19aa4f236c19b0b821540baf811aef9590875893 Mon Sep 17 00:00:00 2001 From: chavic Date: Wed, 27 Aug 2025 10:43:19 +0200 Subject: [PATCH 5/5] fix: added logic to render inner type converters when compound types are rendered --- src/gen/compounds.rs | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/src/gen/compounds.rs b/src/gen/compounds.rs index 283f6b4..59de21b 100644 --- a/src/gen/compounds.rs +++ b/src/gen/compounds.rs @@ -5,7 +5,7 @@ use paste::paste; use uniffi_bindgen::interface::Type; use super::oracle::{AsCodeType, DartCodeOracle}; -use crate::gen::render::{Renderable, TypeHelperRenderer}; +use crate::gen::render::{AsRenderable, Renderable, TypeHelperRenderer}; macro_rules! impl_code_type_for_compound { ($T:ty, $type_label_pattern:literal, $canonical_name_pattern: literal) => { @@ -47,7 +47,13 @@ macro_rules! impl_renderable_for_compound { let inner_codetype = DartCodeOracle::find(self.inner()); let inner_type_label = inner_codetype.type_label(); - type_helper.include_once_check(&inner_codetype.canonical_name(), &self.inner()); // Add the Inner FFI Converter + // Add the Inner FFI Converter and ensure it's rendered + let inner_type_rendered = if !type_helper.include_once_check(&inner_codetype.canonical_name(), &self.inner()) { + // If this is the first time we're seeing this inner type, render it now + self.inner().as_renderable().render_type_helper(type_helper) + } else { + quote!() + }; let cl_name = &format!($canonical_name_pattern, &inner_codetype.canonical_name()); let type_label = &format!($type_label_pattern, &inner_type_label); @@ -58,6 +64,8 @@ macro_rules! impl_renderable_for_compound { quote! { + $inner_type_rendered + class $cl_name { static $type_label lift( RustBuffer buf) { @@ -124,7 +132,13 @@ macro_rules! impl_renderable_for_compound { let inner_codetype = self.inner().as_codetype(); let inner_type_label = inner_codetype.type_label(); - type_helper.include_once_check(&inner_codetype.canonical_name(), &self.inner()); // Add the Inner FFI Converter + // Add the Inner FFI Converter and ensure it's rendered + let inner_type_rendered = if !type_helper.include_once_check(&inner_codetype.canonical_name(), &self.inner()) { + // If this is the first time we're seeing this inner type, render it now + self.inner().as_renderable().render_type_helper(type_helper) + } else { + quote!() + }; let cl_name = &format!($canonical_name_pattern, &inner_codetype.canonical_name()); let type_label = &format!("List<{}>", &inner_type_label); @@ -135,6 +149,8 @@ macro_rules! impl_renderable_for_compound { quote! { + $inner_type_rendered + class $cl_name { static $type_label lift( RustBuffer buf) { @@ -233,8 +249,19 @@ impl Renderable for MapCodeType { let key_codetype = DartCodeOracle::find(self.key()); let val_codetype = DartCodeOracle::find(self.value()); - type_helper.include_once_check(&key_codetype.canonical_name(), self.key()); - type_helper.include_once_check(&val_codetype.canonical_name(), self.value()); + // Add the key and value FFI Converters and ensure they're rendered + let key_type_rendered = + if !type_helper.include_once_check(&key_codetype.canonical_name(), self.key()) { + self.key().as_renderable().render_type_helper(type_helper) + } else { + quote!() + }; + let val_type_rendered = + if !type_helper.include_once_check(&val_codetype.canonical_name(), self.value()) { + self.value().as_renderable().render_type_helper(type_helper) + } else { + quote!() + }; let cl_name = &self.ffi_converter_name(); let key_type_label_owned = key_codetype.type_label(); @@ -248,6 +275,9 @@ impl Renderable for MapCodeType { let val_conv = &val_conv_owned; quote! { + $key_type_rendered + $val_type_rendered + class $cl_name { static Map<$key_type_label, $val_type_label> lift(RustBuffer buf) { return $cl_name.read(buf.asUint8List()).value;