mirror of
https://github.com/rustwasm/wasm-bindgen.git
synced 2024-12-26 11:34:22 +03:00
wip
This commit is contained in:
parent
ec1c263480
commit
8f8da49dab
@ -109,6 +109,7 @@ fn extract_program(module: &mut Module) -> shared::Program {
|
||||
structs: Vec::new(),
|
||||
free_functions: Vec::new(),
|
||||
imports: Vec::new(),
|
||||
imported_structs: Vec::new(),
|
||||
};
|
||||
let data = match data {
|
||||
Some(data) => data,
|
||||
@ -126,10 +127,11 @@ fn extract_program(module: &mut Module) -> shared::Program {
|
||||
Ok(f) => f,
|
||||
Err(_) => continue,
|
||||
};
|
||||
let shared::Program { structs, free_functions, imports } = p;
|
||||
let shared::Program { structs, free_functions, imports, imported_structs } = p;
|
||||
ret.structs.extend(structs);
|
||||
ret.free_functions.extend(free_functions);
|
||||
ret.imports.extend(imports);
|
||||
ret.imported_structs.extend(imported_structs);
|
||||
}
|
||||
data.entries_mut().remove(i);
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ pub struct Program {
|
||||
pub structs: Vec<Struct>,
|
||||
pub free_functions: Vec<Function>,
|
||||
pub imports: Vec<Import>,
|
||||
pub imported_structs: Vec<ImportStruct>,
|
||||
}
|
||||
|
||||
pub struct Function {
|
||||
@ -16,11 +17,21 @@ pub struct Function {
|
||||
|
||||
pub struct Import {
|
||||
pub module: String,
|
||||
pub function: Function,
|
||||
pub decl: Box<syn::FnDecl>,
|
||||
pub function: ImportFunction,
|
||||
}
|
||||
|
||||
pub struct ImportFunction {
|
||||
pub ident: syn::Ident,
|
||||
pub vis: syn::Visibility,
|
||||
pub attrs: Vec<syn::Attribute>,
|
||||
pub wasm_function: Function,
|
||||
pub rust_decl: Box<syn::FnDecl>,
|
||||
pub rust_vis: syn::Visibility,
|
||||
pub rust_attrs: Vec<syn::Attribute>,
|
||||
}
|
||||
|
||||
pub struct ImportStruct {
|
||||
pub module: Option<String>,
|
||||
pub name: syn::Ident,
|
||||
pub functions: Vec<(bool, ImportFunction)>,
|
||||
}
|
||||
|
||||
pub enum Type {
|
||||
@ -103,23 +114,51 @@ impl Program {
|
||||
})
|
||||
.expect("must specify `#[wasm_module = ...]` for module to import from");
|
||||
for item in f.items.iter() {
|
||||
self.push_foreign_item(&module, item);
|
||||
let import = self.gen_foreign_item(item, false).0;
|
||||
self.imports.push(Import {
|
||||
module: module.clone(),
|
||||
function: import,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_foreign_item(&mut self, module: &str, f: &syn::ForeignItem) {
|
||||
pub fn gen_foreign_item(&mut self,
|
||||
f: &syn::ForeignItem,
|
||||
allow_self: bool) -> (ImportFunction, bool) {
|
||||
let f = match *f {
|
||||
syn::ForeignItem::Fn(ref f) => f,
|
||||
_ => panic!("only foreign functions allowed for now, not statics"),
|
||||
};
|
||||
|
||||
self.imports.push(Import {
|
||||
module: module.to_string(),
|
||||
attrs: f.attrs.clone(),
|
||||
vis: f.vis.clone(),
|
||||
decl: f.decl.clone(),
|
||||
let (wasm, mutable) = Function::from_decl(f.ident, &f.decl, allow_self);
|
||||
let is_method = match mutable {
|
||||
Some(false) => true,
|
||||
None => false,
|
||||
Some(true) => {
|
||||
panic!("mutable self methods not allowed in extern structs");
|
||||
}
|
||||
};
|
||||
|
||||
(ImportFunction {
|
||||
rust_attrs: f.attrs.clone(),
|
||||
rust_vis: f.vis.clone(),
|
||||
rust_decl: f.decl.clone(),
|
||||
ident: f.ident.clone(),
|
||||
function: Function::from_decl(f.ident, &f.decl),
|
||||
wasm_function: wasm,
|
||||
}, is_method)
|
||||
}
|
||||
|
||||
pub fn push_extern_class(&mut self, class: &ExternClass) {
|
||||
let functions = class.functions.iter()
|
||||
.map(|f| {
|
||||
let (f, method) = self.gen_foreign_item(f, true);
|
||||
(method, f)
|
||||
})
|
||||
.collect();
|
||||
self.imported_structs.push(ImportStruct {
|
||||
module: class.module.as_ref().map(|s| s.value()),
|
||||
name: class.name,
|
||||
functions,
|
||||
});
|
||||
}
|
||||
|
||||
@ -128,7 +167,10 @@ impl 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.module.clone(), i.function.shared()))
|
||||
.map(|i| (i.module.clone(), i.function.wasm_function.shared()))
|
||||
.collect(),
|
||||
imported_structs: self.imported_structs.iter()
|
||||
.map(|i| i.shared())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
@ -153,10 +195,12 @@ impl Function {
|
||||
panic!("can only bindgen Rust ABI functions")
|
||||
}
|
||||
|
||||
Function::from_decl(input.ident, &input.decl)
|
||||
Function::from_decl(input.ident, &input.decl, false).0
|
||||
}
|
||||
|
||||
pub fn from_decl(name: syn::Ident, decl: &syn::FnDecl) -> Function {
|
||||
pub fn from_decl(name: syn::Ident,
|
||||
decl: &syn::FnDecl,
|
||||
allow_self: bool) -> (Function, Option<bool>) {
|
||||
if decl.variadic.is_some() {
|
||||
panic!("can't bindgen variadic functions")
|
||||
}
|
||||
@ -164,10 +208,19 @@ impl Function {
|
||||
panic!("can't bindgen functions with lifetime or type parameters")
|
||||
}
|
||||
|
||||
let mut mutable = None;
|
||||
let arguments = decl.inputs.iter()
|
||||
.map(|arg| {
|
||||
.filter_map(|arg| {
|
||||
match *arg {
|
||||
syn::FnArg::Captured(ref c) => c,
|
||||
syn::FnArg::Captured(ref c) => Some(c),
|
||||
syn::FnArg::SelfValue(_) => {
|
||||
panic!("by-value `self` not yet supported");
|
||||
}
|
||||
syn::FnArg::SelfRef(ref a) if allow_self => {
|
||||
assert!(mutable.is_none());
|
||||
mutable = Some(a.mutability.is_some());
|
||||
None
|
||||
}
|
||||
_ => panic!("arguments cannot be `self` or ignored"),
|
||||
}
|
||||
})
|
||||
@ -179,7 +232,7 @@ impl Function {
|
||||
syn::ReturnType::Type(_, ref t) => Some(Type::from(t)),
|
||||
};
|
||||
|
||||
Function { name, arguments, ret }
|
||||
(Function { name, arguments, ret }, mutable)
|
||||
}
|
||||
|
||||
pub fn free_function_export_name(&self) -> syn::LitStr {
|
||||
@ -346,38 +399,9 @@ impl Struct {
|
||||
panic!("can only bindgen safe functions");
|
||||
}
|
||||
|
||||
if method.sig.decl.variadic.is_some() {
|
||||
panic!("can't bindgen variadic functions")
|
||||
}
|
||||
if method.sig.decl.generics.params.len() > 0 {
|
||||
panic!("can't bindgen functions with lifetime or type parameters")
|
||||
}
|
||||
|
||||
let mut mutable = None;
|
||||
let arguments = method.sig.decl.inputs.iter()
|
||||
.filter_map(|arg| {
|
||||
match *arg {
|
||||
syn::FnArg::Captured(ref c) => Some(c),
|
||||
syn::FnArg::SelfValue(_) => {
|
||||
panic!("by-value `self` not yet supported");
|
||||
}
|
||||
syn::FnArg::SelfRef(ref a) => {
|
||||
assert!(mutable.is_none());
|
||||
mutable = Some(a.mutability.is_some());
|
||||
None
|
||||
}
|
||||
_ => panic!("arguments cannot be `self` or ignored"),
|
||||
}
|
||||
})
|
||||
.map(|arg| Type::from(&arg.ty))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let ret = match method.sig.decl.output {
|
||||
syn::ReturnType::Default => None,
|
||||
syn::ReturnType::Type(_, ref t) => Some(Type::from(t)),
|
||||
};
|
||||
|
||||
let function = Function { name: method.sig.ident, arguments, ret };
|
||||
let (function, mutable) = Function::from_decl(method.sig.ident,
|
||||
&method.sig.decl,
|
||||
true);
|
||||
match mutable {
|
||||
Some(mutable) => {
|
||||
self.methods.push(Method { mutable, function });
|
||||
@ -405,3 +429,77 @@ impl Method {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ImportStruct {
|
||||
fn shared(&self) -> shared::ImportStruct {
|
||||
shared::ImportStruct {
|
||||
module: self.module.clone(),
|
||||
name: self.name.to_string(),
|
||||
functions: self.functions.iter()
|
||||
.map(|&(b, ref f)| (b, f.wasm_function.shared()))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct File {
|
||||
pub items: Vec<MyItem>,
|
||||
}
|
||||
|
||||
impl syn::synom::Synom for File {
|
||||
named!(parse -> Self, map!(many0!(syn!(MyItem)), |items| File { items }));
|
||||
}
|
||||
|
||||
pub enum MyItem {
|
||||
Normal(syn::Item),
|
||||
ExternClass(ExternClass),
|
||||
}
|
||||
|
||||
impl syn::synom::Synom for MyItem {
|
||||
named!(parse -> Self, alt!(
|
||||
syn!(syn::Item) => { MyItem::Normal }
|
||||
|
|
||||
syn!(ExternClass) => { MyItem::ExternClass }
|
||||
));
|
||||
}
|
||||
|
||||
pub struct ExternClass {
|
||||
name: syn::Ident,
|
||||
module: Option<syn::LitStr>,
|
||||
functions: Vec<syn::ForeignItem>,
|
||||
}
|
||||
|
||||
impl syn::synom::Synom for ExternClass {
|
||||
named!(parse -> Self, do_parse!(
|
||||
module: option!(do_parse!(
|
||||
punct!(#) >>
|
||||
name: brackets!(do_parse!(
|
||||
call!(term, "wasm_module") >>
|
||||
punct!(=) >>
|
||||
val: syn!(syn::LitStr) >>
|
||||
(val)
|
||||
)) >>
|
||||
(name.1)
|
||||
)) >>
|
||||
keyword!(extern) >>
|
||||
keyword!(struct) >>
|
||||
name: syn!(syn::Ident) >>
|
||||
items: braces!(many0!(syn!(syn::ForeignItem))) >>
|
||||
(ExternClass {
|
||||
name,
|
||||
module,
|
||||
functions: items.1,
|
||||
})
|
||||
));
|
||||
}
|
||||
|
||||
fn term<'a>(cursor: syn::buffer::Cursor<'a>, name: &str)
|
||||
-> syn::synom::PResult<'a, ()>
|
||||
{
|
||||
if let Some((_span, term, next)) = cursor.term() {
|
||||
if term.as_str() == name {
|
||||
return Ok(((), next))
|
||||
}
|
||||
}
|
||||
syn::parse_error()
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#![feature(proc_macro)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
@ -26,7 +27,7 @@ macro_rules! my_quote {
|
||||
#[proc_macro]
|
||||
pub fn wasm_bindgen(input: TokenStream) -> TokenStream {
|
||||
// Parse the input as a list of Rust items, reusing the `syn::File` parser.
|
||||
let file = syn::parse::<syn::File>(input)
|
||||
let file = syn::parse::<ast::File>(input)
|
||||
.expect("expected a set of valid Rust items");
|
||||
|
||||
let mut ret = Tokens::new();
|
||||
@ -35,12 +36,21 @@ pub fn wasm_bindgen(input: TokenStream) -> TokenStream {
|
||||
structs: Vec::new(),
|
||||
free_functions: Vec::new(),
|
||||
imports: Vec::new(),
|
||||
imported_structs: Vec::new(),
|
||||
};
|
||||
|
||||
// Translate all input items into our own internal representation (the `ast`
|
||||
// module). We'll be panicking here on anything that we can't process
|
||||
|
||||
for item in file.items.iter() {
|
||||
let item = match *item {
|
||||
ast::MyItem::ExternClass(ref c) => {
|
||||
program.push_extern_class(c);
|
||||
continue
|
||||
}
|
||||
ast::MyItem::Normal(ref item) => item,
|
||||
};
|
||||
|
||||
match *item {
|
||||
syn::Item::Fn(ref f) => {
|
||||
item.to_tokens(&mut ret);
|
||||
@ -76,6 +86,9 @@ pub fn wasm_bindgen(input: TokenStream) -> TokenStream {
|
||||
for i in program.imports.iter() {
|
||||
bindgen_import(i, &mut ret);
|
||||
}
|
||||
for i in program.imported_structs.iter() {
|
||||
bindgen_imported_struct(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
|
||||
@ -416,18 +429,53 @@ 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;
|
||||
bindgen_import_function(&import.function,
|
||||
&import.function.ident.to_string(),
|
||||
Some(&import.module),
|
||||
tokens);
|
||||
}
|
||||
|
||||
fn bindgen_imported_struct(import: &ast::ImportStruct, tokens: &mut Tokens) {
|
||||
let name = import.name;
|
||||
(my_quote! {
|
||||
pub struct #name {
|
||||
obj: ::wasm_bindgen::JsObject,
|
||||
}
|
||||
}).to_tokens(tokens);
|
||||
|
||||
let mut methods = Tokens::new();
|
||||
|
||||
for &(_is_method, ref f) in import.functions.iter() {
|
||||
let import_name = format!("__wbg_{}_{}", name, f.ident);
|
||||
|
||||
bindgen_import_function(f,
|
||||
&import_name,
|
||||
import.module.as_ref().map(|s| &**s),
|
||||
&mut methods);
|
||||
}
|
||||
|
||||
(my_quote! {
|
||||
impl #name {
|
||||
#methods
|
||||
}
|
||||
}).to_tokens(tokens);
|
||||
}
|
||||
|
||||
fn bindgen_import_function(import: &ast::ImportFunction,
|
||||
import_name: &str,
|
||||
_import_module: Option<&str>,
|
||||
tokens: &mut Tokens) {
|
||||
let vis = &import.rust_vis;
|
||||
let ret = &import.rust_decl.output;
|
||||
let fn_token = &import.rust_decl.fn_token;
|
||||
let arguments = &import.rust_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
|
||||
let names = import.rust_decl.inputs
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
match *arg {
|
||||
@ -449,7 +497,7 @@ fn bindgen_import(import: &ast::Import, tokens: &mut Tokens) {
|
||||
}
|
||||
});
|
||||
|
||||
for (ty, name) in import.function.arguments.iter().zip(names) {
|
||||
for (ty, name) in import.wasm_function.arguments.iter().zip(names) {
|
||||
match *ty {
|
||||
ast::Type::Integer(i) => {
|
||||
abi_argument_names.push(name);
|
||||
@ -507,7 +555,7 @@ fn bindgen_import(import: &ast::Import, tokens: &mut Tokens) {
|
||||
}
|
||||
let abi_ret;
|
||||
let convert_ret;
|
||||
match import.function.ret {
|
||||
match import.wasm_function.ret {
|
||||
Some(ast::Type::Integer(i)) => {
|
||||
abi_ret = my_quote! { #i };
|
||||
convert_ret = my_quote! { #ret_ident };
|
||||
@ -542,14 +590,16 @@ fn bindgen_import(import: &ast::Import, tokens: &mut Tokens) {
|
||||
}
|
||||
}
|
||||
|
||||
let name = import.ident;
|
||||
let import_name = syn::Ident::from(import_name);
|
||||
(quote! {
|
||||
#vis #fn_token #name(#arguments) #ret {
|
||||
extern {
|
||||
fn #name(#(#abi_arguments),*) -> #abi_ret;
|
||||
fn #import_name(#(#abi_arguments),*) -> #abi_ret;
|
||||
}
|
||||
unsafe {
|
||||
#(#arg_conversions)*
|
||||
let #ret_ident = #name(#(#abi_argument_names),*);
|
||||
let #ret_ident = #import_name(#(#abi_argument_names),*);
|
||||
#convert_ret
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ pub struct Program {
|
||||
pub structs: Vec<Struct>,
|
||||
pub free_functions: Vec<Function>,
|
||||
pub imports: Vec<(String, Function)>,
|
||||
pub imported_structs: Vec<ImportStruct>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@ -15,6 +16,13 @@ pub struct Struct {
|
||||
pub methods: Vec<Method>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ImportStruct {
|
||||
pub module: Option<String>,
|
||||
pub name: String,
|
||||
pub functions: Vec<(bool, Function)>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Method {
|
||||
pub mutable: bool,
|
||||
|
31
tests/import-class.rs
Normal file
31
tests/import-class.rs
Normal file
@ -0,0 +1,31 @@
|
||||
extern crate test_support;
|
||||
|
||||
#[test]
|
||||
fn simple() {
|
||||
test_support::project()
|
||||
.file("src/lib.rs", r#"
|
||||
#![feature(proc_macro)]
|
||||
|
||||
extern crate wasm_bindgen;
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
wasm_bindgen! {
|
||||
extern struct Math {
|
||||
fn random() -> f64;
|
||||
}
|
||||
|
||||
pub fn get_random() -> f64 {
|
||||
Math::random()
|
||||
}
|
||||
}
|
||||
"#)
|
||||
.file("test.ts", r#"
|
||||
import * as wasm from "./out";
|
||||
|
||||
export function test() {
|
||||
wasm.get_random();
|
||||
}
|
||||
"#)
|
||||
.test();
|
||||
}
|
Loading…
Reference in New Issue
Block a user