deno_libffi/high/call.rs
1//! Simple dynamic calls.
2//!
3//! This API allows us to call a code pointer with an array of
4//! arguments, using libffi to set up the call.
5//!
6//! # Examples
7//!
8//! ```
9//! extern "C" fn hypot(x: f32, y: f32) -> f32 {
10//! (x * x + y * y).sqrt()
11//! }
12//!
13//! use deno_libffi::ffi_call;
14//!
15//! let result = unsafe { ffi_call!{ hypot(3f32, 4f32) -> f32 } };
16//!
17//! assert!((result - 5f32).abs() < 0.0001);
18//! ```
19
20use std::marker::PhantomData;
21
22use crate::middle;
23pub use middle::CodePtr;
24
25/// Encapsulates an argument with its type information.
26///
27/// In order to set up calls using [`call`](index.html#method.call), we
28/// need to wrap (a reference to) each argument in an `Arg`. The usual
29/// way to do this is with function [`arg`](fn.arg.html).
30#[derive(Clone, Debug)]
31pub struct Arg<'a> {
32 // There should be some type T such that type_ is the middle-layer
33 // value of Type<T> and value is T::reify().
34 type_: middle::Type,
35 value: middle::Arg,
36 _marker: PhantomData<&'a ()>,
37}
38
39impl<'a> Arg<'a> {
40 /// Wraps an argument reference for passing to `high::call::call`.
41 ///
42 /// For a shorter alias of the same, see
43 /// [`high::call::arg`](fn.arg.html).
44 pub fn new<T: super::CType>(arg: &'a T) -> Self {
45 Arg {
46 type_: T::reify().into_middle(),
47 value: middle::Arg::new(arg),
48 _marker: PhantomData,
49 }
50 }
51}
52
53/// Constructs an [`Arg`](struct.Arg.html) for passing to
54/// [`call`](fn.call.html).
55pub fn arg<T: super::CType>(arg: &T) -> Arg {
56 Arg::new(arg)
57}
58
59/// Performs a dynamic call to a C function.
60///
61/// To reduce boilerplate, see [`ffi_call!`](../../macro.ffi_call!.html).
62///
63/// # Examples
64///
65/// ```
66/// extern "C" fn hypot(x: f32, y: f32) -> f32 {
67/// (x * x + y * y).sqrt()
68/// }
69///
70/// use deno_libffi::high::call::*;
71///
72/// let result = unsafe {
73/// call::<f32>(CodePtr(hypot as *mut _), &[arg(&3f32), arg(&4f32)])
74/// };
75///
76/// assert!((result - 5f32).abs() < 0.0001);
77/// ```
78pub unsafe fn call<R: super::CType>(fun: CodePtr, args: &[Arg]) -> R {
79 let types = args.iter().map(|arg| arg.type_.clone());
80 let cif = middle::Cif::new(types, R::reify().into_middle());
81
82 let values = args.iter().map(|arg| arg.value.clone()).collect::<Vec<_>>();
83 cif.call(fun, &values)
84}
85
86/// Performs a dynamic call to a C function.
87///
88/// This macro provides sugar for `call::arg` and `call::call`. For more
89/// control, see [`high::call::call`](high/call/fn.call.html).
90///
91/// # Examples
92///
93/// ```
94/// extern "C" fn hypot(x: f32, y: f32) -> f32 {
95/// (x * x + y * y).sqrt()
96/// }
97///
98/// use deno_libffi::ffi_call;
99///
100/// let result = unsafe { ffi_call!{ hypot(3f32, 4f32) -> f32 } };
101///
102/// assert!((result - 5f32).abs() < 0.0001);
103/// ```
104#[macro_export]
105macro_rules! ffi_call {
106
107 { ( $fun:expr ) ( $( $arg:expr ),* ) -> $ty:ty }
108 =>
109 {
110 $crate::high::call::call::<$ty>(
111 $crate::high::call::CodePtr($fun as *mut _),
112 &[$($crate::high::call::arg(&$arg)),*])
113 };
114
115 { $fun:ident ( $( $arg:expr ),* ) -> $ty:ty }
116 =>
117 { ffi_call!{ ($fun)($($arg),*) -> $ty } };
118
119 { ( $fun:expr ) ( $( $arg:expr ),* ) }
120 =>
121 { ffi_call!{ ($fun)($(arg),*) -> () } };
122
123 { $fun:ident ( $( $arg:expr ),* ) }
124 =>
125 { ffi_call!{ ($fun)($($arg),*) -> () } };
126
127}