-
Notifications
You must be signed in to change notification settings - Fork 702
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Michael-F-Bryan <[email protected]>
- Loading branch information
1 parent
94bce16
commit 51e3702
Showing
15 changed files
with
929 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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.: | ||
/// ```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>, | ||
|
||
/// Keeps track of the items that we have seen. | ||
items_seen: HashSet<ItemId>, | ||
} | ||
|
||
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<Id: Into<ItemId>>(&self, item: Id) -> bool { | ||
self.items_seen.contains(&item.into()) | ||
} | ||
|
||
pub fn set_seen<Id: Into<ItemId>>(&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<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( | ||
&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, | ||
) { | ||
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 | ||
}); | ||
} | ||
} | ||
|
||
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!( | ||
"<Item as DynamicBindingGenerator>::dyngen: Ignoring hidden or seen: \ | ||
self = {:?}", | ||
self | ||
); | ||
return; | ||
} | ||
|
||
debug!( | ||
"<Item as DynamicBindingGenerator>::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); | ||
} | ||
} |
Oops, something went wrong.