Skip to content

Commit

Permalink
Make Ref work with more than just interface types (#3394)
Browse files Browse the repository at this point in the history
  • Loading branch information
kennykerr authored Dec 14, 2024
1 parent aca1a34 commit 17b1640
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 22 deletions.
38 changes: 18 additions & 20 deletions crates/libs/core/src/ref.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
use super::*;
use core::ffi::c_void;
use core::marker::PhantomData;
use core::mem::transmute;

/// A borrowed type with the same memory layout as the type itself that can be used to construct ABI-compatible function signatures.
#[repr(transparent)]
pub struct Ref<'a, T: Type<T>>(T::Abi, PhantomData<&'a T>);

impl<T: Type<T, Default = Option<T>, Abi = *mut c_void>> Ref<'_, T> {
impl<T: Type<T>> Ref<'_, T> {
/// Returns `true` if the argument is null.
pub fn is_null(&self) -> bool {
self.0.is_null()
T::is_null(&self.0)
}

/// Converts the argument to a [Result<&T>] reference.
/// Converts the argument to a [`Result<&T>`] reference.
pub fn ok(&self) -> Result<&T> {
if self.0.is_null() {
Err(Error::from_hresult(imp::E_POINTER))
self.as_ref()
.ok_or_else(|| Error::from_hresult(imp::E_POINTER))
}

/// Converts the argument to a [`Option<&T>`] reference.
pub fn as_ref(&self) -> Option<&T> {
if self.is_null() {
None
} else {
unsafe { Ok(self.assume_init()) }
unsafe { Some(self.assume_init_ref()) }
}
}

Expand All @@ -27,25 +32,18 @@ impl<T: Type<T, Default = Option<T>, Abi = *mut c_void>> Ref<'_, T> {
/// # Panics
///
/// Panics if the argument is null.
#[track_caller]
pub fn unwrap(&self) -> &T {
if self.0.is_null() {
panic!("called `Ref::unwrap` on a null value")
} else {
unsafe { self.assume_init() }
}
self.as_ref().expect("called `Ref::unwrap` on a null value")
}

/// Converts the argument to an [Option<T>] by cloning the reference.
/// Converts the argument to an [`Option<T>`] by cloning the reference.
pub fn cloned(&self) -> Option<T> {
if self.0.is_null() {
None
} else {
unsafe { Some(self.assume_init().clone()) }
}
self.as_ref().cloned()
}

unsafe fn assume_init(&self) -> &T {
unsafe { transmute::<&*mut c_void, &T>(&self.0) }
unsafe fn assume_init_ref(&self) -> &T {
unsafe { T::assume_init_ref(&self.0) }
}
}

Expand Down
27 changes: 26 additions & 1 deletion crates/libs/core/src/type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ pub trait Type<T: TypeKind, C = <T as TypeKind>::TypeKind>: TypeKind + Sized + C
type Abi;
type Default;

/// # Safety
fn is_null(abi: &Self::Abi) -> bool;
unsafe fn assume_init_ref(abi: &Self::Abi) -> &Self;
unsafe fn from_abi(abi: Self::Abi) -> Result<Self>;
fn from_default(default: &Self::Default) -> Result<Self>;
}
Expand All @@ -31,6 +32,14 @@ where
type Abi = *mut core::ffi::c_void;
type Default = Option<Self>;

fn is_null(abi: &Self::Abi) -> bool {
abi.is_null()
}

unsafe fn assume_init_ref(abi: &Self::Abi) -> &Self {
unsafe { core::mem::transmute::<&*mut core::ffi::c_void, &T>(abi) }
}

unsafe fn from_abi(abi: Self::Abi) -> Result<Self> {
unsafe {
if !abi.is_null() {
Expand All @@ -53,6 +62,14 @@ where
type Abi = core::mem::MaybeUninit<Self>;
type Default = Self;

fn is_null(_: &Self::Abi) -> bool {
false
}

unsafe fn assume_init_ref(abi: &Self::Abi) -> &Self {
unsafe { abi.assume_init_ref() }
}

unsafe fn from_abi(abi: Self::Abi) -> Result<Self> {
unsafe { Ok(abi.assume_init()) }
}
Expand All @@ -69,6 +86,14 @@ where
type Abi = Self;
type Default = Self;

fn is_null(_: &Self::Abi) -> bool {
false
}

unsafe fn assume_init_ref(abi: &Self::Abi) -> &Self {
abi
}

unsafe fn from_abi(abi: Self::Abi) -> Result<Self> {
Ok(abi)
}
Expand Down
5 changes: 5 additions & 0 deletions crates/tests/winrt/ref_params/src/interop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ struct Test : implements<Test, ITest>

int32_t Input(ITest const& input)
{
if (!input)
{
throw hresult_error(E_POINTER);
}

return input.Current();
}

Expand Down
1 change: 1 addition & 0 deletions crates/tests/winrt/ref_params/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ fn test_interface(test: &ITest) -> Result<()> {

assert_eq!(test.Input(&one_two_three)?, 123);
assert_eq!(test.Input(&four_five_six)?, 456);
assert_eq!(test.Input(None).unwrap_err().code(), HRESULT(-2147467261)); // E_POINTER

let mut seven_eight_nine = None;
test.Output(789, &mut seven_eight_nine)?;
Expand Down
2 changes: 1 addition & 1 deletion crates/tests/winrt/reference/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ unsafe extern "system" fn DllGetActivationFactory(
name: Ref<HSTRING>,
factory: OutRef<IActivationFactory>,
) -> HRESULT {
if *name == "test_reference.Reference" {
if name.unwrap() == "test_reference.Reference" {
factory.write(Some(ReferenceFactory.into())).into()
} else {
_ = factory.write(None);
Expand Down

0 comments on commit 17b1640

Please sign in to comment.