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}
255