Skip to content

Commit 56c5b64

Browse files
author
Joe Ellis
committed
Add dynamic loading support
1 parent 94bce16 commit 56c5b64

15 files changed

+616
-3
lines changed

src/codegen/dynamic.rs

Whitespace-only changes.

src/codegen/mod.rs

+195
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,122 @@ 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+
struct_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+
struct_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+
struct_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+
struct_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, struct_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+
struct_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 ret = utils::fnsig_return_ty(ctx, signature);
4029+
4030+
let ident = ctx.rust_ident(&canonical_name);
4031+
4032+
struct_result.push(quote! {
4033+
#ident: libloading::Symbol<'a, unsafe extern #abi fn ( #( #args ),* ) #ret>,
4034+
});
4035+
4036+
impl_result.push(quote! {
4037+
#ident: lib.get(#canonical_name.as_bytes()).unwrap(),
4038+
});
4039+
}
4040+
}
4041+
39164042
pub(crate) fn codegen(
39174043
context: BindgenContext,
39184044
) -> (Vec<proc_macro2::TokenStream>, BindgenOptions) {
@@ -3948,6 +4074,75 @@ pub(crate) fn codegen(
39484074
&(),
39494075
);
39504076

4077+
// If the set of items to generate dynamic bindings for is nonempty...
4078+
if !context.dyngen_items().is_empty() {
4079+
let _t = context.timer("dyngen");
4080+
debug!("dyngen: {:?}", context.options());
4081+
4082+
// `struct_result` tracks the tokens that will appears inside the library struct, e.g.:
4083+
// ```
4084+
// struct Lib {
4085+
// x: libloading::Symbol<'a, ...>, // <- tracks these!
4086+
// }
4087+
// ```
4088+
//
4089+
// `impl_result` tracks the tokens that will appear inside the library struct's
4090+
// implementation, e.g.:
4091+
//
4092+
// ```
4093+
// impl<'a> Lib<'a> {
4094+
// pub fn new(lib: &libloading::Library) -> Lib {
4095+
// unsafe {
4096+
// x: lib.get(...), // <- tracks these!
4097+
// }
4098+
// }
4099+
// }
4100+
// ```
4101+
//
4102+
// We clone the CodeGenerator object used for normal code generation, but set its items
4103+
// to the empty vector. We do this so that we can keep track of which items we have
4104+
// seen, etc.
4105+
let mut struct_result = result.clone();
4106+
struct_result.items = vec![];
4107+
let mut impl_result = result.clone();
4108+
impl_result.items = vec![];
4109+
4110+
// Run dynamic binding generation for each of the required items.
4111+
for item in context.dyngen_items() {
4112+
context.resolve_item(*item).dyngen(
4113+
context,
4114+
&mut struct_result,
4115+
&mut impl_result,
4116+
&(),
4117+
);
4118+
}
4119+
4120+
let lib_ident = context.rust_ident(
4121+
context.options().dynamic_library_name.as_ref().unwrap(),
4122+
);
4123+
4124+
let struct_items = struct_result.items;
4125+
result.push(quote! {
4126+
extern crate libloading;
4127+
pub struct #lib_ident<'a> {
4128+
#(#struct_items)*
4129+
}
4130+
});
4131+
4132+
let impl_items = impl_result.items;
4133+
result.push(quote! {
4134+
impl<'a> #lib_ident<'a> {
4135+
pub fn new(lib: &libloading::Library) -> #lib_ident {
4136+
unsafe {
4137+
#lib_ident {
4138+
#(#impl_items)*
4139+
}
4140+
}
4141+
}
4142+
}
4143+
});
4144+
}
4145+
39514146
result.items
39524147
})
39534148
}

src/ir/context.rs

+81-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use super::derive::{
1111
CanDerive, CanDeriveCopy, CanDeriveDebug, CanDeriveDefault, CanDeriveEq,
1212
CanDeriveHash, CanDeriveOrd, CanDerivePartialEq, CanDerivePartialOrd,
1313
};
14-
use super::function::Function;
14+
use super::function::{Function, FunctionKind};
1515
use super::int::IntKind;
1616
use super::item::{IsOpaque, Item, ItemAncestors, ItemSet};
1717
use super::item_kind::ItemKind;
@@ -386,6 +386,11 @@ pub struct BindgenContext {
386386
/// It's computed right after computing the whitelisted items.
387387
codegen_items: Option<ItemSet>,
388388

389+
/// The set of `ItemId`s that we will perform dynamic binding generation for.
390+
///
391+
/// It's computed following the codegen/whitelisted items.
392+
dyngen_items: Option<ItemSet>,
393+
389394
/// Map from an item's id to the set of template parameter items that it
390395
/// uses. See `ir::named` for more details. Always `Some` during the codegen
391396
/// phase.
@@ -619,6 +624,7 @@ If you encounter an error missing from this list, please file an issue or a PR!"
619624
generated_bindgen_complex: Cell::new(false),
620625
whitelisted: None,
621626
codegen_items: None,
627+
dyngen_items: None,
622628
used_template_parameters: None,
623629
need_bitfield_allocation: Default::default(),
624630
cannot_derive_debug: None,
@@ -1189,6 +1195,9 @@ If you encounter an error missing from this list, please file an issue or a PR!"
11891195
// graph, and their completion means that the IR graph is now frozen.
11901196
self.compute_whitelisted_and_codegen_items();
11911197

1198+
// Compute the items for dynamic generation.
1199+
self.compute_dynamic_generation_items();
1200+
11921201
// Make sure to do this after processing replacements, since that messes
11931202
// with the parentage and module children, and we want to assert that it
11941203
// messes with them correctly.
@@ -2262,6 +2271,13 @@ If you encounter an error missing from this list, please file an issue or a PR!"
22622271
self.codegen_items.as_ref().unwrap()
22632272
}
22642273

