Skip to content

Commit 7cd63db

Browse files
committed
native-lib: pass structs to native code
1 parent a0ad04e commit 7cd63db

File tree

6 files changed

+366
-108
lines changed

6 files changed

+366
-108
lines changed

src/shims/native_lib/ffi.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
use libffi::low::CodePtr;
2+
use libffi::middle::{Arg as ArgPtr, Cif, Type as FfiType};
3+
4+
/// Perform the actual FFI call.
5+
///
6+
/// SAFETY: The `FfiArg`s passed must have been correctly instantiated (i.e. their
7+
/// type layout must match the data they point to), and the safety invariants of
8+
/// the foreign function being called must be upheld (if any).
9+
pub unsafe fn call<'a, R: libffi::high::CType>(fun: CodePtr, args: Vec<FfiArg<'a>>) -> R {
10+
let mut arg_tys = vec![];
11+
let mut arg_ptrs = vec![];
12+
for arg in args {
13+
arg_tys.push(arg.ty);
14+
arg_ptrs.push(arg.ptr)
15+
}
16+
let cif = Cif::new(arg_tys, R::reify().into_middle());
17+
unsafe { cif.call(fun, &arg_ptrs) }
18+
}
19+
20+
/// A wrapper type for `libffi::middle::Type` which also holds a pointer to the data.
21+
pub struct FfiArg<'a> {
22+
/// The type layout information for the pointed-to data.
23+
ty: FfiType,
24+
/// A pointer to the data described in `ty`.
25+
ptr: ArgPtr,
26+
/// Lifetime of the actual pointed-to data.
27+
_p: std::marker::PhantomData<&'a [u8]>,
28+
}
29+
30+
impl<'a> FfiArg<'a> {
31+
fn new(ty: FfiType, ptr: ArgPtr) -> Self {
32+
Self { ty, ptr, _p: std::marker::PhantomData }
33+
}
34+
}
35+
36+
/// An owning form of `FfiArg`.
37+
/// We introduce this enum instead of just calling `Arg::new` and storing a list of
38+
/// `libffi::middle::Arg` directly, because the `libffi::middle::Arg` just wraps a reference to
39+
/// the value it represents and we need to store a copy of the value, and pass a reference to
40+
/// this copy to C instead.
41+
#[derive(Debug, Clone)]
42+
pub enum CArg {
43+
/// Primitive type.
44+
Primitive(CPrimitive),
45+
/// Struct with its computed type layout and bytes.
46+
Struct(FfiType, Box<[u8]>),
47+
}
48+
49+
impl CArg {
50+
/// Convert a `CArg` to the required FFI argument type.
51+
pub fn arg_downcast(&self) -> FfiArg<'_> {
52+
match self {
53+
CArg::Primitive(cprim) => cprim.arg_downcast(),
54+
CArg::Struct(cstruct, items) => FfiArg::new(cstruct.clone(), ArgPtr::new(&items[0])),
55+
}
56+
}
57+
}
58+
59+
impl From<CPrimitive> for CArg {
60+
fn from(prim: CPrimitive) -> Self {
61+
Self::Primitive(prim)
62+
}
63+
}
64+
65+
#[derive(Debug, Clone)]
66+
/// Enum of supported primitive arguments to external C functions.
67+
pub enum CPrimitive {
68+
/// 8-bit signed integer.
69+
Int8(i8),
70+
/// 16-bit signed integer.
71+
Int16(i16),
72+
/// 32-bit signed integer.
73+
Int32(i32),
74+
/// 64-bit signed integer.
75+
Int64(i64),
76+
/// isize.
77+
ISize(isize),
78+
/// 8-bit unsigned integer.
79+
UInt8(u8),
80+
/// 16-bit unsigned integer.
81+
UInt16(u16),
82+
/// 32-bit unsigned integer.
83+
UInt32(u32),
84+
/// 64-bit unsigned integer.
85+
UInt64(u64),
86+
/// usize.
87+
USize(usize),
88+
/// Raw pointer, stored as C's `void*`.
89+
RawPtr(*mut std::ffi::c_void),
90+
}
91+
92+
impl CPrimitive {
93+
/// Convert a primitive to the required FFI argument type.
94+
fn arg_downcast(&self) -> FfiArg<'_> {
95+
match self {
96+
CPrimitive::Int8(i) => FfiArg::new(FfiType::i8(), ArgPtr::new(i)),
97+
CPrimitive::Int16(i) => FfiArg::new(FfiType::i16(), ArgPtr::new(i)),
98+
CPrimitive::Int32(i) => FfiArg::new(FfiType::i32(), ArgPtr::new(i)),
99+
CPrimitive::Int64(i) => FfiArg::new(FfiType::i64(), ArgPtr::new(i)),
100+
CPrimitive::ISize(i) => FfiArg::new(FfiType::isize(), ArgPtr::new(i)),
101+
CPrimitive::UInt8(i) => FfiArg::new(FfiType::u8(), ArgPtr::new(i)),
102+
CPrimitive::UInt16(i) => FfiArg::new(FfiType::u16(), ArgPtr::new(i)),
103+
CPrimitive::UInt32(i) => FfiArg::new(FfiType::u32(), ArgPtr::new(i)),
104+
CPrimitive::UInt64(i) => FfiArg::new(FfiType::u64(), ArgPtr::new(i)),
105+
CPrimitive::USize(i) => FfiArg::new(FfiType::usize(), ArgPtr::new(i)),
106+
CPrimitive::RawPtr(i) => FfiArg::new(FfiType::pointer(), ArgPtr::new(i)),
107+
}
108+
}
109+
}

0 commit comments

Comments
 (0)