safer_ffi/layout/
macros.rs

1#![cfg_attr(rustfmt, rustfmt::skip)]
2
3export_cfgs! {$
4    "headers" => __cfg_headers__!,
5    "headers" => __cfg_csharp__!,
6    "js" => __cfg_js__!,
7    "python-headers" => __cfg_python__!,
8}
9/// Generates convenience `__cfg_xxx__! { … }` macros with the semantics of
10/// `#[cfg(feature = "xxx")] emit! { … }`, but usable by downstream code, such
11/// as the one emitted by our own derive macros and whatnot.
12macro_rules! export_cfgs {(
13    $_:tt
14    $(
15        $feature:tt => $macro_name:ident !
16    ),* $(,)?
17) => (
18    $(
19        match_cfg! {
20            feature = $feature => {
21                #[doc(hidden)] /** not part of the public API */ #[macro_export]
22                macro_rules! $macro_name {(
23                    $_($item:item)*
24                ) => (
25                    $_($item)*
26                )}
27            },
28            _ => {
29                #[doc(hidden)] /** not part of the public API */ #[macro_export]
30                macro_rules! $macro_name {(
31                    $_($item:item)*
32                ) => (
33                    // nothing
34                )}
35            },
36        }
37        #[allow(unused)]
38        pub(in crate) use $macro_name;
39    )*
40)} use export_cfgs;
41
42// Defines a special `__with_cfg_python__!` for-downstream-code, based on the
43// usable-everywhere `$($($if_cfg_python)? … )?` conditional expansion trick:
44//
45// ```rust
46// __with_cfg_python__!(|$if_cfg_python| {
47//     match … {
48//         Language::C => …,
49//         $($($if_cfg_python)?
50//             Language::Python => …,
51//         )?
52//     }
53// })
54// ```
55match_cfg! {
56    feature = "python-headers" => {
57        #[doc(hidden)] /** Not part of the public API */ #[macro_export]
58        macro_rules! __with_cfg_python__ {(
59            |$_:tt $if_python:ident $(, $__:tt $dol:ident)?| $body:tt
60        ) => ({
61            macro_rules! __emit__ {
62                (
63                    [$( $__ $dol : tt )? $_($__rest:tt)*]
64                    $_(true $_($_ $if_python:tt)?)?
65                    $_(false)?
66                ) => $body
67            }
68            __emit__! { [$] true }
69        })}
70    },
71    _ => {
72        #[doc(hidden)] /** Not part of the public API */ #[macro_export]
73        macro_rules! __with_cfg_python__ {(
74            |$_:tt $if_python:ident $(, $__:tt $dol:ident)?| $body:tt
75        ) => ({
76            macro_rules! __emit__ {
77                (
78                    [$( $__ $dol : tt )? $_($__rest:tt)*]
79                    $_(true $_($_ $if_python:tt)?)?
80                    $_(false)?
81                ) => $body
82            }
83            __emit__! { [$] false }
84        })}
85    },
86}
87
88/// Safely implement [`CType`][`trait@crate::layout::LegacyCType`]
89/// for a `#[repr(C)]` struct **when all its fields are `CType`**.
90///
91/// Note: you rarely need to call this macro directly. Instead, look for the
92/// [`ReprC!`] macro to safely implement [`ReprC`][`trait@crate::layout::ReprC`].
93///
94/// [`ReprC!`]: crate::ReprC!
95#[macro_export]
96macro_rules! CType {(
97    $(
98        @doc_meta( $($doc_meta:tt)* )
99    )?
100    #[repr(C $(, js $(@$js:tt)?)? $(,)?)]
101    $(#[$($meta:tt)*])*
102    $pub:vis
103    struct $StructName:ident $(
104        [
105            $($lt:lifetime ,)*
106            $($($generics:ident $(= $Default:ty)?),+ $(,)?)?
107        ]
108            $(where { $($bounds:tt)* })?
109    )?
110    {
111        $(
112            $(#[$($field_meta:tt)*])*
113            $field_pub:vis
114            $field_name:ident : $field_ty:ty
115        ),+ $(,)?
116    }
117) => (
118        impl $(<$($lt ,)* $($($generics),+)?>)?
119            $crate::js::ReprNapi
120        for
121            $StructName $(<$($lt ,)* $($($generics),+)?>)?
122        where
123            Self : 'static,
124            $(
125                $field_ty : $crate::layout::ReprC,
126                <$field_ty as $crate::layout::ReprC>::CLayout : $crate::js::ReprNapi,
127            )*
128            $(
129                $($(
130                    $generics : $crate::layout::ReprC,
131                    <$generics as $crate::layout::ReprC>::CLayout : $crate::js::ReprNapi,
132                )+)?
133                $($($bounds)*)?
134            )?
135        {
136            type NapiValue = $crate::js::JsUnknown;
137
138            fn to_napi_value (
139                self: Self,
140                env: &'_ $crate::js::Env,
141            ) -> $crate::js::Result<$crate::js::JsUnknown>
142            {
143                let mut _obj = env.create_object()?;
144                $(
145                    _obj.set_named_property(
146                        $crate::ඞ::stringify!($field_name),
147                        <
148                            <$field_ty as $crate::layout::ReprC>::CLayout
149                            as
150                            $crate::js::ReprNapi
151                        >::to_napi_value(
152                            unsafe { $crate::layout::into_raw(self.$field_name) },
153                            env,
154                        )?,
155                    )?;
156                )*
157                $crate::js::Result::Ok(_obj.into_unknown())
158            }
159
160            fn from_napi_value (
161                env: &'_ $crate::js::Env,
162                obj: $crate::js::JsUnknown,
163            ) -> $crate::js::Result<Self>
164            {
165                use $crate::ඞ::convert::TryFrom as _;
166                let mut is_buffer = false;
167                // Poor man's specialization.
168                if  $crate::ඞ::any::TypeId::of::<Self>()
169                    ==
170                    $crate::ඞ::any::TypeId::of::<$crate::slice::slice_ref_Layout<'_, u8>>()
171                &&  (
172                        { is_buffer = obj.is_buffer()?; is_buffer }
173                        ||
174                        $crate::ඞ::matches!(
175                            obj.get_type(),
176                            $crate::js::Result::Ok($crate::js::ValueType::Null)
177                        )
178                    )
179                {
180                    return if is_buffer {
181                        let js_buffer = $crate::js::JsBuffer::try_from(obj)?;
182                        let (buf, _storage): (&[u8], _);
183                        #[cfg(target_arch = "wasm32")] {
184                            _storage = ();
185                            let bytes = js_buffer.into_value()?.into_boxed_slice();
186                            let raw = $crate::ඞ::boxed::Box::into_raw(bytes);
187                            env.__push_drop_glue($crate::ඞ::scopeguard::guard(raw, |raw| unsafe {
188                                $crate::ඞ::mem::drop($crate::ඞ::boxed::Box::from_raw(raw))
189                            }));
190                            buf = unsafe { &*raw };
191                        } /* else */
192                        #[cfg(not(target_arch = "wasm32"))] {
193                            _storage = js_buffer.into_value()?;
194                            buf = &_storage;
195                        }
196                        let xs = buf;
197                        $crate::js::Result::Ok(unsafe { $crate::ඞ::mem::transmute_copy(&{
198                            $crate::slice::slice_raw_Layout::<u8> {
199                                ptr: xs.as_ptr() as _,
200                                len: xs.len(),
201                            }
202                        })})
203                    } else { // it's NULL
204                        $crate::js::Result::Ok(unsafe { $crate::ඞ::mem::transmute_copy::<_, Self>(&{
205                            $crate::slice::slice_raw_Layout::<u8> {
206                                ptr: $crate::NULL!(),
207                                len: 0xbad000,
208                            }
209                        })})
210                    };
211                }
212                let obj = $crate::js::JsObject::try_from(obj)?;
213                $crate::js::Result::Ok(Self {
214                    $(
215                        $field_name: unsafe { $crate::layout::from_raw_unchecked(
216                            <
217                                <$field_ty as $crate::layout::ReprC>::CLayout
218                                as
219                                $crate::js::ReprNapi
220                            >::from_napi_value(
221                                env,
222                                obj.get_named_property($crate::ඞ::stringify!($field_name))?,
223                            )?
224                        )},
225                    )*
226                })
227            }
228        }
229); (
230    @js_enum
231    $Enum_Layout:ident {
232        $(
233            $Variant:ident = $Discriminant:expr
234        ),* $(,)?
235    }
236) => (
237    #[allow(nonstandard_style)]
238    const _: () = {
239        impl $Enum_Layout {
240            $(
241                pub const $Variant: $Enum_Layout = $Discriminant;
242            )*
243        }
244
245        impl $crate::js::ReprNapi for $Enum_Layout {
246            type NapiValue = $crate::js::JsString;
247
248            fn to_napi_value (
249                self: Self,
250                env: &'_ $crate::js::Env,
251            ) -> $crate::js::Result< $crate::js::JsString >
252            {
253                env.create_string(match self {
254                $(
255                    | $Enum_Layout::$Variant => $crate::ඞ::stringify!($Variant),
256                )*
257                    | _ => $crate::ඞ::panic!(
258                        "ill-formed enum variant ({:?}) for type `{}`",
259                        &self.discriminant,
260                        <$Enum_Layout as $crate::layout::CType>::short_name(),
261                    ),
262                })
263            }
264
265            fn from_napi_value (
266                env: &'_ $crate::js::Env,
267                js_string: $crate::js::JsString,
268            ) -> $crate::js::Result<Self>
269            {
270                match js_string.into_utf8()?.as_str()? {
271                $(
272                    | $crate::ඞ::stringify!($Variant) => $crate::js::Result::Ok($Enum_Layout::$Variant),
273                )*
274                    | _ => $crate::js::Result::Err($crate::js::Error::new(
275                        // status
276                        $crate::js::Status::InvalidArg,
277                        // reason
278                        $crate::ඞ::concat!(
279                            "Expected one of: "
280                            $(
281                                , "`", $crate::ඞ::stringify!($Variant), "`",
282                            )", "*
283                        ).into(),
284                    ).into()),
285                }
286            }
287        }
288    };
289)}
290
291/// Transitioning helper macro: still uses the old `ReprC!` syntax, but just to
292/// forward to the new `#[derive_ReprC2($(js)?)]` one.
293#[macro_export]
294macro_rules! ReprC {(
295    $(
296        @[doc = $doc:expr]
297    )?
298    $(
299        #[doc = $doc2:expr]
300    )*
301    #[repr(
302        $C_or_transparent:ident $(,
303            $($(@$if_js:tt)?
304        js $(,)?
305            )?
306        )?
307    )]
308    $(
309        #[$attr:meta]
310    )*
311    $pub:vis
312    struct $StructName:ident $([$($generics:tt)*])?
313    $(
314        where { $($wc:tt)* }
315    )?
316    $({
317        $($body:tt)*
318    })?
319    $((
320        $($body2:tt)*
321    );)?
322) => (
323    #[$crate::prelude::derive_ReprC2($($($($if_js)? js)?)?)]
324    $(
325        #[doc = $doc]
326    )?
327    $(
328        #[doc = $doc2]
329    )*
330    #[cfg_attr(feature = "stabby", stabby::stabby)]
331    #[repr($C_or_transparent)]
332    $(
333        #[$attr]
334    )*
335    $pub
336    struct $StructName $(<$($generics)*>)?
337    $(
338        where $($wc)*
339    )?
340    $({
341        $($body)*
342    })?
343    $((
344        $($body2)*
345    );)?
346)}
347
348#[cfg(test)]
349#[crate::derive_ReprC]
350#[repr(u8)]
351#[derive(Debug)]
352/// Some docstring
353pub
354enum MyBool {
355    /// Some variant docstring
356    False = 42,
357    True, // = 43
358}
359
360#[cfg(any(test, docs))]
361mod test {
362    use crate::layout::ReprC;
363
364    #[crate::derive_ReprC]
365    /// Some docstring before
366    #[repr(u8)]
367    #[derive(Debug)]
368    /// Some docstring after
369    pub
370    enum MyBool {
371        False = 42,
372        True, // = 43
373    }
374
375    ReprC! {
376        #[repr(opaque)]
377        struct Opaque
378        {}
379    }
380
381    ReprC! {
382        #[repr(C)]
383        struct GenericStruct['lifetime, T]
384        where {
385            T : 'lifetime,
386        }
387        {
388            inner: &'lifetime T,
389        }
390    }
391
392    doc_test! { derive_ReprC_supports_generics:
393        fn main () {}
394
395        use ::safer_ffi::prelude::*;
396
397        /// Some docstring before
398        #[derive_ReprC]
399        #[repr(u8)]
400        #[derive(Debug)]
401        /// Some docstring after
402        pub
403        enum MyBool {
404            False = 42,
405            True, // = 43
406        }
407
408        #[derive_ReprC]
409        #[repr(C)]
410        struct GenericStruct<'lifetime, T : 'lifetime>
411        where
412            T : ReprC,
413        {
414            inner: &'lifetime T,
415        }
416    }
417
418    mod opaque {
419        doc_test! { unused:
420            fn main () {}
421
422            use ::safer_ffi::prelude::*;
423
424            ReprC! {
425                #[repr(opaque)]
426                struct Foo {}
427            }
428        }
429
430        doc_test! { with_indirection:
431            fn main () {}
432
433            use ::safer_ffi::prelude::*;
434
435            ReprC! {
436                #[repr(opaque)]
437                pub
438                struct Foo {}
439            }
440
441            #[ffi_export]
442            fn foo (_it: &'_ Foo)
443            {}
444        }
445
446        doc_test! { without_indirection:
447            #![compile_fail]
448            fn main () {}
449
450            use ::safer_ffi::prelude::*;
451
452            ReprC! {
453                #[repr(opaque)]
454                pub
455                struct Foo {}
456            }
457
458            #[ffi_export]
459            fn foo (it: Foo)
460            {}
461        }
462    }
463}