bindgen/codegen/
dyngen.rs1use crate::codegen;
2use crate::ir::context::BindgenContext;
3use crate::ir::function::ClangAbi;
4use proc_macro2::{Ident, TokenStream};
5
6#[derive(Default)]
8pub(crate) struct DynamicItems {
9 struct_members: Vec<TokenStream>,
18
19 struct_implementation: Vec<TokenStream>,
30
31 constructor_inits: Vec<TokenStream>,
49
50 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 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 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 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}