Skip to content

Commit

Permalink
Add dynamic loading support
Browse files Browse the repository at this point in the history
  • Loading branch information
Joe Ellis committed Jul 30, 2020
1 parent 94bce16 commit 5c5c155
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 9 deletions.
114 changes: 106 additions & 8 deletions src/codegen/mod.rs
Expand Up @@ -45,6 +45,7 @@ use crate::ir::var::Var;
use proc_macro2::{self, Ident, Span};
use quote::TokenStreamExt;

use crate::ir::item::ItemSet;
use crate::{Entry, HashMap, HashSet};
use std;
use std::borrow::Cow;
Expand Down Expand Up @@ -484,11 +485,62 @@ impl CodeGenerator for Module {

let codegen_self = |result: &mut CodegenResult,
found_any: &mut bool| {
for child in self.children() {
if ctx.codegen_items().contains(child) {
*found_any = true;
ctx.resolve_item(*child).codegen(ctx, result, &());

// Partition our items into functions and non-functions.
let (functions, non_functions): (ItemSet, ItemSet) =
self.children().iter().partition(|&child| {
let resolved = ctx.resolve_item(*child);
match resolved.kind() {
ItemKind::Function(_) => true,
_ => false,
}
});

for &child in &non_functions {
if !ctx.codegen_items().contains(&child) {
continue;
}
*found_any = true;
ctx.resolve_item(child).codegen(ctx, result, &());
}

let counter = Cell::new(0);
let mut functions_result = CodegenResult::new(&counter);
for &child in &functions {
if !ctx.codegen_items().contains(&child) {
continue;
}
*found_any = true;
ctx.resolve_item(child).codegen(
ctx,
&mut functions_result,
&(),
);
}

let tokens = &functions_result.items;

// If we're using dynamic loading, wrap the generated bindings in a struct and append
// the libloading boilerplate.
if ctx.options().dynamic_loading {
let lib_ident = format_ident!(
"{}",
ctx.options().dynamic_library_name.as_ref().unwrap()
);
result.push(quote! {
pub struct #lib_ident<'a> {
#(#tokens)*
}
});
utils::append_libloading_boilerplate(
ctx,
&functions,
&mut *result,
);
} else {
result.push(quote! {
#(#tokens)*
});
}

if item.id() == ctx.root_module() {
Expand Down Expand Up @@ -3697,11 +3749,19 @@ impl CodeGenerator for Function {
});

let ident = ctx.rust_ident(canonical_name);
let tokens = quote! {
#wasm_link_attribute
extern #abi {

let tokens = if ctx.options().dynamic_loading {
quote! {
#(#attributes)*
pub fn #ident ( #( #args ),* ) #ret;
#ident: libloading::Symbol<'a, unsafe extern #abi fn ( #( #args ),* ) #ret>,
}
} else {
quote! {
#wasm_link_attribute
extern #abi {
#(#attributes)*
pub fn #ident ( #( #args ),* ) #ret;
}
}
};
result.push(tokens);
Expand Down Expand Up @@ -3956,6 +4016,7 @@ mod utils {
use super::{error, ToRustTyOrOpaque};
use crate::ir::context::BindgenContext;
use crate::ir::function::{Abi, FunctionSig};
use crate::ir::item::ItemSet;
use crate::ir::item::{Item, ItemCanonicalPath};
use crate::ir::ty::TypeKind;
use proc_macro2;
Expand Down Expand Up @@ -4453,4 +4514,41 @@ mod utils {

true
}

pub fn append_libloading_boilerplate(
ctx: &BindgenContext,
functions: &ItemSet,
result: &mut Vec<proc_macro2::TokenStream>,
) {
let mut function_definitions: Vec<proc_macro2::TokenStream> =
Vec::new();

for &function in functions {
let function_item = ctx.resolve_item(function).expect_function();
let function_name = function_item.name();
let ident = format_ident!("{}", function_name);
let function_definition = quote! {
#ident: lib.get(#function_name.as_bytes())
};
function_definitions.push(function_definition);
}

let lib_ident = format_ident!(
"{}",
ctx.options().dynamic_library_name.as_ref().unwrap()
);
let libloading_code = quote! {
impl<'a> #lib_ident<'a> {
pub fn new(lib: &libloading::Library) -> #lib_ident {
unsafe {
#lib_ident {
#(#function_definitions.unwrap()),*
}
}
}
}
};

result.push(libloading_code);
}
}
23 changes: 23 additions & 0 deletions src/lib.rs
Expand Up @@ -1431,6 +1431,21 @@ impl Builder {
self.options.wasm_import_module_name = Some(import_name.into());
self
}

/// Specify whether dynamic loading will be used with these bindings.
pub fn dynamic_loading(mut self, dynamic_loading: bool) -> Self {
self.options.dynamic_loading = dynamic_loading;
if self.options.dynamic_library_name.is_none() {
self.options.dynamic_library_name = Some("Lib".to_string());
}
self
}

/// Specify the dynamic library name if we are generating bindings for a shared library.
pub fn dynamic_library_name<T: Into<String>>(mut self, dynamic_library_name: T) -> Self {
self.options.dynamic_library_name = Some(dynamic_library_name.into());
self
}
}

/// Configuration options for generated bindings.
Expand Down Expand Up @@ -1699,6 +1714,12 @@ struct BindgenOptions {

/// Wasm import module name.
wasm_import_module_name: Option<String>,

/// Whether or not we are generating bindings for a shared library.
dynamic_loading: bool,

/// The name of the dynamic library if we are generating bindings for a shared library.
dynamic_library_name: Option<String>,
}

/// TODO(emilio): This is sort of a lie (see the error message that results from
Expand Down Expand Up @@ -1827,6 +1848,8 @@ impl Default for BindgenOptions {
no_hash_types: Default::default(),
array_pointers_in_arguments: false,
wasm_import_module_name: None,
dynamic_loading: false,
dynamic_library_name: None,
}
}
}
Expand Down
16 changes: 15 additions & 1 deletion src/options.rs
Expand Up @@ -449,8 +449,14 @@ where
Arg::with_name("wasm-import-module-name")
.long("wasm-import-module-name")
.value_name("name")
.takes_value(true),
Arg::with_name("dynamic-loading")
.long("dynamic-loading")
.help("Use dynamic loading mode."),
Arg::with_name("dynamic-library-name")
.long("dynamic-library-name")
.takes_value(true)
.help("The name to be used in a #[link(wasm_import_module = ...)] statement")
.help("Set the name of dynamic library. Ignored if --dynamic-loading is not used.")
]) // .args()
.get_matches_from(args);

Expand Down Expand Up @@ -837,6 +843,14 @@ where
}
}

if matches.is_present("dynamic-loading") {
builder = builder.dynamic_loading(true);
}

if let Some(dynamic_library_name) = matches.value_of("dynamic-library-name") {
builder = builder.dynamic_library_name(dynamic_library_name);
}

let verbose = matches.is_present("verbose");

Ok((builder, output, verbose))
Expand Down

0 comments on commit 5c5c155

Please sign in to comment.