allo_isolate/
ffi.rs

1#![allow(missing_docs, clippy::derive_partial_eq_without_eq)]
2
3use std::{
4    ffi::{c_void, CString},
5    os::raw,
6};
7
8use crate::{
9    dart_array::DartArray,
10    into_dart::{
11        visit_dart_typed_data_type, DartTypedDataTypeTrait,
12        DartTypedDataTypeVisitor,
13    },
14};
15
16/// A port is used to send or receive inter-isolate messages
17pub type DartPort = i64;
18
19#[repr(i32)]
20#[derive(Copy, Clone, PartialEq, Debug)]
21pub enum DartTypedDataType {
22    ByteData = 0,
23    Int8 = 1,
24    Uint8 = 2,
25    Uint8Clamped = 3,
26    Int16 = 4,
27    Uint16 = 5,
28    Int32 = 6,
29    Uint32 = 7,
30    Int64 = 8,
31    Uint64 = 9,
32    Float32 = 10,
33    Float64 = 11,
34    Float32x4 = 12,
35    Invalid = 13,
36}
37
38/// A Dart_CObject is used for representing Dart objects as native C
39/// data outside the Dart heap. These objects are totally detached from
40/// the Dart heap. Only a subset of the Dart objects have a
41/// representation as a Dart_CObject.
42///
43/// The string encoding in the 'value.as_string' is UTF-8.
44///
45/// All the different types from dart:typed_data are exposed as type
46/// kTypedData. The specific type from dart:typed_data is in the type
47/// field of the as_typed_data structure. The length in the
48/// as_typed_data structure is always in bytes.
49///
50/// The data for kTypedData is copied on message send and ownership remains with
51/// the caller. The ownership of data for kExternalTyped is passed to the VM on
52/// message send and returned when the VM invokes the
53/// Dart_WeakPersistentHandleFinalizer callback; a non-NULL callback must be
54/// provided.
55///
56/// https://github.com/dart-lang/sdk/blob/main/runtime/include/dart_native_api.h
57#[repr(i32)]
58#[derive(PartialEq, Debug, Clone, Copy)]
59pub enum DartCObjectType {
60    DartNull = 0,
61    DartBool = 1,
62    DartInt32 = 2,
63    DartInt64 = 3,
64    DartDouble = 4,
65    DartString = 5,
66    DartArray = 6,
67    DartTypedData = 7,
68    DartExternalTypedData = 8,
69    DartSendPort = 9,
70    DartCapability = 10,
71    DartNativePointer = 11,
72    DartUnsupported = 12,
73    DartNumberOfTypes = 13,
74}
75
76#[allow(missing_debug_implementations)]
77#[repr(C)]
78pub struct DartCObject {
79    pub ty: DartCObjectType,
80    pub value: DartCObjectValue,
81}
82
83#[allow(missing_debug_implementations)]
84#[repr(C)]
85#[derive(Clone, Copy)]
86pub union DartCObjectValue {
87    pub as_bool: bool,
88    pub as_int32: i32,
89    pub as_int64: i64,
90    pub as_double: f64,
91    pub as_string: *mut raw::c_char,
92    pub as_send_port: DartNativeSendPort,
93    pub as_capability: DartNativeCapability,
94    pub as_array: DartNativeArray,
95    pub as_typed_data: DartNativeTypedData,
96    pub as_external_typed_data: DartNativeExternalTypedData,
97    pub as_native_pointer: DartNativePointer,
98    _bindgen_union_align: [u64; 5usize],
99}
100
101#[repr(C)]
102#[derive(Debug, Copy, Clone)]
103pub struct DartNativeSendPort {
104    pub id: DartPort,
105    pub origin_id: DartPort,
106}
107
108#[repr(C)]
109#[derive(Debug, Copy, Clone)]
110pub struct DartNativeCapability {
111    pub id: i64,
112}
113
114#[repr(C)]
115#[derive(Debug, Copy, Clone)]
116pub struct DartNativeArray {
117    pub length: isize,
118    pub values: *mut *mut DartCObject,
119}
120
121#[repr(C)]
122#[derive(Debug, Copy, Clone)]
123pub struct DartNativeTypedData {
124    pub ty: DartTypedDataType,
125    pub length: isize, // in elements, not bytes
126    pub values: *mut u8,
127}
128
129#[repr(C)]
130#[derive(Debug, Copy, Clone)]
131pub struct DartNativeExternalTypedData {
132    pub ty: DartTypedDataType,
133    pub length: isize, // in elements, not bytes
134    pub data: *mut u8,
135    pub peer: *mut c_void,
136    pub callback: DartHandleFinalizer,
137}
138
139#[repr(C)]
140#[derive(Debug, Copy, Clone)]
141pub struct DartNativePointer {
142    pub ptr: isize,
143    pub size: isize,
144    pub callback: DartHandleFinalizer,
145}
146
147/// https://github.com/dart-lang/sdk/blob/main/runtime/include/dart_api.h
148pub type DartHandleFinalizer =
149    unsafe extern "C" fn(isolate_callback_data: *mut c_void, peer: *mut c_void);
150
151/// Wrapping a Vec<u8> in this tuple struct will allow into_dart()
152/// to send it as a DartNativeExternalTypedData buffer with no copy overhead
153#[derive(Debug, Clone)]
154pub struct ZeroCopyBuffer<T>(pub T);
155
156///  Posts a message on some port. The message will contain the
157///  Dart_CObject object graph rooted in 'message'.
158///
159///  While the message is being sent the state of the graph of
160///  Dart_CObject structures rooted in 'message' should not be accessed,
161///  as the message generation will make temporary modifications to the
162///  data. When the message has been sent the graph will be fully
163///  restored.
164///
165///  `port_id` The destination port.
166///  `message` The message to send.
167///
168///  return true if the message was posted.
169pub type DartPostCObjectFnType =
170    unsafe extern "C" fn(port_id: DartPort, message: *mut DartCObject) -> bool;
171
172impl Drop for DartCObject {
173    fn drop(&mut self) {
174        match self.ty {
175            DartCObjectType::DartString => {
176                let _ = unsafe { CString::from_raw(self.value.as_string) };
177            },
178            DartCObjectType::DartArray => {
179                let _ = DartArray::from(unsafe { self.value.as_array });
180            },
181            DartCObjectType::DartTypedData => {
182                struct MyVisitor<'a>(&'a DartNativeTypedData);
183                impl DartTypedDataTypeVisitor for MyVisitor<'_> {
184                    fn visit<T: DartTypedDataTypeTrait>(&self) {
185                        if self.0.values.is_null() {
186                            return;
187                        }
188                        let _ = unsafe {
189                            Vec::from_raw_parts(
190                                self.0.values as *mut T,
191                                self.0.length as usize,
192                                self.0.length as usize,
193                            )
194                        };
195                    }
196                }
197
198                let v = unsafe { self.value.as_typed_data };
199                visit_dart_typed_data_type(v.ty, &MyVisitor(&v));
200            },
201            // write out all cases in order to be explicit - we do not want to
202            // leak any memory
203            DartCObjectType::DartNull
204            | DartCObjectType::DartBool
205            | DartCObjectType::DartInt32
206            | DartCObjectType::DartInt64
207            | DartCObjectType::DartDouble => {
208                // do nothing, since they are primitive types
209            },
210            DartCObjectType::DartExternalTypedData => {
211                // do NOT free any memory here
212                // see https://github.com/sunshine-protocol/allo-isolate/issues/7
213            },
214            DartCObjectType::DartSendPort
215            | DartCObjectType::DartCapability
216            | DartCObjectType::DartUnsupported
217            | DartCObjectType::DartNumberOfTypes => {
218                // not sure what to do here
219            },
220            DartCObjectType::DartNativePointer => {
221                // do not free the memory here, this will be done when the
222                // callback is called
223            },
224        }
225    }
226}
227
228/// Exposed only for tests.
229#[doc(hidden)]
230pub unsafe fn run_destructors(obj: &DartCObject) {
231    use DartCObjectType::*;
232    match obj.ty {
233        DartExternalTypedData => unsafe {
234            (obj.value.as_external_typed_data.callback)(
235                obj.value.as_external_typed_data.data as *mut c_void,
236                obj.value.as_external_typed_data.peer,
237            )
238        },
239        DartArray => {
240            let items = unsafe {
241                std::slice::from_raw_parts_mut(
242                    obj.value.as_array.values,
243                    obj.value.as_array.length as usize,
244                )
245            };
246            for item in items {
247                run_destructors(&**item)
248            }
249        },
250        _ => {},
251    }
252}