allo_isolate/
into_dart.rs

1use std::collections::{HashMap, HashSet};
2use std::{
3    ffi::{c_void, CString},
4    mem::ManuallyDrop,
5};
6
7use crate::{
8    dart_array::DartArray,
9    ffi::{DartHandleFinalizer, *},
10};
11
12/// A trait to convert between Rust types and Dart Types that could then
13/// be sent to the isolate
14///
15/// see: [`crate::Isolate::post`]
16pub trait IntoDart {
17    /// Consumes `Self` and Performs the conversion.
18    fn into_dart(self) -> DartCObject;
19}
20
21/// A trait that is [`IntoDart`] and is also not a primitive type. It is used to
22/// avoid the ambiguity of whether types such as [`Vec<i32>`] should be
23/// converted into [`Int32List`] or [`List<int>`]
24pub trait IntoDartExceptPrimitive: IntoDart {}
25
26impl<T> IntoDart for T
27where
28    T: Into<DartCObject>,
29{
30    fn into_dart(self) -> DartCObject {
31        self.into()
32    }
33}
34
35impl<T> IntoDartExceptPrimitive for T where T: IntoDart + Into<DartCObject> {}
36
37impl IntoDart for () {
38    fn into_dart(self) -> DartCObject {
39        DartCObject {
40            ty: DartCObjectType::DartNull,
41            // I don't know what value should we send, so I send a false
42            // I guess dart vm check for the type first, so if it is null it
43            // return null and ignore the value
44            value: DartCObjectValue { as_bool: false },
45        }
46    }
47}
48
49#[cfg(feature = "anyhow")]
50impl IntoDart for anyhow::Error {
51    fn into_dart(self) -> DartCObject {
52        format!("{:?}", self).into_dart()
53    }
54}
55
56impl IntoDart for std::backtrace::Backtrace {
57    fn into_dart(self) -> DartCObject {
58        format!("{:?}", self).into_dart()
59    }
60}
61
62#[cfg(feature = "backtrace")]
63impl IntoDart for backtrace::Backtrace {
64    fn into_dart(self) -> DartCObject {
65        format!("{:?}", self).into_dart()
66    }
67}
68
69impl IntoDart for i32 {
70    fn into_dart(self) -> DartCObject {
71        DartCObject {
72            ty: DartCObjectType::DartInt32,
73            value: DartCObjectValue { as_int32: self },
74        }
75    }
76}
77
78impl IntoDart for i64 {
79    fn into_dart(self) -> DartCObject {
80        DartCObject {
81            ty: DartCObjectType::DartInt64,
82            value: DartCObjectValue { as_int64: self },
83        }
84    }
85}
86
87impl IntoDart for f32 {
88    fn into_dart(self) -> DartCObject {
89        (self as f64).into_dart()
90    }
91}
92
93impl IntoDart for f64 {
94    fn into_dart(self) -> DartCObject {
95        DartCObject {
96            ty: DartCObjectType::DartDouble,
97            value: DartCObjectValue { as_double: self },
98        }
99    }
100}
101
102impl IntoDart for bool {
103    fn into_dart(self) -> DartCObject {
104        DartCObject {
105            ty: DartCObjectType::DartBool,
106            value: DartCObjectValue { as_bool: self },
107        }
108    }
109}
110
111// https://github.com/sunshine-protocol/allo-isolate/pull/47
112//
113// Since Dart doesn't have a primitive list implemented for boolean (e.g. `Uint8List` for 8-bit unsigned int),
114// we should implement `IntoDartExceptPrimitive` for `bool` so that `Vec<bool>` can be converted to `List<bool>`.
115impl IntoDartExceptPrimitive for bool {}
116
117impl IntoDart for String {
118    fn into_dart(self) -> DartCObject {
119        let s = CString::new(self).unwrap_or_default();
120        s.into_dart()
121    }
122}
123
124impl IntoDartExceptPrimitive for String {}
125
126impl IntoDart for &'_ str {
127    fn into_dart(self) -> DartCObject {
128        self.to_string().into_dart()
129    }
130}
131
132impl IntoDartExceptPrimitive for &'_ str {}
133
134impl IntoDart for CString {
135    fn into_dart(self) -> DartCObject {
136        DartCObject {
137            ty: DartCObjectType::DartString,
138            value: DartCObjectValue {
139                as_string: self.into_raw(),
140            },
141        }
142    }
143}
144
145impl IntoDartExceptPrimitive for CString {}
146
147/// It is used when you want to write a generic function on different data types
148/// and do not want to repeat yourself dozens of times.
149/// For example, inside [Drop] of [DartCObject].
150pub(crate) trait DartTypedDataTypeVisitor {
151    fn visit<T: DartTypedDataTypeTrait>(&self);
152}
153
154/// The Rust type for corresponding [DartTypedDataType]
155pub trait DartTypedDataTypeTrait {
156    fn dart_typed_data_type() -> DartTypedDataType;
157
158    fn function_pointer_of_free_zero_copy_buffer() -> DartHandleFinalizer;
159}
160
161fn vec_to_dart_native_external_typed_data<T>(
162    vec_from_rust: Vec<T>,
163) -> DartCObject
164where
165    T: DartTypedDataTypeTrait,
166{
167    if vec_from_rust.is_empty() {
168        let data = DartNativeTypedData {
169            ty: T::dart_typed_data_type(),
170            length: 0,
171            values: std::ptr::null_mut(),
172        };
173        return DartCObject {
174            ty: DartCObjectType::DartTypedData,
175            value: DartCObjectValue {
176                as_typed_data: data,
177            },
178        };
179    }
180
181    let mut vec = vec_from_rust;
182    vec.shrink_to_fit();
183    let length = vec.len();
184    assert_eq!(length, vec.capacity());
185    let ptr = vec.as_mut_ptr();
186
187    DartCObject {
188        ty: DartCObjectType::DartExternalTypedData,
189        value: DartCObjectValue {
190            as_external_typed_data: DartNativeExternalTypedData {
191                ty: T::dart_typed_data_type(),
192                length: length as isize,
193                data: ptr as *mut u8,
194                peer: Box::into_raw(Box::new(vec)).cast(),
195                callback: T::function_pointer_of_free_zero_copy_buffer(),
196            },
197        },
198    }
199}
200
201macro_rules! dart_typed_data_type_trait_impl {
202    ($($dart_type:path => $rust_type:ident + $free_zero_copy_buffer_func:ident),+) => {
203        $(
204            impl DartTypedDataTypeTrait for $rust_type {
205                fn dart_typed_data_type() -> DartTypedDataType {
206                    $dart_type
207                }
208
209                fn function_pointer_of_free_zero_copy_buffer() -> DartHandleFinalizer {
210                    $free_zero_copy_buffer_func
211                }
212            }
213
214            impl<const N: usize> IntoDart for [$rust_type;N] {
215                fn into_dart(self) -> DartCObject {
216                    let vec: Vec<_> = self.into();
217                    vec.into_dart()
218                }
219            }
220
221            #[cfg(not(feature="zero-copy"))]
222            impl IntoDart for Vec<$rust_type> {
223                fn into_dart(self) -> DartCObject {
224                    let mut vec = ManuallyDrop::new(self);
225                    let data = DartNativeTypedData {
226                        ty: $rust_type::dart_typed_data_type(),
227                        length: vec.len() as isize,
228                        values: vec.as_mut_ptr() as *mut _,
229                    };
230                    DartCObject {
231                        ty: DartCObjectType::DartTypedData,
232                        value: DartCObjectValue {
233                            as_typed_data: data,
234                        },
235                    }
236                }
237            }
238            #[cfg(feature="zero-copy")]
239            impl IntoDart for Vec<$rust_type> {
240                fn into_dart(self) -> DartCObject {
241                    vec_to_dart_native_external_typed_data(self)
242                }
243            }
244
245            impl IntoDartExceptPrimitive for Vec<$rust_type> {}
246
247            impl IntoDart for HashSet<$rust_type> {
248                fn into_dart(self) -> DartCObject {
249                    self.into_iter().collect::<Vec<_>>().into_dart()
250                }
251            }
252
253            #[doc(hidden)]
254            #[no_mangle]
255            pub(crate) unsafe extern "C" fn $free_zero_copy_buffer_func(
256                _isolate_callback_data: *mut c_void,
257                peer: *mut c_void,
258            ) {
259                drop(Box::from_raw(peer.cast::<Vec<$rust_type>>()));
260            }
261        )+
262
263        pub(crate) fn visit_dart_typed_data_type<V: DartTypedDataTypeVisitor>(ty: DartTypedDataType, visitor: &V) {
264            match ty {
265                $(
266                    $dart_type => visitor.visit::<$rust_type>(),
267                )+
268                _ => panic!("visit_dart_typed_data_type see unexpected DartTypedDataType={:?}", ty)
269            }
270        }
271    }
272}
273
274dart_typed_data_type_trait_impl!(
275    DartTypedDataType::Int8 => i8 + free_zero_copy_buffer_i8,
276    DartTypedDataType::Uint8 => u8 + free_zero_copy_buffer_u8,
277    DartTypedDataType::Int16 => i16 + free_zero_copy_buffer_i16,
278    DartTypedDataType::Uint16 => u16 + free_zero_copy_buffer_u16,
279    DartTypedDataType::Int32 => i32 + free_zero_copy_buffer_i32,
280    DartTypedDataType::Uint32 => u32 + free_zero_copy_buffer_u32,
281    DartTypedDataType::Int64 => i64 + free_zero_copy_buffer_i64,
282    DartTypedDataType::Uint64 => u64 + free_zero_copy_buffer_u64,
283    DartTypedDataType::Float32 => f32 + free_zero_copy_buffer_f32,
284    DartTypedDataType::Float64 => f64 + free_zero_copy_buffer_f64
285);
286
287macro_rules! isize_usize {
288    ($rust_type:ident, $delegate_target_type:ident) => {
289        impl<const N: usize> IntoDart for [$rust_type; N] {
290            fn into_dart(self) -> DartCObject {
291                let vec: Vec<_> = self.into();
292                vec.into_dart()
293            }
294        }
295
296        impl IntoDart for Vec<$rust_type> {
297            fn into_dart(self) -> DartCObject {
298                let vec: Vec<$delegate_target_type> =
299                    self.into_iter().map(|x| x as _).collect();
300                vec.into_dart()
301            }
302        }
303
304        impl<const N: usize> IntoDart for ZeroCopyBuffer<[$rust_type; N]> {
305            fn into_dart(self) -> DartCObject {
306                let vec: Vec<$rust_type> = self.0.into();
307                ZeroCopyBuffer(vec).into_dart()
308            }
309        }
310
311        impl IntoDart for ZeroCopyBuffer<Vec<$rust_type>> {
312            fn into_dart(self) -> DartCObject {
313                let vec: Vec<$delegate_target_type> =
314                    self.0.into_iter().map(|x| x as _).collect();
315                ZeroCopyBuffer(vec).into_dart()
316            }
317        }
318
319        impl IntoDartExceptPrimitive for Vec<$rust_type> {}
320    };
321}
322
323isize_usize!(isize, i64);
324isize_usize!(usize, u64);
325
326impl<T> IntoDart for ZeroCopyBuffer<Vec<T>>
327where
328    T: DartTypedDataTypeTrait,
329{
330    fn into_dart(self) -> DartCObject {
331        vec_to_dart_native_external_typed_data(self.0)
332    }
333}
334
335impl<T> IntoDartExceptPrimitive for ZeroCopyBuffer<Vec<T>> where
336    T: DartTypedDataTypeTrait
337{
338}
339
340impl<T> IntoDart for Vec<T>
341where
342    T: IntoDartExceptPrimitive,
343{
344    fn into_dart(self) -> DartCObject {
345        DartArray::from(self.into_iter()).into_dart()
346    }
347}
348
349impl<T> IntoDartExceptPrimitive for Vec<T> where T: IntoDartExceptPrimitive {}
350
351impl<T> IntoDart for HashSet<T>
352where
353    T: IntoDartExceptPrimitive,
354{
355    fn into_dart(self) -> DartCObject {
356        // Treated as `Vec<T>` and become `List` in Dart. It is unordered even though the type is a "list".
357        self.into_iter().collect::<Vec<_>>().into_dart()
358    }
359}
360
361impl<T> IntoDartExceptPrimitive for HashSet<T> where T: IntoDartExceptPrimitive {}
362
363impl<K, V> IntoDart for HashMap<K, V>
364where
365    K: IntoDart,
366    V: IntoDart,
367    (K, V): IntoDartExceptPrimitive,
368{
369    fn into_dart(self) -> DartCObject {
370        // Treated as `Vec<(K, V)>` and thus become `List<dynamic>` in Dart
371        self.into_iter().collect::<Vec<_>>().into_dart()
372    }
373}
374
375impl<K, V> IntoDartExceptPrimitive for HashMap<K, V>
376where
377    K: IntoDart,
378    V: IntoDart,
379{
380}
381
382impl<T, const N: usize> IntoDart for ZeroCopyBuffer<[T; N]>
383where
384    T: DartTypedDataTypeTrait,
385{
386    fn into_dart(self) -> DartCObject {
387        let vec: Vec<_> = self.0.into();
388        ZeroCopyBuffer(vec).into_dart()
389    }
390}
391
392impl<T, const N: usize> IntoDartExceptPrimitive for ZeroCopyBuffer<[T; N]> where
393    T: DartTypedDataTypeTrait
394{
395}
396
397impl<T, const N: usize> IntoDart for [T; N]
398where
399    T: IntoDartExceptPrimitive,
400{
401    fn into_dart(self) -> DartCObject {
402        DartArray::from(IntoIterator::into_iter(self)).into_dart()
403    }
404}
405
406impl<T> IntoDart for Option<T>
407where
408    T: IntoDart,
409{
410    fn into_dart(self) -> DartCObject {
411        match self {
412            Some(v) => v.into_dart(),
413            None => ().into_dart(),
414        }
415    }
416}
417
418impl<T> IntoDartExceptPrimitive for Option<T> where T: IntoDart {}
419
420impl<T, E> IntoDart for Result<T, E>
421where
422    T: IntoDart,
423    E: ToString,
424{
425    fn into_dart(self) -> DartCObject {
426        match self {
427            Ok(v) => v.into_dart(),
428            Err(e) => e.to_string().into_dart(),
429        }
430    }
431}
432
433impl<T, E> IntoDartExceptPrimitive for Result<T, E>
434where
435    T: IntoDart,
436    E: ToString,
437{
438}
439
440/// A workaround to send raw pointers to dart over the port.
441/// it will be sent as int64 on 64bit targets, and as int32 on 32bit targets.
442#[cfg(target_pointer_width = "64")]
443impl<T> IntoDart for *const T {
444    fn into_dart(self) -> DartCObject {
445        DartCObject {
446            ty: DartCObjectType::DartInt64,
447            value: DartCObjectValue {
448                as_int64: self as _,
449            },
450        }
451    }
452}
453
454#[cfg(target_pointer_width = "64")]
455impl<T> IntoDart for *mut T {
456    fn into_dart(self) -> DartCObject {
457        DartCObject {
458            ty: DartCObjectType::DartInt64,
459            value: DartCObjectValue {
460                as_int64: self as _,
461            },
462        }
463    }
464}
465
466#[cfg(target_pointer_width = "32")]
467impl<T> IntoDart for *const T {
468    fn into_dart(self) -> DartCObject {
469        DartCObject {
470            ty: DartCObjectType::DartInt32,
471            value: DartCObjectValue {
472                as_int32: self as _,
473            },
474        }
475    }
476}
477
478#[cfg(target_pointer_width = "32")]
479impl<T> IntoDart for *mut T {
480    fn into_dart(self) -> DartCObject {
481        DartCObject {
482            ty: DartCObjectType::DartInt32,
483            value: DartCObjectValue {
484                as_int32: self as _,
485            },
486        }
487    }
488}
489
490macro_rules! impl_into_dart_for_tuple {
491    ($( ($($A:ident)+) )*) => {$(
492        impl<$($A: IntoDart),+> IntoDart for ($($A),+,) {
493            #[allow(non_snake_case)]
494            fn into_dart(self) -> DartCObject {
495                let ($($A),+,) = self;
496                vec![$($A.into_dart()),+].into_dart()
497            }
498        }
499        impl<$($A: IntoDart),+> IntoDartExceptPrimitive for ($($A),+,) {}
500    )*};
501}
502
503impl_into_dart_for_tuple! {
504    (A)
505    (A B)
506    (A B C)
507    (A B C D)
508    (A B C D E)
509    (A B C D E F)
510    (A B C D E F G)
511    (A B C D E F G H)
512    (A B C D E F G H I)
513    (A B C D E F G H I J)
514}