diff --git a/src/codegen/dyngen.rs b/src/codegen/dyngen.rs new file mode 100644 index 0000000000..a3173d27eb --- /dev/null +++ b/src/codegen/dyngen.rs @@ -0,0 +1,306 @@ +use super::ToRustTyOrOpaque; +use crate::codegen::utils; +use crate::ir::context::{BindgenContext, ItemId}; +use crate::ir::function::{Abi, Function, FunctionKind}; +use crate::ir::item::{Item, ItemCanonicalName}; +use crate::ir::item_kind::ItemKind; +use crate::ir::ty::TypeKind; +use crate::HashSet; +use proc_macro2::Ident; + +/// This trait is similar to the CodeGenerator trait in src/codegen/mod.rs, but is instead focused +/// on the generation of dynamic bindings rather than static. +pub trait DynamicBindingGenerator { + /// Extra information from the caller. + type Extra; + + /// Generate dynamic bindings for a particular item type. + fn dyngen<'a>( + &self, + ctx: &BindgenContext, + result: &mut DynamicBindingCodegenResult, + extra: &Self::Extra, + ); +} + +/// Used to build the output tokens for dynamic bindings. +pub struct DynamicBindingCodegenResult { + /// Tracks the tokens that will appears inside the library struct -- e.g.: + /// ``` + /// struct Lib { + /// __library: ::libloading::Library, + /// x: Result, // <- tracks these + /// ... + /// } + /// ``` + struct_members: Vec, + + /// Tracks the tokens that will appear inside the library struct's implementation, e.g.: + /// + /// ```no_run + /// impl Lib { + /// ... + /// pub unsafe fn foo(&self, ...) { // <- tracks these + /// ... + /// } + /// } + /// ``` + struct_implementation: Vec, + + /// Tracks the tokens that will appear inside the struct used for checking if a symbol is + /// usable, e.g.: + /// ```no_run + /// pub fn f(&self) -> Result<(), &'a ::libloading::Error> { // <- tracks these + /// self.__library.f.as_ref().map(|_| ()) + /// } + /// ``` + runtime_checks: Vec, + + /// Tracks the initialization of the fields inside the `::new` constructor of the library + /// struct, e.g.: + /// ```no_run + /// 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.: + /// ```no_run + /// 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, + + /// Keeps track of the items that we have seen. + items_seen: HashSet, +} + +impl DynamicBindingCodegenResult { + pub fn new() -> Self { + DynamicBindingCodegenResult { + struct_members: vec![], + struct_implementation: vec![], + runtime_checks: vec![], + constructor_inits: vec![], + init_fields: vec![], + items_seen: Default::default(), + } + } + + pub fn seen>(&self, item: Id) -> bool { + self.items_seen.contains(&item.into()) + } + + pub fn set_seen>(&mut self, item: Id) { + self.items_seen.insert(item.into()); + } + + pub fn get_tokens( + &self, + lib_ident: Ident, + check_struct_ident: Ident, + ) -> 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 runtime_checks = &self.runtime_checks; + quote! { + extern crate libloading; + + 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)?; + #( #constructor_inits )* + Ok( + #lib_ident { + __library: __library, + #( #init_fields ),* + } + ) + } + + pub fn can_call(&self) -> #check_struct_ident { + #check_struct_ident { __library: self } + } + + #( #struct_implementation )* + } + + pub struct #check_struct_ident<'a> { + __library: &'a #lib_ident, + } + + impl<'a> #check_struct_ident<'a> { + #( #runtime_checks )* + } + } + } + + pub fn add_function( + &mut self, + ident: Ident, + abi: Abi, + args: Vec, + args_identifiers: Vec, + ret: proc_macro2::TokenStream, + ret_ty: proc_macro2::TokenStream, + ) { + self.struct_members.push(quote!{ + #ident: Result, + }); + + self.struct_implementation.push(quote! { + pub unsafe fn #ident ( &self, #( #args ),* ) -> #ret_ty { + let sym = self.#ident.as_ref().expect("Expected function, got error."); + (sym)(#( #args_identifiers ),*) + } + }); + + self.runtime_checks.push(quote! { + pub fn #ident (&self) -> Result<(), &'a::libloading::Error> { + self.__library.#ident.as_ref().map(|_| ()) + } + }); + + let ident_str = ident.to_string(); + self.constructor_inits.push(quote! { + let #ident = __library.get(#ident_str.as_bytes()).map(|sym| *sym); + }); + + self.init_fields.push(quote! { + #ident + }); + } +} + +impl DynamicBindingGenerator for Item { + type Extra = (); + + fn dyngen<'a>( + &self, + ctx: &BindgenContext, + result: &mut DynamicBindingCodegenResult, + _extra: &(), + ) { + assert!(self.is_dynamic(ctx)); + if !self.is_enabled_for_codegen(ctx) { + return; + } + + // If this item is blacklisted, or we've already seen it, nothing to do. + if self.is_blacklisted(ctx) || result.seen(self.id()) { + debug!( + "::dyngen: Ignoring hidden or seen: \ + self = {:?}", + self + ); + return; + } + + debug!( + "::dyngen: self = {:?}", + self + ); + + if !ctx.codegen_items().contains(&self.id()) { + warn!("Found non-whitelisted item in dynamic binding generation: {:?}", self); + } + + result.set_seen(self.id()); + + match *self.kind() { + ItemKind::Function(ref fun) => { + assert!(fun.kind() == FunctionKind::Function); + fun.dyngen(ctx, result, self); + } + _ => panic!( + "Unexpected item type when doing dynamic bindings generation." + ), + } + } +} + +impl DynamicBindingGenerator for Function { + type Extra = Item; + + fn dyngen<'a>( + &self, + ctx: &BindgenContext, + result: &mut DynamicBindingCodegenResult, + item: &Item, + ) { + let signature_item = ctx.resolve_item(self.signature()); + let signature = signature_item.kind().expect_type().canonical_type(ctx); + let signature = match *signature.kind() { + TypeKind::Function(ref sig) => sig, + _ => panic!("Signature kind is not a Function: {:?}", signature), + }; + + let canonical_name = item.canonical_name(ctx); + let abi = match signature.abi() { + Abi::ThisCall if !ctx.options().rust_features().thiscall_abi => { + warn!("Skipping function with thiscall ABI that isn't supported by the configured Rust target"); + return; + } + Abi::Win64 if signature.is_variadic() => { + warn!("Skipping variadic function with Win64 ABI that isn't supported"); + return; + } + Abi::Unknown(unknown_abi) => { + panic!( + "Invalid or unknown abi {:?} for function {:?} ({:?})", + unknown_abi, canonical_name, self + ); + } + abi => abi, + }; + + let args = utils::fnsig_arguments(ctx, signature); + let args_identifiers = + utils::fnsig_argument_identifiers(ctx, signature); + let ret = utils::fnsig_return_ty(ctx, signature); + + let ident = ctx.rust_ident(&canonical_name); + + let return_item = ctx.resolve_item(signature.return_type()); + let ret_ty = match *return_item.kind().expect_type().kind() { + TypeKind::Void => quote! {()}, + _ => return_item.to_rust_ty_or_opaque(ctx, &()), + }; + + result.add_function(ident, abi, args, args_identifiers, ret, ret_ty); + } +} diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index c68eb6da95..a1d71a6ce1 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -1,3 +1,4 @@ +mod dyngen; mod error; mod helpers; mod impl_debug; @@ -10,6 +11,7 @@ pub(crate) mod bitfield_unit; #[cfg(all(test, target_endian = "little"))] mod bitfield_unit_tests; +use self::dyngen::{DynamicBindingCodegenResult, DynamicBindingGenerator}; use self::helpers::attributes; use self::struct_layout::StructLayoutTracker; @@ -435,6 +437,15 @@ impl CodeGenerator for Item { return; } + if self.is_dynamic(ctx) { + debug!( + "Item is to be dynamically generated; ignoring it for now. \ + self = {:?}", + self + ); + return; + } + if self.is_blacklisted(ctx) || result.seen(self.id()) { debug!( "::codegen: Ignoring hidden or seen: \ @@ -3948,11 +3959,45 @@ pub(crate) fn codegen( &(), ); + // If the set of items to generate dynamic bindings for is nonempty... + if !context.dyngen_items().is_empty() { + let _t = context.timer("dyngen"); + debug!("dyngen: {:?}", context.options()); + + let mut dynamic_bindings_result = + DynamicBindingCodegenResult::new(); + + // Run dynamic binding generation for each of the required items. + for item in context.dyngen_items() { + context.resolve_item(*item).dyngen( + context, + &mut dynamic_bindings_result, + &(), + ); + } + + let lib_ident = context.rust_ident( + context.options().dynamic_library_name.as_ref().unwrap(), + ); + let check_struct_ident = context.rust_ident( + [ + "Check", + context.options().dynamic_library_name.as_ref().unwrap(), + ] + .join(""), + ); + + result.push( + dynamic_bindings_result + .get_tokens(lib_ident, check_struct_ident), + ); + } + result.items }) } -mod utils { +pub mod utils { use super::{error, ToRustTyOrOpaque}; use crate::ir::context::BindgenContext; use crate::ir::function::{Abi, FunctionSig}; @@ -4356,6 +4401,35 @@ mod utils { args } + pub fn fnsig_argument_identifiers( + ctx: &BindgenContext, + sig: &FunctionSig, + ) -> Vec { + let mut unnamed_arguments = 0; + let args = sig + .argument_types() + .iter() + .map(|&(ref name, _ty)| { + let arg_name = match *name { + Some(ref name) => ctx.rust_mangle(name).into_owned(), + None => { + unnamed_arguments += 1; + format!("arg{}", unnamed_arguments) + } + }; + + assert!(!arg_name.is_empty()); + let arg_name = ctx.rust_ident(arg_name); + + quote! { + #arg_name + } + }) + .collect::>(); + + args + } + pub fn fnsig_block( ctx: &BindgenContext, sig: &FunctionSig, diff --git a/src/ir/context.rs b/src/ir/context.rs index 38d73b7e64..2d853c217b 100644 --- a/src/ir/context.rs +++ b/src/ir/context.rs @@ -11,7 +11,7 @@ use super::derive::{ CanDerive, CanDeriveCopy, CanDeriveDebug, CanDeriveDefault, CanDeriveEq, CanDeriveHash, CanDeriveOrd, CanDerivePartialEq, CanDerivePartialOrd, }; -use super::function::Function; +use super::function::{Function, FunctionKind}; use super::int::IntKind; use super::item::{IsOpaque, Item, ItemAncestors, ItemSet}; use super::item_kind::ItemKind; @@ -386,6 +386,11 @@ pub struct BindgenContext { /// It's computed right after computing the whitelisted items. codegen_items: Option, + /// The set of `ItemId`s that we will perform dynamic binding generation for. + /// + /// It's computed following the codegen/whitelisted items. + dyngen_items: Option, + /// Map from an item's id to the set of template parameter items that it /// uses. See `ir::named` for more details. Always `Some` during the codegen /// phase. @@ -619,6 +624,7 @@ If you encounter an error missing from this list, please file an issue or a PR!" generated_bindgen_complex: Cell::new(false), whitelisted: None, codegen_items: None, + dyngen_items: None, used_template_parameters: None, need_bitfield_allocation: Default::default(), cannot_derive_debug: None, @@ -1189,6 +1195,9 @@ If you encounter an error missing from this list, please file an issue or a PR!" // graph, and their completion means that the IR graph is now frozen. self.compute_whitelisted_and_codegen_items(); + // Compute the items for dynamic generation. + self.compute_dynamic_generation_items(); + // Make sure to do this after processing replacements, since that messes // with the parentage and module children, and we want to assert that it // messes with them correctly. @@ -2262,6 +2271,13 @@ If you encounter an error missing from this list, please file an issue or a PR!" self.codegen_items.as_ref().unwrap() } + /// Get a reference to the set of dynamic bindings we should generate. + pub fn dyngen_items(&self) -> &ItemSet { + assert!(self.in_codegen_phase()); + assert!(self.current_module == self.root_module); + self.dyngen_items.as_ref().unwrap() + } + /// Compute the whitelisted items set and populate `self.whitelisted`. fn compute_whitelisted_and_codegen_items(&mut self) { assert!(self.in_codegen_phase()); @@ -2412,6 +2428,70 @@ If you encounter an error missing from this list, please file an issue or a PR!" } } + /// Compute the items for dynamic generation. + fn compute_dynamic_generation_items(&mut self) { + assert!(self.in_codegen_phase()); + assert!(self.current_module == self.root_module); + let _t = self.timer("compute_dynamic_generation_items"); + + let dyngen_items = self + .items() + // Only consider items that are enabled for codegen. + .filter(|&(_, item)| item.is_enabled_for_codegen(self)) + .filter(|&(_, item)| { + // If the user has not chosen to do dynamic loading, then we have nothing to + // do. + if !self.options().dynamic_loading { + return false; + } + + let name = item.path_for_whitelisting(self)[1..].join("::"); + + // If there is a whitelist and this function is not on it, don't add it. + if !self.options().whitelisted_functions.is_empty() && + !self.options().whitelisted_functions.matches(&name) + { + return false; + } + + // If there is a blacklist and this function is on it, don't add it. + if !self.options().blacklisted_functions.is_empty() && + self.options.blacklisted_functions.matches(&name) + { + return false; + } + + // We don't want to include the root module in the list of items to generate + // dynamic bindings for. + if item.id() == self.root_module { + return false; + } + + // We only want to generate bindings for the stuff that we have at the toplevel. + if item.parent_id() != self.root_module { + return false; + } + + // If we get to here: + // - The user wants to generate dynamic bindings. + // - The item being considered is at the toplevel (but is not the root itself). + // - The item is permitted by the {white,black}list. + + // Finally, only generate dynamic bindings for functions (for now! this could + // change in the future to support variables). + match item.kind() { + ItemKind::Function(ref f) => { + f.kind() == FunctionKind::Function + } + _ => false, + } + }) + .map(|(id, _)| id) + .collect::(); + + self.dyngen_items = Some(dyngen_items); + } + /// Convenient method for getting the prefix to use for most traits in /// codegen depending on the `use_core` option. pub fn trait_prefix(&self) -> Ident { diff --git a/src/ir/item.rs b/src/ir/item.rs index 0f94f02091..620ed754a8 100644 --- a/src/ir/item.rs +++ b/src/ir/item.rs @@ -624,6 +624,15 @@ impl Item { &self.annotations } + /// Whether this item should have dynamic bindings generated, rather than normal bindings. + pub fn is_dynamic(&self, ctx: &BindgenContext) -> bool { + debug_assert!( + ctx.in_codegen_phase(), + "You're not supposed to call this yet" + ); + ctx.dyngen_items().contains(&self.id) + } + /// Whether this item should be blacklisted. /// /// This may be due to either annotations or to other kind of configuration. diff --git a/src/lib.rs b/src/lib.rs index 5a86364e6d..483506a6d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -503,6 +503,12 @@ impl Builder { output_vector.push(path.into()); } + if self.options.dynamic_loading { + let libname = self.options.dynamic_library_name.as_ref().unwrap(); + output_vector.push("--dynamic-loading".into()); + output_vector.push(libname.clone()); + } + // Add clang arguments output_vector.push("--".into()); @@ -1431,6 +1437,16 @@ impl Builder { self.options.wasm_import_module_name = Some(import_name.into()); self } + + /// Specify the dynamic library name if we are generating bindings for a shared library. + pub fn dynamic_library_name>( + mut self, + dynamic_library_name: T, + ) -> Self { + self.options.dynamic_loading = true; + self.options.dynamic_library_name = Some(dynamic_library_name.into()); + self + } } /// Configuration options for generated bindings. @@ -1699,6 +1715,12 @@ struct BindgenOptions { /// Wasm import module name. wasm_import_module_name: Option, + + /// Whether or not we are generating bindings for a shared library. + dynamic_loading: bool, + + /// The name of the dynamic library if we are generating bindings for a shared library. + dynamic_library_name: Option, } /// TODO(emilio): This is sort of a lie (see the error message that results from @@ -1827,6 +1849,8 @@ impl Default for BindgenOptions { no_hash_types: Default::default(), array_pointers_in_arguments: false, wasm_import_module_name: None, + dynamic_loading: false, + dynamic_library_name: None, } } } diff --git a/src/options.rs b/src/options.rs index 0add7b4cbe..c543724824 100644 --- a/src/options.rs +++ b/src/options.rs @@ -450,7 +450,11 @@ where .long("wasm-import-module-name") .value_name("name") .takes_value(true) - .help("The name to be used in a #[link(wasm_import_module = ...)] statement") + .help("The name to be used in a #[link(wasm_import_module = ...)] statement"), + Arg::with_name("dynamic-loading") + .long("dynamic-loading") + .takes_value(true) + .help("Use dynamic loading mode with the given library name."), ]) // .args() .get_matches_from(args); @@ -837,6 +841,10 @@ where } } + if let Some(dynamic_library_name) = matches.value_of("dynamic-loading") { + builder = builder.dynamic_library_name(dynamic_library_name); + } + let verbose = matches.is_present("verbose"); Ok((builder, output, verbose)) diff --git a/tests/expectations/Cargo.toml b/tests/expectations/Cargo.toml index 6acb1d406d..c78cc09a0b 100644 --- a/tests/expectations/Cargo.toml +++ b/tests/expectations/Cargo.toml @@ -10,4 +10,5 @@ authors = [ [dependencies] objc = "0.2" -block = "0.1" \ No newline at end of file +block = "0.1" +libloading = "0.6.2" diff --git a/tests/expectations/tests/dynamic_loading_simple.rs b/tests/expectations/tests/dynamic_loading_simple.rs new file mode 100644 index 0000000000..b195a05d84 --- /dev/null +++ b/tests/expectations/tests/dynamic_loading_simple.rs @@ -0,0 +1,81 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +extern crate libloading; +pub struct TestLib { + __library: ::libloading::Library, + foo: Result< + unsafe extern "C" fn( + x: ::std::os::raw::c_int, + y: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, + ::libloading::Error, + >, + bar: Result< + unsafe extern "C" fn( + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, + ::libloading::Error, + >, + baz: Result< + unsafe extern "C" fn() -> ::std::os::raw::c_int, + ::libloading::Error, + >, +} +impl TestLib { + pub unsafe fn new

