Source code

Revision control

Copy as Markdown

Other Tools

/* Copyright 2018-2019 Mozilla Foundation
*
* Licensed under the Apache License (Version 2.0), or the MIT license,
* (the "Licenses") at your option. You may not use this file except in
* compliance with one of the Licenses. You may obtain copies of the
* Licenses at:
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the Licenses is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Licenses for the specific language governing permissions and
* limitations under the Licenses. */
/// Implements [`IntoFfi`][crate::IntoFfi] for the provided types (more than one
/// may be passed in) by allocating `$T` on the heap as an opaque pointer.
///
/// This is typically going to be used from the "Rust component", and not the
/// "FFI component" (see the top level crate documentation for more
/// information), however you will still need to implement a destructor in the
/// FFI component using [`define_box_destructor!`][crate::define_box_destructor].
///
/// In general, is only safe to do for `send` types (even this is dodgy, but
/// it's often necessary to keep the locking on the other side of the FFI, so
/// Sync is too harsh), so we enforce this in this macro. (You're still free to
/// implement this manually, if this restriction is too harsh for your use case
/// and you're certain you know what you're doing).
#[macro_export]
macro_rules! implement_into_ffi_by_pointer {
($($T:ty),* $(,)*) => {$(
unsafe impl $crate::IntoFfi for $T where $T: Send {
type Value = *mut $T;
#[inline]
fn ffi_default() -> *mut $T {
std::ptr::null_mut()
}
#[inline]
fn into_ffi_value(self) -> *mut $T {
Box::into_raw(Box::new(self))
}
}
)*}
}
/// Implements [`IntoFfi`][crate::IntoFfi] for the provided types (more than one
/// may be passed in) by converting to the type to a JSON string.
///
/// Additionally, most of the time we recomment using this crate's protobuf
/// support, instead of JSON.
///
/// This is typically going to be used from the "Rust component", and not the
/// "FFI component" (see the top level crate documentation for more
/// information).
///
/// Note: Each type passed in must implement or derive `serde::Serialize`.
///
/// Note: for this to works, the crate it's called in must depend on `serde` and
/// `serde_json`.
///
/// ## Panics
///
/// The [`IntoFfi`][crate::IntoFfi] implementation this macro generates may
/// panic in the following cases:
///
/// - You've passed a type that contains a Map that has non-string keys (which
/// can't be represented in JSON).
///
/// - You've passed a type which has a custom serializer, and the custom
/// serializer failed.
///
/// These cases are both rare enough that this still seems fine for the majority
/// of uses.
#[macro_export]
macro_rules! implement_into_ffi_by_json {
($($T:ty),* $(,)*) => {$(
unsafe impl $crate::IntoFfi for $T where $T: serde::Serialize {
type Value = *mut std::os::raw::c_char;
#[inline]
fn ffi_default() -> *mut std::os::raw::c_char {
std::ptr::null_mut()
}
#[inline]
fn into_ffi_value(self) -> *mut std::os::raw::c_char {
// This panic is inside our catch_panic, so it should be fine.
// We've also documented the case where the IntoFfi impl that
// calls this panics, and it's rare enough that it shouldn't
// matter that if it happens we return an ExternError
// representing a panic instead of one of some other type
// (especially given that the application isn't likely to be
// able to meaningfully handle JSON serialization failure).
let as_string = serde_json::to_string(&self).unwrap();
$crate::rust_string_to_c(as_string)
}
}
)*}
}
/// Implements [`IntoFfi`][crate::IntoFfi] for the provided types (more than one
/// may be passed in) implementing `prost::Message` (protobuf auto-generated
/// type) by converting to the type to a [`ByteBuffer`][crate::ByteBuffer]. This
/// [`ByteBuffer`][crate::ByteBuffer] should later be passed by value.
///
/// Note: for this to works, the crate it's called in must depend on `prost`.
///
/// Note: Each type passed in must implement or derive `prost::Message`.
#[macro_export]
macro_rules! implement_into_ffi_by_protobuf {
($($FFIType:ty),* $(,)*) => {$(
unsafe impl $crate::IntoFfi for $FFIType where $FFIType: prost::Message {
type Value = $crate::ByteBuffer;
#[inline]
fn ffi_default() -> Self::Value {
Default::default()
}
#[inline]
fn into_ffi_value(self) -> Self::Value {
use prost::Message;
let mut bytes = Vec::with_capacity(self.encoded_len());
// Unwrap is safe, since we have reserved sufficient capacity in
// the vector.
self.encode(&mut bytes).unwrap();
bytes.into()
}
}
)*}
}
/// Implement [`InfoFfi`][crate::IntoFfi] for a type by converting through
/// another type.
///
/// The argument `$MidTy` argument must implement `From<$SrcTy>` and
/// [`InfoFfi`][crate::IntoFfi].
///
/// This is provided (even though it's trivial) because it is always safe (well,
/// so long as `$MidTy`'s [`IntoFfi`][crate::IntoFfi] implementation is
/// correct), but would otherwise require use of `unsafe` to implement.
#[macro_export]
macro_rules! implement_into_ffi_by_delegation {
($SrcTy:ty, $MidTy:ty) => {
unsafe impl $crate::IntoFfi for $SrcTy
where
$MidTy: From<$SrcTy> + $crate::IntoFfi,
{
// The <$MidTy as SomeTrait>::method is required even when it would
// be ambiguous due to some obscure details of macro syntax.
type Value = <$MidTy as $crate::IntoFfi>::Value;
#[inline]
fn ffi_default() -> Self::Value {
<$MidTy as $crate::IntoFfi>::ffi_default()
}
#[inline]
fn into_ffi_value(self) -> Self::Value {
use $crate::IntoFfi;
<$MidTy as From<$SrcTy>>::from(self).into_ffi_value()
}
}
};
}
/// For a number of reasons (name collisions are a big one, but, it also wouldn't work on all
/// platforms), we cannot export `extern "C"` functions from this library. However, it's pretty
/// common to want to free strings allocated by rust, so many libraries will need this, so we
/// provide it as a macro.
///
/// It simply expands to a `#[no_mangle] pub unsafe extern "C" fn` which wraps this crate's
/// [`destroy_c_string`][crate::destroy_c_string] function.
///
/// ## Caveats
///
/// If you're using multiple separately compiled rust libraries in your application, it's critical
/// that you are careful to only ever free strings allocated by a Rust library using the same rust
/// library. Passing them to a different Rust library's string destructor will cause you to corrupt
/// multiple heaps.
///
/// Additionally, be sure that all strings you pass to this were actually allocated by rust. It's a
/// common issue for JNA code to transparently convert Pointers to things to Strings behind the
/// scenes, which is quite risky here. (To avoid this in JNA, only use `String` for passing
/// read-only strings into Rust, e.g. it's for passing `*const c_char`. All other uses should use
/// `Pointer` and `getString()`).
///
/// Finally, to avoid name collisions, it is strongly recommended that you provide an name for this
/// function unique to your library.
///
/// ## Example
///
/// ```rust
/// # use ffi_support::define_string_destructor;
/// define_string_destructor!(mylib_destroy_string);
/// ```
#[macro_export]
macro_rules! define_string_destructor {
($mylib_destroy_string:ident) => {
/// Public destructor for strings managed by the other side of the FFI.
///
/// # Safety
///
/// This will free the string pointer it gets passed in as an argument,
/// and thus can be wildly unsafe if misused.
///
/// See the documentation of `ffi_support::destroy_c_string` and
/// `ffi_support::define_string_destructor!` for further info.
#[no_mangle]
pub unsafe extern "C" fn $mylib_destroy_string(s: *mut std::os::raw::c_char) {
// Note: This should never happen, but in the case of a bug aborting
// here is better than the badness that happens if we unwind across
// the FFI boundary.
$crate::abort_on_panic::with_abort_on_panic(|| {
if !s.is_null() {
$crate::destroy_c_string(s)
}
});
}
};
}
/// Define a (public) destructor for a type that was allocated by
/// `Box::into_raw(Box::new(value))` (e.g. a pointer which is probably opaque).
///
/// ## Caveats
///
/// When called over the FFI, this can go wrong in a ridiculous number of ways,
/// and we can't really prevent any of them. But essentially, the caller (on the
/// other side of the FFI) needs to be extremely careful to ensure that it stops
/// using the pointer after it's freed.
///
/// Also, to avoid name collisions, it is strongly recommended that you provide
/// an name for this function unique to your library. (This is true for all
/// functions you expose).
///
/// However, when called from rust, this is safe, as it becomes a function that
/// just drops a `Option<Box<T>>` with some panic handling.
///
/// ## Example
///
/// ```rust
/// # use ffi_support::define_box_destructor;
/// struct CoolType(Vec<i32>);
///
/// define_box_destructor!(CoolType, mylib_destroy_cooltype);
/// ```
#[macro_export]
macro_rules! define_box_destructor {
($T:ty, $destructor_name:ident) => {
/// # Safety
/// This is equivalent to calling Box::from_raw with panic handling, and
/// thus inherits [`Box::from_raw`]'s safety properties. That is to say,
/// this function is wildly unsafe.
#[no_mangle]
pub unsafe extern "C" fn $destructor_name(v: *mut $T) {
// We should consider passing an error parameter in here rather than
// aborting, but at the moment the only case where we do this
// (interrupt handles) should never panic in Drop, so it's probably
// fine.
$crate::abort_on_panic::with_abort_on_panic(|| {
if !v.is_null() {
drop(Box::from_raw(v))
}
});
}
};
}
/// Define a (public) destructor for the [`ByteBuffer`][crate::ByteBuffer] type.
///
/// ## Caveats
///
/// If you're using multiple separately compiled rust libraries in your application, it's critical
/// that you are careful to only ever free `ByteBuffer` instances allocated by a Rust library using
/// the same rust library. Passing them to a different Rust library's string destructor will cause
/// you to corrupt multiple heaps.
/// One common ByteBuffer destructor is defined per Rust library.
///
/// Also, to avoid name collisions, it is strongly recommended that you provide an name for this
/// function unique to your library. (This is true for all functions you expose).
///
/// ## Example
///
/// ```rust
/// # use ffi_support::define_bytebuffer_destructor;
/// define_bytebuffer_destructor!(mylib_destroy_bytebuffer);
/// ```
#[macro_export]
macro_rules! define_bytebuffer_destructor {
($destructor_name:ident) => {
#[no_mangle]
pub extern "C" fn $destructor_name(v: $crate::ByteBuffer) {
// Note: This should never happen, but in the case of a bug aborting
// here is better than the badness that happens if we unwind across
// the FFI boundary.
$crate::abort_on_panic::with_abort_on_panic(|| v.destroy())
}
};
}
/// Define a (public) destructor for a type that lives inside a lazy_static
/// [`ConcurrentHandleMap`][crate::ConcurrentHandleMap].
///
/// Note that this is actually totally safe, unlike the other
/// `define_blah_destructor` macros.
///
/// A critical difference, however, is that this dtor takes an `err` out
/// parameter to indicate failure. This difference is why the name is different
/// as well (deleter vs destructor).
///
/// ## Example
///
/// ```rust
/// # use lazy_static::lazy_static;
/// # use ffi_support::{ConcurrentHandleMap, define_handle_map_deleter};
/// struct Thing(Vec<i32>);
/// // Somewhere...
/// lazy_static! {
/// static ref THING_HANDLES: ConcurrentHandleMap<Thing> = ConcurrentHandleMap::new();
/// }
/// define_handle_map_deleter!(THING_HANDLES, mylib_destroy_thing);
/// ```
#[macro_export]
macro_rules! define_handle_map_deleter {
($HANDLE_MAP_NAME:ident, $destructor_name:ident) => {
#[no_mangle]
pub extern "C" fn $destructor_name(v: u64, err: &mut $crate::ExternError) {
$crate::call_with_result(err, || {
// Force type errors here.
let map: &$crate::ConcurrentHandleMap<_> = &*$HANDLE_MAP_NAME;
map.delete_u64(v)
})
}
};
}
/// Force a compile error if the condition is not met. Requires a unique name
/// for the assertion for... reasons. This is included mainly because it's a
/// common desire for FFI code, but not for other sorts of code.
///
/// # Examples
///
/// Failing example:
///
/// ```compile_fail
/// ffi_support::static_assert!(THIS_SHOULD_FAIL, false);
/// ```
///
/// Passing example:
///
/// ```
/// ffi_support::static_assert!(THIS_SHOULD_PASS, true);
/// ```
#[macro_export]
macro_rules! static_assert {
($ASSERT_NAME:ident, $test:expr) => {
#[allow(dead_code, nonstandard_style)]
const $ASSERT_NAME: [u8; 0 - (!$test as bool as usize)] =
[0u8; 0 - (!$test as bool as usize)];
};
}