Get imports working in a basic capacity

This commit is contained in:
Alex Crichton
2017-12-18 21:43:16 -08:00
parent 1ffcb90d2d
commit eda9beae25
7 changed files with 319 additions and 48 deletions

View File

@ -5,6 +5,7 @@ use wasm_bindgen_shared as shared;
pub struct Program {
pub structs: Vec<Struct>,
pub free_functions: Vec<Function>,
pub imports: Vec<Import>,
}
pub struct Function {
@ -13,6 +14,14 @@ pub struct Function {
pub ret: Option<Type>,
}
pub struct Import {
pub function: Function,
pub decl: Box<syn::FnDecl>,
pub ident: syn::Ident,
pub vis: syn::Visibility,
pub attrs: Vec<syn::Attribute>,
}
pub enum Type {
Integer(syn::Ident),
BorrowedStr,
@ -62,10 +71,36 @@ impl Program {
}
}
pub fn push_foreign_mod(&mut self, f: &syn::ItemForeignMod) {
match f.abi.kind {
syn::AbiKind::Named(ref l) if l.to_string() == "\"JS\"" => {}
_ => panic!("only foreign mods with the `JS` ABI are allowed"),
}
for item in f.items.iter() {
self.push_foreign_item(item);
}
}
pub fn push_foreign_item(&mut self, f: &syn::ForeignItem) {
let f = match *f {
syn::ForeignItem::Fn(ref f) => f,
_ => panic!("only foreign functions allowed for now, not statics"),
};
self.imports.push(Import {
attrs: f.attrs.clone(),
vis: f.vis.clone(),
decl: f.decl.clone(),
ident: f.ident.clone(),
function: Function::from_decl(f.ident, &f.decl),
});
}
pub fn shared(&self) -> shared::Program {
shared::Program {
structs: self.structs.iter().map(|s| s.shared()).collect(),
free_functions: self.free_functions.iter().map(|s| s.shared()).collect(),
imports: self.imports.iter().map(|i| i.function.shared()).collect(),
}
}
}
@ -91,14 +126,18 @@ impl Function {
panic!("can only bindgen Rust ABI functions")
}
if input.decl.variadic {
Function::from_decl(input.ident, &input.decl)
}
pub fn from_decl(name: syn::Ident, decl: &syn::FnDecl) -> Function {
if decl.variadic {
panic!("can't bindgen variadic functions")
}
if input.decl.generics.params.len() > 0 {
if decl.generics.params.len() > 0 {
panic!("can't bindgen functions with lifetime or type parameters")
}
let arguments = input.decl.inputs.iter()
let arguments = decl.inputs.iter()
.map(|i| i.into_item())
.map(|arg| {
match *arg {
@ -109,12 +148,12 @@ impl Function {
.map(|arg| Type::from(&arg.ty))
.collect::<Vec<_>>();
let ret = match input.decl.output {
let ret = match decl.output {
syn::ReturnType::Default => None,
syn::ReturnType::Type(ref t, _) => Some(Type::from(t)),
};
Function { name: input.ident, arguments, ret }
Function { name, arguments, ret }
}
pub fn free_function_export_name(&self) -> syn::Lit {
@ -153,21 +192,22 @@ impl Function {
}
}
pub fn extract_path_ident(path: &syn::Path) -> syn::Ident {
if path.leading_colon.is_some() {
panic!("unsupported leading colon in path")
}
if path.segments.len() != 1 {
panic!("unsupported path that needs name resolution")
}
match path.segments.get(0).item().arguments {
syn::PathArguments::None => {}
_ => panic!("unsupported path that has path arguments")
}
path.segments.get(0).item().ident
}
impl Type {
pub fn from(ty: &syn::Type) -> Type {
let extract_path_ident = |path: &syn::Path| {
if path.leading_colon.is_some() {
panic!("unsupported leading colon in path")
}
if path.segments.len() != 1 {
panic!("unsupported path that needs name resolution")
}
match path.segments.get(0).item().arguments {
syn::PathArguments::None => {}
_ => panic!("unsupported path that has path arguments")
}
path.segments.get(0).item().ident
};
match *ty {
syn::Type::Reference(ref r) => {
if r.lifetime.is_some() {

View File

@ -35,6 +35,7 @@ pub fn wasm_bindgen(input: TokenStream) -> TokenStream {
let mut program = ast::Program {
structs: Vec::new(),
free_functions: Vec::new(),
imports: Vec::new(),
};
// Translate all input items into our own internal representation (the `ast`
@ -54,9 +55,12 @@ pub fn wasm_bindgen(input: TokenStream) -> TokenStream {
}
program.structs.push(s);
}
syn::Item::Impl(ref s) => {
syn::Item::Impl(ref i) => {
item.to_tokens(&mut ret);
program.push_impl(s);
program.push_impl(i);
}
syn::Item::ForeignMod(ref f) => {
program.push_foreign_mod(f);
}
_ => panic!("unexpected item in bindgen macro"),
}
@ -70,6 +74,9 @@ pub fn wasm_bindgen(input: TokenStream) -> TokenStream {
for s in program.structs.iter() {
bindgen_struct(s, &mut ret);
}
for i in program.imports.iter() {
bindgen_import(i, &mut ret);
}
// Finally generate a static which will eventually be what lives in a custom
// section of the wasm executable. For now it's just a plain old static, but
@ -94,7 +101,7 @@ pub fn wasm_bindgen(input: TokenStream) -> TokenStream {
*#generated_static_value;
}).to_tokens(&mut ret);
// println!("{}", ret);
println!("{}", ret);
ret.into()
}
@ -345,3 +352,97 @@ impl ToTokens for Receiver {
}
}
}
fn bindgen_import(import: &ast::Import, tokens: &mut Tokens) {
let vis = &import.vis;
let ret = &import.decl.output;
let name = &import.ident;
let fn_token = &import.decl.fn_token;
let arguments = &import.decl.inputs;
let mut abi_argument_names = Vec::new();
let mut abi_arguments = Vec::new();
let mut arg_conversions = Vec::new();
let ret_ident = syn::Ident::from("_ret");
let names = import.decl.inputs
.iter()
.map(|i| i.into_item())
.map(|arg| {
match *arg {
syn::FnArg::Captured(ref c) => c,
_ => panic!("arguments cannot be `self` or ignored"),
}
})
.map(|arg| {
match arg.pat {
syn::Pat::Ident(syn::PatIdent {
mode: syn::BindingMode::ByValue(_),
ident,
subpat: None,
..
}) => {
ident
}
_ => panic!("unsupported pattern in foreign function"),
}
});
for (ty, name) in import.function.arguments.iter().zip(names) {
match *ty {
ast::Type::Integer(i) => {
abi_argument_names.push(name);
abi_arguments.push(my_quote! { #name: #i });
arg_conversions.push(my_quote! {});
}
ast::Type::BorrowedStr => {
let ptr = syn::Ident::from(format!("{}_ptr", name));
let len = syn::Ident::from(format!("{}_len", name));
abi_argument_names.push(ptr);
abi_argument_names.push(len);
abi_arguments.push(my_quote! { #ptr: *const u8 });
abi_arguments.push(my_quote! { #len: usize });
arg_conversions.push(my_quote! {
let #ptr = #name.as_ptr();
let #len = #name.len();
});
}
ast::Type::String => panic!("can't use `String` in foreign functions"),
ast::Type::ByValue(_name) |
ast::Type::ByRef(_name) |
ast::Type::ByMutRef(_name) => {
panic!("can't use strct types in foreign functions yet");
}
}
}
let abi_ret;
let convert_ret;
match import.function.ret {
Some(ast::Type::Integer(i)) => {
abi_ret = my_quote! { #i };
convert_ret = my_quote! { #ret_ident };
}
Some(ast::Type::BorrowedStr) => panic!("can't return a borrowed string"),
Some(ast::Type::ByRef(_)) => panic!("can't return a borrowed ref"),
Some(ast::Type::ByMutRef(_)) => panic!("can't return a borrowed ref"),
Some(ast::Type::String) => panic!("can't return a string in foreign functions"),
Some(ast::Type::ByValue(_)) => panic!("can't return a struct in a foreign function"),
None => {
abi_ret = my_quote! { () };
convert_ret = my_quote! {};
}
}
(quote! {
#vis #fn_token #name(#arguments) #ret {
extern {
fn #name(#(#abi_arguments),*) -> #abi_ret;
}
unsafe {
#(#arg_conversions)*
let #ret_ident = #name(#(#abi_argument_names),*);
#convert_ret
}
}
}).to_tokens(tokens);
}