1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 //! # Runtime support code for uniffi
6 //!
7 //! This crate provides the small amount of runtime code that is required by the generated uniffi
8 //! component scaffolding in order to transfer data back and forth across the C-style FFI layer,
9 //! as well as some utilities for testing the generated bindings.
10 //!
11 //! The key concept here is the [`FfiConverter`] trait, which is responsible for converting between
12 //! a Rust type and a low-level C-style type that can be passed across the FFI:
13 //!
14 //!  * How to [represent](FfiConverter::FfiType) values of the Rust type in the low-level C-style type
15 //!    system of the FFI layer.
16 //!  * How to ["lower"](FfiConverter::lower) values of the Rust type into an appropriate low-level
17 //!    FFI value.
18 //!  * How to ["lift"](FfiConverter::try_lift) low-level FFI values back into values of the Rust
19 //!    type.
20 //!  * How to [write](FfiConverter::write) values of the Rust type into a buffer, for cases
21 //!    where they are part of a compound data structure that is serialized for transfer.
22 //!  * How to [read](FfiConverter::try_read) values of the Rust type from buffer, for cases
23 //!    where they are received as part of a compound data structure that was serialized for transfer.
24 //!  * How to [return](FfiConverter::lower_return) values of the Rust type from scaffolding
25 //!    functions.
26 //!
27 //! This logic encapsulates the Rust-side handling of data transfer. Each foreign-language binding
28 //! must also implement a matching set of data-handling rules for each data type.
29 //!
30 //! In addition to the core `FfiConverter` trait, we provide a handful of struct definitions useful
31 //! for passing core rust types over the FFI, such as [`RustBuffer`].
32 
33 #![warn(rust_2018_idioms, unused_qualifications)]
34 
35 use anyhow::bail;
36 use bytes::buf::Buf;
37 
38 // Make Result<> public to support external impls of FfiConverter
39 pub use anyhow::Result;
40 
41 pub mod ffi;
42 mod ffi_converter_impls;
43 mod ffi_converter_traits;
44 pub mod metadata;
45 
46 pub use ffi::*;
47 pub use ffi_converter_traits::{
48     ConvertError, FfiConverter, FfiConverterArc, HandleAlloc, Lift, LiftRef, LiftReturn, Lower,
49     LowerReturn,
50 };
51 pub use metadata::*;
52 
53 // Re-export the libs that we use in the generated code,
54 // so the consumer doesn't have to depend on them directly.
55 pub mod deps {
56     pub use anyhow;
57     #[cfg(feature = "tokio")]
58     pub use async_compat;
59     pub use bytes;
60     pub use log;
61     pub use oneshot;
62     pub use static_assertions;
63 }
64 
65 mod panichook;
66 
67 const PACKAGE_VERSION: &str = env!("CARGO_PKG_VERSION");
68 
69 // For the significance of this magic number 10 here, and the reason that
70 // it can't be a named constant, see the `check_compatible_version` function.
71 static_assertions::const_assert!(PACKAGE_VERSION.as_bytes().len() < 10);
72 
73 /// Check whether the uniffi runtime version is compatible a given uniffi_bindgen version.
74 ///
75 /// The result of this check may be used to ensure that generated Rust scaffolding is
76 /// using a compatible version of the uniffi runtime crate. It's a `const fn` so that it
77 /// can be used to perform such a check at compile time.
78 #[allow(clippy::len_zero)]
check_compatible_version(bindgen_version: &'static str) -> bool79 pub const fn check_compatible_version(bindgen_version: &'static str) -> bool {
80     // While UniFFI is still under heavy development, we require that
81     // the runtime support crate be precisely the same version as the
82     // build-time bindgen crate.
83     //
84     // What we want to achieve here is checking two strings for equality.
85     // Unfortunately Rust doesn't yet support calling the `&str` equals method
86     // in a const context. We can hack around that by doing a byte-by-byte
87     // comparison of the underlying bytes.
88     let package_version = PACKAGE_VERSION.as_bytes();
89     let bindgen_version = bindgen_version.as_bytes();
90     // What we want to achieve here is a loop over the underlying bytes,
91     // something like:
92     // ```
93     //  if package_version.len() != bindgen_version.len() {
94     //      return false
95     //  }
96     //  for i in 0..package_version.len() {
97     //      if package_version[i] != bindgen_version[i] {
98     //          return false
99     //      }
100     //  }
101     //  return true
102     // ```
103     // Unfortunately stable Rust doesn't allow `if` or `for` in const contexts,
104     // so code like the above would only work in nightly. We can hack around it by
105     // statically asserting that the string is shorter than a certain length
106     // (currently 10 bytes) and then manually unrolling that many iterations of the loop.
107     //
108     // Yes, I am aware that this is horrific, but the externally-visible
109     // behaviour is quite nice for consumers!
110     package_version.len() == bindgen_version.len()
111         && (package_version.len() == 0 || package_version[0] == bindgen_version[0])
112         && (package_version.len() <= 1 || package_version[1] == bindgen_version[1])
113         && (package_version.len() <= 2 || package_version[2] == bindgen_version[2])
114         && (package_version.len() <= 3 || package_version[3] == bindgen_version[3])
115         && (package_version.len() <= 4 || package_version[4] == bindgen_version[4])
116         && (package_version.len() <= 5 || package_version[5] == bindgen_version[5])
117         && (package_version.len() <= 6 || package_version[6] == bindgen_version[6])
118         && (package_version.len() <= 7 || package_version[7] == bindgen_version[7])
119         && (package_version.len() <= 8 || package_version[8] == bindgen_version[8])
120         && (package_version.len() <= 9 || package_version[9] == bindgen_version[9])
121         && package_version.len() < 10
122 }
123 
124 /// Assert that the uniffi runtime version matches an expected value.
125 ///
126 /// This is a helper hook for the generated Rust scaffolding, to produce a compile-time
127 /// error if the version of `uniffi_bindgen` used to generate the scaffolding was
128 /// incompatible with the version of `uniffi` being used at runtime.
129 #[macro_export]
130 macro_rules! assert_compatible_version {
131     ($v:expr $(,)?) => {
132         uniffi::deps::static_assertions::const_assert!(uniffi::check_compatible_version($v));
133     };
134 }
135 
136 /// Struct to use when we want to lift/lower/serialize types inside the `uniffi` crate.
137 struct UniFfiTag;
138 
139 /// A helper function to ensure we don't read past the end of a buffer.
140 ///
141 /// Rust won't actually let us read past the end of a buffer, but the `Buf` trait does not support
142 /// returning an explicit error in this case, and will instead panic. This is a look-before-you-leap
143 /// helper function to instead return an explicit error, to help with debugging.
check_remaining(buf: &[u8], num_bytes: usize) -> Result<()>144 pub fn check_remaining(buf: &[u8], num_bytes: usize) -> Result<()> {
145     if buf.remaining() < num_bytes {
146         bail!(
147             "not enough bytes remaining in buffer ({} < {num_bytes})",
148             buf.remaining(),
149         );
150     }
151     Ok(())
152 }
153 
154 /// Macro to implement lowering/lifting using a `RustBuffer`
155 ///
156 /// For complex types where it's too fiddly or too unsafe to convert them into a special-purpose
157 /// C-compatible value, you can use this trait to implement `lower()` in terms of `write()` and
158 /// `lift` in terms of `read()`.
159 ///
160 /// This macro implements the boilerplate needed to define `lower`, `lift` and `FFIType`.
161 #[macro_export]
162 macro_rules! ffi_converter_rust_buffer_lift_and_lower {
163     ($uniffi_tag:ty) => {
164         type FfiType = $crate::RustBuffer;
165 
166         fn lower(v: Self) -> $crate::RustBuffer {
167             let mut buf = ::std::vec::Vec::new();
168             <Self as $crate::FfiConverter<$uniffi_tag>>::write(v, &mut buf);
169             $crate::RustBuffer::from_vec(buf)
170         }
171 
172         fn try_lift(buf: $crate::RustBuffer) -> $crate::Result<Self> {
173             let vec = buf.destroy_into_vec();
174             let mut buf = vec.as_slice();
175             let value = <Self as $crate::FfiConverter<$uniffi_tag>>::try_read(&mut buf)?;
176             match $crate::deps::bytes::Buf::remaining(&buf) {
177                 0 => Ok(value),
178                 n => $crate::deps::anyhow::bail!(
179                     "junk data left in buffer after lifting (count: {n})",
180                 ),
181             }
182         }
183     };
184 }
185 
186 /// Macro to implement `FfiConverter<T>` for a UniFfiTag using a different UniFfiTag
187 ///
188 /// This is used for external types
189 #[macro_export]
190 macro_rules! ffi_converter_forward {
191     // Forward a `FfiConverter` implementation
192     ($T:ty, $existing_impl_tag:ty, $new_impl_tag:ty) => {
193         ::uniffi::do_ffi_converter_forward!(
194             FfiConverter,
195             $T,
196             $T,
197             $existing_impl_tag,
198             $new_impl_tag
199         );
200 
201         $crate::derive_ffi_traits!(local $T);
202     };
203 }
204 
205 /// Macro to implement `FfiConverterArc<T>` for a UniFfiTag using a different UniFfiTag
206 ///
207 /// This is used for external types
208 #[macro_export]
209 macro_rules! ffi_converter_arc_forward {
210     ($T:ty, $existing_impl_tag:ty, $new_impl_tag:ty) => {
211         ::uniffi::do_ffi_converter_forward!(
212             FfiConverterArc,
213             ::std::sync::Arc<$T>,
214             $T,
215             $existing_impl_tag,
216             $new_impl_tag
217         );
218 
219         // Note: no need to call derive_ffi_traits! because there is a blanket impl for all Arc<T>
220     };
221 }
222 
223 // Generic code between the two macros above
224 #[doc(hidden)]
225 #[macro_export]
226 macro_rules! do_ffi_converter_forward {
227     ($trait:ident, $rust_type:ty, $T:ty, $existing_impl_tag:ty, $new_impl_tag:ty) => {
228         unsafe impl $crate::$trait<$new_impl_tag> for $T {
229             type FfiType = <$T as $crate::$trait<$existing_impl_tag>>::FfiType;
230 
231             fn lower(obj: $rust_type) -> Self::FfiType {
232                 <$T as $crate::$trait<$existing_impl_tag>>::lower(obj)
233             }
234 
235             fn try_lift(v: Self::FfiType) -> $crate::Result<$rust_type> {
236                 <$T as $crate::$trait<$existing_impl_tag>>::try_lift(v)
237             }
238 
239             fn write(obj: $rust_type, buf: &mut Vec<u8>) {
240                 <$T as $crate::$trait<$existing_impl_tag>>::write(obj, buf)
241             }
242 
243             fn try_read(buf: &mut &[u8]) -> $crate::Result<$rust_type> {
244                 <$T as $crate::$trait<$existing_impl_tag>>::try_read(buf)
245             }
246 
247             const TYPE_ID_META: ::uniffi::MetadataBuffer =
248                 <$T as $crate::$trait<$existing_impl_tag>>::TYPE_ID_META;
249         }
250     };
251 }
252 
253 #[cfg(test)]
254 mod test {
255     use super::{FfiConverter, UniFfiTag};
256     use std::time::{Duration, SystemTime};
257 
258     #[test]
timestamp_roundtrip_post_epoch()259     fn timestamp_roundtrip_post_epoch() {
260         let expected = SystemTime::UNIX_EPOCH + Duration::new(100, 100);
261         let result =
262             <SystemTime as FfiConverter<UniFfiTag>>::try_lift(<SystemTime as FfiConverter<
263                 UniFfiTag,
264             >>::lower(expected))
265             .expect("Failed to lift!");
266         assert_eq!(expected, result)
267     }
268 
269     #[test]
timestamp_roundtrip_pre_epoch()270     fn timestamp_roundtrip_pre_epoch() {
271         let expected = SystemTime::UNIX_EPOCH - Duration::new(100, 100);
272         let result =
273             <SystemTime as FfiConverter<UniFfiTag>>::try_lift(<SystemTime as FfiConverter<
274                 UniFfiTag,
275             >>::lower(expected))
276             .expect("Failed to lift!");
277         assert_eq!(
278             expected, result,
279             "Expected results after lowering and lifting to be equal"
280         )
281     }
282 }
283 
284 #[cfg(test)]
285 pub mod test_util {
286     use std::{error::Error, fmt};
287 
288     use super::*;
289 
290     #[derive(Clone, Debug, PartialEq, Eq)]
291     pub struct TestError(pub String);
292 
293     // Use FfiConverter to simplify lifting TestError out of RustBuffer to check it
294     unsafe impl<UT> FfiConverter<UT> for TestError {
295         ffi_converter_rust_buffer_lift_and_lower!(UniFfiTag);
296 
write(obj: TestError, buf: &mut Vec<u8>)297         fn write(obj: TestError, buf: &mut Vec<u8>) {
298             <String as FfiConverter<UniFfiTag>>::write(obj.0, buf);
299         }
300 
try_read(buf: &mut &[u8]) -> Result<TestError>301         fn try_read(buf: &mut &[u8]) -> Result<TestError> {
302             <String as FfiConverter<UniFfiTag>>::try_read(buf).map(TestError)
303         }
304 
305         // Use a dummy value here since we don't actually need TYPE_ID_META
306         const TYPE_ID_META: MetadataBuffer = MetadataBuffer::new();
307     }
308 
309     impl fmt::Display for TestError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result310         fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
311             write!(f, "{}", self.0)
312         }
313     }
314 
315     impl Error for TestError {}
316 
317     impl<T: Into<String>> From<T> for TestError {
from(v: T) -> Self318         fn from(v: T) -> Self {
319             Self(v.into())
320         }
321     }
322 
323     derive_ffi_traits!(blanket TestError);
324 }
325