Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions fixtures/arithmetic/test/simple_arithmetic_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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', () {
Expand All @@ -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);
Expand All @@ -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', () {
Expand All @@ -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<DivisionByZeroMathException>()));
() => divideByZero(BigInt.from(10), BigInt.zero), throwsA(isA<DivisionByZeroMathException>()));
});

test('divide by zero - specific error type', () {
expect(() => divideByZero(10, 0), throwsA(isA<MathException>()));
expect(() => divideByZero(BigInt.from(10), BigInt.zero), throwsA(isA<MathException>()));
});
}
14 changes: 7 additions & 7 deletions fixtures/duration_type_test/test/duration_type_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
}
23 changes: 21 additions & 2 deletions fixtures/streams_ext/test/strems_ext_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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)));

Expand Down
24 changes: 14 additions & 10 deletions fixtures/type-limits/test/type_limits_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
});
});

Expand Down Expand Up @@ -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', () {
Expand All @@ -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);
});
Expand Down
42 changes: 36 additions & 6 deletions src/gen/compounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down Expand Up @@ -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);
Expand All @@ -58,6 +64,8 @@ macro_rules! impl_renderable_for_compound {


quote! {
$inner_type_rendered

class $cl_name {

static $type_label lift( RustBuffer buf) {
Expand Down Expand Up @@ -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);
Expand All @@ -135,6 +149,8 @@ macro_rules! impl_renderable_for_compound {


quote! {
$inner_type_rendered

class $cl_name {

static $type_label lift( RustBuffer buf) {
Expand Down Expand Up @@ -222,7 +238,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}")
}
}

Expand All @@ -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();
Expand All @@ -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;
Expand Down
23 changes: 13 additions & 10 deletions src/gen/oracle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,16 +215,19 @@ 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::Int64
| Type::UInt64
| Type::Float32
| Type::Float64 => inner,
| Type::UInt8
| Type::UInt16
| Type::UInt32
| Type::UInt64 => {
quote!($(ty.as_codetype().ffi_converter_name()).lower($inner))
}
Type::Boolean
| Type::Duration
| Type::String
Expand Down Expand Up @@ -261,11 +264,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),
Expand Down
42 changes: 42 additions & 0 deletions src/gen/primitives/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
}
}
}
}
}
};
}
Loading
Loading