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