2274+
/// Get a reference to the set of dynamic bindings we should generate.
2275+
pub fn dyngen_items(&self) -> &ItemSet {
2276+
assert!(self.in_codegen_phase());
2277+
assert!(self.current_module == self.root_module);
2278+
self.dyngen_items.as_ref().unwrap()
2279+
}
2280+
22652281
/// Compute the whitelisted items set and populate `self.whitelisted`.
22662282
fn compute_whitelisted_and_codegen_items(&mut self) {
22672283
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!"
24122428
}
24132429
}
24142430

2431+
/// Compute the items for dynamic generation.
2432+
fn compute_dynamic_generation_items(&mut self) {
2433+
assert!(self.in_codegen_phase());
2434+
assert!(self.current_module == self.root_module);
2435+
let _t = self.timer("compute_dynamic_generation_items");
2436+
2437+
let dyngen_items = self
2438+
.items()
2439+
// Only consider items that are enabled for codegen.
2440+
.filter(|&(_, item)| item.is_enabled_for_codegen(self))
2441+
.filter(|&(_, item)| {
2442+
// If the user has not chosen to do dynamic loading, then we have nothing to
2443+
// do.
2444+
if !self.options().dynamic_loading {
2445+
return false;
2446+
}
2447+
2448+
let name = item.path_for_whitelisting(self)[1..].join("::");
2449+
2450+
// If there is a whitelist and this function is not on it, don't add it.
2451+
if !self.options().whitelisted_functions.is_empty() &&
2452+
!self.options().whitelisted_functions.matches(&name)
2453+
{
2454+
return false;
2455+
}
2456+
2457+
// If there is a blacklist and this function is on it, don't add it.
2458+
if !self.options().blacklisted_functions.is_empty() &&
2459+
self.options.blacklisted_functions.matches(&name)
2460+
{
2461+
return false;
2462+
}
2463+
2464+
// We don't want to include the root module in the list of items to generate
2465+
// dynamic bindings for.
2466+
if item.id() == self.root_module {
2467+
return false;
2468+
}
2469+
2470+
// We only want to generate bindings for the stuff that we have at the toplevel.
2471+
if item.parent_id() != self.root_module {
2472+
return false;
2473+
}
2474+
2475+
// If we get to here:
2476+
// - The user wants to generate dynamic bindings.
2477+
// - The item being considered is at the toplevel (but is not the root itself).
2478+
// - The item is permitted by the {white,black}list.
2479+
2480+
// Finally, only generate dynamic bindings for functions (for now! this could
2481+
// change in the future to support variables).
2482+
match item.kind() {
2483+
ItemKind::Function(ref f) => {
2484+
f.kind() == FunctionKind::Function
2485+
}
2486+
_ => false,
2487+
}
2488+
})
2489+
.map(|(id, _)| id)
2490+
.collect::<ItemSet>();
2491+
2492+
self.dyngen_items = Some(dyngen_items);
2493+
}
2494+
24152495
/// Convenient method for getting the prefix to use for most traits in
24162496
/// codegen depending on the `use_core` option.
24172497
pub fn trait_prefix(&self) -> Ident {

src/ir/item.rs

+9
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,15 @@ impl Item {
624624
&self.annotations
625625
}
626626

627+
/// Whether this item should have dynamic bindings generated, rather than normal bindings.
628+
pub fn is_dynamic(&self, ctx: &BindgenContext) -> bool {
629+
debug_assert!(
630+
ctx.in_codegen_phase(),
631+
"You're not supposed to call this yet"
632+
);
633+
ctx.dyngen_items().contains(&self.id)
634+
}
635+
627636
/// Whether this item should be blacklisted.
628637
///
629638
/// This may be due to either annotations or to other kind of configuration.

0 commit comments

Comments
 (0)