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