Skip to content

Commit

Permalink
Updating the LiftReturn trait to handle foreign trait interface
Browse files Browse the repository at this point in the history
I'm hoping that we can implement trait interfaces differently than
callback interfaces.  I want the foreign impl of a trait method to
exactly like the Rust scaffolding code: input a `&mut RustCallStatus` as
an out param and return the FFI type.
  • Loading branch information
bendk committed Oct 6, 2023
1 parent 41470e5 commit fc5a813
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 7 deletions.
8 changes: 5 additions & 3 deletions uniffi_core/src/ffi/callbackinterface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ impl ForeignCallbackInternals {
.unwrap_or_else(|code| panic!("Callback failed with unexpected return code: {code}"));
match result {
CallbackResult::Success => R::lift_callback_return(ret_rbuf),
CallbackResult::Error => R::lift_callback_error(ret_rbuf),
CallbackResult::Error => R::lift_error(ret_rbuf),
CallbackResult::UnexpectedError => {
let reason = if !ret_rbuf.is_empty() {
match <String as Lift<UniFfiTag>>::try_lift(ret_rbuf) {
Expand Down Expand Up @@ -216,8 +216,10 @@ pub struct UnexpectedUniFFICallbackError {
}

impl UnexpectedUniFFICallbackError {
pub fn from_reason(reason: String) -> Self {
Self { reason }
pub fn new(reason: impl fmt::Display) -> Self {
Self {
reason: reason.to_string(),
}
}
}

Expand Down
7 changes: 7 additions & 0 deletions uniffi_core/src/ffi/rustcalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ pub struct RustCallStatus {
}

impl RustCallStatus {
pub fn new() -> Self {
Self {
code: RustCallStatusCode::Success,
error_buf: MaybeUninit::new(RustBuffer::new()),
}
}

pub fn cancelled() -> Self {
Self {
code: RustCallStatusCode::Cancelled,
Expand Down
16 changes: 14 additions & 2 deletions uniffi_core/src/ffi_converter_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,12 @@ unsafe impl<UT> LowerReturn<UT> for () {
}

unsafe impl<UT> LiftReturn<UT> for () {
type ReturnType = ();

fn try_lift_successful_return(_: ()) -> Result<Self> {
Ok(())
}

fn lift_callback_return(_buf: RustBuffer) -> Self {}

const TYPE_ID_META: MetadataBuffer = MetadataBuffer::from_code(metadata::codes::TYPE_UNIT);
Expand Down Expand Up @@ -535,13 +541,19 @@ where
unsafe impl<UT, R, E> LiftReturn<UT> for Result<R, E>
where
R: LiftReturn<UT>,
E: Lift<UT> + From<UnexpectedUniFFICallbackError>,
E: Lift<UT, FfiType = RustBuffer> + From<UnexpectedUniFFICallbackError>,
{
type ReturnType = R::ReturnType;

fn try_lift_successful_return(v: R::ReturnType) -> Result<Self> {
R::try_lift_successful_return(v).map(Ok)
}

fn lift_callback_return(buf: RustBuffer) -> Self {
Ok(R::lift_callback_return(buf))
}

fn lift_callback_error(buf: RustBuffer) -> Self {
fn lift_error(buf: RustBuffer) -> Self {
match E::try_lift_from_rust_buffer(buf) {
Ok(lifted_error) => Err(lifted_error),
Err(anyhow_error) => {
Expand Down
43 changes: 41 additions & 2 deletions uniffi_core/src/ffi_converter_traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ use std::{borrow::Borrow, sync::Arc};
use anyhow::bail;
use bytes::Buf;

use crate::{FfiDefault, MetadataBuffer, Result, RustBuffer, UnexpectedUniFFICallbackError};
use crate::{
FfiDefault, MetadataBuffer, Result, RustBuffer, RustCallStatus, RustCallStatusCode,
UnexpectedUniFFICallbackError,
};

/// Generalized FFI conversions
///
Expand Down Expand Up @@ -302,14 +305,44 @@ pub unsafe trait LowerReturn<UT>: Sized {
/// These traits should not be used directly, only in generated code, and the generated code should
/// have fixture tests to test that everything works correctly together.
pub unsafe trait LiftReturn<UT>: Sized {
/// FFI return type for trait interfaces
type ReturnType;

/// Lift a successfully returned value from a trait interface
fn try_lift_successful_return(v: Self::ReturnType) -> Result<Self>;

/// Lift a foreign returned value from a trait interface
///
/// When we call a foreign-implemented trait interface method, we pass a &mut RustCallStatus
/// and get [Self::ReturnType] returned. This method takes both of those and lifts `Self` from
/// it.
fn lift_foreign_return(ffi_return: Self::ReturnType, call_status: RustCallStatus) -> Self {
match call_status.code {
RustCallStatusCode::Success => Self::try_lift_successful_return(ffi_return)
.unwrap_or_else(|e| {
Self::handle_callback_unexpected_error(UnexpectedUniFFICallbackError::new(e))
}),
RustCallStatusCode::Error => {
Self::lift_error(unsafe { call_status.error_buf.assume_init() })
}
_ => {
let e = <String as FfiConverter<crate::UniFfiTag>>::try_lift(unsafe {
call_status.error_buf.assume_init()
})
.unwrap_or_else(|e| format!("(Error lifting message: {e}"));
Self::handle_callback_unexpected_error(UnexpectedUniFFICallbackError::new(e))
}
}
}

/// Lift a Rust value for a callback interface method result
fn lift_callback_return(buf: RustBuffer) -> Self;

/// Lift a Rust value for a callback interface method error result
///
/// This is called for "expected errors" -- the callback method returns a Result<> type and the
/// foreign code throws an exception that corresponds to the error type.
fn lift_callback_error(_buf: RustBuffer) -> Self {
fn lift_error(_buf: RustBuffer) -> Self {
panic!("Callback interface method returned unexpected error")
}

Expand Down Expand Up @@ -433,6 +466,12 @@ macro_rules! derive_ffi_traits {
(impl $(<$($generic:ident),*>)? $(::uniffi::)? LiftReturn<$ut:path> for $ty:ty $(where $($where:tt)*)?) => {
unsafe impl $(<$($generic),*>)* $crate::LiftReturn<$ut> for $ty $(where $($where)*)*
{
type ReturnType = <Self as $crate::Lift<$ut>>::FfiType;

fn try_lift_successful_return(v: Self::ReturnType) -> $crate::Result<Self> {
<Self as $crate::Lift<$ut>>::try_lift(v)
}

fn lift_callback_return(buf: $crate::RustBuffer) -> Self {
<Self as $crate::Lift<$ut>>::try_lift_from_rust_buffer(buf)
.expect("Error reading callback interface result")
Expand Down

0 comments on commit fc5a813

Please sign in to comment.