From a086e560ad51e2d6abb9dabf0c6e4a270acbeb47 Mon Sep 17 00:00:00 2001 From: Joe Ellis Date: Mon, 27 Jul 2020 10:26:38 +0100 Subject: [PATCH 1/2] Add dynamic loading support Co-authored-by: Michael-F-Bryan --- src/codegen/dyngen.rs | 181 ++++++++++++++++++++++++++++++++++++++++++ src/codegen/mod.rs | 80 ++++++++++++++++++- src/lib.rs | 20 +++++ src/options.rs | 10 ++- 4 files changed, 288 insertions(+), 3 deletions(-) create mode 100644 src/codegen/dyngen.rs diff --git a/src/codegen/dyngen.rs b/src/codegen/dyngen.rs new file mode 100644 index 0000000000..aab6cac9ea --- /dev/null +++ b/src/codegen/dyngen.rs @@ -0,0 +1,181 @@ +use crate::ir::function::Abi; +use proc_macro2::Ident; + +/// Used to build the output tokens for dynamic bindings. +pub struct DynamicItems { + /// Tracks the tokens that will appears inside the library struct -- e.g.: + /// ```ignore + /// 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.: + /// + /// ```ignore + /// 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.: + /// ```ignore + /// 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.: + /// ```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 Default for DynamicItems { + fn default() -> Self { + DynamicItems { + struct_members: vec![], + struct_implementation: vec![], + runtime_checks: vec![], + constructor_inits: vec![], + init_fields: vec![], + } + } +} + +impl DynamicItems { + pub fn new() -> Self { + Self::default() + } + + 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, + ) { + assert_eq!(args.len(), args_identifiers.len()); + + 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 + }); + } +} diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index c68eb6da95..344ab0da75 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::DynamicItems; use self::helpers::attributes; use self::struct_layout::StructLayoutTracker; @@ -184,6 +186,7 @@ impl From for Vec<&'static str> { struct CodegenResult<'a> { items: Vec, + dynamic_items: DynamicItems, /// A monotonic counter used to add stable unique id's to stuff that doesn't /// need to be referenced by anything. @@ -234,6 +237,7 @@ impl<'a> CodegenResult<'a> { fn new(codegen_id: &'a Cell) -> Self { CodegenResult { items: vec![], + dynamic_items: DynamicItems::new(), saw_bindgen_union: false, saw_incomplete_array: false, saw_objc: false, @@ -247,6 +251,10 @@ impl<'a> CodegenResult<'a> { } } + fn dynamic_items(&mut self) -> &mut DynamicItems { + &mut self.dynamic_items + } + fn saw_bindgen_union(&mut self) { self.saw_bindgen_union = true; } @@ -3704,7 +3712,29 @@ impl CodeGenerator for Function { pub fn #ident ( #( #args ),* ) #ret; } }; - result.push(tokens); + + // If we're doing dynamic binding generation, add to the dynamic items. + if ctx.options().dynamic_library_name.is_some() && + self.kind() == FunctionKind::Function + { + let args_identifiers = + utils::fnsig_argument_identifiers(ctx, signature); + 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.dynamic_items().add_function( + ident, + abi, + args, + args_identifiers, + ret, + ret_ty, + ); + } else { + result.push(tokens); + } } } @@ -3948,11 +3978,28 @@ pub(crate) fn codegen( &(), ); + if context.options().dynamic_library_name.is_some() { + 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(""), + ); + let dynamic_items_tokens = result + .dynamic_items() + .get_tokens(lib_ident, check_struct_ident); + result.push(dynamic_items_tokens); + } + result.items }) } -mod utils { +pub mod utils { use super::{error, ToRustTyOrOpaque}; use crate::ir::context::BindgenContext; use crate::ir::function::{Abi, FunctionSig}; @@ -4356,6 +4403,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/lib.rs b/src/lib.rs index 5a86364e6d..67ff0b0a4d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -503,6 +503,12 @@ impl Builder { output_vector.push(path.into()); } + if self.options.dynamic_library_name.is_some() { + 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,15 @@ 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_library_name = Some(dynamic_library_name.into()); + self + } } /// Configuration options for generated bindings. @@ -1699,6 +1714,10 @@ struct BindgenOptions { /// Wasm import module name. wasm_import_module_name: Option, + + /// The name of the dynamic library (if we are generating bindings for a shared library). If + /// this is None, no dynamic bindings are created. + dynamic_library_name: Option, } /// TODO(emilio): This is sort of a lie (see the error message that results from @@ -1827,6 +1846,7 @@ impl Default for BindgenOptions { no_hash_types: Default::default(), array_pointers_in_arguments: false, wasm_import_module_name: None, + 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)) From 3594e93cd709bb8451261bb6f131cf8ac05402d4 Mon Sep 17 00:00:00 2001 From: Joe Ellis Date: Tue, 27 Oct 2020 18:17:45 +0000 Subject: [PATCH 2/2] Add tests for dynamic binding generation --- tests/expectations/Cargo.toml | 3 +- .../tests/dynamic_loading_simple.rs | 81 ++++++++++++ .../tests/dynamic_loading_template.rs | 56 +++++++++ .../tests/dynamic_loading_with_blacklist.rs | 117 ++++++++++++++++++ .../tests/dynamic_loading_with_class.rs | 109 ++++++++++++++++ .../tests/dynamic_loading_with_whitelist.rs | 66 ++++++++++ tests/headers/dynamic_loading_simple.h | 5 + tests/headers/dynamic_loading_template.hpp | 10 ++ .../dynamic_loading_with_blacklist.hpp | 15 +++ tests/headers/dynamic_loading_with_class.hpp | 15 +++ .../dynamic_loading_with_whitelist.hpp | 15 +++ 11 files changed, 491 insertions(+), 1 deletion(-) create mode 100644 tests/expectations/tests/dynamic_loading_simple.rs create mode 100644 tests/expectations/tests/dynamic_loading_template.rs create mode 100644 tests/expectations/tests/dynamic_loading_with_blacklist.rs create mode 100644 tests/expectations/tests/dynamic_loading_with_class.rs create mode 100644 tests/expectations/tests/dynamic_loading_with_whitelist.rs create mode 100644 tests/headers/dynamic_loading_simple.h create mode 100644 tests/headers/dynamic_loading_template.hpp create mode 100644 tests/headers/dynamic_loading_with_blacklist.hpp create mode 100644 tests/headers/dynamic_loading_with_class.hpp create mode 100644 tests/headers/dynamic_loading_with_whitelist.hpp 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_template.rs b/tests/expectations/tests/dynamic_loading_template.rs new file mode 100644 index 0000000000..b9fa1698db --- /dev/null +++ b/tests/expectations/tests/dynamic_loading_template.rs @@ -0,0 +1,56 @@ +#![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) -> ::std::os::raw::c_int, + ::libloading::Error, + >, + foo1: Result f32, ::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 foo1 = __library.get("foo1".as_bytes()).map(|sym| *sym); + Ok(TestLib { + __library: __library, + foo, + foo1, + }) + } + pub fn can_call(&self) -> CheckTestLib { + CheckTestLib { __library: self } + } + pub unsafe fn foo( + &self, + x: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int { + let sym = self.foo.as_ref().expect("Expected function, got error."); + (sym)(x) + } + pub unsafe fn foo1(&self, x: f32) -> f32 { + let sym = self.foo1.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 foo1(&self) -> Result<(), &'a ::libloading::Error> { + self.__library.foo1.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_template.hpp b/tests/headers/dynamic_loading_template.hpp new file mode 100644 index 0000000000..27f04c8e29 --- /dev/null +++ b/tests/headers/dynamic_loading_template.hpp @@ -0,0 +1,10 @@ +// bindgen-flags: --dynamic-loading TestLib + +template +T foo(T x); + +template<> +int foo(int x); + +template<> +float foo(float x); 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);