panda/callbacks/export.rs
1/// Declare and export a plugin-to-plugin (PPP) callback, which allows other plugins
2/// to add callbacks for this plugin to run.
3///
4/// ## Example
5///
6/// For example, if you were writing a Rust plugin that would use a callback that
7/// runs every other basic block, you could declare and use two callbacks like so:
8///
9/// ```
10/// use panda::{Callback, export_ppp_callback};
11/// use panda::prelude::*;
12///
13/// export_ppp_callback! {
14/// pub(crate) fn on_every_even_block(cpu: &mut CPUState);
15/// pub(crate) fn on_every_odd_block(cpu: &mut CPUState);
16/// }
17///
18/// #[panda::init]
19/// fn init() {
20/// let mut i = 0;
21/// Callback::new().before_block_exec(move |cpu, _| {
22/// if i % 2 == 0 {
23/// on_every_even_block::trigger(cpu);
24/// } else {
25/// on_every_odd_block::trigger(cpu);
26/// }
27///
28/// i += 1;
29/// });
30/// }
31/// ```
32///
33/// (For further usage see `panda-rs/examples/ppp_callback_export.rs`)
34///
35/// The return type of each callback can be any which implements [`CallbackReturn`], a
36/// trait which describes how to fold all the return values into a single return value
37/// to be returned by `<callback_name>::trigger(...)`. For example a callback that returns
38/// a `bool` will return `true` if any of the callbacks return `true`, and will only return
39/// false if every registered callback returns false.
40///
41/// If you wish to alter this behavior for existing types, use the [newtype pattern],
42/// which will allow you to provide your own implementation by implementing the trait.
43///
44/// [newtype pattern]: https://doc.rust-lang.org/rust-by-example/generics/new_types.html
45///
46/// **Note:** All callback arguments and return values are expected to be FFI safe. If rustc
47/// emits a warning for a given type, it is very likely it is not compatible with the
48/// C ABI. In order to have your own custom types be FFI-safe, they should be marked
49/// either `#[repr(transparent)]` or `#[repr(C)]` or should be treated as opaque types
50/// (and thus should only be created and accessed within the same plugin and passed as
51/// references).
52#[macro_export]
53macro_rules! export_ppp_callback {
54 {
55 $(
56 $vis:vis fn $cb_name:ident (
57 $(
58 $arg:ident : $arg_ty:ty
59 ),* $(,)?
60 ) $(-> $ret_ty:ty)?;
61 )*
62 } => {$(
63 $vis mod $cb_name {
64 use super::*;
65
66 use ::std::ffi::c_void;
67
68 #[derive(PartialEq)]
69 struct PppContextInternal(*mut c_void);
70
71 unsafe impl Sync for PppContextInternal {}
72 unsafe impl Send for PppContextInternal {}
73
74 $vis type CallbackType = extern "C" fn($( $arg_ty ),*) $(-> $ret_ty)?;
75 $vis type CallbackTypeWithContext = extern "C" fn(*mut c_void, $( $arg_ty ),*) $(-> $ret_ty)?;
76
77 extern "C" fn trampoline(context: *mut c_void, $($arg : $arg_ty),*) $(-> $ret_ty)? {
78 let cb: CallbackType = unsafe {
79 ::core::mem::transmute(context)
80 };
81
82 cb($($arg),*)
83 }
84
85 #[export_name = concat!("ppp_add_cb_", stringify!($cb_name))]
86 $vis extern "C" fn add_callback(callback: CallbackType) {
87 unsafe {
88 add_callback_with_context(trampoline, ::core::mem::transmute(callback))
89 }
90 }
91
92 #[export_name = concat!("ppp_add_cb_", stringify!($cb_name), "_with_context")]
93 $vis extern "C" fn add_callback_with_context(
94 callback: CallbackTypeWithContext,
95 context: *mut c_void,
96 ) {
97 CALLBACKS
98 .lock()
99 .unwrap()
100 .push((callback, PppContextInternal(context)));
101 }
102
103 #[export_name = concat!("ppp_remove_cb_", stringify!($cb_name))]
104 $vis extern "C" fn remove_callback(callback: CallbackType) -> bool {
105 unsafe {
106 remove_callback_with_context(trampoline, ::core::mem::transmute(callback))
107 }
108 }
109
110 #[export_name = concat!("ppp_remove_cb_", stringify!($cb_name), "_with_context")]
111 $vis extern "C" fn remove_callback_with_context(
112 callback: CallbackTypeWithContext,
113 context: *mut c_void,
114 ) -> bool {
115 let context = PppContextInternal(context);
116 let mut callbacks = CALLBACKS.lock().unwrap();
117 let old_len = callbacks.len();
118
119 callbacks.retain(
120 |(cb, cb_ctxt)| (*cb as usize, cb_ctxt) != (callback as _, &context)
121 );
122
123 callbacks.len() != old_len
124 }
125
126 $crate::lazy_static::lazy_static! {
127 static ref CALLBACKS: ::std::sync::Mutex<
128 Vec<(CallbackTypeWithContext, PppContextInternal)>
129 > = ::std::sync::Mutex::new(Vec::new());
130 }
131
132 $vis fn trigger($($arg : $arg_ty),*) $(-> $ret_ty)? {
133 CALLBACKS.lock()
134 .unwrap()
135 .iter_mut()
136 .map(|(callback, PppContextInternal(context))| callback(
137 *context,
138 $($arg),*
139 ))
140 .fold(
141 $crate::__callback_fold_default!($($ret_ty)?),
142 $crate::__callback_fold_fn!($($ret_ty)?)
143 )
144 }
145 }
146 )*};
147}
148
149#[doc(hidden)]
150#[macro_export]
151macro_rules! __callback_fold_default {
152 () => {
153 ()
154 };
155 ($ty:ty) => {
156 <$ty as $crate::CallbackReturn>::callback_fold_default()
157 };
158}
159
160#[doc(hidden)]
161#[macro_export]
162macro_rules! __callback_fold_fn {
163 () => {
164 (|(), _| ())
165 };
166 ($ty:ty) => {
167 <$ty as $crate::CallbackReturn>::fold_callback_return
168 };
169}
170
171/// A type which can be returned from a callback and folded into a single value
172///
173/// As an example, here's the provided implementation for `bool`:
174///
175/// ```no_run
176/// /// Returns true if any of the callbacks returned true without short circuiting
177/// impl CallbackReturn for bool {
178/// type FoldType = bool;
179///
180/// fn fold_callback_return(folded: Self::FoldType, ret: Self) -> Self::FoldType {
181/// folded | ret
182/// }
183/// }
184/// ```
185///
186/// The way this is used is by taking the `FoldType` and creating a default instance. For
187/// a `bool` this will be `false`. Then, for each callback return value it will take the
188/// previous instance (starting with `false`) and do `previous | current_callback_return`.
189///
190/// The result will mean that if callbacks `a`, `b`, and `c` are registered, the resulting
191/// value returned from `<callback>::trigger(...)` is `((false | a) | b) | c`. (Parenthesis
192/// added to demonstrate folding order)
193pub trait CallbackReturn {
194 type FoldType: Default;
195
196 /// Function for folding each callback return value into a single value
197 fn fold_callback_return(folded: Self::FoldType, ret: Self) -> Self::FoldType;
198
199 /// Get the default value for folding the callback returns into a single value
200 fn callback_fold_default() -> Self::FoldType {
201 Self::FoldType::default()
202 }
203}
204
205/// Returns true if any of the callbacks returned true without short circuiting
206impl CallbackReturn for bool {
207 type FoldType = bool;
208
209 fn fold_callback_return(folded: Self::FoldType, ret: Self) -> Self::FoldType {
210 folded | ret
211 }
212}
213
214macro_rules! impl_for_ints {
215 ($($ty:ty)*) => {
216 $(
217 /// Returns the first non-zero value without short-circuiting
218 impl CallbackReturn for $ty {
219 type FoldType = $ty;
220
221 fn fold_callback_return(folded: Self::FoldType, ret: Self) -> Self::FoldType {
222 if folded != 0 {
223 folded
224 } else {
225 ret
226 }
227 }
228 }
229 )*
230 };
231}
232
233impl_for_ints!(u8 u16 u32 u64 usize i8 i16 i32 i64 isize);