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<proc_macro2::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<proc_macro2::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<proc_macro2::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<proc_macro2::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 ) -> proc_macro2::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 from_library = if ctx.options().wrap_unsafe_ops {
87 quote!(unsafe { Self::from_library(library) })
88 } else {
89 quote!(Self::from_library(library))
90 };
91
92 quote! {
93 pub struct #lib_ident {
94 __library: ::libloading::Library,
95 #(#struct_members)*
96 }
97
98 impl #lib_ident {
99 pub unsafe fn new<P>(
100 path: P
101 ) -> Result<Self, ::libloading::Error>
102 where P: AsRef<::std::ffi::OsStr> {
103 let library = ::libloading::Library::new(path)?;
104 #from_library
105 }
106
107 pub unsafe fn from_library<L>(
108 library: L
109 ) -> Result<Self, ::libloading::Error>
110 where L: Into<::libloading::Library> {
111 let __library = library.into();
112 #( #constructor_inits )*
113 Ok(#lib_ident {
114 __library,
115 #( #init_fields ),*
116 })
117 }
118
119 #( #struct_implementation )*
120 }
121 }
122 }
123
124 #[allow(clippy::too_many_arguments)]
125 pub(crate) fn push_func(
126 &mut self,
127 ident: Ident,
128 abi: ClangAbi,
129 is_variadic: bool,
130 is_required: bool,
131 args: Vec<proc_macro2::TokenStream>,
132 args_identifiers: Vec<proc_macro2::TokenStream>,
133 ret: proc_macro2::TokenStream,
134 ret_ty: proc_macro2::TokenStream,
135 attributes: Vec<proc_macro2::TokenStream>,
136 ctx: &BindgenContext,
137 ) {
138 if !is_variadic {
139 assert_eq!(args.len(), args_identifiers.len());
140 }
141
142 let signature = quote! { unsafe extern #abi fn ( #( #args),* ) #ret };
143 let member = if is_required {
144 signature
145 } else {
146 quote! { Result<#signature, ::libloading::Error> }
147 };
148
149 self.struct_members.push(quote! {
150 pub #ident: #member,
151 });
152
153 // N.B: If the signature was required, it won't be wrapped in a Result<...>
154 // and we can simply call it directly.
155 let fn_ = if is_required {
156 quote! { self.#ident }
157 } else {
158 quote! { self.#ident.as_ref().expect("Expected function, got error.") }
159 };
160 let call_body = if ctx.options().wrap_unsafe_ops {
161 quote!(unsafe { (#fn_)(#( #args_identifiers ),*) })
162 } else {
163 quote!((#fn_)(#( #args_identifiers ),*) )
164 };
165
166 // We can't implement variadic functions from C easily, so we allow to
167 // access the function pointer so that the user can call it just fine.
168 if !is_variadic {
169 self.struct_implementation.push(quote! {
170 #(#attributes)*
171 pub unsafe fn #ident ( &self, #( #args ),* ) #ret_ty {
172 #call_body
173 }
174 });
175 }
176
177 // N.B: Unwrap the signature upon construction if it is required to be resolved.
178 let ident_str = codegen::helpers::ast_ty::cstr_expr(ident.to_string());
179 let library_get = if ctx.options().wrap_unsafe_ops {
180 quote!(unsafe { __library.get(#ident_str) })
181 } else {
182 quote!(__library.get(#ident_str))
183 };
184
185 self.constructor_inits.push(if is_required {
186 quote! {
187 let #ident = #library_get.map(|sym| *sym)?;
188 }
189 } else {
190 quote! {
191 let #ident = #library_get.map(|sym| *sym);
192 }
193 });
194
195 self.init_fields.push(quote! {
196 #ident
197 });
198 }
199
200 pub fn push_var(
201 &mut self,
202 ident: Ident,
203 ty: TokenStream,
204 is_required: bool,
205 ) {
206 let member = if is_required {
207 quote! { *mut #ty }
208 } else {
209 quote! { Result<*mut #ty, ::libloading::Error> }
210 };
211
212 self.struct_members.push(quote! {
213 pub #ident: #member,
214 });
215
216 let deref = if is_required {
217 quote! { self.#ident }
218 } else {
219 quote! { *self.#ident.as_ref().expect("Expected variable, got error.") }
220 };
221 self.struct_implementation.push(quote! {
222 pub unsafe fn #ident (&self) -> *mut #ty {
223 #deref
224 }
225 });
226
227 let ident_str = codegen::helpers::ast_ty::cstr_expr(ident.to_string());
228 self.constructor_inits.push(if is_required {
229 quote! {
230 let #ident = __library.get::<*mut #ty>(#ident_str).map(|sym| *sym)?;
231 }
232 } else {
233 quote! {
234 let #ident = __library.get::<*mut #ty>(#ident_str).map(|sym| *sym);
235 }
236 });
237
238 self.init_fields.push(quote! {
239 #ident
240 });
241 }
242}
243