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        abi: ClangAbi,
135        is_variadic: bool,
136        is_required: bool,
137        args: Vec<TokenStream>,
138        args_identifiers: Vec<TokenStream>,
139        ret: TokenStream,
140        ret_ty: TokenStream,
141        attributes: Vec<TokenStream>,
142        ctx: &BindgenContext,
143    ) {
144        if !is_variadic {
145            assert_eq!(args.len(), args_identifiers.len());
146        }
147
148        let signature = quote! { unsafe extern #abi fn ( #( #args),* ) #ret };
149        let member = if is_required {
150            signature
151        } else {
152            quote! { Result<#signature, ::libloading::Error> }
153        };
154
155        self.struct_members.push(quote! {
156            pub #ident: #member,
157        });
158
159        // N.B: If the signature was required, it won't be wrapped in a Result<...>
160        //      and we can simply call it directly.
161        let fn_ = if is_required {
162            quote! { self.#ident }
163        } else {
164            quote! { self.#ident.as_ref().expect("Expected function, got error.") }
165        };
166        let call_body = if ctx.options().wrap_unsafe_ops {
167            quote!(unsafe { (#fn_)(#( #args_identifiers ),*) })
168        } else {
169            quote!((#fn_)(#( #args_identifiers ),*) )
170        };
171
172        // We can't implement variadic functions from C easily, so we allow to
173        // access the function pointer so that the user can call it just fine.
174        if !is_variadic {
175            self.struct_implementation.push(quote! {
176                #(#attributes)*
177                pub unsafe fn #ident ( &self, #( #args ),* ) #ret_ty {
178                    #call_body
179                }
180            });
181        }
182
183        // N.B: Unwrap the signature upon construction if it is required to be resolved.
184        let ident_str = codegen::helpers::ast_ty::cstr_expr(ident.to_string());
185        let library_get = if ctx.options().wrap_unsafe_ops {
186            quote!(unsafe { __library.get(#ident_str) })
187        } else {
188            quote!(__library.get(#ident_str))
189        };
190
191        self.constructor_inits.push(if is_required {
192            quote! {
193                let #ident = #library_get.map(|sym| *sym)?;
194            }
195        } else {
196            quote! {
197                let #ident = #library_get.map(|sym| *sym);
198            }
199        });
200
201        self.init_fields.push(quote! {
202            #ident
203        });
204    }
205
206    pub fn push_var(
207        &mut self,
208        ident: Ident,
209        ty: TokenStream,
210        is_required: bool,
211        wrap_unsafe_ops: bool,
212    ) {
213        let member = if is_required {
214            quote! { *mut #ty }
215        } else {
216            quote! { Result<*mut #ty, ::libloading::Error> }
217        };
218
219        self.struct_members.push(quote! {
220            pub #ident: #member,
221        });
222
223        let deref = if is_required {
224            quote! { self.#ident }
225        } else {
226            quote! { *self.#ident.as_ref().expect("Expected variable, got error.") }
227        };
228        self.struct_implementation.push(quote! {
229            pub unsafe fn #ident (&self) -> *mut #ty {
230                #deref
231            }
232        });
233
234        let ident_str = codegen::helpers::ast_ty::cstr_expr(ident.to_string());
235
236        let library_get = if wrap_unsafe_ops {
237            quote!(unsafe { __library.get::<*mut #ty>(#ident_str) })
238        } else {
239            quote!(__library.get::<*mut #ty>(#ident_str))
240        };
241
242        let qmark = if is_required { quote!(?) } else { quote!() };
243
244        let var_get = quote! {
245            let #ident = #library_get.map(|sym| *sym)#qmark;
246        };
247
248        self.constructor_inits.push(var_get);
249
250        self.init_fields.push(quote! {
251            #ident
252        });
253    }
254}