Skip to content

Commit 48bb826

Browse files
author
Joe Ellis
committed
Add dynamic loading support
1 parent 94bce16 commit 48bb826

File tree

3 files changed

+145
-10
lines changed

3 files changed

+145
-10
lines changed

src/codegen/mod.rs

+105-8
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ use crate::ir::var::Var;
4545
use proc_macro2::{self, Ident, Span};
4646
use quote::TokenStreamExt;
4747

48+
use crate::ir::item::ItemSet;
4849
use crate::{Entry, HashMap, HashSet};
4950
use std;
5051
use std::borrow::Cow;
@@ -484,11 +485,61 @@ impl CodeGenerator for Module {
484485

485486
let codegen_self = |result: &mut CodegenResult,
486487
found_any: &mut bool| {
487-
for child in self.children() {
488-
if ctx.codegen_items().contains(child) {
489-
*found_any = true;
490-
ctx.resolve_item(*child).codegen(ctx, result, &());
488+
// Partition our items into functions and non-functions.
489+
let (functions, non_functions): (ItemSet, ItemSet) =
490+
self.children().iter().partition(|&child| {
491+
let resolved = ctx.resolve_item(*child);
492+
match resolved.kind() {
493+
ItemKind::Function(_) => true,
494+
_ => false,
495+
}
496+
});
497+
498+
for &child in &non_functions {
499+
if !ctx.codegen_items().contains(&child) {
500+
continue;
491501
}
502+
*found_any = true;
503+
ctx.resolve_item(child).codegen(ctx, result, &());
504+
}
505+
506+
let counter = Cell::new(0);
507+
let mut functions_result = CodegenResult::new(&counter);
508+
for &child in &functions {
509+
if !ctx.codegen_items().contains(&child) {
510+
continue;
511+
}
512+
*found_any = true;
513+
ctx.resolve_item(child).codegen(
514+
ctx,
515+
&mut functions_result,
516+
&(),
517+
);
518+
}
519+
520+
let tokens = &functions_result.items;
521+
522+
// If we're using dynamic loading, wrap the generated bindings in a struct and append
523+
// the libloading boilerplate.
524+
if ctx.options().dynamic_loading {
525+
let lib_ident = format_ident!(
526+
"{}",
527+
ctx.options().dynamic_library_name.as_ref().unwrap()
528+
);
529+
result.push(quote! {
530+
pub struct #lib_ident<'a> {
531+
#(#tokens)*
532+
}
533+
});
534+
utils::append_libloading_boilerplate(
535+
ctx,
536+
&functions,
537+
&mut *result,
538+
);
539+
} else {
540+
result.push(quote! {
541+
#(#tokens)*
542+
});
492543
}
493544

494545
if item.id() == ctx.root_module() {
@@ -3697,11 +3748,19 @@ impl CodeGenerator for Function {
36973748
});
36983749

36993750
let ident = ctx.rust_ident(canonical_name);
3700-
let tokens = quote! {
3701-
#wasm_link_attribute
3702-
extern #abi {
3751+
3752+
let tokens = if ctx.options().dynamic_loading {
3753+
quote! {
37033754
#(#attributes)*
3704-
pub fn #ident ( #( #args ),* ) #ret;
3755+
#ident: libloading::Symbol<'a, unsafe extern #abi fn ( #( #args ),* ) #ret>,
3756+
}
3757+
} else {
3758+
quote! {
3759+
#wasm_link_attribute
3760+
extern #abi {
3761+
#(#attributes)*
3762+
pub fn #ident ( #( #args ),* ) #ret;
3763+
}
37053764
}
37063765
};
37073766
result.push(tokens);
@@ -3956,6 +4015,7 @@ mod utils {
39564015
use super::{error, ToRustTyOrOpaque};
39574016
use crate::ir::context::BindgenContext;
39584017
use crate::ir::function::{Abi, FunctionSig};
4018+
use crate::ir::item::ItemSet;
39594019
use crate::ir::item::{Item, ItemCanonicalPath};
39604020
use crate::ir::ty::TypeKind;
39614021
use proc_macro2;
@@ -4453,4 +4513,41 @@ mod utils {
44534513

44544514
true
44554515
}
4516+
4517+
pub fn append_libloading_boilerplate(
4518+
ctx: &BindgenContext,
4519+
functions: &ItemSet,
4520+
result: &mut Vec<proc_macro2::TokenStream>,
4521+
) {
4522+
let mut function_definitions: Vec<proc_macro2::TokenStream> =
4523+
Vec::new();
4524+
4525+
for &function in functions {
4526+
let function_item = ctx.resolve_item(function).expect_function();
4527+
let function_name = function_item.name();
4528+
let ident = format_ident!("{}", function_name);
4529+
let function_definition = quote! {
4530+
#ident: lib.get(#function_name.as_bytes())
4531+
};
4532+
function_definitions.push(function_definition);
4533+
}
4534+
4535+
let lib_ident = format_ident!(
4536+
"{}",
4537+
ctx.options().dynamic_library_name.as_ref().unwrap()
4538+
);
4539+
let libloading_code = quote! {
4540+
impl<'a> #lib_ident<'a> {
4541+
pub fn new(lib: &libloading::Library) -> #lib_ident {
4542+
unsafe {
4543+
#lib_ident {
4544+
#(#function_definitions.unwrap()),*
4545+
}
4546+
}
4547+
}
4548+
}
4549+
};
4550+
4551+
result.push(libloading_code);
4552+
}
44564553
}

src/lib.rs

+26
Original file line numberDiff line numberDiff line change
@@ -1431,6 +1431,24 @@ impl Builder {
14311431
self.options.wasm_import_module_name = Some(import_name.into());
14321432
self
14331433
}
1434+
1435+
/// Specify whether dynamic loading will be used with these bindings.
1436+
pub fn dynamic_loading(mut self, dynamic_loading: bool) -> Self {
1437+
self.options.dynamic_loading = dynamic_loading;
1438+
if self.options.dynamic_library_name.is_none() {
1439+
self.options.dynamic_library_name = Some("Lib".to_string());
1440+
}
1441+
self
1442+
}
1443+
1444+
/// Specify the dynamic library name if we are generating bindings for a shared library.
1445+
pub fn dynamic_library_name<T: Into<String>>(
1446+
mut self,
1447+
dynamic_library_name: T,
1448+
) -> Self {
1449+
self.options.dynamic_library_name = Some(dynamic_library_name.into());
1450+
self
1451+
}
14341452
}
14351453

