Skip to content

Commit a48a137

Browse files
Joe EllisMichael-F-Bryan
Joe Ellis
andcommitted
Add dynamic loading support
Co-authored-by: Michael-F-Bryan <[email protected]>
1 parent 94bce16 commit a48a137

14 files changed

+716
-3
lines changed

src/codegen/mod.rs

+264
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ impl From<DerivableTraits> for Vec<&'static str> {
182182
}
183183
}
184184

185+
#[derive(Clone)]
185186
struct CodegenResult<'a> {
186187
items: Vec<proc_macro2::TokenStream>,
187188

@@ -435,6 +436,15 @@ impl CodeGenerator for Item {
435436
return;
436437
}
437438

439+
if self.is_dynamic(ctx) {
440+
debug!(
441+
"Item is to be dynamically generated; ignoring it for now. \
442+
self = {:?}",
443+
self
444+
);
445+
return;
446+
}
447+
438448
if self.is_blacklisted(ctx) || result.seen(self.id()) {
439449
debug!(
440450
"<Item as CodeGenerator>::codegen: Ignoring hidden or seen: \
@@ -3913,6 +3923,126 @@ impl CodeGenerator for ObjCInterface {
39133923
}
39143924
}
39153925

3926+
trait DynamicBindingGenerator {
3927+
/// Extra information from the caller.
3928+
type Extra;
3929+
3930+
fn dyngen<'a>(
3931+
&self,
3932+
ctx: &BindgenContext,
3933+
def_result: &mut CodegenResult<'a>,
3934+
impl_result: &mut CodegenResult<'a>,
3935+
extra: &Self::Extra,
3936+
);
3937+
}
3938+
3939+
impl DynamicBindingGenerator for Item {
3940+
type Extra = ();
3941+
3942+
fn dyngen<'a>(
3943+
&self,
3944+
ctx: &BindgenContext,
3945+
def_result: &mut CodegenResult<'a>,
3946+
impl_result: &mut CodegenResult<'a>,
3947+
_extra: &(),
3948+
) {
3949+
assert!(self.is_dynamic(ctx));
3950+
if !self.is_enabled_for_codegen(ctx) {
3951+
return;
3952+
}
3953+
3954+
// If this item is blacklisted, or we've already seen it, nothing to do.
3955+
if self.is_blacklisted(ctx) ||
3956+
def_result.seen(self.id()) ||
3957+
impl_result.seen(self.id())
3958+
{
3959+
debug!(
3960+
"<Item as DynamicBindingGenerator>::codegen: Ignoring hidden or seen: \
3961+
self = {:?}",
3962+
self
3963+
);
3964+
return;
3965+
}
3966+
3967+
debug!(
3968+
"<Item as DynamicBindingGenerator>::dyngen: self = {:?}",
3969+
self
3970+
);
3971+
3972+
if !ctx.codegen_items().contains(&self.id()) {
3973+
warn!("Found non-whitelisted item in dynamic binding generation: {:?}", self);
3974+
}
3975+
3976+
def_result.set_seen(self.id());
3977+
impl_result.set_seen(self.id());
3978+
3979+
match *self.kind() {
3980+
ItemKind::Function(ref fun) => {
3981+
assert!(fun.kind() == FunctionKind::Function);
3982+
fun.dyngen(ctx, def_result, impl_result, self);
3983+
}
3984+
_ => panic!(
3985+
"Unexpected item type when doing dynamic bindings generation."
3986+
),
3987+
}
3988+
}
3989+
}
3990+
3991+
impl DynamicBindingGenerator for Function {
3992+
type Extra = Item;
3993+
3994+
fn dyngen<'a>(
3995+
&self,
3996+
ctx: &BindgenContext,
3997+
def_result: &mut CodegenResult<'a>,
3998+
impl_result: &mut CodegenResult<'a>,
3999+
item: &Item,
4000+
) {
4001+
let signature_item = ctx.resolve_item(self.signature());
4002+
let signature = signature_item.kind().expect_type().canonical_type(ctx);
4003+
let signature = match *signature.kind() {
4004+
TypeKind::Function(ref sig) => sig,
4005+
_ => panic!("Signature kind is not a Function: {:?}", signature),
4006+
};
4007+
4008+
let canonical_name = item.canonical_name(ctx);
4009+
let abi = match signature.abi() {
4010+
Abi::ThisCall if !ctx.options().rust_features().thiscall_abi => {
4011+
warn!("Skipping function with thiscall ABI that isn't supported by the configured Rust target");
4012+
return;
4013+
}
4014+
Abi::Win64 if signature.is_variadic() => {
4015+
warn!("Skipping variadic function with Win64 ABI that isn't supported");
4016+
return;
4017+
}
4018+
Abi::Unknown(unknown_abi) => {
4019+
panic!(
4020+
"Invalid or unknown abi {:?} for function {:?} ({:?})",
4021+
unknown_abi, canonical_name, self
4022+
);
4023+
}
4024+
abi => abi,
4025+
};
4026+
4027+
let args = utils::fnsig_arguments(ctx, signature);
4028+
let args_identifiers =
4029+
utils::fnsig_argument_identifiers(ctx, signature);
4030+
let ret = utils::fnsig_return_ty(ctx, signature);
4031+
4032+
let ident = ctx.rust_ident(&canonical_name);
4033+
4034+
def_result.push(quote! {
4035+
#ident: unsafe extern #abi fn ( #( #args ),* ) #ret,
4036+
});
4037+
4038+
impl_result.push(quote! {
4039+
pub unsafe extern #abi fn #ident ( &self, #( #args ),* ) #ret {
4040+
(self.#ident)(#( #args_identifiers ),*)
4041+
}
4042+
});
4043+
}
4044+
}
4045+
39164046
pub(crate) fn codegen(
39174047
context: BindgenContext,
39184048
) -> (Vec<proc_macro2::TokenStream>, BindgenOptions) {
@@ -3948,6 +4078,111 @@ pub(crate) fn codegen(
39484078
&(),
39494079
);
39504080

4081+
// If the set of items to generate dynamic bindings for is nonempty...
4082+
if !context.dyngen_items().is_empty() {
4083+
let _t = context.timer("dyngen");
4084+
debug!("dyngen: {:?}", context.options());
4085+
4086+
// `def_result` tracks the tokens that will appears inside the library struct -- these
4087+
// are the definitions of the symbols inside the library struct, e.g.:
4088+
// ```
4089+
// struct Lib {
4090+
// __library: ::libloading::Library,
4091+
// x: unsafe extern ..., // <- tracks these!
4092+
// ...
4093+
// }
4094+
// ```
4095+
//
4096+
// `impl_result` tracks the tokens that will appear inside the call implementation,
4097+
// e.g.:
4098+
//
4099+
// ```
4100+
// impl Lib {
4101+
// ...
4102+
// pub unsafe extern "C" fn foo(&self, ...) { // <- tracks these!
4103+
// (self.foo)(...)
4104+
// }
4105+
// }
4106+
// ```
4107+
//
4108+
// We clone the CodeGenerator object used for normal code generation, but set its items
4109+
// to the empty vector. We do this so that we can keep track of which items we have
4110+
// seen, etc.
4111+
let mut def_result = result.clone();
4112+
def_result.items = vec![];
4113+
let mut impl_result = result.clone();
4114+
impl_result.items = vec![];
4115+
4116+
// Run dynamic binding generation for each of the required items.
4117+
for item in context.dyngen_items() {
4118+
context.resolve_item(*item).dyngen(
4119+
context,
4120+
&mut def_result,
4121+
&mut impl_result,
4122+
&(),
4123+
);
4124+
}
4125+
4126+
let lib_ident = context.rust_ident(
4127+
context.options().dynamic_library_name.as_ref().unwrap(),
4128+
);
4129+
4130+
let struct_items = def_result.items;
4131+
result.push(quote! {
4132+
pub struct #lib_ident {
4133+
__library: ::libloading::Library,
4134+
#(#struct_items)*
4135+
}
4136+
});
4137+
4138+
let build_locals = context
4139+
.dyngen_items()
4140+
.iter()
4141+
.map(|&item| {
4142+
let canonical_name = item.canonical_name(context);
4143+
let ident = context.rust_ident(&canonical_name);
4144+
4145+
quote! {
4146+
let #ident = *__library.get(#canonical_name.as_bytes())?;
4147+
}
4148+
})
4149+
.collect::<Vec<_>>();
4150+
4151+
let init_fields = context
4152+
.dyngen_items()
4153+
.iter()
4154+
.map(|&item| {
4155+
let canonical_name = item.canonical_name(context);
4156+
let ident = context.rust_ident(&canonical_name);
4157+
4158+
quote! {
4159+
#ident
4160+
}
4161+
})
4162+
.collect::<Vec<_>>();
4163+
4164+
let impl_items = impl_result.items;
4165+
result.push(quote! {
4166+
impl #lib_ident {
4167+
pub unsafe fn new<P>(
4168+
path: P
4169+
) -> Result<Self, ::libloading::Error>
4170+
where P: AsRef<::std::ffi::OsStr> {
4171+
let __library = ::libloading::Library::new(path)?;
4172+
#( #build_locals )*
4173+
Ok(
4174+
#lib_ident {
4175+
__library: __library,
4176+
#( #init_fields ),*
4177+
}
4178+
)
4179+
}
4180+
4181+
#( #impl_items )*
4182+
}
4183+
});
4184+
}
4185+
39514186
result.items
39524187
})
39534188
}
@@ -4356,6 +4591,35 @@ mod utils {
43564591
args
43574592
}
43584593

4594+
pub fn fnsig_argument_identifiers(
4595+
ctx: &BindgenContext,
4596+
sig: &FunctionSig,
4597+
) -> Vec<proc_macro2::TokenStream> {
4598+
let mut unnamed_arguments = 0;
4599+
let args = sig
4600+
.argument_types()
4601+
.iter()
4602+
.map(|&(ref name, _ty)| {
4603+
let arg_name = match *name {
4604+
Some(ref name) => ctx.rust_mangle(name).into_owned(),
4605+
None => {
4606+
unnamed_arguments += 1;
4607+
format!("arg{}", unnamed_arguments)
4608+
}
4609+
};
4610+
4611+
assert!(!arg_name.is_empty());
4612+
let arg_name = ctx.rust_ident(arg_name);
4613+
4614+
quote! {
4615+
#arg_name
4616+
}
4617+
})
4618+
.collect::<Vec<_>>();
4619+
4620+
args
4621+
}
4622+
43594623
pub fn fnsig_block(
43604624
ctx: &BindgenContext,
43614625
sig: &FunctionSig,

0 commit comments

Comments
 (0)