use crate::codegen; use crate::ir::context::BindgenContext; use crate::ir::function::ClangAbi; use proc_macro2::{Ident, TokenStream}; /// Used to build the output tokens for dynamic bindings. #[derive(Default)] pub(crate) struct DynamicItems { /// Tracks the tokens that will appears inside the library struct -- e.g.: /// ```ignore /// struct Lib { /// __library: ::libloading::Library, /// pub x: Result, // <- tracks these /// ... /// } /// ``` struct_members: Vec, /// Tracks the tokens that will appear inside the library struct's implementation, e.g.: /// /// ```ignore /// impl Lib { /// ... /// pub unsafe fn foo(&self, ...) { // <- tracks these /// ... /// } /// } /// ``` struct_implementation: Vec, /// Tracks the initialization of the fields inside the `::new` constructor of the library /// struct, e.g.: /// ```ignore /// impl Lib { /// /// pub unsafe fn new

(path: P) -> Result /// where /// P: AsRef<::std::ffi::OsStr>, /// { /// ... /// let foo = __library.get(...) ...; // <- tracks these /// ... /// } /// /// ... /// } /// ``` constructor_inits: Vec, /// Tracks the information that is passed to the library struct at the end of the `::new` /// constructor, e.g.: /// ```ignore /// impl LibFoo { /// pub unsafe fn new

(path: P) -> Result /// where /// P: AsRef<::std::ffi::OsStr>, /// { /// ... /// Ok(LibFoo { /// __library: __library, /// foo, /// bar, // <- tracks these /// ... /// }) /// } /// } /// ``` init_fields: Vec, } impl DynamicItems { pub(crate) fn new() -> Self { Self::default() } pub(crate) fn get_tokens( &self, lib_ident: Ident, ctx: &BindgenContext, ) -> proc_macro2::TokenStream { let struct_members = &self.struct_members; let constructor_inits = &self.constructor_inits; let init_fields = &self.init_fields; let struct_implementation = &self.struct_implementation; let from_library = if ctx.options().wrap_unsafe_ops { quote!(unsafe { Self::from_library(library) }) } else { quote!(Self::from_library(library)) }; quote! { pub struct #lib_ident { __library: ::libloading::Library, #(#struct_members)* } impl #lib_ident { pub unsafe fn new

( path: P ) -> Result where P: AsRef<::std::ffi::OsStr> { let library = ::libloading::Library::new(path)?; #from_library } pub unsafe fn from_library( library: L ) -> Result where L: Into<::libloading::Library> { let __library = library.into(); #( #constructor_inits )* Ok(#lib_ident { __library, #( #init_fields ),* }) } #( #struct_implementation )* } } } #[allow(clippy::too_many_arguments)] pub(crate) fn push_func( &mut self, ident: Ident, abi: ClangAbi, is_variadic: bool, is_required: bool, args: Vec, args_identifiers: Vec, ret: proc_macro2::TokenStream, ret_ty: proc_macro2::TokenStream, attributes: Vec, ctx: &BindgenContext, ) { if !is_variadic { assert_eq!(args.len(), args_identifiers.len()); } let signature = quote! { unsafe extern #abi fn ( #( #args),* ) #ret }; let member = if is_required { signature } else { quote! { Result<#signature, ::libloading::Error> } }; self.struct_members.push(quote! { pub #ident: #member, }); // N.B: If the signature was required, it won't be wrapped in a Result<...> // and we can simply call it directly. let fn_ = if is_required { quote! { self.#ident } } else { quote! { self.#ident.as_ref().expect("Expected function, got error.") } }; let call_body = if ctx.options().wrap_unsafe_ops { quote!(unsafe { (#fn_)(#( #args_identifiers ),*) }) } else { quote!((#fn_)(#( #args_identifiers ),*) ) }; // We can't implement variadic functions from C easily, so we allow to // access the function pointer so that the user can call it just fine. if !is_variadic { self.struct_implementation.push(quote! { #(#attributes)* pub unsafe fn #ident ( &self, #( #args ),* ) #ret_ty { #call_body } }); } // N.B: Unwrap the signature upon construction if it is required to be resolved. let ident_str = codegen::helpers::ast_ty::cstr_expr(ident.to_string()); let library_get = if ctx.options().wrap_unsafe_ops { quote!(unsafe { __library.get(#ident_str) }) } else { quote!(__library.get(#ident_str)) }; self.constructor_inits.push(if is_required { quote! { let #ident = #library_get.map(|sym| *sym)?; } } else { quote! { let #ident = #library_get.map(|sym| *sym); } }); self.init_fields.push(quote! { #ident }); } pub fn push_var( &mut self, ident: Ident, ty: TokenStream, is_required: bool, ) { let member = if is_required { quote! { *mut #ty } } else { quote! { Result<*mut #ty, ::libloading::Error> } }; self.struct_members.push(quote! { pub #ident: #member, }); let deref = if is_required { quote! { self.#ident } } else { quote! { *self.#ident.as_ref().expect("Expected variable, got error.") } }; self.struct_implementation.push(quote! { pub unsafe fn #ident (&self) -> *mut #ty { #deref } }); let ident_str = codegen::helpers::ast_ty::cstr_expr(ident.to_string()); self.constructor_inits.push(if is_required { quote! { let #ident = __library.get::<*mut #ty>(#ident_str).map(|sym| *sym)?; } } else { quote! { let #ident = __library.get::<*mut #ty>(#ident_str).map(|sym| *sym); } }); self.init_fields.push(quote! { #ident }); } }