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