(path: P) -> Result + where + P: AsRef<::std::ffi::OsStr>, + { + let __library = ::libloading::Library::new(path)?; + let foo = __library.get("foo".as_bytes()).map(|sym| *sym); + let bar = __library.get("bar".as_bytes()).map(|sym| *sym); + let baz = __library.get("baz".as_bytes()).map(|sym| *sym); + Ok(TestLib { + __library: __library, + foo, + bar, + baz, + }) + } + pub fn can_call(&self) -> CheckTestLib { + CheckTestLib { __library: self } + } + pub unsafe fn foo( + &self, + x: ::std::os::raw::c_int, + y: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int { + let sym = self.foo.as_ref().expect("Expected function, got error."); + (sym)(x, y) + } + pub unsafe fn bar( + &self, + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int { + let sym = self.bar.as_ref().expect("Expected function, got error."); + (sym)(x) + } + pub unsafe fn baz(&self) -> ::std::os::raw::c_int { + let sym = self.baz.as_ref().expect("Expected function, got error."); + (sym)() + } +} +pub struct CheckTestLib<'a> { + __library: &'a TestLib, +} +impl<'a> CheckTestLib<'a> { + pub fn foo(&self) -> Result<(), &'a ::libloading::Error> { + self.__library.foo.as_ref().map(|_| ()) + } + pub fn bar(&self) -> Result<(), &'a ::libloading::Error> { + self.__library.bar.as_ref().map(|_| ()) + } + pub fn baz(&self) -> Result<(), &'a ::libloading::Error> { + self.__library.baz.as_ref().map(|_| ()) + } +} diff --git a/tests/expectations/tests/dynamic_loading_with_blacklist.rs b/tests/expectations/tests/dynamic_loading_with_blacklist.rs new file mode 100644 index 0000000000..259054b266 --- /dev/null +++ b/tests/expectations/tests/dynamic_loading_with_blacklist.rs @@ -0,0 +1,117 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct X { + pub _x: ::std::os::raw::c_int, +} +#[test] +fn bindgen_test_layout_X() { + assert_eq!( + ::std::mem::size_of::(), + 4usize, + concat!("Size of: ", stringify!(X)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(X)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::()))._x as *const _ as usize }, + 0usize, + concat!("Offset of field: ", stringify!(X), "::", stringify!(_x)) + ); +} +extern "C" { + #[link_name = "\u{1}_ZN1X13some_functionEv"] + pub fn X_some_function(this: *mut X); +} +extern "C" { + #[link_name = "\u{1}_ZN1X19some_other_functionEv"] + pub fn X_some_other_function(this: *mut X); +} +extern "C" { + #[link_name = "\u{1}_ZN1XC1Ei"] + pub fn X_X(this: *mut X, x: ::std::os::raw::c_int); +} +impl X { + #[inline] + pub unsafe fn some_function(&mut self) { + X_some_function(self) + } + #[inline] + pub unsafe fn some_other_function(&mut self) { + X_some_other_function(self) + } + #[inline] + pub unsafe fn new(x: ::std::os::raw::c_int) -> Self { + let mut __bindgen_tmp = ::std::mem::MaybeUninit::uninit(); + X_X(__bindgen_tmp.as_mut_ptr(), x); + __bindgen_tmp.assume_init() + } +} +extern crate libloading; +pub struct TestLib { + __library: ::libloading::Library, + foo: Result< + unsafe extern "C" fn( + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, + ::libloading::Error, + >, + bar: Result< + unsafe extern "C" fn( + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, + ::libloading::Error, + >, +} +impl TestLib { + pub unsafe fn new

(path: P) -> Result + where + P: AsRef<::std::ffi::OsStr>, + { + let __library = ::libloading::Library::new(path)?; + let foo = __library.get("foo".as_bytes()).map(|sym| *sym); + let bar = __library.get("bar".as_bytes()).map(|sym| *sym); + Ok(TestLib { + __library: __library, + foo, + bar, + }) + } + pub fn can_call(&self) -> CheckTestLib { + CheckTestLib { __library: self } + } + pub unsafe fn foo( + &self, + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int { + let sym = self.foo.as_ref().expect("Expected function, got error."); + (sym)(x) + } + pub unsafe fn bar( + &self, + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int { + let sym = self.bar.as_ref().expect("Expected function, got error."); + (sym)(x) + } +} +pub struct CheckTestLib<'a> { + __library: &'a TestLib, +} +impl<'a> CheckTestLib<'a> { + pub fn foo(&self) -> Result<(), &'a ::libloading::Error> { + self.__library.foo.as_ref().map(|_| ()) + } + pub fn bar(&self) -> Result<(), &'a ::libloading::Error> { + self.__library.bar.as_ref().map(|_| ()) + } +} diff --git a/tests/expectations/tests/dynamic_loading_with_class.rs b/tests/expectations/tests/dynamic_loading_with_class.rs new file mode 100644 index 0000000000..636f01bb2a --- /dev/null +++ b/tests/expectations/tests/dynamic_loading_with_class.rs @@ -0,0 +1,109 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct A { + pub _x: ::std::os::raw::c_int, +} +#[test] +fn bindgen_test_layout_A() { + assert_eq!( + ::std::mem::size_of::(), + 4usize, + concat!("Size of: ", stringify!(A)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(A)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::()))._x as *const _ as usize }, + 0usize, + concat!("Offset of field: ", stringify!(A), "::", stringify!(_x)) + ); +} +extern "C" { + #[link_name = "\u{1}_ZN1A13some_functionEv"] + pub fn A_some_function(this: *mut A); +} +extern "C" { + #[link_name = "\u{1}_ZN1A19some_other_functionEv"] + pub fn A_some_other_function(this: *mut A); +} +extern "C" { + #[link_name = "\u{1}_ZN1AC1Ei"] + pub fn A_A(this: *mut A, x: ::std::os::raw::c_int); +} +impl A { + #[inline] + pub unsafe fn some_function(&mut self) { + A_some_function(self) + } + #[inline] + pub unsafe fn some_other_function(&mut self) { + A_some_other_function(self) + } + #[inline] + pub unsafe fn new(x: ::std::os::raw::c_int) -> Self { + let mut __bindgen_tmp = ::std::mem::MaybeUninit::uninit(); + A_A(__bindgen_tmp.as_mut_ptr(), x); + __bindgen_tmp.assume_init() + } +} +extern crate libloading; +pub struct TestLib { + __library: ::libloading::Library, + foo: Result< + unsafe extern "C" fn( + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, + ::libloading::Error, + >, + bar: Result, +} +impl TestLib { + pub unsafe fn new

(path: P) -> Result + where + P: AsRef<::std::ffi::OsStr>, + { + let __library = ::libloading::Library::new(path)?; + let foo = __library.get("foo".as_bytes()).map(|sym| *sym); + let bar = __library.get("bar".as_bytes()).map(|sym| *sym); + Ok(TestLib { + __library: __library, + foo, + bar, + }) + } + pub fn can_call(&self) -> CheckTestLib { + CheckTestLib { __library: self } + } + pub unsafe fn foo( + &self, + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int { + let sym = self.foo.as_ref().expect("Expected function, got error."); + (sym)(x) + } + pub unsafe fn bar(&self) -> () { + let sym = self.bar.as_ref().expect("Expected function, got error."); + (sym)() + } +} +pub struct CheckTestLib<'a> { + __library: &'a TestLib, +} +impl<'a> CheckTestLib<'a> { + pub fn foo(&self) -> Result<(), &'a ::libloading::Error> { + self.__library.foo.as_ref().map(|_| ()) + } + pub fn bar(&self) -> Result<(), &'a ::libloading::Error> { + self.__library.bar.as_ref().map(|_| ()) + } +} diff --git a/tests/expectations/tests/dynamic_loading_with_whitelist.rs b/tests/expectations/tests/dynamic_loading_with_whitelist.rs new file mode 100644 index 0000000000..a1f8a2a497 --- /dev/null +++ b/tests/expectations/tests/dynamic_loading_with_whitelist.rs @@ -0,0 +1,66 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +extern crate libloading; +pub struct TestLib { + __library: ::libloading::Library, + foo: Result< + unsafe extern "C" fn( + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, + ::libloading::Error, + >, + baz: Result< + unsafe extern "C" fn( + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, + ::libloading::Error, + >, +} +impl TestLib { + pub unsafe fn new

(path: P) -> Result + where + P: AsRef<::std::ffi::OsStr>, + { + let __library = ::libloading::Library::new(path)?; + let foo = __library.get("foo".as_bytes()).map(|sym| *sym); + let baz = __library.get("baz".as_bytes()).map(|sym| *sym); + Ok(TestLib { + __library: __library, + foo, + baz, + }) + } + pub fn can_call(&self) -> CheckTestLib { + CheckTestLib { __library: self } + } + pub unsafe fn foo( + &self, + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int { + let sym = self.foo.as_ref().expect("Expected function, got error."); + (sym)(x) + } + pub unsafe fn baz( + &self, + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int { + let sym = self.baz.as_ref().expect("Expected function, got error."); + (sym)(x) + } +} +pub struct CheckTestLib<'a> { + __library: &'a TestLib, +} +impl<'a> CheckTestLib<'a> { + pub fn foo(&self) -> Result<(), &'a ::libloading::Error> { + self.__library.foo.as_ref().map(|_| ()) + } + pub fn baz(&self) -> Result<(), &'a ::libloading::Error> { + self.__library.baz.as_ref().map(|_| ()) + } +} diff --git a/tests/headers/dynamic_loading_simple.h b/tests/headers/dynamic_loading_simple.h new file mode 100644 index 0000000000..f418851beb --- /dev/null +++ b/tests/headers/dynamic_loading_simple.h @@ -0,0 +1,5 @@ +// bindgen-flags: --dynamic-loading TestLib + +int foo(int x, int y); +int bar(void *x); +int baz(); diff --git a/tests/headers/dynamic_loading_with_blacklist.hpp b/tests/headers/dynamic_loading_with_blacklist.hpp new file mode 100644 index 0000000000..2988ba691e --- /dev/null +++ b/tests/headers/dynamic_loading_with_blacklist.hpp @@ -0,0 +1,15 @@ +// bindgen-flags: --dynamic-loading TestLib --blacklist-function baz + +class X { + int _x; + + public: + X(int x); + + void some_function(); + void some_other_function(); +}; + +int foo(void *x); +int bar(void *x); +int baz(void *x); diff --git a/tests/headers/dynamic_loading_with_class.hpp b/tests/headers/dynamic_loading_with_class.hpp new file mode 100644 index 0000000000..632db4d065 --- /dev/null +++ b/tests/headers/dynamic_loading_with_class.hpp @@ -0,0 +1,15 @@ +// bindgen-flags: --dynamic-loading TestLib + +int foo(void *x); + +class A { + int _x; + + public: + A(int x); + + void some_function(); + void some_other_function(); +}; + +void bar(); diff --git a/tests/headers/dynamic_loading_with_whitelist.hpp b/tests/headers/dynamic_loading_with_whitelist.hpp new file mode 100644 index 0000000000..922b546124 --- /dev/null +++ b/tests/headers/dynamic_loading_with_whitelist.hpp @@ -0,0 +1,15 @@ +// bindgen-flags: --dynamic-loading TestLib --whitelist-function baz --whitelist-function foo + +class X { + int _x; + + public: + X(int x); + + void some_function(); + void some_other_function(); +}; + +int foo(void *x); +int bar(void *x); +int baz(void *x);