bindgen/codegen/
dyngen.rs

1use crate::codegen;
2use crate::ir::context::BindgenContext;
3use crate::ir::function::ClangAbi;
4use proc_macro2::{Ident, TokenStream};
5
6/// Used to build the output tokens for dynamic bindings.
7#[derive(Default)]
8pub(crate) struct DynamicItems {
9    /// Tracks the tokens that will appears inside the library struct -- e.g.:
10    /// ```ignore
11    /// struct Lib {
12    ///    __library: ::libloading::Library,
13    ///    pub x: Result<unsafe extern ..., ::libloading::Error>, // <- tracks these
14    ///    ...
15    /// }
16    /// ```
17    struct_members: Vec<TokenStream>,
18
19    /// Tracks the tokens that will appear inside the library struct's implementation, e.g.:
20    ///
21    /// ```ignore
22    /// impl Lib {
23    ///     ...
24    ///     pub unsafe fn foo(&self, ...) { // <- tracks these
25    ///         ...
26    ///     }
27    /// }
28    /// ```
29    struct_implementation: Vec<TokenStream>,
30
31    /// Tracks the initialization of the fields inside the `::new` constructor of the library
32    /// struct, e.g.:
33    /// ```ignore
34    /// impl Lib {
35    ///
36    ///     pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
37    ///     where
38    ///         P: AsRef<::std::ffi::OsStr>,
39    ///     {
40    ///         ...
41    ///         let foo = __library.get(...) ...; // <- tracks these
42    ///         ...
43    ///     }
44    ///
45    ///     ...
46    /// }
47    /// ```
48    constructor_inits: Vec<TokenStream>,
49
50    /// Tracks the information that is passed to the library struct at the end of the `::new`
51    /// constructor, e.g.:
52    /// ```ignore
53    /// impl LibFoo {
54    ///     pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
55    ///     where
56    ///         P: AsRef<::std::ffi::OsStr>,
57    ///     {
58    ///         ...
59    ///         Ok(LibFoo {
60    ///             __library: __library,
61    ///             foo,
62    ///             bar, // <- tracks these
63    ///             ...
64    ///         })
65    ///     }
66    /// }
67    /// ```
68    init_fields: Vec<TokenStream>,
69}
70
71impl DynamicItems {
72    pub(crate) fn new() -> Self {
73        Self::default()
74    }
75
76    pub(crate) fn get_tokens(
77        &self,
78        lib_ident: &Ident,
79        ctx: &BindgenContext,
80    ) -> TokenStream {
81        let struct_members = &self.struct_members;
82        let constructor_inits = &self.constructor_inits;
83        let init_fields = &self.init_fields;
84        let struct_implementation = &self.struct_implementation;
85
86        let library_new = if ctx.options().wrap_unsafe_ops {
87            quote!(unsafe { ::libloading::Library::new(path) })
88        } else {
89            quote!(::libloading::Library::new(path))
90        };
91
92        let from_library = if ctx.options().wrap_unsafe_ops {
93            quote!(unsafe { Self::from_library(library) })
94        } else {
95            quote!(Self::from_library(library))
96        };
97
98        quote! {
99            pub struct #lib_ident {
100                __library: ::libloading::Library,
101                #(#struct_members)*
102            }
103
104            impl #lib_ident {
105                pub unsafe fn new<P>(
106                    path: P
107                ) -> Result<Self, ::libloading::Error>
108                where P: AsRef<::std::ffi::OsStr> {
109                    let library = #library_new?;
110                    #from_library
111                }
112
113                pub unsafe fn from_library<L>(
114                    library: L
115                ) -> Result<Self, ::libloading::Error>
116                where L: Into<::libloading::Library> {
117                    let __library = library.into();
118                    #( #constructor_inits )*
119                    Ok(#lib_ident {
120                        __library,
121                        #( #init_fields ),*
122                    })
123                }
124
125                #( #struct_implementation )*
126            }
127        }
128    }
129
130    #[allow(clippy::too_many_arguments)]
131    pub(crate) fn push_func(
132        &mut self,
133        ident: &Ident,
134        symbol: &str,
135        abi: ClangAbi,
136        is_variadic: bool,
137        is_required: bool,
138        args: &[TokenStream],
139        args_identifiers: &[TokenStream],
140        ret: &TokenStream,
141        ret_ty: &TokenStream,
142        attributes: &[TokenStream],
143        ctx: &BindgenContext,
144    ) {
145        if !is_variadic {
146            assert_eq!(args.len(), args_identifiers.len());
147        }
148
149        let signature = quote! { unsafe extern #abi fn ( #( #args),* ) #ret };
150        let member = if is_required {
151            signature
152        } else {
153            quote! { Result<#signature, ::libloading::Error> }
154        };
155
156        self.struct_members.push(quote! {
157            pub #ident: #member,
158        });
159
160        // N.B: If the signature was required, it won't be wrapped in a Result<...>
161        //      and we can simply call it directly.
162        let fn_ = if is_required {
163            quote! { self.#ident }
164        } else {
165            quote! { self.#ident.as_ref().expect("Expected function, got error.") }
166        };
167        let call_body = if ctx.options().wrap_unsafe_ops {
168            quote!(unsafe { (#fn_)(#( #args_identifiers ),*) })
169        } else {
170            quote!((#fn_)(#( #args_identifiers ),*) )
171        };
172
173        // We can't implement variadic functions from C easily, so we allow to
174        // access the function pointer so that the user can call it just fine.
175        if !is_variadic {
176            self.struct_implementation.push(quote! {
177                #(#attributes)*
178                pub unsafe fn #ident ( &self, #( #args ),* ) #ret_ty {
179                    #call_body
180                }
181            });
182        }
183
184        // N.B: Unwrap the signature upon construction if it is required to be resolved.
185        let symbol_cstr =
186            codegen::helpers::ast_ty::cstr_expr(symbol.to_string());
187        let library_get = if ctx.options().wrap_unsafe_ops {
188            quote!(unsafe { __library.get(#symbol_cstr) })
189        } else {
190            quote!(__library.get(#symbol_cstr))
191        };
192
193        self.constructor_inits.push(if is_required {
194            quote! {
195                let #ident = #library_get.map(|sym| *sym)?;
196            }
197        } else {
198            quote! {
199                let #ident = #library_get.map(|sym| *sym);
200            }
201        });
202
203        self.init_fields.push(quote! {
204            #ident
205        });
206    }
207
208    pub fn push_var(
209        &mut self,
210        ident: &Ident,
211        symbol: &str,
212        ty: &TokenStream,
213        is_required: bool,
214        wrap_unsafe_ops: bool,
215    ) {
216        let member = if is_required {
217            quote! { *mut #ty }
218        } else {
219            quote! { Result<*mut #ty, ::libloading::Error> }
220        };
221
222        self.struct_members.push(quote! {
223            pub #ident: #member,
224        });
225
226        let deref = if is_required {
227            quote! { self.#ident }
228        } else {
229            quote! { *self.#ident.as_ref().expect("Expected variable, got error.") }
230        };
231        self.struct_implementation.push(quote! {
232            pub unsafe fn #ident (&self) -> *mut #ty {
233                #deref
234            }
235        });
236
237        let symbol_cstr =
238            codegen::helpers::ast_ty::cstr_expr(symbol.to_string());
239
240        let library_get = if wrap_unsafe_ops {
241            quote!(unsafe { __library.get::<*mut #ty>(#symbol_cstr) })
242        } else {
243            quote!(__library.get::<*mut #ty>(#symbol_cstr))
244        };
245
246        let qmark = if is_required { quote!(?) } else { quote!() };
247
248        let var_get = quote! {
249            let #ident = #library_get.map(|sym| *sym)#qmark;
250        };
251
252        self.constructor_inits.push(var_get);
253
254        self.init_fields.push(quote! {
255            #ident
256        });
257    }
258}