1#[doc(hidden)]
6#[cfg(feature = "no_std")]
7pub use alloc::boxed::Box;
8use core::{marker::PhantomData, mem::ManuallyDrop};
9#[doc(hidden)]
10#[cfg(not(feature = "no_std"))]
11pub use std::boxed::Box;
12
13#[cfg(feature = "proc_macros")]
14#[doc(inline)]
15pub use closure_ffi_proc_macros::bare_dyn;
16#[cfg(feature = "bundled_jit_alloc")]
17use jit_alloc::GlobalJitAlloc;
18
19#[allow(unused_imports)]
21use crate::{
22 arch::{create_thunk, ThunkInfo},
23 cc,
24 jit_alloc::{self, JitAlloc, JitAllocError},
25 thunk::{FnMutThunk, FnOnceThunk, FnThunk},
26};
27
28macro_rules! cc_shorthand {
29 ($fn_name:ident, $trait_ident:ident, $cc_ty:ty, $cc_name:literal $(,$cfg:meta)?) => {
30 $(#[cfg(any($cfg, doc))])?
31 #[doc = "Create a bare function thunk using the "]
32 #[doc = $cc_name]
33 #[doc = "calling convention for `fun`."]
34 #[inline]
37 pub fn $fn_name(fun: F) -> Self
38 where
39 ($cc_ty, F): $trait_ident<$cc_ty, B>,
40 {
41 Self::new(<$cc_ty>::default(), fun)
42 }
43 };
44}
45
46macro_rules! cc_shorthand_in {
47 ($fn_name:ident, $trait_ident:ident, $cc_ty:ty, $cc_name:literal $(,$cfg:meta)?) => {
48 $(#[cfg(any($cfg, doc))])?
49 #[doc = "Create a bare function thunk using the "]
50 #[doc = $cc_name]
51 #[doc = "calling convention for `fun`."]
52 #[inline]
55 pub fn $fn_name(fun: F, jit_alloc: A) -> Self
56 where
57 ($cc_ty, F): $trait_ident<$cc_ty, B>,
58 {
59 Self::new_in(<$cc_ty>::default(), fun, jit_alloc)
60 }
61 };
62}
63
64macro_rules! bare_closure_impl {
65 (
66 $ty_name:ident,
67 $trait_ident:ident,
68 $thunk_template:ident,
69 $bare_toggle:meta,
70 $bare_receiver:ty,
71 $fn_trait_doc:literal,
72 $safety_doc:literal
73 ) => {
74 #[cfg(feature = "bundled_jit_alloc")]
75 #[cfg_attr(feature = "build-docs", doc(cfg(all())))]
76 #[doc = $fn_trait_doc]
78 #[allow(dead_code)]
81 pub struct $ty_name<B: Copy, F, A: JitAlloc = GlobalJitAlloc> {
82 thunk_info: ThunkInfo,
83 jit_alloc: A,
84 closure: *mut F,
88 phantom: PhantomData<B>,
89 }
90
91 #[cfg(not(feature = "bundled_jit_alloc"))]
92 #[doc = $fn_trait_doc]
94 #[allow(dead_code)]
97 pub struct $ty_name<B: Copy, F, A: JitAlloc> {
98 thunk_info: ThunkInfo,
99 jit_alloc: A,
100 closure: *mut F,
101 phantom: PhantomData<B>,
102 }
103
104 unsafe impl<B: Copy, F: Send, A: JitAlloc + Send> Send for $ty_name<B, F, A> {}
106 unsafe impl<B: Copy, F: Sync, A: JitAlloc + Sync> Sync for $ty_name<B, F, A> {}
108
109 impl<B: Copy, F, A: JitAlloc> $ty_name<B, F, A> {
110 #[allow(unused_variables)]
115 #[deprecated(since = "0.3.0", note = "please use `try_new_in` instead")]
116 pub fn with_jit_alloc<CC>(
117 cconv: CC,
118 fun: F,
119 jit_alloc: A,
120 ) -> Result<Self, JitAllocError>
121 where
122 (CC, F): $trait_ident<CC, B>,
123 {
124 Self::try_new_in(cconv, fun, jit_alloc)
125 }
126
127 #[allow(unused_variables)]
132 pub fn try_new_in<CC>(cconv: CC, fun: F, jit_alloc: A) -> Result<Self, JitAllocError>
133 where
134 (CC, F): $trait_ident<CC, B>,
135 {
136 let closure = Box::into_raw(Box::new(fun));
137
138 let thunk_info = unsafe {
142 create_thunk(<(CC, F)>::$thunk_template, closure as *const _, &jit_alloc)?
143 };
144 Ok(Self {
145 thunk_info,
146 jit_alloc,
147 closure,
148 phantom: PhantomData,
149 })
150 }
151
152 #[allow(unused_variables)]
161 pub fn new_in<CC>(cconv: CC, fun: F, jit_alloc: A) -> Self
162 where
163 (CC, F): $trait_ident<CC, B>,
164 {
165 Self::try_new_in(cconv, fun, jit_alloc).unwrap()
166 }
167
168 cc_shorthand_in!(new_c_in, $trait_ident, cc::C, "C");
169
170 cc_shorthand_in!(new_system_in, $trait_ident, cc::System, "system");
171
172 cc_shorthand_in!(
173 new_sysv64_in,
174 $trait_ident,
175 cc::Sysv64,
176 "sysv64",
177 all(not(windows), target_arch = "x86_64")
178 );
179
180 cc_shorthand_in!(
181 new_aapcs_in,
182 $trait_ident,
183 cc::Aapcs,
184 "aapcs",
185 any(doc, target_arch = "arm")
186 );
187
188 cc_shorthand_in!(
189 new_fastcall_in,
190 $trait_ident,
191 cc::Fastcall,
192 "fastcall",
193 all(windows, any(target_arch = "x86_64", target_arch = "x86"))
194 );
195
196 cc_shorthand_in!(
197 new_stdcall_in,
198 $trait_ident,
199 cc::Stdcall,
200 "stdcall",
201 all(windows, any(target_arch = "x86_64", target_arch = "x86"))
202 );
203
204 cc_shorthand_in!(
205 new_cdecl_in,
206 $trait_ident,
207 cc::Cdecl,
208 "cdecl",
209 all(windows, any(target_arch = "x86_64", target_arch = "x86"))
210 );
211
212 cc_shorthand_in!(
213 new_thiscall_in,
214 $trait_ident,
215 cc::Thiscall,
216 "thiscall",
217 all(windows, target_arch = "x86")
218 );
219
220 cc_shorthand_in!(
221 new_win64_in,
222 $trait_ident,
223 cc::Win64,
224 "win64",
225 all(windows, target_arch = "x86_64")
226 );
227
228 #[$bare_toggle]
229 #[doc = $safety_doc]
236 #[inline]
237 pub fn bare(self: $bare_receiver) -> B {
238 unsafe { core::mem::transmute_copy(&self.thunk_info.thunk) }
240 }
241
242 #[doc = $safety_doc]
251 #[inline]
252 pub fn leak(self) -> B
253 where
254 Self: 'static,
255 {
256 let no_drop = ManuallyDrop::new(self);
257 unsafe { core::mem::transmute_copy(&no_drop.thunk_info.thunk) }
259 }
260 }
261
262 impl<B: Copy, F, A: JitAlloc> Drop for $ty_name<B, F, A> {
263 fn drop(&mut self) {
264 unsafe { self.jit_alloc.release(self.thunk_info.alloc_base).ok() };
271
272 drop(unsafe { Box::from_raw(self.closure) })
277 }
278 }
279
280 #[cfg(any(test, feature = "bundled_jit_alloc"))]
281 impl<B: Copy, F> $ty_name<B, F, GlobalJitAlloc> {
282 #[inline]
286 pub fn new<CC>(cconv: CC, fun: F) -> Self
287 where
288 (CC, F): $trait_ident<CC, B>,
289 {
290 Self::new_in(cconv, fun, Default::default())
291 }
292
293 cc_shorthand!(new_c, $trait_ident, cc::C, "C");
294
295 cc_shorthand!(new_system, $trait_ident, cc::System, "system");
296
297 cc_shorthand!(
298 new_sysv64,
299 $trait_ident,
300 cc::Sysv64,
301 "sysv64",
302 all(not(windows), target_arch = "x86_64")
303 );
304
305 cc_shorthand!(
306 new_aapcs,
307 $trait_ident,
308 cc::Aapcs,
309 "aapcs",
310 any(doc, target_arch = "arm")
311 );
312
313 cc_shorthand!(
314 new_fastcall,
315 $trait_ident,
316 cc::Fastcall,
317 "fastcall",
318 all(windows, any(target_arch = "x86_64", target_arch = "x86"))
319 );
320
321 cc_shorthand!(
322 new_stdcall,
323 $trait_ident,
324 cc::Stdcall,
325 "stdcall",
326 all(windows, any(target_arch = "x86_64", target_arch = "x86"))
327 );
328
329 cc_shorthand!(
330 new_cdecl,
331 $trait_ident,
332 cc::Cdecl,
333 "cdecl",
334 all(windows, any(target_arch = "x86_64", target_arch = "x86"))
335 );
336
337 cc_shorthand!(
338 new_thiscall,
339 $trait_ident,
340 cc::Thiscall,
341 "thiscall",
342 all(windows, target_arch = "x86")
343 );
344
345 cc_shorthand!(
346 new_win64,
347 $trait_ident,
348 cc::Win64,
349 "win64",
350 all(windows, target_arch = "x86_64")
351 );
352 }
353 };
354}
355
356bare_closure_impl!(
365 BareFnOnce,
366 FnOnceThunk,
367 THUNK_TEMPLATE_ONCE,
368 cfg(any()),
369 Self,
370 "[`FnOnce`]",
371 "- The function has been called before.\n
372- The closure is not `Send`, if calling from a different thread than the current one."
373);
374
375bare_closure_impl!(
376 BareFnMut,
377 FnMutThunk,
378 THUNK_TEMPLATE_MUT,
379 cfg(all()),
380 &Self,
381 "[`FnMut`]",
382 "- A borrow induced by a previous call is still active.\n
383- The closure is not `Sync`, if calling from a different thread than the current one."
384);
385bare_closure_impl!(
386 BareFn,
387 FnThunk,
388 THUNK_TEMPLATE,
389 cfg(all()),
390 &Self,
391 "[`Fn`]",
392 "- The closure is not `Sync`, if calling from a different thread than the current one."
393);
394
395#[cfg(test)]
396mod tests {
397 #[cfg(feature = "no_std")]
398 use alloc::{borrow::ToOwned, string::ToString};
399
400 #[test]
401 fn test_fn_once() {
402 use super::BareFnOnce;
403
404 let value = "test".to_owned();
405 let bare_closure = BareFnOnce::new_c(move |n: usize| value + &n.to_string());
406
407 let bare = bare_closure.leak();
409
410 let result = unsafe { bare(5) };
411 assert_eq!(&result, "test5");
412 }
413
414 #[test]
415 fn test_fn_mut() {
416 use super::BareFnMut;
417
418 let mut value = "0".to_owned();
419 let bare_closure = BareFnMut::new_c(|n: usize| {
420 value += &n.to_string();
421 value.clone()
422 });
423
424 let bare = bare_closure.bare();
425
426 let result = unsafe { bare(1) };
427 assert_eq!(&result, "01");
428
429 let result = unsafe { bare(2) };
430 assert_eq!(&result, "012");
431 }
432
433 #[test]
434 fn test_fn() {
435 use super::BareFn;
436
437 let cell = core::cell::RefCell::new("0".to_owned());
438 let bare_closure = BareFn::new_c(|n: usize| {
439 *cell.borrow_mut() += &n.to_string();
440 cell.borrow().clone()
441 });
442
443 let bare = bare_closure.bare();
444
445 let result = unsafe { bare(1) };
446 assert_eq!(&result, "01");
447
448 let result = unsafe { bare(2) };
449 assert_eq!(&result, "012");
450 }
451}