diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index c68eb6da95..905688ded0 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -182,6 +182,7 @@ impl From for Vec<&'static str> { } } +#[derive(Clone)] struct CodegenResult<'a> { items: Vec, @@ -435,6 +436,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: \ @@ -3913,6 +3923,126 @@ impl CodeGenerator for ObjCInterface { } } +trait DynamicBindingGenerator { + /// Extra information from the caller. + type Extra; + + fn dyngen<'a>( + &self, + ctx: &BindgenContext, + def_result: &mut CodegenResult<'a>, + impl_result: &mut CodegenResult<'a>, + extra: &Self::Extra, + ); +} + +impl DynamicBindingGenerator for Item { + type Extra = (); + + fn dyngen<'a>( + &self, + ctx: &BindgenContext, + def_result: &mut CodegenResult<'a>, + impl_result: &mut CodegenResult<'a>, + _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) || + def_result.seen(self.id()) || + impl_result.seen(self.id()) + { + debug!( + "::codegen: 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); + } + + def_result.set_seen(self.id()); + impl_result.set_seen(self.id()); + + match *self.kind() { + ItemKind::Function(ref fun) => { + assert!(fun.kind() == FunctionKind::Function); + fun.dyngen(ctx, def_result, impl_result, self); + } + _ => panic!( + "Unexpected item type when doing dynamic bindings generation." + ), + } + } +} + +impl DynamicBindingGenerator for Function { + type Extra = Item; + + fn dyngen<'a>( + &self, + ctx: &BindgenContext, + def_result: &mut CodegenResult<'a>, + impl_result: &mut CodegenResult<'a>, + 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); + + def_result.push(quote! { + #ident: unsafe extern #abi fn ( #( #args ),* ) #ret, + }); + + impl_result.push(quote! { + pub unsafe extern #abi fn #ident ( &self, #( #args ),* ) #ret { + (self.#ident)(#( #args_identifiers ),*) + } + }); + } +} + pub(crate) fn codegen( context: BindgenContext, ) -> (Vec, BindgenOptions) { @@ -3948,6 +4078,111 @@ 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()); + + // `def_result` tracks the tokens that will appears inside the library struct -- these + // are the definitions of the symbols inside the library struct, e.g.: + // ``` + // struct Lib { + // __library: ::libloading::Library, + // x: unsafe extern ..., // <- tracks these! + // ... + // } + // ``` + // + // `impl_result` tracks the tokens that will appear inside the call implementation, + // e.g.: + // + // ``` + // impl Lib { + // ... + // pub unsafe extern "C" fn foo(&self, ...) { // <- tracks these! + // (self.foo)(...) + // } + // } + // ``` + // + // We clone the CodeGenerator object used for normal code generation, but set its items + // to the empty vector. We do this so that we can keep track of which items we have + // seen, etc. + let mut def_result = result.clone(); + def_result.items = vec![]; + let mut impl_result = result.clone(); + impl_result.items = vec![]; + + // Run dynamic binding generation for each of the required items. + for item in context.dyngen_items() { + context.resolve_item(*item).dyngen( + context, + &mut def_result, + &mut impl_result, + &(), + ); + } + + let lib_ident = context.rust_ident( + context.options().dynamic_library_name.as_ref().unwrap(), + ); + + let struct_items = def_result.items; + result.push(quote! { + pub struct #lib_ident { + __library: ::libloading::Library, + #(#struct_items)* + } + }); + + let build_locals = context + .dyngen_items() + .iter() + .map(|&item| { + let canonical_name = item.canonical_name(context); + let ident = context.rust_ident(&canonical_name); + + quote! { + let #ident = *__library.get(#canonical_name.as_bytes())?; + } + }) + .collect::>(); + + let init_fields = context + .dyngen_items() + .iter() + .map(|&item| { + let canonical_name = item.canonical_name(context); + let ident = context.rust_ident(&canonical_name); + + quote! { + #ident + } + }) + .collect::>(); + + let impl_items = impl_result.items; + result.push(quote! { + impl #lib_ident { + pub unsafe fn new

( + path: P + ) -> Result + where P: AsRef<::std::ffi::OsStr> { + let __library = ::libloading::Library::new(path)?; + #( #build_locals )* + Ok( + #lib_ident { + __library: __library, + #( #init_fields ),* + } + ) + } + + #( #impl_items )* + } + }); + } + result.items }) } @@ -4356,6 +4591,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..f85877c366 --- /dev/null +++ b/tests/expectations/tests/dynamic_loading_simple.rs @@ -0,0 +1,51 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +pub struct TestLib { + __library: ::libloading::Library, + foo: unsafe extern "C" fn( + x: ::std::os::raw::c_int, + y: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, + bar: unsafe extern "C" fn( + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, + baz: unsafe extern "C" fn() -> ::std::os::raw::c_int, +} +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())?; + let bar = *__library.get("bar".as_bytes())?; + let baz = *__library.get("baz".as_bytes())?; + Ok(TestLib { + __library: __library, + foo, + bar, + baz, + }) + } + pub unsafe extern "C" fn foo( + &self, + x: ::std::os::raw::c_int, + y: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int { + (self.foo)(x, y) + } + pub unsafe extern "C" fn bar( + &self, + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int { + (self.bar)(x) + } + pub unsafe extern "C" fn baz(&self) -> ::std::os::raw::c_int { + (self.baz)() + } +} 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..816e1fb951 --- /dev/null +++ b/tests/expectations/tests/dynamic_loading_with_blacklist.rs @@ -0,0 +1,94 @@ +#![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() + } +} +pub struct TestLib { + __library: ::libloading::Library, + foo: unsafe extern "C" fn( + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, + bar: unsafe extern "C" fn( + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, +} +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())?; + let bar = *__library.get("bar".as_bytes())?; + Ok(TestLib { + __library: __library, + foo, + bar, + }) + } + pub unsafe extern "C" fn foo( + &self, + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int { + (self.foo)(x) + } + pub unsafe extern "C" fn bar( + &self, + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int { + (self.bar)(x) + } +} 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..145b6dc436 --- /dev/null +++ b/tests/expectations/tests/dynamic_loading_with_class.rs @@ -0,0 +1,89 @@ +#![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() + } +} +pub struct TestLib { + __library: ::libloading::Library, + foo: unsafe extern "C" fn( + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, + bar: unsafe extern "C" fn(), +} +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())?; + let bar = *__library.get("bar".as_bytes())?; + Ok(TestLib { + __library: __library, + foo, + bar, + }) + } + pub unsafe extern "C" fn foo( + &self, + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int { + (self.foo)(x) + } + pub unsafe extern "C" fn bar(&self) { + (self.bar)() + } +} 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..7888600cf2 --- /dev/null +++ b/tests/expectations/tests/dynamic_loading_with_whitelist.rs @@ -0,0 +1,43 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +pub struct TestLib { + __library: ::libloading::Library, + foo: unsafe extern "C" fn( + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, + baz: unsafe extern "C" fn( + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, +} +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())?; + let baz = *__library.get("baz".as_bytes())?; + Ok(TestLib { + __library: __library, + foo, + baz, + }) + } + pub unsafe extern "C" fn foo( + &self, + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int { + (self.foo)(x) + } + pub unsafe extern "C" fn baz( + &self, + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int { + (self.baz)(x) + } +} 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);