14361454
/// Configuration options for generated bindings.
@@ -1699,6 +1717,12 @@ struct BindgenOptions {
16991717

17001718
/// Wasm import module name.
17011719
wasm_import_module_name: Option<String>,
1720+
1721+
/// Whether or not we are generating bindings for a shared library.
1722+
dynamic_loading: bool,
1723+
1724+
/// The name of the dynamic library if we are generating bindings for a shared library.
1725+
dynamic_library_name: Option<String>,
17021726
}
17031727

17041728
/// TODO(emilio): This is sort of a lie (see the error message that results from
@@ -1827,6 +1851,8 @@ impl Default for BindgenOptions {
18271851
no_hash_types: Default::default(),
18281852
array_pointers_in_arguments: false,
18291853
wasm_import_module_name: None,
1854+
dynamic_loading: false,
1855+
dynamic_library_name: None,
18301856
}
18311857
}
18321858
}

src/options.rs

+14-2
Original file line numberDiff line numberDiff line change
@@ -449,8 +449,11 @@ where
449449
Arg::with_name("wasm-import-module-name")
450450
.long("wasm-import-module-name")
451451
.value_name("name")
452-
.takes_value(true)
453-
.help("The name to be used in a #[link(wasm_import_module = ...)] statement")
452+
.takes_value(true),
453+
Arg::with_name("dynamic-loading")
454+
.help("Use dynamic loading mode."),
455+
Arg::with_name("dynamic-library-name")
456+
.help("Set the name of dynamic library. Ignored if --dynamic-loading is not used.")
454457
]) // .args()
455458
.get_matches_from(args);
456459

@@ -837,6 +840,15 @@ where
837840
}
838841
}
839842

843+
if matches.is_present("dynamic-loading") {
844+
builder = builder.dynamic_loading(true);
845+
}
846+
847+
if let Some(dynamic_library_name) = matches.value_of("dynamic-library-name")
848+
{
849+
builder = builder.dynamic_library_name(dynamic_library_name);
850+
}
851+
840852
let verbose = matches.is_present("verbose");
841853

842854
Ok((builder, output, verbose))

0 commit comments

Comments
 (0)