-
Notifications
You must be signed in to change notification settings - Fork 700
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add dynamic loading support #1846
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<unsafe extern ..., ::libloading::Error>, // <- tracks these | ||
/// ... | ||
/// } | ||
/// ``` | ||
struct_members: Vec<proc_macro2::TokenStream>, | ||
|
||
/// 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<proc_macro2::TokenStream>, | ||
|
||
/// 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<proc_macro2::TokenStream>, | ||
|
||
/// Tracks the initialization of the fields inside the `::new` constructor of the library | ||
/// struct, e.g.: | ||
/// ```ignore | ||
/// impl Lib { | ||
/// | ||
/// pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error> | ||
/// where | ||
/// P: AsRef<::std::ffi::OsStr>, | ||
/// { | ||
/// ... | ||
/// let foo = __library.get(...) ...; // <- tracks these | ||
/// ... | ||
/// } | ||
/// | ||
/// ... | ||
/// } | ||
/// ``` | ||
constructor_inits: Vec<proc_macro2::TokenStream>, | ||
|
||
/// 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<P>(path: P) -> Result<Self, ::libloading::Error> | ||
/// where | ||
/// P: AsRef<::std::ffi::OsStr>, | ||
/// { | ||
/// ... | ||
/// Ok(LibFoo { | ||
/// __library: __library, | ||
/// foo, | ||
/// bar, // <- tracks these | ||
/// ... | ||
/// }) | ||
/// } | ||
/// } | ||
/// ``` | ||
init_fields: Vec<proc_macro2::TokenStream>, | ||
} | ||
|
||
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<P>( | ||
path: P | ||
) -> Result<Self, ::libloading::Error> | ||
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( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe just |
||
&mut self, | ||
ident: Ident, | ||
abi: Abi, | ||
args: Vec<proc_macro2::TokenStream>, | ||
args_identifiers: Vec<proc_macro2::TokenStream>, | ||
ret: proc_macro2::TokenStream, | ||
ret_ty: proc_macro2::TokenStream, | ||
) { | ||
assert_eq!(args.len(), args_identifiers.len()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just a note, this seems to assert on varargs parameters. Not sure if those are not supported widely and this is expected? At least things seem to work when I remove the assert. The following snippet will trigger the assert.
|
||
|
||
self.struct_members.push(quote!{ | ||
#ident: Result<unsafe extern #abi fn ( #( #args ),* ) #ret, ::libloading::Error>, | ||
}); | ||
|
||
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 | ||
}); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<DerivableTraits> for Vec<&'static str> { | |
|
||
struct CodegenResult<'a> { | ||
items: Vec<proc_macro2::TokenStream>, | ||
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<usize>) -> 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() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Then use that instead of unwrapping around. |
||
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(""), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just |
||
); | ||
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( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you document what this returns? The signature is not super-self-descriptive. |
||
ctx: &BindgenContext, | ||
sig: &FunctionSig, | ||
) -> Vec<proc_macro2::TokenStream> { | ||
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::<Vec<_>>(); | ||
|
||
args | ||
} | ||
|
||
pub fn fnsig_block( | ||
ctx: &BindgenContext, | ||
sig: &FunctionSig, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,4 +10,5 @@ authors = [ | |
|
||
[dependencies] | ||
objc = "0.2" | ||
block = "0.1" | ||
block = "0.1" | ||
libloading = "0.6.2" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Derive this?